diff --git a/src/main/java/bio/overture/ego/controller/VisaController.java b/src/main/java/bio/overture/ego/controller/VisaController.java new file mode 100644 index 000000000..853400cdc --- /dev/null +++ b/src/main/java/bio/overture/ego/controller/VisaController.java @@ -0,0 +1,103 @@ +package bio.overture.ego.controller; + +import static bio.overture.ego.controller.resolver.PageableResolver.*; +import static org.springframework.web.bind.annotation.RequestMethod.*; + +import bio.overture.ego.model.dto.*; +import bio.overture.ego.model.entity.*; +import bio.overture.ego.security.AdminScoped; +import bio.overture.ego.service.*; +import bio.overture.ego.view.Views; +import com.fasterxml.jackson.annotation.JsonView; +import io.swagger.annotations.*; +import java.util.UUID; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; +import springfox.documentation.annotations.ApiIgnore; + +@Slf4j +@RestController +@RequestMapping("/visa") +@Api(tags = "Visa") +public class VisaController { + + /** Dependencies */ + private final VisaService visaService; + + private final UserPermissionService userPermissionService; + private final GroupPermissionService groupPermissionService; + private final ApplicationPermissionService applicationPermissionService; + + @Autowired + public VisaController( + @NonNull VisaService visaService, + @NonNull UserPermissionService userPermissionService, + @NonNull GroupPermissionService groupPermissionService, + @NonNull ApplicationPermissionService applicationPermissionService) { + this.visaService = visaService; + this.groupPermissionService = groupPermissionService; + this.userPermissionService = userPermissionService; + this.applicationPermissionService = applicationPermissionService; + } + + @AdminScoped + @RequestMapping(method = GET, value = "/{id}") + @ApiResponses( + value = {@ApiResponse(code = 200, message = "Get Visa by id", response = Visa.class)}) + @JsonView(Views.REST.class) + public @ResponseBody Visa getVisa( + @ApiIgnore @RequestHeader(value = "Authorization", required = true) + final String authorization, + @PathVariable(value = "id", required = true) UUID id) { + return visaService.getById(id); + } + + @AdminScoped + @RequestMapping(method = GET, value = "") + @ApiResponses(value = {@ApiResponse(code = 200, message = "All Visas")}) + @JsonView(Views.REST.class) + public @ResponseBody PageDTO listVisa( + @ApiIgnore @RequestHeader(value = "Authorization", required = true) + final String authorization, + @ApiIgnore Pageable pageable) { + return new PageDTO<>(visaService.listVisa(pageable)); + } + + @AdminScoped + @RequestMapping(method = POST, value = "") + @ApiResponses( + value = { + @ApiResponse(code = 200, message = "New Visa", response = Visa.class), + }) + public @ResponseBody Visa createVisa( + @ApiIgnore @RequestHeader(value = "Authorization", required = true) + final String authorization, + @RequestBody(required = true) VisaRequest visaRequest) { + return visaService.create(visaRequest); + } + + @AdminScoped + @RequestMapping(method = PUT, value = "/{id}") + @ApiResponses(value = {@ApiResponse(code = 200, message = "Update Visa", response = Visa.class)}) + public @ResponseBody Visa updateVisa( + @ApiIgnore @RequestHeader(value = "Authorization", required = true) + final String authorization, + @PathVariable(value = "id", required = true) UUID id, + @RequestBody(required = true) VisaRequest visaRequest) { + return visaService.partialUpdate(id, visaRequest); + } + + @AdminScoped + @RequestMapping(method = DELETE, value = "/{id}") + @ResponseStatus(value = HttpStatus.OK) + public void deleteVisa( + @ApiIgnore @RequestHeader(value = "Authorization", required = true) + final String authorization, + @PathVariable(value = "id", required = true) UUID id) { + visaService.delete(id); + } +} diff --git a/src/main/java/bio/overture/ego/model/dto/VisaRequest.java b/src/main/java/bio/overture/ego/model/dto/VisaRequest.java new file mode 100644 index 000000000..6225d995a --- /dev/null +++ b/src/main/java/bio/overture/ego/model/dto/VisaRequest.java @@ -0,0 +1,22 @@ +package bio.overture.ego.model.dto; + +import javax.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class VisaRequest { + + @NotNull private String type; + + @NotNull private String source; + + @NotNull private String value; + + @NotNull private String by; +} diff --git a/src/main/java/bio/overture/ego/model/entity/Visa.java b/src/main/java/bio/overture/ego/model/entity/Visa.java new file mode 100644 index 000000000..b4204f4c8 --- /dev/null +++ b/src/main/java/bio/overture/ego/model/entity/Visa.java @@ -0,0 +1,61 @@ +package bio.overture.ego.model.entity; + +import bio.overture.ego.model.enums.JavaFields; +import bio.overture.ego.model.enums.SqlFields; +import bio.overture.ego.model.enums.Tables; +import bio.overture.ego.view.Views; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonView; +import java.util.UUID; +import javax.persistence.*; +import javax.validation.constraints.NotNull; +import lombok.*; +import lombok.experimental.FieldNameConstants; +import org.hibernate.annotations.GenericGenerator; + +@Entity +@Table(name = Tables.GA4GHVISA) +@JsonInclude() +@JsonPropertyOrder({ + JavaFields.ID, + JavaFields.TYPE, + JavaFields.SOURCE, + JavaFields.VALUE, + JavaFields.BY +}) +@JsonView(Views.REST.class) +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@FieldNameConstants +@EqualsAndHashCode(of = {"id"}) +public class Visa implements Identifiable { + + @Id + @Column(name = SqlFields.ID, updatable = false, nullable = false) + @GenericGenerator(name = "visa_uuid", strategy = "org.hibernate.id.UUIDGenerator") + @GeneratedValue(generator = "visa_uuid") + private UUID id; + + @NotNull + @Column(name = SqlFields.TYPE, nullable = false) + @JsonView({Views.JWTAccessToken.class, Views.REST.class}) + private String type; + + @NotNull + @JsonView({Views.JWTAccessToken.class, Views.REST.class}) + @Column(name = SqlFields.SOURCE) + private String source; + + @NotNull + @JsonView({Views.JWTAccessToken.class, Views.REST.class}) + @Column(name = SqlFields.VALUE) + private String value; + + @NotNull + @JsonView({Views.JWTAccessToken.class, Views.REST.class}) + @Column(name = SqlFields.BY) + private String by; +} diff --git a/src/main/java/bio/overture/ego/model/enums/JavaFields.java b/src/main/java/bio/overture/ego/model/enums/JavaFields.java index 7ec650a4e..38f9df6c1 100644 --- a/src/main/java/bio/overture/ego/model/enums/JavaFields.java +++ b/src/main/java/bio/overture/ego/model/enums/JavaFields.java @@ -66,4 +66,9 @@ public class JavaFields { public static final String PROVIDERTYPE = "providerType"; public static final String PROVIDER_SUBJECT_ID = "providerSubjectId"; public static final String ERROR_REDIRECT_URI = "errorRedirectUri"; + // Visas Added + public static final String SOURCE = "source"; + public static final String VALUE = "value"; + + public static final String BY = "by"; } diff --git a/src/main/java/bio/overture/ego/model/enums/SqlFields.java b/src/main/java/bio/overture/ego/model/enums/SqlFields.java index 5fde6918d..afc7d0845 100644 --- a/src/main/java/bio/overture/ego/model/enums/SqlFields.java +++ b/src/main/java/bio/overture/ego/model/enums/SqlFields.java @@ -38,4 +38,8 @@ public class SqlFields { public static final String PROVIDERSUBJECTID = "providersubjectid"; public static final String INITIALIZED = "initialized"; public static final String ERRORREDIRECTURI = "errorredirecturi"; + public static final String SOURCE = "source"; + public static final String VALUE = "value"; + + public static final String BY = "by"; } diff --git a/src/main/java/bio/overture/ego/model/enums/Tables.java b/src/main/java/bio/overture/ego/model/enums/Tables.java index 9eab02c90..50ec199aa 100644 --- a/src/main/java/bio/overture/ego/model/enums/Tables.java +++ b/src/main/java/bio/overture/ego/model/enums/Tables.java @@ -22,4 +22,6 @@ public class Tables { public static final String APPLICATION_PERMISSION = "applicationpermission"; public static final String DEFAULTPROVIDERTRIPWIRE = "defaultprovidertripwire"; public static final String INITTRIPWIRE = "inittripwire"; + + public static final String GA4GHVISA = "ga4ghvisa"; } diff --git a/src/main/java/bio/overture/ego/model/enums/VisaType.java b/src/main/java/bio/overture/ego/model/enums/VisaType.java new file mode 100644 index 000000000..0eb87dab1 --- /dev/null +++ b/src/main/java/bio/overture/ego/model/enums/VisaType.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017. The Ontario Institute for Cancer Research. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package bio.overture.ego.model.enums; + +import static bio.overture.ego.utils.Joiners.COMMA; +import static bio.overture.ego.utils.Streams.stream; +import static java.lang.String.format; + +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public enum VisaType { + STANDARD_VISA_TYPE, + CUSTOM_VISA_TYPE; + + public static VisaType resolveStatusType(@NonNull String statusType) { + return stream(values()) + .filter(x -> x.toString().equals(statusType)) + .findFirst() + .orElseThrow( + () -> + new IllegalArgumentException( + format( + "The status type '%s' cannot be resolved. Must be one of: [%s]", + statusType, COMMA.join(values())))); + } + + @Override + public String toString() { + return this.name(); + } +} diff --git a/src/main/java/bio/overture/ego/repository/VisaRepository.java b/src/main/java/bio/overture/ego/repository/VisaRepository.java new file mode 100644 index 000000000..50d1ec130 --- /dev/null +++ b/src/main/java/bio/overture/ego/repository/VisaRepository.java @@ -0,0 +1,16 @@ +package bio.overture.ego.repository; + +import bio.overture.ego.model.entity.Visa; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public interface VisaRepository extends NamedRepository { + @Override + @Deprecated + default Optional findByName(String name) { + return null; + } + + List findAll(); +} diff --git a/src/main/java/bio/overture/ego/service/VisaService.java b/src/main/java/bio/overture/ego/service/VisaService.java new file mode 100644 index 000000000..9f5b07a66 --- /dev/null +++ b/src/main/java/bio/overture/ego/service/VisaService.java @@ -0,0 +1,91 @@ +package bio.overture.ego.service; + +import static bio.overture.ego.model.exceptions.NotFoundException.checkNotFound; +import static bio.overture.ego.model.exceptions.RequestValidationException.checkRequestValid; +import static org.mapstruct.factory.Mappers.getMapper; + +import bio.overture.ego.event.token.ApiKeyEventsPublisher; +import bio.overture.ego.model.dto.VisaRequest; +import bio.overture.ego.model.entity.Visa; +import bio.overture.ego.repository.VisaRepository; +import java.util.Optional; +import java.util.UUID; +import javax.validation.constraints.NotNull; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.NullValueCheckStrategy; +import org.mapstruct.ReportingPolicy; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@Transactional +public class VisaService extends AbstractNamedService { + + /** Constants */ + private static final VisaService.VisaConverter VISA_CONVERTER = + getMapper(VisaService.VisaConverter.class); + + /** Dependencies */ + @Autowired private VisaRepository visaRepository; + + private final ApiKeyEventsPublisher apiKeyEventsPublisher; + + @Autowired + public VisaService( + @NonNull VisaRepository visaRepository, + @NonNull ApiKeyEventsPublisher apiKeyEventsPublisher) { + super(Visa.class, visaRepository); + this.visaRepository = visaRepository; + this.apiKeyEventsPublisher = apiKeyEventsPublisher; + } + + public Visa create(@NonNull VisaRequest createRequest) { + checkRequestValid(createRequest); + val visa = VISA_CONVERTER.convertToVisa(createRequest); + return getRepository().save(visa); + } + + @Override + public Visa getById(@NonNull UUID uuid) { + val result = (Optional) getRepository().findById(uuid); + checkNotFound(result.isPresent(), "The visaId '%s' does not exist", uuid); + return result.get(); + } + + public void delete(@NonNull UUID id) { + checkExistence(id); + super.delete(id); + } + + @Override + public Visa getWithRelationships(UUID uuid) { + return null; + } + + public Page listVisa(@NonNull Pageable pageable) { + return visaRepository.findAll(pageable); + } + + public Visa partialUpdate(@NotNull UUID id, @NonNull VisaRequest updateRequest) { + val visa = getById(id); + VISA_CONVERTER.updateVisa(updateRequest, visa); + return getRepository().save(visa); + } + + @Mapper( + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, + unmappedTargetPolicy = ReportingPolicy.WARN) + public abstract static class VisaConverter { + public abstract Visa convertToVisa(VisaRequest request); + + public abstract void updateVisa(VisaRequest request, @MappingTarget Visa visaToUpdate); + } +} diff --git a/src/main/resources/flyway/sql/V1_22__add_visa.sql b/src/main/resources/flyway/sql/V1_22__add_visa.sql new file mode 100644 index 000000000..bf99727bf --- /dev/null +++ b/src/main/resources/flyway/sql/V1_22__add_visa.sql @@ -0,0 +1,8 @@ +CREATE TABLE GA4GHVISA ( + id UUID PRIMARY KEY, + type varchar(255) NOT NULL, + source varchar(255) NOT NULL, + value varchar(255) NOT NULL, + by varchar(255) NOT NULL +); +