Commit 70b43658 authored by femiadeyemi's avatar femiadeyemi
Browse files

Cerebrum-API: refactor the controllers and make code DRY

Motivation:

The Cerebrum API controllers have the same structure and design
implementation. This leads to quite a number of code dublications.

Modification:

- decouple the service code from the controllers and create a
    service per controller
- create an inteface for the cerebrum's controllers service
- create a common base class for all the services.
- adjust the controller to use its corresponding service
- add tests to check the unchecked part of the base service class
- create a new method called checkUuidValidity to the
    CerebrumEntityUuidGenerator class. This method will throw
    an exception if uuid is invalid.

Result:

Reduce dublication from 1.7% to 0%  and increase code maintainability.

Target: master
Review-at: https://gitlab.hzdr.de/hifis-technical-platform/helmholtz-cerebrum/-/merge_requests/28
parent c6956a9e
......@@ -19,7 +19,8 @@ public class CerebrumConfig
ClientHttpConnector clientConnector = new JettyClientHttpConnector(httpClient);
@Bean
public WebClient authorisationServer() {
public WebClient authorisationServer()
{
return WebClient.builder()
.filter(new ServletBearerExchangeFilterFunction())
.clientConnector(clientConnector)
......
......@@ -26,22 +26,16 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import javax.validation.Valid;
import javax.validation.constraints.Min;
import java.net.URI;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import de.helmholtz.marketplace.cerebrum.entities.MarketService;
import de.helmholtz.marketplace.cerebrum.errorhandling.CerebrumApiError;
import de.helmholtz.marketplace.cerebrum.errorhandling.exception.CerebrumEntityNotFoundException;
import de.helmholtz.marketplace.cerebrum.errorhandling.exception.CerebrumInvalidUuidException;
import de.helmholtz.marketplace.cerebrum.repository.MarketServiceRepository;
import de.helmholtz.marketplace.cerebrum.service.MarketServiceService;
import de.helmholtz.marketplace.cerebrum.utils.CerebrumControllerUtilities;
import de.helmholtz.marketplace.cerebrum.utils.CerebrumEntityUuidGenerator;
@RestController
@Validated
......@@ -49,10 +43,10 @@ import de.helmholtz.marketplace.cerebrum.utils.CerebrumEntityUuidGenerator;
@Tag(name = "services", description = "The API of the Service")
public class MarketServiceController
{
private final MarketServiceRepository marketServiceRepository;
private final MarketServiceService marketServiceService;
public MarketServiceController(MarketServiceRepository marketServiceRepository) {
this.marketServiceRepository = marketServiceRepository;
public MarketServiceController(MarketServiceService marketServiceService) {
this.marketServiceService = marketServiceService;
}
/* get Services */
......@@ -74,7 +68,7 @@ public class MarketServiceController
"name property; the value will be set to name.asc")
@RequestParam(value = "sort", defaultValue = "name.asc") List<String> sorts)
{
return marketServiceRepository.findAll(
return marketServiceService.getServices(
PageRequest.of(page, size, Sort.by(CerebrumControllerUtilities.getOrders(sorts))));
}
......@@ -93,12 +87,7 @@ public class MarketServiceController
@Parameter(description = "UUID of the service that needs to be fetched")
@PathVariable() String uuid)
{
if (Boolean.TRUE.equals(CerebrumEntityUuidGenerator.isValid(uuid))) {
return marketServiceRepository
.findByUuid(uuid)
.orElseThrow(() -> new CerebrumEntityNotFoundException("service", uuid));
}
else throw new CerebrumInvalidUuidException(uuid);
return marketServiceService.getUser(uuid);
}
/* create Service */
......@@ -118,11 +107,7 @@ public class MarketServiceController
required = true, schema = @Schema(implementation = MarketService.class))
@Valid @RequestBody MarketService marketService, UriComponentsBuilder uriComponentsBuilder)
{
MarketService service = marketServiceRepository.save(marketService);
UriComponents uriComponents =
uriComponentsBuilder.path("/api/v0/services/{id}").buildAndExpand(service.getUuid());
URI location = uriComponents.toUri();
return ResponseEntity.created(location).body(service);
return marketServiceService.createService(marketService, uriComponentsBuilder);
}
/* update Service */
......@@ -148,35 +133,7 @@ public class MarketServiceController
@Parameter(description = "UUID of the service that needs to be updated")
@PathVariable() String uuid, UriComponentsBuilder uriComponentsBuilder)
{
if (Boolean.TRUE.equals(CerebrumEntityUuidGenerator.isValid(uuid))) {
AtomicBoolean isCreated = new AtomicBoolean(false);
MarketService service = marketServiceRepository.findByUuid(uuid)
.map(svc -> {
svc.setUrl(marketService.getUrl());
svc.setOrganizations(marketService.getOrganizations());
svc.setName(marketService.getName());
svc.setLifecycleStatus(marketService.getLifecycleStatus());
svc.setAuthentication(marketService.getAuthentication());
svc.setDescription(marketService.getDescription());
svc.setCreated(marketService.getCreated());
svc.setLastModified(marketService.getLastModified());
return marketServiceRepository.save(svc);
})
.orElseGet(() -> {
marketService.setUuid(uuid);
isCreated.set(true);
return marketServiceRepository.save(marketService);
});
if (isCreated.get()) {
UriComponents uriComponents =
uriComponentsBuilder.path("/api/v0/services/{id}").buildAndExpand(service.getUuid());
URI location = uriComponents.toUri();
return ResponseEntity.created(location).body(service);
}
return ResponseEntity.ok().body(service);
}
else throw new CerebrumInvalidUuidException(uuid);
return marketServiceService.updateService(uuid, marketService, uriComponentsBuilder);
}
/* JSON PATCH Service */
......@@ -202,17 +159,7 @@ public class MarketServiceController
@Parameter(description = "ID of the service that needs to be partially updated")
@PathVariable() String uuid)
{
if (Boolean.TRUE.equals(CerebrumEntityUuidGenerator.isValid(uuid))) {
MarketService partiallyUpdatedService = marketServiceRepository.findByUuid(uuid)
.map(service -> {
MarketService marketServicePatched =
CerebrumControllerUtilities.applyPatch(patch, service, MarketService.class);
return marketServiceRepository.save(marketServicePatched);
})
.orElseThrow(() -> new CerebrumEntityNotFoundException("user", uuid));
return ResponseEntity.ok().body(partiallyUpdatedService);
}
else throw new CerebrumInvalidUuidException(uuid);
return marketServiceService.partiallyUpdateService(uuid, patch);
}
/* delete Service */
......@@ -230,7 +177,6 @@ public class MarketServiceController
@DeleteMapping(path = "/{uuid}")
public ResponseEntity<MarketService> deleteMarketService(@PathVariable("uuid") String uuid)
{
marketServiceRepository.deleteByUuid(uuid);
return ResponseEntity.noContent().build();
return marketServiceService.deleteService(uuid);
}
}
......@@ -11,7 +11,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.http.MediaType;
......@@ -29,22 +28,16 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import javax.validation.Valid;
import javax.validation.constraints.Min;
import java.net.URI;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import de.helmholtz.marketplace.cerebrum.entities.MarketUser;
import de.helmholtz.marketplace.cerebrum.errorhandling.CerebrumApiError;
import de.helmholtz.marketplace.cerebrum.errorhandling.exception.CerebrumEntityNotFoundException;
import de.helmholtz.marketplace.cerebrum.errorhandling.exception.CerebrumInvalidUuidException;
import de.helmholtz.marketplace.cerebrum.repository.MarketUserRepository;
import de.helmholtz.marketplace.cerebrum.service.MarketUserService;
import de.helmholtz.marketplace.cerebrum.utils.CerebrumControllerUtilities;
import de.helmholtz.marketplace.cerebrum.utils.CerebrumEntityUuidGenerator;
@RestController
@Validated
......@@ -53,12 +46,13 @@ import de.helmholtz.marketplace.cerebrum.utils.CerebrumEntityUuidGenerator;
@Tag(name = "users", description = "The User API")
public class MarketUserController {
private final WebClient authorisationServer;
private final MarketUserRepository marketUserRepository;
private final MarketUserService marketUserService;
@Autowired
public MarketUserController(WebClient authorisationServer, MarketUserRepository marketUserRepository) {
public MarketUserController(WebClient authorisationServer,
MarketUserService marketUserService)
{
this.authorisationServer = authorisationServer;
this.marketUserRepository = marketUserRepository;
this.marketUserService = marketUserService;
}
@PreAuthorize("isAuthenticated()")
......@@ -112,7 +106,7 @@ public class MarketUserController {
"firstName property; the value will be set to firstName.asc")
@RequestParam(value = "sort", defaultValue = "firstName.asc") List<String> sorts)
{
return marketUserRepository.findAll(
return marketUserService.getUsers(
PageRequest.of(page, size, Sort.by(CerebrumControllerUtilities.getOrders(sorts))));
}
......@@ -132,11 +126,7 @@ public class MarketUserController {
@Parameter(description = "UUID of the user that needs to be fetched")
@PathVariable() String uuid)
{
if (Boolean.TRUE.equals(CerebrumEntityUuidGenerator.isValid(uuid))) {
return marketUserRepository.findByUuid(uuid)
.orElseThrow(() -> new CerebrumEntityNotFoundException("user", uuid));
}
else throw new CerebrumInvalidUuidException(uuid);
return marketUserService.getUser(uuid);
}
/* create user */
......@@ -157,12 +147,7 @@ public class MarketUserController {
required = true, schema = @Schema(implementation = MarketUser.class))
@Valid @RequestBody MarketUser marketUser, UriComponentsBuilder uriComponentsBuilder)
{
MarketUser newUser = marketUserRepository.save(marketUser);
UriComponents uriComponents =
uriComponentsBuilder.path("/api/v0/users/{id}").buildAndExpand(newUser.getUuid());
URI location = uriComponents.toUri();
return ResponseEntity.created(location).body(newUser);
return marketUserService.createUser(marketUser, uriComponentsBuilder);
}
/* update user */
......@@ -189,32 +174,7 @@ public class MarketUserController {
@Parameter(description = "UUID of the user that needs to be updated")
@PathVariable() String uuid, UriComponentsBuilder uriComponentsBuilder)
{
if (Boolean.TRUE.equals(CerebrumEntityUuidGenerator.isValid(uuid))) {
AtomicBoolean isCreated = new AtomicBoolean(false);
MarketUser org = marketUserRepository.findByUuid(uuid)
.map(marketUser -> {
marketUser.setEmail(newMarketUser.getEmail());
marketUser.setFirstName(newMarketUser.getFirstName());
marketUser.setLastName(newMarketUser.getLastName());
marketUser.setScreenName(newMarketUser.getScreenName());
marketUser.setSub(newMarketUser.getSub());
return marketUserRepository.save(marketUser);
})
.orElseGet(() -> {
newMarketUser.setUuid(uuid);
isCreated.set(true);
return marketUserRepository.save(newMarketUser);
});
if (isCreated.get()) {
UriComponents uriComponents =
uriComponentsBuilder.path("/api/v0/users/{id}").buildAndExpand(org.getUuid());
URI location = uriComponents.toUri();
return ResponseEntity.created(location).body(org);
}
return ResponseEntity.ok().body(org);
}
else throw new CerebrumInvalidUuidException(uuid);
return marketUserService.updateUser(uuid, newMarketUser, uriComponentsBuilder);
}
/* JSON PATCH user */
......@@ -243,17 +203,7 @@ public class MarketUserController {
@Parameter(description = "UUID of the user that needs to be partially updated")
@PathVariable() String uuid)
{
if (Boolean.TRUE.equals(CerebrumEntityUuidGenerator.isValid(uuid))) {
MarketUser partiallyUpdatedUser = marketUserRepository.findByUuid(uuid)
.map(user -> {
MarketUser marketUserPatched =
CerebrumControllerUtilities.applyPatch(patch, user, MarketUser.class);
return marketUserRepository.save(marketUserPatched);
})
.orElseThrow(() -> new CerebrumEntityNotFoundException("user", uuid));
return ResponseEntity.ok().body(partiallyUpdatedUser);
}
else throw new CerebrumInvalidUuidException(uuid);
return marketUserService.partiallyUpdateUser(uuid, patch);
}
/* delete user */
......@@ -273,7 +223,6 @@ public class MarketUserController {
@Parameter(description = "user UUID to delete", required = true)
@PathVariable(name = "uuid") String uuid)
{
marketUserRepository.deleteByUuid(uuid);
return ResponseEntity.noContent().build();
return marketUserService.deleteUser(uuid);
}
}
\ No newline at end of file
......@@ -26,22 +26,16 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import javax.validation.Valid;
import javax.validation.constraints.Min;
import java.net.URI;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import de.helmholtz.marketplace.cerebrum.entities.Organization;
import de.helmholtz.marketplace.cerebrum.errorhandling.CerebrumApiError;
import de.helmholtz.marketplace.cerebrum.errorhandling.exception.CerebrumEntityNotFoundException;
import de.helmholtz.marketplace.cerebrum.errorhandling.exception.CerebrumInvalidUuidException;
import de.helmholtz.marketplace.cerebrum.repository.OrganizationRepository;
import de.helmholtz.marketplace.cerebrum.service.OrganizationService;
import de.helmholtz.marketplace.cerebrum.utils.CerebrumControllerUtilities;
import de.helmholtz.marketplace.cerebrum.utils.CerebrumEntityUuidGenerator;
@RestController
@Validated
......@@ -49,10 +43,11 @@ import de.helmholtz.marketplace.cerebrum.utils.CerebrumEntityUuidGenerator;
path = "${spring.data.rest.base-path}/organizations")
@Tag(name = "organizations", description = "The Organization API")
public class OrganizationController {
private final OrganizationRepository organizationRepository;
private final OrganizationService organizationService;
public OrganizationController(OrganizationRepository organizationRepository) {
this.organizationRepository = organizationRepository;
public OrganizationController(OrganizationService organizationService)
{
this.organizationService = organizationService;
}
/* get Organizations */
......@@ -78,7 +73,7 @@ public class OrganizationController {
"name property; the value will be set to name.asc")
@RequestParam(value = "sort", defaultValue = "name.asc") List<String> sorts)
{
return organizationRepository.findAll(
return organizationService.getOrganizations(
PageRequest.of(page, size, Sort.by(CerebrumControllerUtilities.getOrders(sorts))));
}
......@@ -100,11 +95,7 @@ public class OrganizationController {
@Parameter(description = "ID of the organization that needs to be fetched")
@PathVariable(name = "uuid") String uuid)
{
if (Boolean.TRUE.equals(CerebrumEntityUuidGenerator.isValid(uuid))) {
return organizationRepository.findByUuid(uuid)
.orElseThrow(() -> new CerebrumEntityNotFoundException("organization", uuid));
}
else throw new CerebrumInvalidUuidException(uuid);
return organizationService.getOrganization(uuid);
}
/* create Organization */
......@@ -124,14 +115,7 @@ public class OrganizationController {
required = true, schema = @Schema(implementation = Organization.class))
@Valid @RequestBody Organization organization, UriComponentsBuilder uriComponentsBuilder)
{
Organization createdOrg = organizationRepository.save(organization);
UriComponents uriComponents =
uriComponentsBuilder.path("/api/v0/organizations/{id}").buildAndExpand(createdOrg.getUuid());
URI location = uriComponents.toUri();
return ResponseEntity
.created(location)
.body(createdOrg);
return organizationService.createOrganisation(organization, uriComponentsBuilder);
}
/* update Organization */
......@@ -156,31 +140,8 @@ public class OrganizationController {
@Parameter(description = "Unique identifier of the organization that needs to be updated")
@PathVariable(name = "uuid") String uuid, UriComponentsBuilder uriComponentsBuilder)
{
if (Boolean.TRUE.equals(CerebrumEntityUuidGenerator.isValid(uuid))) {
AtomicBoolean isCreated = new AtomicBoolean(false);
Organization org = organizationRepository.findByUuid(uuid)
.map(organization -> {
organization.setAbbreviation(newOrganization.getAbbreviation());
organization.setName(newOrganization.getName());
organization.setImg(newOrganization.getImg());
organization.setUrl(newOrganization.getUrl());
return organizationRepository.save(organization);
})
.orElseGet(() -> {
newOrganization.setUuid(uuid);
isCreated.set(true);
return organizationRepository.save(newOrganization);
});
if (isCreated.get()) {
UriComponents uriComponents =
uriComponentsBuilder.path("/api/v0/organizations/{id}").buildAndExpand(org.getUuid());
URI location = uriComponents.toUri();
return ResponseEntity.created(location).body(org);
}
return ResponseEntity.ok().body(org);
}
else throw new CerebrumInvalidUuidException(uuid);
return organizationService
.updateOrganisation(uuid, newOrganization, uriComponentsBuilder);
}
/* JSON PATCH Organization */
......@@ -208,17 +169,7 @@ public class OrganizationController {
@Parameter(description = "ID of the organization that needs to be partially updated")
@PathVariable(name = "uuid") String uuid)
{
if (Boolean.TRUE.equals(CerebrumEntityUuidGenerator.isValid(uuid))) {
Organization partialUpdateOrganisation = organizationRepository.findByUuid(uuid)
.map(organization -> {
Organization organizationPatched =
CerebrumControllerUtilities.applyPatch(patch, organization, Organization.class);
return organizationRepository.save(organizationPatched);
})
.orElseThrow(() -> new CerebrumEntityNotFoundException("organization", uuid));
return ResponseEntity.ok().body(partialUpdateOrganisation);
}
else throw new CerebrumInvalidUuidException(uuid);
return organizationService.partiallyUpdateOrganisation(uuid, patch);
}
/* delete Organization */
......@@ -237,7 +188,6 @@ public class OrganizationController {
@Parameter(description="organization id to delete", required=true)
@PathVariable(name = "uuid") String uuid)
{
organizationRepository.deleteByUuid(uuid);
return ResponseEntity.noContent().build();
return organizationService.deleteOrganisation(uuid);
}
}
......@@ -17,7 +17,7 @@ import static de.helmholtz.marketplace.cerebrum.utils.CerebrumEntityUuidGenerato
public class MarketUser
{
@Schema(description = "Unique identifier of the marketplace user.",
example = "hmu-01eac6d7-0d35-1812-a3ed-24aec4231940", required = true)
example = "usr-01eac6d7-0d35-1812-a3ed-24aec4231940", required = true)
@Id @GeneratedValue(strategy = CerebrumEntityUuidGenerator.class)
private String uuid;
......
......@@ -89,19 +89,23 @@ public class Organization {
this.url = url;
}
public Iterable<MarketService> getServiceList() {
public Iterable<MarketService> getServiceList()
{
return serviceList;
}
public void setServiceList(Iterable<MarketService> serviceList) {
public void setServiceList(Iterable<MarketService> serviceList)
{
this.serviceList = serviceList;
}
public Iterable<MarketUser> getContactPersons() {
public Iterable<MarketUser> getContactPersons()
{
return contactPersons;
}
public void setContactPersons(Iterable<MarketUser> contactPersons) {
public void setContactPersons(Iterable<MarketUser> contactPersons)
{
this.contactPersons = contactPersons;
}
}
package de.helmholtz.marketplace.cerebrum.repository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import java.util.Optional;
import de.helmholtz.marketplace.cerebrum.entities.MarketService;
public interface MarketServiceRepository extends PagingAndSortingRepository<MarketService, Long>
public interface MarketServiceRepository extends Neo4jRepository<MarketService, Long>
{
Optional<MarketService> findByUuid(String uuid);
......
package de.helmholtz.marketplace.cerebrum.repository;
import de.helmholtz.marketplace.cerebrum.entities.MarketUser;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.data.repository.query.Param;
import java.util.Optional;
public interface MarketUserRepository extends PagingAndSortingRepository<MarketUser, Long>
import de.helmholtz.marketplace.cerebrum.entities.MarketUser;
public interface MarketUserRepository extends Neo4jRepository<MarketUser, Long>
{
MarketUser findBySub(@Param("sub") String sub);
......
package de.helmholtz.marketplace.cerebrum.repository;
import de.helmholtz.marketplace.cerebrum.entities.Organization;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import java.util.Optional;
public interface OrganizationRepository
extends PagingAndSortingRepository<Organization, Long>
import de.helmholtz.marketplace.cerebrum.entities.Organization;
public interface OrganizationRepository extends Neo4jRepository<Organization, Long>
{
Optional<Organization> findByUuid(String uuid);
......
package de.helmholtz.marketplace.cerebrum.service;
import com.github.fge.jsonpatch.JsonPatch;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;
import de.helmholtz.marketplace.cerebrum.entities.MarketService;
import de.helmholtz.marketplace.cerebrum.repository.MarketServiceRepository;
import de.helmholtz.marketplace.cerebrum.service.common.CerebrumServiceBase;
@Service
public class MarketServiceService extends CerebrumServiceBase<MarketService, MarketServiceRepository>
{
private final MarketServiceRepository marketServiceRepository;
public MarketServiceService(MarketServiceRepository marketServiceRepository)
{
super(MarketService.class, MarketServiceRepository.class);
this.marketServiceRepository = marketServiceRepository;
}
public Page<MarketService> getServices(PageRequest page)
{
return getAllEntities(page, marketServiceRepository);
}
public MarketService getUser(String uuid)
{
return getEntity(uuid, marketServiceRepository);
}
public ResponseEntity<MarketService> createService(
MarketService entity, UriComponentsBuilder uriComponentsBuilder)
{
return createEntity(entity, marketServiceRepository, uriComponentsBuilder);
}
public ResponseEntity<MarketService> updateService(
String uuid, MarketService entity, UriComponentsBuilder uriComponentsBuilder)
{
return updateEntity(uuid, entity, marketServiceRepository, uriComponentsBuilder);
}