diff --git a/pom.xml b/pom.xml index e499214be..18d2c84fc 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ bio.overture song pom - 3.0.1 + 3.1.0 song-core song-java-sdk diff --git a/song-client/pom.xml b/song-client/pom.xml index 0b7b6b442..fdd96a774 100644 --- a/song-client/pom.xml +++ b/song-client/pom.xml @@ -18,7 +18,7 @@ song bio.overture - 3.0.1 + 3.1.0 4.0.0 @@ -35,12 +35,12 @@ bio.overture song-java-sdk - 3.0.1 + 3.1.0 bio.overture song-core - 3.0.1 + 3.1.0 diff --git a/song-core/pom.xml b/song-core/pom.xml index b3f299d96..14fe5f6f3 100644 --- a/song-core/pom.xml +++ b/song-core/pom.xml @@ -19,7 +19,7 @@ song bio.overture - 3.0.1 + 3.1.0 4.0.0 diff --git a/song-core/src/main/java/bio/overture/song/core/model/FileUpdateResponse.java b/song-core/src/main/java/bio/overture/song/core/model/FileUpdateResponse.java index 098224428..f2177e2b9 100644 --- a/song-core/src/main/java/bio/overture/song/core/model/FileUpdateResponse.java +++ b/song-core/src/main/java/bio/overture/song/core/model/FileUpdateResponse.java @@ -30,8 +30,8 @@ @AllArgsConstructor public class FileUpdateResponse { private FileUpdateTypes fileUpdateType; - private AnalysisStates originalAnalysisState; - private boolean unpublishedAnalysis; + @Deprecated private AnalysisStates originalAnalysisState; + @Deprecated private boolean unpublishedAnalysis; private String message; private FileDTO originalFile; } diff --git a/song-java-sdk/pom.xml b/song-java-sdk/pom.xml index a11202050..4b0250c72 100644 --- a/song-java-sdk/pom.xml +++ b/song-java-sdk/pom.xml @@ -18,7 +18,7 @@ song bio.overture - 3.0.1 + 3.1.0 4.0.0 diff --git a/song-server/pom.xml b/song-server/pom.xml index 1cda9e6c7..04c99d2b9 100644 --- a/song-server/pom.xml +++ b/song-server/pom.xml @@ -19,7 +19,7 @@ song bio.overture - 3.0.1 + 3.1.0 4.0.0 @@ -37,7 +37,7 @@ bio.overture song-core - 3.0.1 + 3.1.0 diff --git a/song-server/src/main/java/bio/overture/song/server/controller/StudyController.java b/song-server/src/main/java/bio/overture/song/server/controller/StudyController.java index 42eb90e15..671a29d3d 100644 --- a/song-server/src/main/java/bio/overture/song/server/controller/StudyController.java +++ b/song-server/src/main/java/bio/overture/song/server/controller/StudyController.java @@ -22,6 +22,7 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; +import bio.overture.song.server.model.dto.GenericMessage; import bio.overture.song.server.model.entity.Study; import bio.overture.song.server.model.entity.composites.StudyWithDonors; import bio.overture.song.server.service.StudyService; @@ -78,7 +79,7 @@ public List findAllStudies() { consumes = {APPLICATION_JSON_VALUE, APPLICATION_JSON_UTF8_VALUE}) @PreAuthorize("@studySecurity.authorize(authentication, #studyId)") @ResponseBody - public String saveStudy( + public GenericMessage saveStudy( @PathVariable("studyId") String studyId, @RequestHeader(value = AUTHORIZATION, required = false) final String accessToken, @RequestBody Study study) { @@ -89,6 +90,6 @@ public String saveStudy( "The studyId in the URL '%s' should match the studyId '%s' in the payload", studyId, study.getStudyId()); - return studyService.saveStudy(study); + return new GenericMessage(studyService.saveStudy(study)); } } diff --git a/song-server/src/main/java/bio/overture/song/server/model/dto/GenericMessage.java b/song-server/src/main/java/bio/overture/song/server/model/dto/GenericMessage.java new file mode 100644 index 000000000..bdc692ab1 --- /dev/null +++ b/song-server/src/main/java/bio/overture/song/server/model/dto/GenericMessage.java @@ -0,0 +1,9 @@ +package bio.overture.song.server.model.dto; + +import lombok.NonNull; +import lombok.Value; + +@Value +public class GenericMessage { + @NonNull private final String message; +} diff --git a/song-server/src/main/java/bio/overture/song/server/model/entity/Donor.java b/song-server/src/main/java/bio/overture/song/server/model/entity/Donor.java index e94ad4f5e..16f02d003 100644 --- a/song-server/src/main/java/bio/overture/song/server/model/entity/Donor.java +++ b/song-server/src/main/java/bio/overture/song/server/model/entity/Donor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018. Ontario Institute for Cancer Research + * Copyright (c) 2018 - 2019. Ontario Institute for Cancer Research * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,8 +17,6 @@ package bio.overture.song.server.model.entity; -import static bio.overture.song.server.model.enums.Constants.DONOR_GENDER; -import static bio.overture.song.server.model.enums.Constants.validate; import static bio.overture.song.server.model.enums.TableNames.DONOR; import bio.overture.song.core.model.Metadata; @@ -30,18 +28,20 @@ import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import lombok.NonNull; -import lombok.RequiredArgsConstructor; import lombok.ToString; @Entity @Table(name = DONOR) @Data @Builder -@RequiredArgsConstructor +@NoArgsConstructor +@AllArgsConstructor @ToString(callSuper = true) @EqualsAndHashCode(callSuper = true) @JsonPropertyOrder({ @@ -68,23 +68,6 @@ public class Donor extends Metadata { @Column(name = TableAttributeNames.GENDER, nullable = true) private String donorGender; - // NOTE: Since the donorGender field is validated upon setting it, using Lomboks default Builder - // when - // the @AllArgsConstructor is used will by pass the validation since the Builder uses the All Arg - // Constructor. - // By using the setter inside the constructor, the building of a Donor will always be validated - public Donor(String donorId, String studyId, String donorSubmitterId, String donorGender) { - this.donorId = donorId; - this.studyId = studyId; - this.donorSubmitterId = donorSubmitterId; - setDonorGender(donorGender); - } - - public void setDonorGender(String gender) { - validate(DONOR_GENDER, gender); - this.donorGender = gender; - } - public void setWithDonor(@NonNull Donor donorUpdate) { setDonorSubmitterId(donorUpdate.getDonorSubmitterId()); setDonorGender(donorUpdate.getDonorGender()); diff --git a/song-server/src/main/java/bio/overture/song/server/model/entity/FileEntity.java b/song-server/src/main/java/bio/overture/song/server/model/entity/FileEntity.java index dbefa903f..778cd1032 100644 --- a/song-server/src/main/java/bio/overture/song/server/model/entity/FileEntity.java +++ b/song-server/src/main/java/bio/overture/song/server/model/entity/FileEntity.java @@ -17,8 +17,6 @@ package bio.overture.song.server.model.entity; -import static bio.overture.song.core.model.enums.AccessTypes.resolveAccessType; - import bio.overture.song.core.model.File; import bio.overture.song.core.model.FileData; import bio.overture.song.core.model.Metadata; @@ -32,18 +30,20 @@ import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import lombok.NonNull; -import lombok.RequiredArgsConstructor; import lombok.ToString; @Entity @Table(name = TableNames.FILE) @Data @Builder -@RequiredArgsConstructor +@NoArgsConstructor +@AllArgsConstructor @ToString(callSuper = true) @EqualsAndHashCode(callSuper = true) @JsonInclude(JsonInclude.Include.NON_ABSENT) @@ -74,38 +74,19 @@ public class FileEntity extends Metadata implements Serializable, FileData, File @Column(name = TableAttributeNames.ACCESS, nullable = false) private String fileAccess; - public FileEntity( - String objectId, - String studyId, - String analysisId, - String fileName, - Long fileSize, - String fileType, - String fileMd5sum, - String fileAccess) { - this.objectId = objectId; - this.studyId = studyId; - this.analysisId = analysisId; - this.fileName = fileName; - this.fileSize = fileSize; - setFileType(fileType); - this.fileMd5sum = fileMd5sum; - setFileAccess(fileAccess); + public void setFileType(@NonNull String fileType) { + this.fileType = fileType; } - public void setFileType(FileTypes type) { + public void setFileType(@NonNull FileTypes type) { this.fileType = type.toString(); } - public void setFileType(String type) { - setFileType(FileTypes.resolveFileType(type)); + public void setFileAccess(@NonNull String fileAccess) { + this.fileAccess = fileAccess; } public void setFileAccess(@NonNull AccessTypes access) { this.fileAccess = access.toString(); } - - public void setFileAccess(@NonNull String access) { - setFileAccess(resolveAccessType(access)); - } } diff --git a/song-server/src/main/java/bio/overture/song/server/model/entity/Sample.java b/song-server/src/main/java/bio/overture/song/server/model/entity/Sample.java index cd341a1b3..59dd6e378 100644 --- a/song-server/src/main/java/bio/overture/song/server/model/entity/Sample.java +++ b/song-server/src/main/java/bio/overture/song/server/model/entity/Sample.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018. Ontario Institute for Cancer Research + * Copyright (c) 2018 - 2019. Ontario Institute for Cancer Research * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,9 +17,6 @@ package bio.overture.song.server.model.entity; -import static bio.overture.song.server.model.enums.Constants.SAMPLE_TYPE; -import static bio.overture.song.server.model.enums.Constants.validate; - import bio.overture.song.core.model.Metadata; import bio.overture.song.server.model.enums.TableAttributeNames; import bio.overture.song.server.model.enums.TableNames; @@ -28,18 +25,20 @@ import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import lombok.NonNull; -import lombok.RequiredArgsConstructor; import lombok.ToString; @Entity @Table(name = TableNames.SAMPLE) @Data @Builder -@RequiredArgsConstructor +@NoArgsConstructor +@AllArgsConstructor @ToString(callSuper = true) @EqualsAndHashCode(callSuper = true) @JsonInclude(JsonInclude.Include.ALWAYS) @@ -58,18 +57,6 @@ public class Sample extends Metadata { @Column(name = TableAttributeNames.TYPE, nullable = false) private String sampleType; - public Sample(String sampleId, String specimenId, String sampleSubmitterId, String sampleType) { - this.sampleId = sampleId; - this.specimenId = specimenId; - this.sampleSubmitterId = sampleSubmitterId; - setSampleType(sampleType); - } - - public void setSampleType(String type) { - validate(SAMPLE_TYPE, type); - sampleType = type; - } - public void setWithSample(@NonNull Sample u) { setSampleId(u.getSampleId()); setSampleSubmitterId(u.getSampleSubmitterId()); diff --git a/song-server/src/main/java/bio/overture/song/server/model/entity/Specimen.java b/song-server/src/main/java/bio/overture/song/server/model/entity/Specimen.java index 9244dc1a0..9ef163b81 100644 --- a/song-server/src/main/java/bio/overture/song/server/model/entity/Specimen.java +++ b/song-server/src/main/java/bio/overture/song/server/model/entity/Specimen.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018. Ontario Institute for Cancer Research + * Copyright (c) 2018-2019. Ontario Institute for Cancer Research * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -17,10 +17,6 @@ package bio.overture.song.server.model.entity; -import static bio.overture.song.server.model.enums.Constants.SPECIMEN_CLASS; -import static bio.overture.song.server.model.enums.Constants.SPECIMEN_TYPE; -import static bio.overture.song.server.model.enums.Constants.validate; - import bio.overture.song.core.model.Metadata; import bio.overture.song.server.model.enums.TableAttributeNames; import bio.overture.song.server.model.enums.TableNames; @@ -29,18 +25,20 @@ import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import lombok.NonNull; -import lombok.RequiredArgsConstructor; import lombok.ToString; @Entity @Table(name = TableNames.SPECIMEN) @Data @Builder -@RequiredArgsConstructor +@NoArgsConstructor +@AllArgsConstructor @ToString(callSuper = true) @EqualsAndHashCode(callSuper = true) @JsonInclude(JsonInclude.Include.NON_ABSENT) @@ -62,29 +60,6 @@ public class Specimen extends Metadata { @Column(name = TableAttributeNames.TYPE, nullable = false) private String specimenType; - public Specimen( - String specimenId, - String donorId, - String specimenSubmitterId, - String specimenClass, - String specimenType) { - this.specimenId = specimenId; - this.donorId = donorId; - this.specimenSubmitterId = specimenSubmitterId; - setSpecimenClass(specimenClass); - setSpecimenType(specimenType); - } - - public void setSpecimenClass(String specimenClass) { - validate(SPECIMEN_CLASS, specimenClass); - this.specimenClass = specimenClass; - } - - public void setSpecimenType(String type) { - validate(SPECIMEN_TYPE, type); - specimenType = type; - } - public void setWithSpecimen(@NonNull Specimen specimenUpdate) { setSpecimenSubmitterId(specimenUpdate.getSpecimenSubmitterId()); setDonorId(specimenUpdate.getDonorId()); diff --git a/song-server/src/main/java/bio/overture/song/server/properties/IdProperties.java b/song-server/src/main/java/bio/overture/song/server/properties/IdProperties.java index d0dca7832..2af1e9d3c 100644 --- a/song-server/src/main/java/bio/overture/song/server/properties/IdProperties.java +++ b/song-server/src/main/java/bio/overture/song/server/properties/IdProperties.java @@ -32,7 +32,6 @@ public static class UriTemplateProperties { private String donor; private String specimen; private String sample; - private String file; } @Getter diff --git a/song-server/src/main/java/bio/overture/song/server/service/FileModificationService.java b/song-server/src/main/java/bio/overture/song/server/service/FileModificationService.java index 7d860c3a9..cfd2e0fba 100644 --- a/song-server/src/main/java/bio/overture/song/server/service/FileModificationService.java +++ b/song-server/src/main/java/bio/overture/song/server/service/FileModificationService.java @@ -21,7 +21,6 @@ import static bio.overture.song.core.exceptions.ServerErrors.INVALID_FILE_UPDATE_REQUEST; import static bio.overture.song.core.exceptions.ServerException.buildServerException; import static bio.overture.song.core.exceptions.ServerException.checkServer; -import static bio.overture.song.core.model.enums.AnalysisStates.PUBLISHED; import static bio.overture.song.core.model.enums.AnalysisStates.SUPPRESSED; import static bio.overture.song.core.model.enums.AnalysisStates.UNPUBLISHED; import static bio.overture.song.core.model.enums.FileUpdateTypes.CONTENT_UPDATE; @@ -64,9 +63,33 @@ public FileModificationService( @Transactional public FileUpdateTypes updateWithRequest( @NonNull FileEntity originalFile, FileData fileUpdateRequest) { - val updatedFile = createUpdateFile(originalFile, fileUpdateRequest); - fileService.unsafeUpdate(updatedFile); - return resolveFileUpdateType(originalFile, fileUpdateRequest); + val analysisId = originalFile.getAnalysisId(); + val objectId = originalFile.getObjectId(); + val currentState = analysisService.readState(analysisId); + val fileUpdateType = resolveFileUpdateType(originalFile, fileUpdateRequest); + + boolean doUpdate = false; + if (fileUpdateType == METADATA_UPDATE) { + doUpdate = true; + } else if (fileUpdateType == CONTENT_UPDATE) { + checkServer( + currentState == UNPUBLISHED, + getClass(), + ILLEGAL_FILE_UPDATE_REQUEST, + "The file with objectId '%s' and analysisId '%s' cannot be updated since its analysisState is '%s' and '%s' is required", + objectId, + analysisId, + currentState, + UNPUBLISHED); + doUpdate = true; + } + + if (doUpdate) { + val updatedFile = createUpdateFile(originalFile, fileUpdateRequest); + fileService.unsafeUpdate(updatedFile); + } + + return fileUpdateType; } /** @@ -100,39 +123,27 @@ public FileUpdateResponse securedFileWithAnalysisUpdate( + "be updated since its analysisState is '%s'", objectId, analysisId, - SUPPRESSED.toString()); + currentState.toString()); // Update the target file record using the originalFile and the update request val fileUpdateType = updateWithRequest(originalFile, fileUpdateRequest); - - // Build the response val response = FileUpdateResponse.builder().unpublishedAnalysis(false); + if (fileUpdateType == METADATA_UPDATE || fileUpdateType == CONTENT_UPDATE) { + response.message( + format("Updated file with objectId '%s' and analysisId '%s'", objectId, analysisId)); + } else if (fileUpdateType == NO_UPDATE) { + response.message( + format( + "No update for file with objectId '%s' and analysisId '%s'", objectId, analysisId)); + } else { + throw new IllegalStateException("Could not process fileUpdateType: " + fileUpdateType); + } + // Build the response + response.originalFile(fileConverter.convertToFileDTO(originalFile)); response.originalAnalysisState(currentState); response.fileUpdateType(fileUpdateType); - // Can only transition from PUBLISHED to UNPUBLISHED states. - if (currentState == PUBLISHED) { - if (doUnpublish(fileUpdateType)) { - analysisService.securedUpdateState(studyId, analysisId, UNPUBLISHED); - response.unpublishedAnalysis(true); - response.message( - format( - "[WARNING]: Changed analysis from '%s' to '%s'", - PUBLISHED.toString(), UNPUBLISHED.toString())); - } else { - response.message( - format( - "Original analysisState '%s' was not changed since the fileUpdateType was '%s'", - currentState.toString(), fileUpdateType.name())); - } - } else if (currentState == UNPUBLISHED) { // Can still update an unpublished analysis - response.message( - format("Did not change analysisState since it is '%s'", currentState.toString())); - } else { - throw new IllegalStateException( - format("Could not process the analysisState '%s'", currentState.toString())); - } return response.build(); } @@ -159,21 +170,4 @@ private FileEntity createUpdateFile( fileConverter.updateEntityFromData(fileUpdateData, updatedFile); return updatedFile; } - - /** - * Decides whether or not the input {@code fileUpdateType} should unpublish an analysis - * - * @param fileUpdateType - * @return boolean - */ - public static boolean doUnpublish(@NonNull FileUpdateTypes fileUpdateType) { - if (fileUpdateType == CONTENT_UPDATE) { - return true; - } else if (fileUpdateType == METADATA_UPDATE || fileUpdateType == NO_UPDATE) { - return false; - } else { - throw new IllegalStateException( - format("The updateType '%s' is unrecognized", fileUpdateType.name())); - } - } } diff --git a/song-server/src/main/java/bio/overture/song/server/service/FileService.java b/song-server/src/main/java/bio/overture/song/server/service/FileService.java index 37051c08c..5f19d330a 100644 --- a/song-server/src/main/java/bio/overture/song/server/service/FileService.java +++ b/song-server/src/main/java/bio/overture/song/server/service/FileService.java @@ -58,7 +58,7 @@ public String create( result, getClass(), ID_NOT_FOUND, - "The fileId for analysisId '%s' and fileName '%s' was not found", + "The objectId for analysisId '%s' and fileName '%s' was not found", analysisId, file.getFileName()); diff --git a/song-server/src/main/java/bio/overture/song/server/service/StudyService.java b/song-server/src/main/java/bio/overture/song/server/service/StudyService.java index fc00b4ea7..da49d52c0 100644 --- a/song-server/src/main/java/bio/overture/song/server/service/StudyService.java +++ b/song-server/src/main/java/bio/overture/song/server/service/StudyService.java @@ -19,6 +19,7 @@ import static bio.overture.song.core.exceptions.ServerErrors.STUDY_ALREADY_EXISTS; import static bio.overture.song.core.exceptions.ServerErrors.STUDY_ID_DOES_NOT_EXIST; import static bio.overture.song.core.exceptions.ServerException.checkServer; +import static java.lang.String.format; import static java.lang.Thread.currentThread; import static org.icgc.dcc.common.core.util.stream.Collectors.toImmutableList; @@ -72,7 +73,7 @@ public String saveStudy(Study study) { study); studyRepository.save(study); infoService.create(id, study.getInfoAsString()); - return id; + return format("Successfully created study '%s'", id); } public List findAllStudies() { diff --git a/song-server/src/main/java/bio/overture/song/server/service/UploadService.java b/song-server/src/main/java/bio/overture/song/server/service/UploadService.java index 226de1442..c396c8d28 100644 --- a/song-server/src/main/java/bio/overture/song/server/service/UploadService.java +++ b/song-server/src/main/java/bio/overture/song/server/service/UploadService.java @@ -16,6 +16,21 @@ */ package bio.overture.song.server.service; +import bio.overture.song.core.model.AnalysisTypeId; +import bio.overture.song.core.model.SubmitResponse; +import bio.overture.song.server.model.dto.Payload; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.NonNull; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.io.IOException; + +import static java.util.Objects.isNull; import static bio.overture.song.core.exceptions.ServerErrors.ANALYSIS_TYPE_INCORRECT_VERSION; import static bio.overture.song.core.exceptions.ServerErrors.MALFORMED_PARAMETER; import static bio.overture.song.core.exceptions.ServerErrors.PAYLOAD_PARSING; @@ -27,20 +42,9 @@ import static bio.overture.song.core.utils.JsonUtils.fromJson; import static bio.overture.song.core.utils.JsonUtils.readTree; import static bio.overture.song.core.utils.Responses.OK; +import static bio.overture.song.server.model.enums.ModelAttributeNames.ANALYSIS_TYPE; +import static bio.overture.song.server.model.enums.ModelAttributeNames.NAME; import static bio.overture.song.server.model.enums.ModelAttributeNames.STUDY; -import static java.util.Objects.isNull; - -import bio.overture.song.core.model.SubmitResponse; -import bio.overture.song.server.model.dto.Payload; -import com.fasterxml.jackson.databind.JsonNode; -import java.io.IOException; -import javax.transaction.Transactional; -import lombok.NonNull; -import lombok.SneakyThrows; -import lombok.extern.slf4j.Slf4j; -import lombok.val; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; @Service @Slf4j @@ -82,22 +86,11 @@ public SubmitResponse submit(@NonNull String studyId, String payloadString) { return SubmitResponse.builder().analysisId(analysisId).status(OK).build(); } - private void checkAnalysisTypeVersion(@NonNull Payload payload) { - checkServer( - !isNull(payload.getAnalysisType()), - getClass(), - MALFORMED_PARAMETER, - "The analysisType field cannot be null"); - val analysisTypeId = payload.getAnalysisType(); - val errors = validator.validateAnalysisTypeVersion(analysisTypeId); - checkServer(isNull(errors), getClass(), ANALYSIS_TYPE_INCORRECT_VERSION, errors); - } - private JsonNode parsePayload(String payloadString) { try { val payloadJson = readTree(payloadString); - val payload = fromJson(payloadJson, Payload.class); - checkAnalysisTypeVersion(payload); + val analysisTypeId = parseAnalysisTypeId(payloadJson); + checkAnalysisTypeVersion(analysisTypeId); return payloadJson; } catch (IOException e) { log.error(e.getMessage()); @@ -108,6 +101,18 @@ private JsonNode parsePayload(String payloadString) { } } + private void checkAnalysisTypeVersion(@NonNull AnalysisTypeId analysisTypeId) { + val errors = validator.validateAnalysisTypeVersion(analysisTypeId); + checkServer(isNull(errors), getClass(), ANALYSIS_TYPE_INCORRECT_VERSION, errors); + } + + private AnalysisTypeId parseAnalysisTypeId(@NonNull JsonNode payloadJson){ + checkServer(payloadJson.has(ANALYSIS_TYPE), getClass(), MALFORMED_PARAMETER, "The analysisType field cannot be null"); + val analysisTypePath = payloadJson.path(ANALYSIS_TYPE); + checkServer(analysisTypePath.has(NAME), getClass(), MALFORMED_PARAMETER, "The analysisType name field cannot be null"); + return fromJson(analysisTypePath, AnalysisTypeId.class); + } + private void validatePayload(JsonNode payloadJson) { // Validate payload format and content val error = validator.validate(payloadJson); diff --git a/song-server/src/main/java/bio/overture/song/server/service/id/FederatedIdService.java b/song-server/src/main/java/bio/overture/song/server/service/id/FederatedIdService.java index f2ce160e1..e1457ad00 100644 --- a/song-server/src/main/java/bio/overture/song/server/service/id/FederatedIdService.java +++ b/song-server/src/main/java/bio/overture/song/server/service/id/FederatedIdService.java @@ -20,12 +20,6 @@ public class FederatedIdService implements IdService { @NonNull private final IdService localIdService; @NonNull private final UriResolver uriResolver; - @Override - public Optional getFileId(@NonNull String analysisId, @NonNull String fileName) { - return handleIdServiceGetRequest( - uriResolver.expandFileUri(analysisId, fileName), rest::getString); - } - @Override public Optional getDonorId(@NonNull String studyId, @NonNull String submitterDonorId) { return handleIdServiceGetRequest( @@ -45,13 +39,20 @@ public Optional getSampleId(@NonNull String studyId, @NonNull String sub uriResolver.expandSampleUri(studyId, submitterSampleId), rest::getString); } + /** Always generate the analysisId locally */ @Override public String generateAnalysisId() { return localIdService.generateAnalysisId(); } + // Always generate the objectId locally + @Override + public Optional getFileId(@NonNull String analysisId, @NonNull String fileName) { + return localIdService.getFileId(analysisId, fileName); + } + /** - * This method calls the callback function with the input url, and if successfull (1xx/2xx/3xx + * This method calls the callback function with the input url, and if successful (1xx/2xx/3xx * status code) returns the result, otherwise throws a ServerException */ private static T handleIdServiceGetRequest(String url, Function restCallback) { diff --git a/song-server/src/main/java/bio/overture/song/server/service/id/UriResolver.java b/song-server/src/main/java/bio/overture/song/server/service/id/UriResolver.java index 3b6d9ceac..9c88042c3 100644 --- a/song-server/src/main/java/bio/overture/song/server/service/id/UriResolver.java +++ b/song-server/src/main/java/bio/overture/song/server/service/id/UriResolver.java @@ -30,16 +30,11 @@ public class UriResolver { private static final String STUDY_ID = "studyId"; /** Dependencies */ - @NonNull private final UriTemplate fileUriTemplate; - @NonNull private final UriTemplate donorUriTemplate; + @NonNull private final UriTemplate specimenUriTemplate; @NonNull private final UriTemplate sampleUriTemplate; - public String expandFileUri(@NonNull String analysisId, @NonNull String fileName) { - return fileUriTemplate.expand(Map.of(ANALYSIS_ID, analysisId, FILE_NAME, fileName)).toString(); - } - public String expandDonorUri(@NonNull String studyId, @NonNull String submitterId) { return donorUriTemplate.expand(Map.of(STUDY_ID, studyId, SUBMITTER_ID, submitterId)).toString(); } @@ -60,7 +55,6 @@ public String expandSampleUri(@NonNull String studyId, @NonNull String submitter public static UriResolver createUriResolver( @NonNull UriTemplateProperties uriTemplateProperties) { return UriResolver.builder() - .fileUriTemplate(processTemplate(uriTemplateProperties.getFile(), ANALYSIS_ID, FILE_NAME)) .donorUriTemplate(processTemplate(uriTemplateProperties.getDonor(), STUDY_ID, SUBMITTER_ID)) .specimenUriTemplate( processTemplate(uriTemplateProperties.getSpecimen(), STUDY_ID, SUBMITTER_ID)) diff --git a/song-server/src/main/resources/application.yml b/song-server/src/main/resources/application.yml index 6d40dc4ed..fbffbe3ac 100644 --- a/song-server/src/main/resources/application.yml +++ b/song-server/src/main/resources/application.yml @@ -88,7 +88,6 @@ id: donor: https://id.example.org/donor/id?submittedProjectId={studyId}&submittedDonorId={submitterId}&create=true specimen: https://id.example.org/specimen/id?submittedProjectId={studyId}&submittedSpecimenId={submitterId}&create=true sample: https://id.example.org/sample/id?submittedProjectId={studyId}&submittedSampleId={submitterId}&create=true - file: https://id.example.org/object/id?analysisId={analysisId}&fileName={fileName} validation: delayMs: 30 diff --git a/song-server/src/main/resources/schemas/analysis/analysisPayload.json b/song-server/src/main/resources/schemas/analysis/analysisPayload.json index 2cc4a1fe9..436c49cf3 100644 --- a/song-server/src/main/resources/schemas/analysis/analysisPayload.json +++ b/song-server/src/main/resources/schemas/analysis/analysisPayload.json @@ -181,6 +181,9 @@ }, "required": [ "study", "analysisType", "sample","file"], "properties": { + "analysisId" :{ + "not" : {} + }, "study" : { "type": "string", "minLength": 1 diff --git a/song-server/src/main/resources/schemas/analysis/analysisType.metaschema.json b/song-server/src/main/resources/schemas/analysis/analysisType.metaschema.json index c456f6529..7fdb465b1 100644 --- a/song-server/src/main/resources/schemas/analysis/analysisType.metaschema.json +++ b/song-server/src/main/resources/schemas/analysis/analysisType.metaschema.json @@ -20,6 +20,9 @@ "study" : { "not" : {} }, + "analysisState" : { + "not" : {} + }, "analysisType" : { "not" : {} }, diff --git a/song-server/src/test/java/bio/overture/song/server/controller/AnalysisTypeControllerTest.java b/song-server/src/test/java/bio/overture/song/server/controller/AnalysisTypeControllerTest.java index 805ed7e64..5c35730c3 100644 --- a/song-server/src/test/java/bio/overture/song/server/controller/AnalysisTypeControllerTest.java +++ b/song-server/src/test/java/bio/overture/song/server/controller/AnalysisTypeControllerTest.java @@ -62,6 +62,7 @@ import java.nio.file.Paths; import java.util.List; import java.util.function.Supplier; +import java.util.stream.Stream; import javax.transaction.Transactional; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -86,6 +87,7 @@ @AutoConfigureMockMvc(secure = false) @ActiveProfiles({"test"}) public class AnalysisTypeControllerTest { + private static final boolean ENABLE_HTTP_LOGGING = false; // This was done because the autowired mockMvc wasn't working properly, it was getting http 403 // errors @@ -104,7 +106,7 @@ public class AnalysisTypeControllerTest { @Before public void beforeTest() { this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); - this.endpointTester = createEndpointTester(mockMvc, true); + this.endpointTester = createEndpointTester(mockMvc, ENABLE_HTTP_LOGGING); this.randomGenerator = createRandomGenerator(getClass().getCanonicalName()); } @@ -570,17 +572,20 @@ public void listAnalysisTypes_filterByMultipleNames_success() { private void runInvalidRegisterTest( String filename, String expectedMessage, ServerError expectedServerError) { - val nonExistingName = generateUniqueName(); val inputInvalidSchema = FETCHER.readJsonNode(Paths.get("schema-fixtures/invalid").resolve(filename).toString()); + runInvalidRegisterTest(inputInvalidSchema, expectedMessage, expectedServerError); + } + + private void runInvalidRegisterTest( + JsonNode invalidSchema, String expectedMessage, ServerError expectedServerError) { + val nonExistingName = generateUniqueName(); val registerRequest = - RegisterAnalysisTypeRequest.builder() - .name(nonExistingName) - .schema(inputInvalidSchema) - .build(); + RegisterAnalysisTypeRequest.builder().name(nonExistingName).schema(invalidSchema).build(); val songErrorResponse = endpointTester .registerAnalysisTypePostRequestAnd(registerRequest) + .assertIsError() .assertServerError(expectedServerError) .getResponse(); val songError = parseErrorResponse(songErrorResponse); @@ -647,6 +652,37 @@ public void register_emptySchema_schemaViolation() { endpointTester.registerAnalysisTypePostRequestAnd(r).assertServerError(SCHEMA_VIOLATION); } + @Test + public void register_illegalFields_schemaViolation() { + Stream.of( + "analysisId", + "analysisState", + "study", + "analysisType", + "analysisTypeId", + "sample", + "file") + .forEach( + f -> { + // Create an invalid schema using one of the invalid fields + val inputInvalidSchema = + FETCHER.readJsonNode(Paths.get("schema-fixtures/valid.json").toString()); + val properties = (ObjectNode) inputInvalidSchema.path("properties"); + val field = properties.putObject(f); + field.put("type", "string"); + + log.info("Testing illegal field: " + f); + + // Test + runInvalidRegisterTest( + inputInvalidSchema, + "[AnalysisTypeService::schema.violation] - #/properties/" + + f + + ": subject must not be valid against schema {},#: expected type: Boolean, found: JSONObject", + SCHEMA_VIOLATION); + }); + } + /** Happy Path: test filtering the listing endpoint by multiple versions only */ @Test @Transactional diff --git a/song-server/src/test/java/bio/overture/song/server/controller/EnforcedUploadControllerTest.java b/song-server/src/test/java/bio/overture/song/server/controller/EnforcedUploadControllerTest.java index 2cd8df7ec..77b7bd18d 100644 --- a/song-server/src/test/java/bio/overture/song/server/controller/EnforcedUploadControllerTest.java +++ b/song-server/src/test/java/bio/overture/song/server/controller/EnforcedUploadControllerTest.java @@ -17,15 +17,13 @@ package bio.overture.song.server.controller; -import static bio.overture.song.core.exceptions.ServerErrors.ANALYSIS_TYPE_INCORRECT_VERSION; -import static bio.overture.song.core.utils.JsonUtils.objectToTree; -import static junit.framework.TestCase.assertTrue; -import static org.junit.Assert.assertEquals; - import bio.overture.song.core.model.AnalysisTypeId; +import bio.overture.song.core.utils.ResourceFetcher; import bio.overture.song.server.model.dto.UpdateAnalysisRequest; import bio.overture.song.server.service.AnalysisService; import bio.overture.song.server.service.StudyService; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.nio.file.Paths; import lombok.SneakyThrows; import lombok.val; import org.junit.Test; @@ -38,6 +36,16 @@ import org.springframework.web.client.ResourceAccessException; import org.springframework.web.context.WebApplicationContext; +import java.nio.file.Paths; + +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertEquals; +import static bio.overture.song.core.exceptions.ServerErrors.ANALYSIS_TYPE_INCORRECT_VERSION; +import static bio.overture.song.core.exceptions.ServerErrors.MALFORMED_PARAMETER; +import static bio.overture.song.core.exceptions.ServerErrors.SCHEMA_VIOLATION; +import static bio.overture.song.core.utils.JsonUtils.objectToTree; +import static bio.overture.song.core.utils.ResourceFetcher.ResourceType.TEST; + @RunWith(SpringRunner.class) @AutoConfigureMockMvc(secure = false) @ActiveProfiles({"test"}) @@ -49,6 +57,8 @@ public class EnforcedUploadControllerTest extends AbstractEnforcedTester { @Autowired private StudyService studyService; @Autowired private AnalysisService analysisService; + private static final ResourceFetcher DOCUMENTS_FETCHER = + ResourceFetcher.builder().resourceType(TEST).dataDir(Paths.get("documents/")).build(); /** Implementations */ @Override @@ -73,6 +83,82 @@ public void enforceLatestSubmit_NonLatest_AnalysisTypeIncorrectVersion() { .assertServerError(ANALYSIS_TYPE_INCORRECT_VERSION); } + @Test + public void testInvalidSpecimen() { + val j = (ObjectNode) DOCUMENTS_FETCHER.readJsonNode("variantcall-valid.json"); + val s = (ObjectNode) j.get("sample").get(0).get("specimen"); + s.put("specimenType", "invalid"); + + getEndpointTester().submitPostRequestAnd(getStudyId(), j).assertServerError(SCHEMA_VIOLATION); + + val j2 = (ObjectNode) DOCUMENTS_FETCHER.readJsonNode("variantcall-valid.json"); + val s2 = (ObjectNode) j2.get("sample").get(0).get("specimen"); + s2.put("specimenClass", "invalid"); + getEndpointTester().submitPostRequestAnd(getStudyId(), j2).assertServerError(SCHEMA_VIOLATION); + } + + @Test + @SneakyThrows + public void testInvalidAnalysisType() { + + // Test invalid analysisType format + val j1 = (ObjectNode) DOCUMENTS_FETCHER.readJsonNode("validation/variantcall-malformed-analysisType1.json"); + getEndpointTester() + .submitPostRequestAnd(getStudyId(), j1) + .assertServerError(MALFORMED_PARAMETER); + + // Test invalid analysisType format + val j2 = (ObjectNode) DOCUMENTS_FETCHER.readJsonNode("validation/variantcall-malformed-analysisType2.json"); + getEndpointTester() + .submitPostRequestAnd(getStudyId(), j2) + .assertServerError(MALFORMED_PARAMETER); + } + + @Test + @SneakyThrows + public void testInvalidSample() { + val j = (ObjectNode) DOCUMENTS_FETCHER.readJsonNode("variantcall-valid.json"); + val s = (ObjectNode) j.get("sample").get(0); + s.put("sampleType", "invalid"); + + getEndpointTester() + .submitPostRequestAnd(getStudyId(), j) + .assertServerError(SCHEMA_VIOLATION); + + // Test invalid sample format + val j2 = (ObjectNode) DOCUMENTS_FETCHER.readJsonNode("validation/variantcall-malformed-sample.json"); + getEndpointTester() + .submitPostRequestAnd(getStudyId(), j2) + .assertServerError(SCHEMA_VIOLATION); + } + + @Test + public void testInvalidFile() { + val j = (ObjectNode) DOCUMENTS_FETCHER.readJsonNode("variantcall-valid.json"); + val s = (ObjectNode) j.get("file").get(0); + s.put("fileType", "invalid"); + getEndpointTester().submitPostRequestAnd(getStudyId(), j).assertServerError(SCHEMA_VIOLATION); + + val j2 = (ObjectNode) DOCUMENTS_FETCHER.readJsonNode("variantcall-valid.json"); + val s2 = (ObjectNode) j2.get("file").get(0); + s2.put("fileAccess", "invalid"); + getEndpointTester().submitPostRequestAnd(getStudyId(), j2).assertServerError(SCHEMA_VIOLATION); + + val j3 = (ObjectNode) DOCUMENTS_FETCHER.readJsonNode("variantcall-valid.json"); + val s3 = (ObjectNode) j3.get("file").get(0); + s3.put("fileMd5sum", "invalid"); + getEndpointTester().submitPostRequestAnd(getStudyId(), j3).assertServerError(SCHEMA_VIOLATION); + } + + @Test + public void testInvalidDonor() { + val j = (ObjectNode) DOCUMENTS_FETCHER.readJsonNode("variantcall-valid.json"); + val s = (ObjectNode) j.get("sample").get(0).get("donor"); + s.put("donorGender", "invalid"); + getEndpointTester().submitPostRequestAnd(getStudyId(), j).assertServerError(SCHEMA_VIOLATION); + // 1) Invalid Gender + } + @Test @SneakyThrows public void enforceLatestPublish_NonLatest_AnalysisTypeIncorrectVersion() { diff --git a/song-server/src/test/java/bio/overture/song/server/service/FileModificationServiceTest.java b/song-server/src/test/java/bio/overture/song/server/service/FileModificationServiceTest.java index 5e7d6cfd2..51671a4f8 100644 --- a/song-server/src/test/java/bio/overture/song/server/service/FileModificationServiceTest.java +++ b/song-server/src/test/java/bio/overture/song/server/service/FileModificationServiceTest.java @@ -28,17 +28,13 @@ import static bio.overture.song.core.model.enums.FileUpdateTypes.NO_UPDATE; import static bio.overture.song.core.model.enums.FileUpdateTypes.resolveFileUpdateType; import static bio.overture.song.core.utils.RandomGenerator.createRandomGenerator; -import static bio.overture.song.server.service.FileModificationService.doUnpublish; import static bio.overture.song.server.utils.TestConstants.DEFAULT_ANALYSIS_ID; import static bio.overture.song.server.utils.TestConstants.DEFAULT_FILE_ID; import static bio.overture.song.server.utils.TestConstants.DEFAULT_STUDY_ID; import static bio.overture.song.server.utils.securestudy.impl.SecureFileTester.createSecureFileTester; import static com.google.common.collect.Lists.newArrayList; import static org.icgc.dcc.common.core.json.JsonNodeBuilders.object; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import bio.overture.song.core.model.FileUpdateRequest; import bio.overture.song.core.model.enums.AccessTypes; @@ -86,13 +82,6 @@ public void beforeTest() { this.uniqueMd5 = randomGenerator.generateRandomMD5(); } - @Test - public void testDoPublish() { - assertFalse(doUnpublish(NO_UPDATE)); - assertFalse(doUnpublish(METADATA_UPDATE)); - assertTrue(doUnpublish(CONTENT_UPDATE)); - } - @Test @Transactional public void testCheckFileUnrelatedToStudy() { @@ -127,7 +116,7 @@ public void testFileUpdateWithSuppressedAnalysis() { public void testFileUpdateWithPublishedAnalysis() { analysisService.securedUpdateState(DEFAULT_STUDY_ID, DEFAULT_ANALYSIS_ID, PUBLISHED); val originalAnalysis = analysisService.unsecuredDeepRead(DEFAULT_ANALYSIS_ID); - assertEquals(resolveAnalysisState(originalAnalysis.getAnalysisState()), PUBLISHED); + assertEquals(PUBLISHED, resolveAnalysisState(originalAnalysis.getAnalysisState())); val originalFile = fileConverter.convertToFileDTO(fileService.securedRead(DEFAULT_STUDY_ID, DEFAULT_FILE_ID)); @@ -137,12 +126,12 @@ public void testFileUpdateWithPublishedAnalysis() { fileModificationService.securedFileWithAnalysisUpdate( DEFAULT_STUDY_ID, DEFAULT_FILE_ID, noChangeRequest); assertFalse(noChangeResponse.isUnpublishedAnalysis()); - assertEquals(noChangeResponse.getFileUpdateType(), NO_UPDATE); - assertEquals(noChangeResponse.getOriginalAnalysisState(), PUBLISHED); - assertEquals(noChangeResponse.getOriginalFile(), originalFile); + assertEquals(NO_UPDATE, noChangeResponse.getFileUpdateType()); + assertEquals(PUBLISHED, noChangeResponse.getOriginalAnalysisState()); + assertEquals(originalFile, noChangeResponse.getOriginalFile()); assertEquals( - noChangeResponse.getMessage(), - "Original analysisState 'PUBLISHED' was not changed since the fileUpdateType was 'NO_UPDATE'"); + "No update for file with objectId 'FI1' and analysisId 'AN1'", + noChangeResponse.getMessage()); // Metadata Update val metadataUpdateRequest = @@ -160,35 +149,28 @@ public void testFileUpdateWithPublishedAnalysis() { fileModificationService.securedFileWithAnalysisUpdate( DEFAULT_STUDY_ID, DEFAULT_FILE_ID, metadataUpdateRequest); assertFalse(metadataUpdateResponse.isUnpublishedAnalysis()); - assertEquals(metadataUpdateResponse.getFileUpdateType(), METADATA_UPDATE); - assertEquals(metadataUpdateResponse.getOriginalAnalysisState(), PUBLISHED); - assertEquals(metadataUpdateResponse.getOriginalFile(), originalFile2); + assertEquals(METADATA_UPDATE, metadataUpdateResponse.getFileUpdateType()); + assertEquals(PUBLISHED, metadataUpdateResponse.getOriginalAnalysisState()); + assertEquals(originalFile2, metadataUpdateResponse.getOriginalFile()); assertEquals( - metadataUpdateResponse.getMessage(), - "Original analysisState 'PUBLISHED' was not changed since the fileUpdateType was 'METADATA_UPDATE'"); + "Updated file with objectId 'FI1' and analysisId 'AN1'", + metadataUpdateResponse.getMessage()); // Content Update val contentUpdateRequest = FileUpdateRequest.builder().fileSize(originalFile2.getFileSize() + 77771L).build(); - val originalFile3 = - fileConverter.convertToFileDTO(fileService.securedRead(DEFAULT_STUDY_ID, DEFAULT_FILE_ID)); - val contentUpdateResponse = - fileModificationService.securedFileWithAnalysisUpdate( - DEFAULT_STUDY_ID, DEFAULT_FILE_ID, contentUpdateRequest); - assertTrue(contentUpdateResponse.isUnpublishedAnalysis()); - assertEquals(contentUpdateResponse.getFileUpdateType(), CONTENT_UPDATE); - assertEquals(contentUpdateResponse.getOriginalAnalysisState(), PUBLISHED); - assertEquals(contentUpdateResponse.getOriginalFile(), originalFile3); - assertEquals( - contentUpdateResponse.getMessage(), - "[WARNING]: Changed analysis from 'PUBLISHED' to 'UNPUBLISHED'"); + SongErrorAssertions.assertSongError( + () -> + fileModificationService.securedFileWithAnalysisUpdate( + DEFAULT_STUDY_ID, DEFAULT_FILE_ID, contentUpdateRequest), + ILLEGAL_FILE_UPDATE_REQUEST); } @Test @Transactional public void testFileUpdateWithUnpublishedAnalysis() { val originalAnalysis = analysisService.unsecuredDeepRead(DEFAULT_ANALYSIS_ID); - assertEquals(resolveAnalysisState(originalAnalysis.getAnalysisState()), UNPUBLISHED); + assertEquals(UNPUBLISHED, resolveAnalysisState(originalAnalysis.getAnalysisState())); val originalFile = fileConverter.convertToFileDTO(fileService.securedRead(DEFAULT_STUDY_ID, DEFAULT_FILE_ID)); @@ -198,10 +180,12 @@ public void testFileUpdateWithUnpublishedAnalysis() { fileModificationService.securedFileWithAnalysisUpdate( DEFAULT_STUDY_ID, DEFAULT_FILE_ID, noChangeRequest); assertFalse(noChangeResponse.isUnpublishedAnalysis()); - assertEquals(noChangeResponse.getFileUpdateType(), NO_UPDATE); - assertEquals(noChangeResponse.getOriginalAnalysisState(), UNPUBLISHED); - assertEquals(noChangeResponse.getOriginalFile(), originalFile); - assertTrue(noChangeResponse.getMessage().contains("Did not change analysisState since it is")); + assertEquals(NO_UPDATE, noChangeResponse.getFileUpdateType()); + assertEquals(UNPUBLISHED, noChangeResponse.getOriginalAnalysisState()); + assertEquals(originalFile, noChangeResponse.getOriginalFile()); + assertEquals( + "No update for file with objectId 'FI1' and analysisId 'AN1'", + noChangeResponse.getMessage()); // Metadata Update val metadataUpdateRequest = @@ -219,11 +203,12 @@ public void testFileUpdateWithUnpublishedAnalysis() { fileModificationService.securedFileWithAnalysisUpdate( DEFAULT_STUDY_ID, DEFAULT_FILE_ID, metadataUpdateRequest); assertFalse(metadataUpdateResponse.isUnpublishedAnalysis()); - assertEquals(metadataUpdateResponse.getFileUpdateType(), METADATA_UPDATE); - assertEquals(metadataUpdateResponse.getOriginalAnalysisState(), UNPUBLISHED); - assertEquals(metadataUpdateResponse.getOriginalFile(), originalFile2); - assertTrue( - metadataUpdateResponse.getMessage().contains("Did not change analysisState since it is")); + assertEquals(METADATA_UPDATE, metadataUpdateResponse.getFileUpdateType()); + assertEquals(UNPUBLISHED, metadataUpdateResponse.getOriginalAnalysisState()); + assertEquals(originalFile2, metadataUpdateResponse.getOriginalFile()); + assertEquals( + "Updated file with objectId 'FI1' and analysisId 'AN1'", + metadataUpdateResponse.getMessage()); // Content Update val contentUpdateRequest = @@ -234,11 +219,12 @@ public void testFileUpdateWithUnpublishedAnalysis() { fileModificationService.securedFileWithAnalysisUpdate( DEFAULT_STUDY_ID, DEFAULT_FILE_ID, contentUpdateRequest); assertFalse(contentUpdateResponse.isUnpublishedAnalysis()); - assertEquals(contentUpdateResponse.getFileUpdateType(), CONTENT_UPDATE); - assertEquals(contentUpdateResponse.getOriginalAnalysisState(), UNPUBLISHED); - assertEquals(contentUpdateResponse.getOriginalFile(), originalFile3); - assertTrue( - contentUpdateResponse.getMessage().contains("Did not change analysisState since it is")); + assertEquals(CONTENT_UPDATE, contentUpdateResponse.getFileUpdateType()); + assertEquals(UNPUBLISHED, contentUpdateResponse.getOriginalAnalysisState()); + assertEquals(originalFile3, contentUpdateResponse.getOriginalFile()); + assertEquals( + "Updated file with objectId 'FI1' and analysisId 'AN1'", + contentUpdateResponse.getMessage()); } @Test @@ -314,9 +300,9 @@ public void testUpdateWithRequests() { val goldenFile = converter.copyFile(referenceFile); val u1 = FileUpdateRequest.builder().fileAccess("controlled").build(); - assertEquals(fileModificationService.updateWithRequest(referenceFile, u1), METADATA_UPDATE); + assertEquals(METADATA_UPDATE, fileModificationService.updateWithRequest(referenceFile, u1)); assertFalse(referenceFile == goldenFile); - assertEquals(referenceFile, goldenFile); + assertEquals(goldenFile, referenceFile); u1.setInfo( object() @@ -324,63 +310,63 @@ public void testUpdateWithRequests() { randomGenerator.generateRandomUUIDAsString(), randomGenerator.generateRandomUUIDAsString()) .end()); - assertEquals(fileModificationService.updateWithRequest(referenceFile, u1), METADATA_UPDATE); + assertEquals(METADATA_UPDATE, fileModificationService.updateWithRequest(referenceFile, u1)); assertFalse(referenceFile == goldenFile); - assertEquals(referenceFile, goldenFile); + assertEquals(goldenFile, referenceFile); u1.setFileAccess("open"); - assertEquals(fileModificationService.updateWithRequest(referenceFile, u1), METADATA_UPDATE); + assertEquals(METADATA_UPDATE, fileModificationService.updateWithRequest(referenceFile, u1)); assertFalse(referenceFile == goldenFile); - assertEquals(referenceFile, goldenFile); + assertEquals(goldenFile, referenceFile); u1.setFileAccess(null); - assertEquals(fileModificationService.updateWithRequest(referenceFile, u1), METADATA_UPDATE); + assertEquals(METADATA_UPDATE, fileModificationService.updateWithRequest(referenceFile, u1)); assertFalse(referenceFile == goldenFile); - assertEquals(referenceFile, goldenFile); + assertEquals(goldenFile, referenceFile); u1.setFileSize(19191L); - assertEquals(fileModificationService.updateWithRequest(referenceFile, u1), CONTENT_UPDATE); + assertEquals(CONTENT_UPDATE, fileModificationService.updateWithRequest(referenceFile, u1)); assertFalse(referenceFile == goldenFile); - assertEquals(referenceFile, goldenFile); + assertEquals(goldenFile, referenceFile); u1.setFileMd5sum(randomGenerator.generateRandomMD5()); - assertEquals(fileModificationService.updateWithRequest(referenceFile, u1), CONTENT_UPDATE); + assertEquals(CONTENT_UPDATE, fileModificationService.updateWithRequest(referenceFile, u1)); assertFalse(referenceFile == goldenFile); - assertEquals(referenceFile, goldenFile); + assertEquals(goldenFile, referenceFile); u1.setInfo(null); - assertEquals(fileModificationService.updateWithRequest(referenceFile, u1), CONTENT_UPDATE); + assertEquals(CONTENT_UPDATE, fileModificationService.updateWithRequest(referenceFile, u1)); assertFalse(referenceFile == goldenFile); - assertEquals(referenceFile, goldenFile); + assertEquals(goldenFile, referenceFile); u1.setFileAccess(null); - assertEquals(fileModificationService.updateWithRequest(referenceFile, u1), CONTENT_UPDATE); + assertEquals(CONTENT_UPDATE, fileModificationService.updateWithRequest(referenceFile, u1)); assertFalse(referenceFile == goldenFile); - assertEquals(referenceFile, goldenFile); + assertEquals(goldenFile, referenceFile); u1.setFileMd5sum(uniqueMd5); - assertEquals(fileModificationService.updateWithRequest(referenceFile, u1), CONTENT_UPDATE); + assertEquals(CONTENT_UPDATE, fileModificationService.updateWithRequest(referenceFile, u1)); assertFalse(referenceFile == goldenFile); - assertEquals(referenceFile, goldenFile); + assertEquals(goldenFile, referenceFile); u1.setFileMd5sum(null); - assertEquals(fileModificationService.updateWithRequest(referenceFile, u1), CONTENT_UPDATE); + assertEquals(CONTENT_UPDATE, fileModificationService.updateWithRequest(referenceFile, u1)); assertFalse(referenceFile == goldenFile); - assertEquals(referenceFile, goldenFile); + assertEquals(goldenFile, referenceFile); u1.setFileSize(referenceFile.getFileSize()); - assertEquals(fileModificationService.updateWithRequest(referenceFile, u1), NO_UPDATE); + assertEquals(NO_UPDATE, fileModificationService.updateWithRequest(referenceFile, u1)); assertFalse(referenceFile == goldenFile); - assertEquals(referenceFile, goldenFile); + assertEquals(goldenFile, referenceFile); u1.setFileSize(null); - assertEquals(fileModificationService.updateWithRequest(referenceFile, u1), NO_UPDATE); + assertEquals(NO_UPDATE, fileModificationService.updateWithRequest(referenceFile, u1)); assertNull(u1.getFileAccess()); assertNull(u1.getFileSize()); assertNull(u1.getFileMd5sum()); assertNull(u1.getInfo()); assertFalse(referenceFile == goldenFile); - assertEquals(referenceFile, goldenFile); + assertEquals(goldenFile, referenceFile); } @Test @@ -391,38 +377,38 @@ public void testFileUpdateTypeResolution() { // update access field val u1 = FileUpdateRequest.builder().fileAccess("controlled").build(); - assertEquals(resolveFileUpdateType(f1, u1), METADATA_UPDATE); + assertEquals(METADATA_UPDATE, resolveFileUpdateType(f1, u1)); // update info field u1.setInfo(object().with("myInfoKey2", "myInfoValue2").end()); - assertEquals(resolveFileUpdateType(f1, u1), METADATA_UPDATE); + assertEquals(METADATA_UPDATE, resolveFileUpdateType(f1, u1)); // update file size val u2 = FileUpdateRequest.builder().fileSize(123123L).build(); u1.setFileSize(123456L); // test request u1 with metadata updates - assertEquals(resolveFileUpdateType(f1, u1), CONTENT_UPDATE); + assertEquals(CONTENT_UPDATE, resolveFileUpdateType(f1, u1)); // test request u2 without any metadata updates - assertEquals(resolveFileUpdateType(f1, u2), CONTENT_UPDATE); + assertEquals(CONTENT_UPDATE, resolveFileUpdateType(f1, u2)); // update file md5 u2.setFileMd5sum(randomGenerator.generateRandomMD5()); u1.setFileMd5sum(randomGenerator.generateRandomMD5()); // test request u1 with metadata updates - assertEquals(resolveFileUpdateType(f1, u1), CONTENT_UPDATE); + assertEquals(CONTENT_UPDATE, resolveFileUpdateType(f1, u1)); // test request u2 without any metadata updates - assertEquals(resolveFileUpdateType(f1, u2), CONTENT_UPDATE); + assertEquals(CONTENT_UPDATE, resolveFileUpdateType(f1, u2)); // test nulls val u3 = FileUpdateRequest.builder().build(); - assertEquals(resolveFileUpdateType(f1, u3), NO_UPDATE); + assertEquals(NO_UPDATE, resolveFileUpdateType(f1, u3)); u3.setFileMd5sum(f1.getFileMd5sum()); u3.setFileSize(f1.getFileSize()); u3.setFileAccess(f1.getFileAccess()); u3.setInfo(f1.getInfo()); - assertEquals(resolveFileUpdateType(f1, u3), NO_UPDATE); + assertEquals(NO_UPDATE, resolveFileUpdateType(f1, u3)); - assertEquals(f1, golden); + assertEquals(golden, f1); } private FileEntity buildReferenceFile() { diff --git a/song-server/src/test/java/bio/overture/song/server/service/FileServiceTest.java b/song-server/src/test/java/bio/overture/song/server/service/FileServiceTest.java index 928d14d12..3fceb3348 100644 --- a/song-server/src/test/java/bio/overture/song/server/service/FileServiceTest.java +++ b/song-server/src/test/java/bio/overture/song/server/service/FileServiceTest.java @@ -20,7 +20,7 @@ import static bio.overture.song.core.exceptions.ServerErrors.STUDY_ID_DOES_NOT_EXIST; import static bio.overture.song.core.model.enums.AccessTypes.CONTROLLED; import static bio.overture.song.core.model.enums.AccessTypes.OPEN; -import static bio.overture.song.core.testing.SongErrorAssertions.assertExceptionThrownBy; +import static bio.overture.song.core.model.enums.FileTypes.FAI; import static bio.overture.song.core.testing.SongErrorAssertions.assertSongError; import static bio.overture.song.core.utils.RandomGenerator.createRandomGenerator; import static bio.overture.song.server.utils.TestConstants.DEFAULT_ANALYSIS_ID; @@ -105,7 +105,7 @@ public void testCreateAndDeleteFile() { f.setStudyId(studyId); f.setFileSize(0L); - f.setFileType("FAI"); + f.setFileType(FAI); f.setFileMd5sum("6bb8ee7218e96a59e0ad898b4f5360f1"); f.setInfo(metadata); f.setFileAccess(OPEN); @@ -160,23 +160,6 @@ public void testSaveFileAsTgz() { assertEquals(updatedFile, actualFile); } - @Test - public void testCreateFileUnknownType() { - assertExceptionThrownBy( - IllegalStateException.class, - () -> - FileEntity.builder() - .fileAccess("controlled") - .fileMd5sum(randomGenerator.generateRandomMD5()) - .fileName(randomGenerator.generateRandomAsciiString(10)) - .fileSize((long) randomGenerator.generateRandomInt(100, 100000)) - .analysisId(randomGenerator.generateRandomUUIDAsString()) - .objectId(randomGenerator.generateRandomUUIDAsString()) - .studyId(randomGenerator.generateRandomAsciiString(7)) - .fileType("TGZZZZZ") - .build()); - } - @Test public void testUpdateFile() { diff --git a/song-server/src/test/java/bio/overture/song/server/service/SerializationTest.java b/song-server/src/test/java/bio/overture/song/server/service/SerializationTest.java index 5fb417b73..c27597f23 100644 --- a/song-server/src/test/java/bio/overture/song/server/service/SerializationTest.java +++ b/song-server/src/test/java/bio/overture/song/server/service/SerializationTest.java @@ -248,29 +248,6 @@ public void testDonorValues() { assertEquals(json, expectedJson); } - @Test - public void testInvalidValues() { - val id = "DO000123"; - val submitterId = "123"; - val studyId = "X23-CA"; - val gender = "potatoes"; - - boolean failed = false; - try { - val donor = - Donor.builder() - .donorId(id) - .donorSubmitterId(submitterId) - .studyId(studyId) - .donorGender(gender) - .build(); - } catch (IllegalArgumentException e) { - failed = true; - } - - assertTrue(failed); - } - @Test public void testListFile() throws IOException { val singleQuotedJson = diff --git a/song-server/src/test/java/bio/overture/song/server/service/UploadServiceTest.java b/song-server/src/test/java/bio/overture/song/server/service/UploadServiceTest.java index 6787925ff..fd2836f76 100644 --- a/song-server/src/test/java/bio/overture/song/server/service/UploadServiceTest.java +++ b/song-server/src/test/java/bio/overture/song/server/service/UploadServiceTest.java @@ -17,10 +17,12 @@ package bio.overture.song.server.service; import static bio.overture.song.core.exceptions.ServerErrors.PAYLOAD_PARSING; +import static bio.overture.song.core.exceptions.ServerErrors.SCHEMA_VIOLATION; import static bio.overture.song.core.testing.SongErrorAssertions.assertSongError; import static bio.overture.song.core.utils.JsonUtils.fromJson; import static bio.overture.song.core.utils.JsonUtils.toJson; import static bio.overture.song.core.utils.RandomGenerator.createRandomGenerator; +import static bio.overture.song.server.model.enums.ModelAttributeNames.ANALYSIS_ID; import static bio.overture.song.server.utils.TestAnalysis.extractBoolean; import static bio.overture.song.server.utils.TestAnalysis.extractNode; import static bio.overture.song.server.utils.TestAnalysis.extractString; @@ -45,6 +47,7 @@ import bio.overture.song.server.repository.UploadRepository; import bio.overture.song.server.service.id.IdService; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import java.util.Map; import javax.transaction.Transactional; @@ -139,6 +142,16 @@ public void submit_CorruptedPayload_PayloadParsingError() { assertSongError(() -> uploadService.submit(DEFAULT_STUDY, corruptedPayload), PAYLOAD_PARSING); } + @Test + @SneakyThrows + public void submit_AnalysisIdInPayload_SchemaValidationError() { + val p = createPayloadWithDifferentAnalysisId(); + val invalidPayload = (ObjectNode) new ObjectMapper().readTree(p.getJsonPayload()); + invalidPayload.put(ANALYSIS_ID, p.getAnalysisId()); + assertSongError( + () -> uploadService.submit(DEFAULT_STUDY, invalidPayload.toString()), SCHEMA_VIOLATION); + } + @Test @Transactional public void testSave2PayloadsWithSameSpecimen() { @@ -219,13 +232,6 @@ private InternalPayload createPayloadWithDifferentAnalysisId() { return InternalPayload.builder().analysisId(analysisId).jsonPayload(jsonPayload).build(); } - @SneakyThrows - private void test(String fileName) { - val jsonPayload = getJsonStringFromClasspath(fileName); - val response = uploadService.submit(DEFAULT_STUDY, jsonPayload); - assertEquals(Responses.OK, response.getStatus()); - } - @Value @Builder private static class InternalPayload { diff --git a/song-server/src/test/java/bio/overture/song/server/service/id/FederatedIdServiceTest.java b/song-server/src/test/java/bio/overture/song/server/service/id/FederatedIdServiceTest.java index 8e6048ae5..dcb10d501 100644 --- a/song-server/src/test/java/bio/overture/song/server/service/id/FederatedIdServiceTest.java +++ b/song-server/src/test/java/bio/overture/song/server/service/id/FederatedIdServiceTest.java @@ -12,10 +12,14 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import bio.overture.song.core.utils.RandomGenerator; import java.util.List; +import java.util.Optional; +import java.util.UUID; import lombok.val; import org.apache.commons.lang.NotImplementedException; import org.junit.Before; @@ -47,7 +51,6 @@ enum MODE { private static final String DONOR_URL = "https://example.org/donor/id"; private static final String SPECIMEN_URL = "https://example.org/specimen/id"; private static final String SAMPLE_URL = "https://example.org/sample/id"; - private static final String FILE_URL = "https://example.org/file/id"; /** Mocks */ @Mock private RestClient restClient; @@ -132,25 +135,15 @@ public void getSample_otherError_IdServiceError() { } @Test - public void getFile_existing_id() { - setupFile(GOOD); - val fileResult = idService.getFileId(DEFAULT_ANALYSIS_ID, DEFAULT_FILE_NAME); - assertTrue(fileResult.isPresent()); - assertEquals(fileResult.get(), DEFAULT_ID); - } - - @Test - public void getFile_nonExisting_emptyResult() { - setupFile(NOT_FOUND); - val fileResult = idService.getFileId(DEFAULT_ANALYSIS_ID, DEFAULT_FILE_NAME); - assertFalse(fileResult.isPresent()); - } - - @Test - public void getFile_otherError_IdServiceError() { - setupFile(ERROR); - assertSongError( - () -> idService.getFileId(DEFAULT_ANALYSIS_ID, DEFAULT_FILE_NAME), ID_SERVICE_ERROR); + public void testFileId() { + val expectedObjectId = UUID.randomUUID().toString(); + when(localIdService.getFileId(DEFAULT_ANALYSIS_ID, DEFAULT_FILE_NAME)) + .thenReturn(Optional.of(expectedObjectId)); + val result = idService.getFileId(DEFAULT_ANALYSIS_ID, DEFAULT_FILE_NAME); + assertTrue(result.isPresent()); + val actualObjectId = result.get(); + assertEquals(actualObjectId, expectedObjectId); + verify(localIdService, times(1)).getFileId(DEFAULT_ANALYSIS_ID, DEFAULT_FILE_NAME); } @Test @@ -176,11 +169,6 @@ private void setupSample(MODE mode) { when(uriResolver.expandSampleUri(anyString(), anyString())).thenReturn(SAMPLE_URL); } - private void setupFile(MODE mode) { - baseSetup(mode, FILE_URL); - when(uriResolver.expandFileUri(anyString(), anyString())).thenReturn(FILE_URL); - } - private HttpStatusCodeException generateNonNotFoundStatusCodeException() { val nonNotFoundError = randomGenerator.shuffleList(List.of(HttpStatus.values())).stream() diff --git a/song-server/src/test/java/bio/overture/song/server/service/id/LocalIdServiceTest.java b/song-server/src/test/java/bio/overture/song/server/service/id/LocalIdServiceTest.java index 0d44a8a4c..0310599be 100644 --- a/song-server/src/test/java/bio/overture/song/server/service/id/LocalIdServiceTest.java +++ b/song-server/src/test/java/bio/overture/song/server/service/id/LocalIdServiceTest.java @@ -35,7 +35,6 @@ public class LocalIdServiceTest { private static final Optional ID_A = Optional.of("8540ebac-66f2-553a-b865-0d3006edd892"); private static final Optional ID_B = Optional.of("57f844eb-4ab4-5d3d-8dc1-8b7a463e20c1"); private static final Optional ID_C = Optional.of("b4f5aea1-1f4c-5e12-8557-76dbadb26239"); - private static final String UUID1 = "b7f5aea7-1f4c-5e12-8557-76dbadb26333"; private LocalIdService localIdService; diff --git a/song-server/src/test/java/bio/overture/song/server/service/id/UriResolverTest.java b/song-server/src/test/java/bio/overture/song/server/service/id/UriResolverTest.java index 4fc97fcc6..cccb49879 100644 --- a/song-server/src/test/java/bio/overture/song/server/service/id/UriResolverTest.java +++ b/song-server/src/test/java/bio/overture/song/server/service/id/UriResolverTest.java @@ -56,11 +56,6 @@ public void testUnknownTemplateVariable() { bad.setSample(good.getSample() + "&something={someVar}"); assertOnlyUnknownVariables(bad, "someVar"); bad.setSample(good.getSample()); - - // Test file - bad.setFile(good.getFile() + "&something={someVar}"); - assertOnlyUnknownVariables(bad, "someVar"); - bad.setFile(good.getFile()); } @Test @@ -82,11 +77,6 @@ public void testMissingTemplateVariable() { bad.setSample("https://example.org/proj={studyId}"); assertOnlyMissingVariables(bad, "submitterId"); bad.setSample(good.getSample()); - - // Test file - bad.setFile("https://example.org/an={analysisId}"); - assertOnlyMissingVariables(bad, "fileName"); - bad.setFile(good.getFile()); } private static void assertOnlyMissingVariables(UriTemplateProperties p, String... variables) { @@ -122,10 +112,6 @@ private static void assertGood(UriResolver ur) { assertEquals( "https://example.org?sid=subSample123&proj=ABC123-CA", ur.expandSampleUri("ABC123-CA", "subSample123")); - - assertEquals( - "https://example.org?anid=AN01&fname=my-file.vcf.gz", - ur.expandFileUri("AN01", "my-file.vcf.gz")); } private static void assertExceptionThrown( diff --git a/song-server/src/test/java/bio/overture/song/server/utils/web/ResponseOption.java b/song-server/src/test/java/bio/overture/song/server/utils/web/ResponseOption.java index 6f52a5db0..f81615350 100644 --- a/song-server/src/test/java/bio/overture/song/server/utils/web/ResponseOption.java +++ b/song-server/src/test/java/bio/overture/song/server/utils/web/ResponseOption.java @@ -20,6 +20,7 @@ import static bio.overture.song.core.exceptions.SongError.parseErrorResponse; import static bio.overture.song.core.utils.Deserialization.deserializeList; import static bio.overture.song.core.utils.Deserialization.deserializePage; +import static java.lang.String.format; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -70,6 +71,15 @@ public Set extractManyEntities(@NonNull Class entityClass) { .map(x -> internalExtractManyEntitiesFromResponse(x, entityClass)); } + public ResponseOption assertIsError() { + assertTrue( + format( + "Was expecting an error (4xx or 5xx status code), " + "however status code was [%s]", + response.getStatusCode()), + response.getStatusCode().isError()); + return this; + } + public ResponseOption assertServerError(ServerError serverError) { val songError = parseErrorResponse(response); assertEquals(serverError.getErrorId(), songError.getErrorId()); diff --git a/song-server/src/test/resources/documents/uriResolver/good.json b/song-server/src/test/resources/documents/uriResolver/good.json index c844821ff..e5f0a7cf7 100644 --- a/song-server/src/test/resources/documents/uriResolver/good.json +++ b/song-server/src/test/resources/documents/uriResolver/good.json @@ -1,7 +1,6 @@ { "donor" : "https://example.org?sid={submitterId}&proj={studyId}", "specimen" : "https://example.org?sid={submitterId}&proj={studyId}", - "sample" : "https://example.org?sid={submitterId}&proj={studyId}", - "file" : "https://example.org?anid={analysisId}&fname={fileName}" + "sample" : "https://example.org?sid={submitterId}&proj={studyId}" } diff --git a/song-server/src/test/resources/documents/validation/dynamic-analysis-type/rendered-schema.json b/song-server/src/test/resources/documents/validation/dynamic-analysis-type/rendered-schema.json index 7c74a157a..f5c7d6298 100644 --- a/song-server/src/test/resources/documents/validation/dynamic-analysis-type/rendered-schema.json +++ b/song-server/src/test/resources/documents/validation/dynamic-analysis-type/rendered-schema.json @@ -181,6 +181,9 @@ }, "required": [ "study", "analysisType","experiment","sample","file", "firstName"], "properties": { + "analysisId" : { + "not" : {} + }, "study" : { "type": "string", "minLength": 1 diff --git a/song-server/src/test/resources/documents/validation/variantcall-malformed-analysisType1.json b/song-server/src/test/resources/documents/validation/variantcall-malformed-analysisType1.json new file mode 100644 index 000000000..8d714b52f --- /dev/null +++ b/song-server/src/test/resources/documents/validation/variantcall-malformed-analysisType1.json @@ -0,0 +1,42 @@ +{ + "study": "ABC123", + "analysisType" : "variantCall", + "experiment": { + "variantCallingTool": "silver bullet", + "matchedNormalSampleSubmitterId": "sample x24-11a" + }, + "sample": [ + { + "sampleSubmitterId": "internal_sample_98024759826836", + "sampleType": "Total RNA", + "specimen": { + "specimenSubmitterId": "internal_specimen_9b73gk8s02dk", + "specimenClass": "Tumour", + "specimenType": "Primary tumour - other" + }, + "donor": { + "donorSubmitterId": "internal_donor_123456789-00", + "donorGender": "female" + } + } + ], + "file": [ + { + "fileName": "a3bc0998a-3521-43fd-fa10-a834f3874e46.MUSE_1-0rc-vcf.20170711.somatic.snv_mnv.vcf.gz", + "fileSize": 376953, + "fileMd5sum": "61a91c4bf04ac2bd3795254104f75ad3", + "fileAccess" : "open", + "fileType": "VCF" + }, + { + "fileName": "a3bc0998a-3521-43fd-fa10-a834f3874e46.MUSE_1-0rc-vcf.20170711.somatic.snv_mnv.vcf.gz.idx", + "fileSize": 4840, + "fileMd5sum": "61a91c4bf04bb54d3795254104f75ad3", + "fileAccess" : "open", + "fileType": "IDX" + } + ], + "info": { + "description": "This is extra info in a JSON format" + } +} \ No newline at end of file diff --git a/song-server/src/test/resources/documents/validation/variantcall-malformed-analysisType2.json b/song-server/src/test/resources/documents/validation/variantcall-malformed-analysisType2.json new file mode 100644 index 000000000..b48271b3f --- /dev/null +++ b/song-server/src/test/resources/documents/validation/variantcall-malformed-analysisType2.json @@ -0,0 +1,41 @@ +{ + "study": "ABC123", + "experiment": { + "variantCallingTool": "silver bullet", + "matchedNormalSampleSubmitterId": "sample x24-11a" + }, + "sample": [ + { + "sampleSubmitterId": "internal_sample_98024759826836", + "sampleType": "Total RNA", + "specimen": { + "specimenSubmitterId": "internal_specimen_9b73gk8s02dk", + "specimenClass": "Tumour", + "specimenType": "Primary tumour - other" + }, + "donor": { + "donorSubmitterId": "internal_donor_123456789-00", + "donorGender": "female" + } + } + ], + "file": [ + { + "fileName": "a3bc0998a-3521-43fd-fa10-a834f3874e46.MUSE_1-0rc-vcf.20170711.somatic.snv_mnv.vcf.gz", + "fileSize": 376953, + "fileMd5sum": "61a91c4bf04ac2bd3795254104f75ad3", + "fileAccess" : "open", + "fileType": "VCF" + }, + { + "fileName": "a3bc0998a-3521-43fd-fa10-a834f3874e46.MUSE_1-0rc-vcf.20170711.somatic.snv_mnv.vcf.gz.idx", + "fileSize": 4840, + "fileMd5sum": "61a91c4bf04bb54d3795254104f75ad3", + "fileAccess" : "open", + "fileType": "IDX" + } + ], + "info": { + "description": "This is extra info in a JSON format" + } +} \ No newline at end of file diff --git a/song-server/src/test/resources/documents/validation/variantcall-malformed-sample.json b/song-server/src/test/resources/documents/validation/variantcall-malformed-sample.json new file mode 100644 index 000000000..4b70620f0 --- /dev/null +++ b/song-server/src/test/resources/documents/validation/variantcall-malformed-sample.json @@ -0,0 +1,43 @@ +{ + "study": "ABC123", + "analysisType" : { + "name" : "variantCall", + "version" : 1 + }, + "experiment": { + "variantCallingTool": "silver bullet", + "matchedNormalSampleSubmitterId": "sample x24-11a" + }, + "sample": { + "sampleSubmitterId": "internal_sample_98024759826836", + "sampleType": "Total RNA", + "specimen": { + "specimenSubmitterId": "internal_specimen_9b73gk8s02dk", + "specimenClass": "Tumour", + "specimenType": "Primary tumour - other" + }, + "donor": { + "donorSubmitterId": "internal_donor_123456789-00", + "donorGender": "female" + } + } , + "file": [ + { + "fileName": "a3bc0998a-3521-43fd-fa10-a834f3874e46.MUSE_1-0rc-vcf.20170711.somatic.snv_mnv.vcf.gz", + "fileSize": 376953, + "fileMd5sum": "61a91c4bf04ac2bd3795254104f75ad3", + "fileAccess" : "open", + "fileType": "VCF" + }, + { + "fileName": "a3bc0998a-3521-43fd-fa10-a834f3874e46.MUSE_1-0rc-vcf.20170711.somatic.snv_mnv.vcf.gz.idx", + "fileSize": 4840, + "fileMd5sum": "61a91c4bf04bb54d3795254104f75ad3", + "fileAccess" : "open", + "fileType": "IDX" + } + ], + "info": { + "description": "This is extra info in a JSON format" + } +} \ No newline at end of file