diff --git a/pom.xml b/pom.xml index 1d4c685ba2befeb87f4d6114af6dd676ccaa7bd6..54fd4499067e093ab5cb2c63781e84b544b4cf7d 100644 --- a/pom.xml +++ b/pom.xml @@ -38,6 +38,10 @@ <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-rest</artifactId> </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-hateoas</artifactId> + </dependency> <!-- Dev --> <dependency> diff --git a/src/main/java/de/ozgcloud/admin/RootController.java b/src/main/java/de/ozgcloud/admin/RootController.java index bf17388b3274c557be524e5dc1d48e8dc347a7b2..9e7ffb3bf77b72a1982d32fb073b9b98a8e52b05 100644 --- a/src/main/java/de/ozgcloud/admin/RootController.java +++ b/src/main/java/de/ozgcloud/admin/RootController.java @@ -1,6 +1,7 @@ package de.ozgcloud.admin; import org.springframework.boot.info.BuildProperties; +import org.springframework.hateoas.EntityModel; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -15,9 +16,11 @@ public class RootController { private final BuildProperties buildProperties; + private final RootModelAssembler modelAssembler; + @GetMapping - public Root getRoot() { - return buildRoot(); + public EntityModel<Root> getRoot() { + return modelAssembler.toModel(buildRoot()); } private Root buildRoot() { diff --git a/src/main/java/de/ozgcloud/admin/RootModelAssembler.java b/src/main/java/de/ozgcloud/admin/RootModelAssembler.java new file mode 100644 index 0000000000000000000000000000000000000000..3be41d9440def3d4e8496be1fe46bfb3a41d16ce --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/RootModelAssembler.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch das Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin; + +import org.springframework.boot.autoconfigure.data.rest.RepositoryRestProperties; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.server.RepresentationModelAssembler; +import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; +import org.springframework.stereotype.Component; + +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class RootModelAssembler implements RepresentationModelAssembler<Root, EntityModel<Root>> { + static final String REL_CONFIGURATION = "configuration"; + + private final RepositoryRestProperties restProperties; + + @Override + public EntityModel<Root> toModel(Root root) { + var rootLink = WebMvcLinkBuilder.linkTo(RootController.class); + var configLink = rootLink.toUriComponentsBuilder().replacePath(restProperties.getBasePath()); + return EntityModel.of( + root, + Link.of(configLink.toUriString(), REL_CONFIGURATION), + rootLink.withSelfRel() + ); + } +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 2db35bd1d7794fd943b4010ef35243d1a6077cac..fcfef4c5427332d98f45e4818fb3213f93d86825 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,4 +1,4 @@ spring: data: rest: - basePath: /api \ No newline at end of file + basePath: /api/config \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/admin/ApiRootITCase.java b/src/test/java/de/ozgcloud/admin/ApiRootITCase.java index 209eb0aa10a2d61f67a9433f507e3c2f509ad5ca..e03bccde53f394486abc739711a5b86f04d1753b 100644 --- a/src/test/java/de/ozgcloud/admin/ApiRootITCase.java +++ b/src/test/java/de/ozgcloud/admin/ApiRootITCase.java @@ -53,7 +53,7 @@ class ApiRootITCase { void shouldBetSetToApi() { var basePath = restProperties.getBasePath(); - assertEquals("/api", basePath); + assertEquals("/api/config", basePath); } @Test diff --git a/src/test/java/de/ozgcloud/admin/RootControllerTest.java b/src/test/java/de/ozgcloud/admin/RootControllerTest.java index f2e35aae3631b6179a8c2941c3524296fcfa6b8e..eb76ca0e3a670b5315f6f10980abe4cdac94186b 100644 --- a/src/test/java/de/ozgcloud/admin/RootControllerTest.java +++ b/src/test/java/de/ozgcloud/admin/RootControllerTest.java @@ -14,6 +14,7 @@ import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.boot.info.BuildProperties; +import org.springframework.hateoas.EntityModel; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.setup.MockMvcBuilders; @@ -30,10 +31,14 @@ class RootControllerTest { @Mock private BuildProperties buildProperties; + @Mock + private RootModelAssembler modelAssembler; + private MockMvc mockMvc; @BeforeEach void mock() { + when(modelAssembler.toModel(any())).thenAnswer(a -> EntityModel.of(a.getArgument(0))); mockMvc = MockMvcBuilders.standaloneSetup(rootController).build(); } diff --git a/src/test/java/de/ozgcloud/admin/RootModelAssemblerTest.java b/src/test/java/de/ozgcloud/admin/RootModelAssemblerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9f9e6d6ccbff89663e7476e9611602b1da68bf22 --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/RootModelAssemblerTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch das Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin; + +import static de.ozgcloud.admin.RootModelAssembler.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.boot.autoconfigure.data.rest.RepositoryRestProperties; +import org.springframework.hateoas.IanaLinkRelations; +import org.springframework.hateoas.Link; + +@ExtendWith(MockitoExtension.class) +class RootModelAssemblerTest { + + private static final String BASE_PATH = "/api/base"; + @Spy + @InjectMocks + private RootModelAssembler modelAssembler; + + @Mock + private RepositoryRestProperties restProperties; + + @BeforeEach + void mockBasePath() { + when(restProperties.getBasePath()).thenReturn(BASE_PATH); + } + + @Nested + class TestLinks { + + @DisplayName("should have href to Spring Data REST base path") + @Test + void shouldHaveConfigBaseLink() { + var root = RootTestFactory.create(); + + assertEquals(Optional.of(Link.of(BASE_PATH, REL_CONFIGURATION)), modelAssembler.toModel(root).getLink(REL_CONFIGURATION)); + } + + @DisplayName("should have href to self") + @Test + void shouldHaveSelfLink() { + var root = RootTestFactory.create(); + + assertEquals(Optional.of(Link.of(RootController.PATH)), modelAssembler.toModel(root).getLink(IanaLinkRelations.SELF)); + } + + } + +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/admin/RootTestFactory.java b/src/test/java/de/ozgcloud/admin/RootTestFactory.java index e0879c7530926dbbe52514acab93f2bf3cf63820..4c78550e2495d1307ddaefc3cee0c7a61bb2c6ab 100644 --- a/src/test/java/de/ozgcloud/admin/RootTestFactory.java +++ b/src/test/java/de/ozgcloud/admin/RootTestFactory.java @@ -10,5 +10,16 @@ public class RootTestFactory { public static final String JAVA_VERSION = "1"; public static final String BUILD_VERSION = "2"; public static final String BUILD_NUMBER = "3"; - + + public static Root create() { + return createBuilder().build(); + } + + public static Root.RootBuilder createBuilder() { + return Root.builder() + .buildTime(BUILD_TIME) + .javaVersion(JAVA_VERSION) + .buildVersion(BUILD_VERSION) + .buildNumber(BUILD_NUMBER); + } }