diff --git a/src/main/java/auth_service/modules/user/api/dto/request/AppearanceRequest.java b/src/main/java/auth_service/modules/user/api/dto/request/AppearanceRequest.java index 83b9dec..cfa82d7 100644 --- a/src/main/java/auth_service/modules/user/api/dto/request/AppearanceRequest.java +++ b/src/main/java/auth_service/modules/user/api/dto/request/AppearanceRequest.java @@ -11,5 +11,7 @@ public record AppearanceRequest( @NotNull @Pattern(regexp = "violet|blue|green|orange|pink|red") String accent, @NotNull @Pattern(regexp = "sharp|default|round") String corners, Boolean wideLayout, - @Positive Long backgroundAppid) { + @Positive Long backgroundAppid, + @Positive Long bannerAppid, + Boolean showBanner) { } diff --git a/src/main/java/auth_service/modules/user/api/dto/response/AppearanceResponse.java b/src/main/java/auth_service/modules/user/api/dto/response/AppearanceResponse.java index 6cc5cc3..93d53de 100644 --- a/src/main/java/auth_service/modules/user/api/dto/response/AppearanceResponse.java +++ b/src/main/java/auth_service/modules/user/api/dto/response/AppearanceResponse.java @@ -1,4 +1,5 @@ package auth_service.modules.user.api.dto.response; -public record AppearanceResponse(String accent, String corners, boolean wideLayout, Long backgroundAppid) { +public record AppearanceResponse(String accent, String corners, boolean wideLayout, Long backgroundAppid, + Long bannerAppid, boolean showBanner) { } diff --git a/src/main/java/auth_service/modules/user/api/mapper/ApiMapper.java b/src/main/java/auth_service/modules/user/api/mapper/ApiMapper.java index a67d1b4..4650567 100644 --- a/src/main/java/auth_service/modules/user/api/mapper/ApiMapper.java +++ b/src/main/java/auth_service/modules/user/api/mapper/ApiMapper.java @@ -35,12 +35,13 @@ public abstract class ApiMapper { private static AppearanceResponse toApi(Appearance appearance) { return new AppearanceResponse(appearance.accent(), appearance.corners(), appearance.wideLayout(), - appearance.backgroundAppid()); + appearance.backgroundAppid(), appearance.bannerAppid(), appearance.showBanner()); } public static Appearance toDomain(AppearanceRequest request) { + // A missing showBanner keeps the banner on, the default. return new Appearance(request.accent(), request.corners(), Boolean.TRUE.equals(request.wideLayout()), - request.backgroundAppid()); + request.backgroundAppid(), request.bannerAppid(), !Boolean.FALSE.equals(request.showBanner())); } public static Long toSteamId(UserSettingsRequest request) { diff --git a/src/main/java/auth_service/modules/user/model/Appearance.java b/src/main/java/auth_service/modules/user/model/Appearance.java index 1f4ab93..ca3bde9 100644 --- a/src/main/java/auth_service/modules/user/model/Appearance.java +++ b/src/main/java/auth_service/modules/user/model/Appearance.java @@ -2,8 +2,11 @@ package auth_service.modules.user.model; /** * @param backgroundAppid the Steam app whose art fills the page background; null for none + * @param bannerAppid the Steam app whose art is the dashboard banner; null for the most played game of the last + * two weeks */ -public record Appearance(String accent, String corners, boolean wideLayout, Long backgroundAppid) { +public record Appearance(String accent, String corners, boolean wideLayout, Long backgroundAppid, + Long bannerAppid, boolean showBanner) { - public static final Appearance DEFAULT = new Appearance("violet", "default", false, null); + public static final Appearance DEFAULT = new Appearance("violet", "default", false, null, null, true); } diff --git a/src/main/java/auth_service/modules/user/persistence/UserRepositoryImpl.java b/src/main/java/auth_service/modules/user/persistence/UserRepositoryImpl.java index cd00063..4931e8f 100644 --- a/src/main/java/auth_service/modules/user/persistence/UserRepositoryImpl.java +++ b/src/main/java/auth_service/modules/user/persistence/UserRepositoryImpl.java @@ -109,6 +109,8 @@ public class UserRepositoryImpl implements UserRepository { settingsEntity.setCorners(appearance.corners()); settingsEntity.setWideLayout(appearance.wideLayout()); settingsEntity.setBackgroundAppid(appearance.backgroundAppid()); + settingsEntity.setBannerAppid(appearance.bannerAppid()); + settingsEntity.setShowBanner(appearance.showBanner()); } @Override diff --git a/src/main/java/auth_service/modules/user/persistence/entities/UserSettingsEntity.java b/src/main/java/auth_service/modules/user/persistence/entities/UserSettingsEntity.java index 2240516..0663f73 100644 --- a/src/main/java/auth_service/modules/user/persistence/entities/UserSettingsEntity.java +++ b/src/main/java/auth_service/modules/user/persistence/entities/UserSettingsEntity.java @@ -40,4 +40,10 @@ public class UserSettingsEntity { @Column(name = "background_appid") private Long backgroundAppid; + + @Column(name = "banner_appid") + private Long bannerAppid; + + @Column(name = "show_banner", nullable = false) + private boolean showBanner = Appearance.DEFAULT.showBanner(); } diff --git a/src/main/java/auth_service/modules/user/persistence/mapper/EntityMapper.java b/src/main/java/auth_service/modules/user/persistence/mapper/EntityMapper.java index 4d44b49..2e939c5 100644 --- a/src/main/java/auth_service/modules/user/persistence/mapper/EntityMapper.java +++ b/src/main/java/auth_service/modules/user/persistence/mapper/EntityMapper.java @@ -24,7 +24,8 @@ public abstract class EntityMapper { .theme(entity.getSettings().getTheme()) .publicProfile(entity.getSettings().isPublicProfile()) .appearance(new Appearance(entity.getSettings().getAccent(), entity.getSettings().getCorners(), - entity.getSettings().isWideLayout(), entity.getSettings().getBackgroundAppid())) + entity.getSettings().isWideLayout(), entity.getSettings().getBackgroundAppid(), + entity.getSettings().getBannerAppid(), entity.getSettings().isShowBanner())) .build(); } else { // Fallback default settings for users without an entry in the database diff --git a/src/main/resources/db/migration/V9__Add_Banner.sql b/src/main/resources/db/migration/V9__Add_Banner.sql new file mode 100644 index 0000000..cdd4345 --- /dev/null +++ b/src/main/resources/db/migration/V9__Add_Banner.sql @@ -0,0 +1,4 @@ +-- The dashboard banner: a picked game, or the most played game of the last two weeks when NULL. +ALTER TABLE user_settings + ADD COLUMN banner_appid BIGINT, + ADD COLUMN show_banner BOOLEAN NOT NULL DEFAULT TRUE; diff --git a/src/test/java/auth_service/modules/user/api/UserControllerTest.java b/src/test/java/auth_service/modules/user/api/UserControllerTest.java index 5394b79..4bf85e1 100644 --- a/src/test/java/auth_service/modules/user/api/UserControllerTest.java +++ b/src/test/java/auth_service/modules/user/api/UserControllerTest.java @@ -407,7 +407,7 @@ class UserControllerTest extends AbstractWebMvcTest { void getCurrentUserV1_ShowsAppearance() throws Exception { var email = "test@example.com"; var settings = UserSettings.builder().language("en").theme("dark") - .appearance(new Appearance("blue", "round", true, 620L)).build(); + .appearance(new Appearance("blue", "round", true, 620L, 1086940L, false)).build(); SecurityContextHolder.getContext().setAuthentication( new UsernamePasswordAuthenticationToken(new UserPrincipal("1", email, null), null, Collections.emptyList()) ); @@ -418,7 +418,9 @@ class UserControllerTest extends AbstractWebMvcTest { .andExpect(jsonPath("$.settings.appearance.accent").value("blue")) .andExpect(jsonPath("$.settings.appearance.corners").value("round")) .andExpect(jsonPath("$.settings.appearance.wideLayout").value(true)) - .andExpect(jsonPath("$.settings.appearance.backgroundAppid").value(620)); + .andExpect(jsonPath("$.settings.appearance.backgroundAppid").value(620)) + .andExpect(jsonPath("$.settings.appearance.bannerAppid").value(1086940)) + .andExpect(jsonPath("$.settings.appearance.showBanner").value(false)); } @Test @@ -435,7 +437,9 @@ class UserControllerTest extends AbstractWebMvcTest { .andExpect(jsonPath("$.settings.appearance.accent").value("violet")) .andExpect(jsonPath("$.settings.appearance.corners").value("default")) .andExpect(jsonPath("$.settings.appearance.wideLayout").value(false)) - .andExpect(jsonPath("$.settings.appearance.backgroundAppid").isEmpty()); + .andExpect(jsonPath("$.settings.appearance.backgroundAppid").isEmpty()) + .andExpect(jsonPath("$.settings.appearance.bannerAppid").isEmpty()) + .andExpect(jsonPath("$.settings.appearance.showBanner").value(true)); } @Test @@ -445,14 +449,15 @@ class UserControllerTest extends AbstractWebMvcTest { mockMvc.perform(put("/api/users/settings/appearance") .contentType(MediaType.APPLICATION_JSON) - .content("{\"accent\":\"green\",\"corners\":\"sharp\",\"wideLayout\":true,\"backgroundAppid\":620}")) + .content("{\"accent\":\"green\",\"corners\":\"sharp\",\"wideLayout\":true,\"backgroundAppid\":620," + + "\"bannerAppid\":1086940,\"showBanner\":false}")) .andExpect(status().isNoContent()); - verify(userService).updateAppearance(1L, new Appearance("green", "sharp", true, 620L)); + verify(userService).updateAppearance(1L, new Appearance("green", "sharp", true, 620L, 1086940L, false)); } @Test - void updateAppearance_MissingWideLayoutAndBackground_AreOffAndNone() throws Exception { + void updateAppearance_MissingOptionalFields_AreTheDefaults() throws Exception { SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken( new UserPrincipal("1", "test@example.com", null), null, Collections.emptyList())); @@ -461,7 +466,7 @@ class UserControllerTest extends AbstractWebMvcTest { .content("{\"accent\":\"violet\",\"corners\":\"default\"}")) .andExpect(status().isNoContent()); - verify(userService).updateAppearance(1L, new Appearance("violet", "default", false, null)); + verify(userService).updateAppearance(1L, new Appearance("violet", "default", false, null, null, true)); } // Each body differs from a valid one in one field only, so the 400 comes from that field's constraint. @@ -473,7 +478,8 @@ class UserControllerTest extends AbstractWebMvcTest { "{\"accent\":\"violet\",\"corners\":\"pointy\"}", "{\"accent\":\"violet\"}", "{\"accent\":\"violet\",\"corners\":\"default\",\"backgroundAppid\":0}", - "{\"accent\":\"violet\",\"corners\":\"default\",\"backgroundAppid\":-620}" + "{\"accent\":\"violet\",\"corners\":\"default\",\"backgroundAppid\":-620}", + "{\"accent\":\"violet\",\"corners\":\"default\",\"bannerAppid\":0}" }) void updateAppearance_UnknownOrMissingValue_IsRejected(String body) throws Exception { SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken( diff --git a/src/test/java/auth_service/modules/user/persistence/UserRepositoryTest.java b/src/test/java/auth_service/modules/user/persistence/UserRepositoryTest.java index 8c45b5a..08d2db5 100644 --- a/src/test/java/auth_service/modules/user/persistence/UserRepositoryTest.java +++ b/src/test/java/auth_service/modules/user/persistence/UserRepositoryTest.java @@ -210,12 +210,12 @@ class UserRepositoryTest extends AbstractPersistenceTest { .build()); entityManager.flush(); - userRepository.updateAppearance(userEntity.getId(), new Appearance("orange", "round", true, 620L)); + userRepository.updateAppearance(userEntity.getId(), new Appearance("orange", "round", true, 620L, 1086940L, false)); entityManager.flush(); entityManager.clear(); var settings = userRepository.findByEmail("appearance@example.com").orElseThrow().getSettings(); - assertEquals(new Appearance("orange", "round", true, 620L), settings.getAppearance()); + assertEquals(new Appearance("orange", "round", true, 620L, 1086940L, false), settings.getAppearance()); } @Test @@ -227,7 +227,7 @@ class UserRepositoryTest extends AbstractPersistenceTest { .registrationDate(LocalDateTime.now()) .build()); entityManager.flush(); - userRepository.updateAppearance(userEntity.getId(), new Appearance("pink", "sharp", false, 220L)); + userRepository.updateAppearance(userEntity.getId(), new Appearance("pink", "sharp", false, 220L, null, true)); entityManager.flush(); userRepository.updateSettings(userEntity.getId(), null, null, @@ -236,7 +236,7 @@ class UserRepositoryTest extends AbstractPersistenceTest { entityManager.clear(); var settings = userRepository.findByEmail("keep@example.com").orElseThrow().getSettings(); - assertEquals(new Appearance("pink", "sharp", false, 220L), settings.getAppearance()); + assertEquals(new Appearance("pink", "sharp", false, 220L, null, true), settings.getAppearance()); } @Test diff --git a/src/test/java/auth_service/modules/user/service/UserServiceTest.java b/src/test/java/auth_service/modules/user/service/UserServiceTest.java index ffb2646..f6e89fb 100644 --- a/src/test/java/auth_service/modules/user/service/UserServiceTest.java +++ b/src/test/java/auth_service/modules/user/service/UserServiceTest.java @@ -135,7 +135,7 @@ class UserServiceTest extends AbstractUnitTest { @Test void updateAppearance_StoresItWithoutANewToken() { - var appearance = new Appearance("red", "round", false, null); + var appearance = new Appearance("red", "round", false, null, null, true); userService.updateAppearance(1L, appearance);