From d3b11bd717f265be5c465b314c7d4f5c7b934d1a Mon Sep 17 00:00:00 2001 From: tillias Date: Sat, 16 Jan 2021 07:49:14 +0100 Subject: [PATCH] Implemented custom role and resource for import from descriptor #124 --- .../security/AuthoritiesConstants.java | 2 + .../service/custom/ImportService.java | 10 ++- .../builder/FullMicroserviceDtoBuilder.java | 83 +++++++++++++++++++ ...esource.java => ImportCustomResource.java} | 17 +++- .../config/liquibase/data/authority.csv | 1 + .../resources/config/liquibase/data/user.csv | 1 + .../config/liquibase/data/user_authority.csv | 1 + .../rest/custom/ImportCustomResourceTest.java | 55 ++++++++++++ 8 files changed, 164 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/github/microcatalog/service/dto/custom/builder/FullMicroserviceDtoBuilder.java rename src/main/java/com/github/microcatalog/web/rest/custom/{ImportResource.java => ImportCustomResource.java} (60%) create mode 100644 src/test/java/com/github/microcatalog/web/rest/custom/ImportCustomResourceTest.java diff --git a/src/main/java/com/github/microcatalog/security/AuthoritiesConstants.java b/src/main/java/com/github/microcatalog/security/AuthoritiesConstants.java index e6b9489..83c2c63 100644 --- a/src/main/java/com/github/microcatalog/security/AuthoritiesConstants.java +++ b/src/main/java/com/github/microcatalog/security/AuthoritiesConstants.java @@ -11,6 +11,8 @@ public final class AuthoritiesConstants { public static final String ANONYMOUS = "ROLE_ANONYMOUS"; + public static final String IMPORT = "ROLE_IMPORT"; + private AuthoritiesConstants() { } } diff --git a/src/main/java/com/github/microcatalog/service/custom/ImportService.java b/src/main/java/com/github/microcatalog/service/custom/ImportService.java index 2a864f7..c4701be 100644 --- a/src/main/java/com/github/microcatalog/service/custom/ImportService.java +++ b/src/main/java/com/github/microcatalog/service/custom/ImportService.java @@ -58,8 +58,7 @@ public Optional importFromDescriptor(final MicroserviceImpo final Microservice persistent = microserviceRepository.findByName(name); if (persistent != null) { - log.warn("Microservice with name {} already exists. Stopping import", name); - return Optional.empty(); + throw new ImportException(String.format("Microservice with name (%s) already exists. Stopping import", name)); } else { final Microservice microservice = persistMicroservice(descriptorDto); importDependencies(microservice, descriptorDto.getDependencies()); @@ -127,6 +126,7 @@ private Microservice persistMicroservice(final MicroserviceImportDescriptorDto d private Status getOrCreateStatus(final String name) { final Status persistent = statusRepository.findByName(name); if (persistent != null) { + log.info("Using existing status {}", name); return persistent; } @@ -137,6 +137,7 @@ private Status getOrCreateStatus(final String name) { private Team getOrCreateTeam(final String name) { final Team persistent = teamRepository.findByName(name); if (persistent != null) { + log.info("Using existing team {}", name); return persistent; } @@ -148,6 +149,11 @@ private Team getOrCreateTeam(final String name) { } private void importDependencies(final Microservice source, final List dependencies) { + if (dependencies == null) { + log.info("Importing microservice without dependencies {}", source); + return; + } + for (MicroserviceImportDescriptorDto d : dependencies) { Microservice target = microserviceRepository.findByName(d.getName()); if (target == null) { diff --git a/src/main/java/com/github/microcatalog/service/dto/custom/builder/FullMicroserviceDtoBuilder.java b/src/main/java/com/github/microcatalog/service/dto/custom/builder/FullMicroserviceDtoBuilder.java new file mode 100644 index 0000000..544ee41 --- /dev/null +++ b/src/main/java/com/github/microcatalog/service/dto/custom/builder/FullMicroserviceDtoBuilder.java @@ -0,0 +1,83 @@ +package com.github.microcatalog.service.dto.custom.builder; + +import com.github.microcatalog.service.dto.custom.FullMicroserviceDto; +import com.github.microcatalog.service.dto.custom.StatusDto; +import com.github.microcatalog.service.dto.custom.TeamDto; + +public final class FullMicroserviceDtoBuilder { + private Long id; + private String name; + private String description; + private String imageUrl; + private String swaggerUrl; + private String gitUrl; + private String ciUrl; + private TeamDto team; + private StatusDto status; + + private FullMicroserviceDtoBuilder() { + } + + public static FullMicroserviceDtoBuilder aFullMicroserviceDto() { + return new FullMicroserviceDtoBuilder(); + } + + public FullMicroserviceDtoBuilder withId(Long id) { + this.id = id; + return this; + } + + public FullMicroserviceDtoBuilder withName(String name) { + this.name = name; + return this; + } + + public FullMicroserviceDtoBuilder withDescription(String description) { + this.description = description; + return this; + } + + public FullMicroserviceDtoBuilder withImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + return this; + } + + public FullMicroserviceDtoBuilder withSwaggerUrl(String swaggerUrl) { + this.swaggerUrl = swaggerUrl; + return this; + } + + public FullMicroserviceDtoBuilder withGitUrl(String gitUrl) { + this.gitUrl = gitUrl; + return this; + } + + public FullMicroserviceDtoBuilder withCiUrl(String ciUrl) { + this.ciUrl = ciUrl; + return this; + } + + public FullMicroserviceDtoBuilder withTeam(TeamDto team) { + this.team = team; + return this; + } + + public FullMicroserviceDtoBuilder withStatus(StatusDto status) { + this.status = status; + return this; + } + + public FullMicroserviceDto build() { + FullMicroserviceDto fullMicroserviceDto = new FullMicroserviceDto(); + fullMicroserviceDto.setId(id); + fullMicroserviceDto.setName(name); + fullMicroserviceDto.setDescription(description); + fullMicroserviceDto.setImageUrl(imageUrl); + fullMicroserviceDto.setSwaggerUrl(swaggerUrl); + fullMicroserviceDto.setGitUrl(gitUrl); + fullMicroserviceDto.setCiUrl(ciUrl); + fullMicroserviceDto.setTeam(team); + fullMicroserviceDto.setStatus(status); + return fullMicroserviceDto; + } +} diff --git a/src/main/java/com/github/microcatalog/web/rest/custom/ImportResource.java b/src/main/java/com/github/microcatalog/web/rest/custom/ImportCustomResource.java similarity index 60% rename from src/main/java/com/github/microcatalog/web/rest/custom/ImportResource.java rename to src/main/java/com/github/microcatalog/web/rest/custom/ImportCustomResource.java index 03b13e3..5279fd3 100644 --- a/src/main/java/com/github/microcatalog/web/rest/custom/ImportResource.java +++ b/src/main/java/com/github/microcatalog/web/rest/custom/ImportCustomResource.java @@ -1,27 +1,36 @@ package com.github.microcatalog.web.rest.custom; +import com.github.microcatalog.security.AuthoritiesConstants; import com.github.microcatalog.service.custom.ImportService; import com.github.microcatalog.service.dto.custom.FullMicroserviceDto; import com.github.microcatalog.service.dto.custom.MicroserviceImportDescriptorDto; import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** - * REST controller for importing {@link com.github.microcatalog.domain.Microservice} with {@link com.github.microcatalog.domain.Dependency} + * REST controller for importing {@link com.github.microcatalog.domain.Microservice} and creating it's dependencies */ @RestController @RequestMapping("/api") -public class ImportResource { +public class ImportCustomResource { private final ImportService importService; - public ImportResource(ImportService importService) { + public ImportCustomResource(ImportService importService) { this.importService = importService; } - @PostMapping + /** + * {@code POST /import : import microservices with it's dependencies + * + * @param descriptor describes microservice to be imported and it's dependent microservices + * @return microservice (without dependencies) if created + */ + @PostMapping("/import") + @PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.IMPORT + "\")") public ResponseEntity importMicroservice(@RequestBody MicroserviceImportDescriptorDto descriptor) { return ResponseEntity.of(importService.importFromDescriptor(descriptor)); } diff --git a/src/main/resources/config/liquibase/data/authority.csv b/src/main/resources/config/liquibase/data/authority.csv index af5c6df..4bf8074 100644 --- a/src/main/resources/config/liquibase/data/authority.csv +++ b/src/main/resources/config/liquibase/data/authority.csv @@ -1,3 +1,4 @@ name ROLE_ADMIN ROLE_USER +ROLE_IMPORT diff --git a/src/main/resources/config/liquibase/data/user.csv b/src/main/resources/config/liquibase/data/user.csv index b25922b..c98ab15 100644 --- a/src/main/resources/config/liquibase/data/user.csv +++ b/src/main/resources/config/liquibase/data/user.csv @@ -3,3 +3,4 @@ id;login;password_hash;first_name;last_name;email;image_url;activated;lang_key;c 2;anonymoususer;$2a$10$j8S5d7Sr7.8VTOYNviDPOeWX8KcYILUVJBsYV83Y5NtECayypx9lO;Anonymous;User;anonymous@localhost;;true;en;system;system 3;admin;$2a$10$gSAhZrxMllrbgj/kkK9UceBPpChGWJA7SYIb1Mqo.n5aNLq1/oRrC;Administrator;Administrator;admin@localhost;;true;en;system;system 4;user;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;User;User;user@localhost;;true;en;system;system +5;import;$2a$10$kaYVCI89dXNlKdD2.x/9yu/.IlCyQMAjqB4o998EAMckI2f/zJuLK;Import;Import;import@localhost;;true;en;system;system diff --git a/src/main/resources/config/liquibase/data/user_authority.csv b/src/main/resources/config/liquibase/data/user_authority.csv index 06c5fee..ae7a85c 100644 --- a/src/main/resources/config/liquibase/data/user_authority.csv +++ b/src/main/resources/config/liquibase/data/user_authority.csv @@ -4,3 +4,4 @@ user_id;authority_name 3;ROLE_ADMIN 3;ROLE_USER 4;ROLE_USER +5;ROLE_IMPORT diff --git a/src/test/java/com/github/microcatalog/web/rest/custom/ImportCustomResourceTest.java b/src/test/java/com/github/microcatalog/web/rest/custom/ImportCustomResourceTest.java new file mode 100644 index 0000000..568dc0d --- /dev/null +++ b/src/test/java/com/github/microcatalog/web/rest/custom/ImportCustomResourceTest.java @@ -0,0 +1,55 @@ +package com.github.microcatalog.web.rest.custom; + +import com.github.microcatalog.MockMvcWithUser; +import com.github.microcatalog.service.custom.ImportService; +import com.github.microcatalog.service.custom.exceptions.ImportException; +import com.github.microcatalog.service.dto.custom.MicroserviceImportDescriptorDto; +import com.github.microcatalog.web.rest.TestUtil; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.Optional; + +import static com.github.microcatalog.service.dto.custom.builder.FullMicroserviceDtoBuilder.aFullMicroserviceDto; +import static com.github.microcatalog.service.dto.custom.builder.MicroserviceImportDescriptorDtoBuilder.aMicroserviceImportDescriptorDto; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@SpringBootTest(classes = ImportCustomResource.class) +@MockMvcWithUser +class ImportCustomResourceTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private ImportService service; + + @Test + void import_MissingName_ReturnsImportException() throws Exception { + + final MicroserviceImportDescriptorDto request = aMicroserviceImportDescriptorDto() + .withName("Import") + .build(); + + given(service.importFromDescriptor(any())).willReturn(Optional.of(aFullMicroserviceDto() + .withId(3L) + .build() + )); + + mockMvc.perform(post("/api/import") + .with(csrf()) + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(request))) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.id").value(3L)); + } +}