diff --git a/docker-compose.yml b/docker-compose.yml
index f45227ea7..7e1b2fa24 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -176,6 +176,9 @@ services:
- localstack
environment:
- LOCALSTACK_HOST=localstack
+ - PERSISTENCE=1
+ volumes:
+ - localstack_data:/var/lib/localstack
networks:
backend:
@@ -187,3 +190,5 @@ volumes:
name: ${GIGWA_CONTAINER_NAME:-gigwa}_data
gigwa_mongo_data:
name: ${GIGWA_CONTAINER_NAME:-gigwa}_mongo_data
+ localstack_data:
+ name: localstack_data
diff --git a/pom.xml b/pom.xml
index 1788b1604..e37301b29 100644
--- a/pom.xml
+++ b/pom.xml
@@ -89,7 +89,7 @@
31.0.1-jre
4.9.3
4.3.1
- 2.2.0
+ 2.2.1
2.11.0
2.2.1
@@ -464,6 +464,11 @@
micronaut-amazon-awssdk-s3
2.0.5-micronaut-2.0
+
+ com.github.samtools
+ htsjdk
+ 2.24.1
+
diff --git a/settings.xml b/settings.xml
index 9b5fb7aa5..54c730caf 100644
--- a/settings.xml
+++ b/settings.xml
@@ -49,6 +49,12 @@
true
true
+
+ central-snapshots
+ https://central.sonatype.com/repository/maven-snapshots/
+ false
+ true
+
github-fannypack
FannyPack github repository
diff --git a/src/main/java/org/breedinginsight/api/model/v1/request/query/GenotypeImportQuery.java b/src/main/java/org/breedinginsight/api/model/v1/request/query/GenotypeImportQuery.java
new file mode 100644
index 000000000..a9d596bcf
--- /dev/null
+++ b/src/main/java/org/breedinginsight/api/model/v1/request/query/GenotypeImportQuery.java
@@ -0,0 +1,69 @@
+/*
+ * See the NOTICE file distributed with this work for additional information
+ * regarding copyright ownership.
+ *
+ * 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 org.breedinginsight.api.model.v1.request.query;
+
+import io.micronaut.core.annotation.Introspected;
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Getter
+@Introspected
+public class GenotypeImportQuery extends QueryParams {
+
+ private String sampleSubmissionId;
+ private String projectNameForSampleSubmission;
+ private String sampleSubmissionCreatedBy;
+ private String genotypingFileName;
+ private String genotypingImportDate;
+ private String genotypingImportBy;
+
+ public SearchRequest constructSearchRequest() {
+ List filters = new ArrayList<>();
+
+ if (!StringUtils.isBlank(getSampleSubmissionId())) {
+ filters.add(constructFilterRequest("sampleSubmissionId", getSampleSubmissionId()));
+ }
+ if (!StringUtils.isBlank(getProjectNameForSampleSubmission())) {
+ filters.add(constructFilterRequest("projectNameForSampleSubmission", getProjectNameForSampleSubmission()));
+ }
+ if (!StringUtils.isBlank(getSampleSubmissionCreatedBy())) {
+ filters.add(constructFilterRequest("sampleSubmissionCreatedBy", getSampleSubmissionCreatedBy()));
+ }
+ if (!StringUtils.isBlank(getGenotypingFileName())) {
+ filters.add(constructFilterRequest("genotypingFileName", getGenotypingFileName()));
+ }
+ if (!StringUtils.isBlank(getGenotypingImportDate())) {
+ filters.add(constructFilterRequest("genotypingImportDate", getGenotypingImportDate()));
+ }
+ if (!StringUtils.isBlank(getGenotypingImportBy())) {
+ filters.add(constructFilterRequest("genotypingImportBy", getGenotypingImportBy()));
+ }
+
+ return new SearchRequest(filters);
+ }
+
+ private FilterRequest constructFilterRequest(String field, String value) {
+ return FilterRequest.builder()
+ .field(field)
+ .value(value)
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/breedinginsight/api/v1/controller/geno/GenotypeDataUploadController.java b/src/main/java/org/breedinginsight/api/v1/controller/geno/GenotypeDataUploadController.java
index df6615458..85f3d3311 100644
--- a/src/main/java/org/breedinginsight/api/v1/controller/geno/GenotypeDataUploadController.java
+++ b/src/main/java/org/breedinginsight/api/v1/controller/geno/GenotypeDataUploadController.java
@@ -1,23 +1,52 @@
+/*
+ * See the NOTICE file distributed with this work for additional information
+ * regarding copyright ownership.
+ *
+ * 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 org.breedinginsight.api.v1.controller.geno;
+import io.micronaut.http.HttpHeaders;
import io.micronaut.http.HttpResponse;
+import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.*;
import io.micronaut.http.multipart.CompletedFileUpload;
+import io.micronaut.http.server.types.files.StreamedFile;
import lombok.extern.slf4j.Slf4j;
import org.brapi.client.v2.model.exceptions.ApiException;
-import org.breedinginsight.api.auth.AuthenticatedUser;
-import org.breedinginsight.api.auth.ProgramSecured;
-import org.breedinginsight.api.auth.ProgramSecuredRole;
-import org.breedinginsight.api.auth.SecurityService;
+import org.breedinginsight.api.auth.*;
+import org.breedinginsight.api.model.v1.request.query.GenotypeImportQuery;
+import org.breedinginsight.api.model.v1.request.query.SearchRequest;
+import org.breedinginsight.api.model.v1.response.DataResponse;
import org.breedinginsight.api.model.v1.response.Response;
+import org.breedinginsight.api.model.v1.validators.QueryValid;
import org.breedinginsight.api.v1.controller.metadata.AddMetadata;
import org.breedinginsight.brapps.importer.model.response.ImportResponse;
+import org.breedinginsight.model.DownloadFile;
+import org.breedinginsight.model.GenotypeImportDetails;
+import org.breedinginsight.model.Program;
+import org.breedinginsight.services.ProgramService;
import org.breedinginsight.services.exceptions.AuthorizationException;
import org.breedinginsight.services.exceptions.DoesNotExistException;
import org.breedinginsight.services.geno.GenotypeService;
+import org.breedinginsight.utilities.response.ResponseUtils;
+import org.breedinginsight.utilities.response.mappers.GenotypeImportQueryMapper;
import javax.inject.Inject;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.Optional;
import java.util.UUID;
@Slf4j
@@ -25,22 +54,73 @@
public class GenotypeDataUploadController {
private final GenotypeService genoService;
private final SecurityService securityService;
+ private final ProgramService programService;
+ private final GenotypeImportQueryMapper genotypeImportQueryMapper;
@Inject
- public GenotypeDataUploadController(GenotypeService genoService, SecurityService securityService) {
+ public GenotypeDataUploadController(GenotypeService genoService, SecurityService securityService,
+ ProgramService programService, GenotypeImportQueryMapper genotypeImportQueryMapper) {
this.genoService = genoService;
this.securityService = securityService;
+ this.programService = programService;
+ this.genotypeImportQueryMapper = genotypeImportQueryMapper;
}
- @Post("programs/{programId}/experiments/{experimentId}/geno/import")
+ @Get("programs/{programId}/geno/imports{?genotypeImportQuery*}")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ProgramSecured(roleGroups = ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES)
+ public HttpResponse>> getGenotypeImports(
+ @PathVariable UUID programId,
+ @QueryValue @QueryValid(using = GenotypeImportQueryMapper.class) @Valid GenotypeImportQuery genotypeImportQuery) {
+ Optional program = programService.getById(programId);
+ if (program.isEmpty()) {
+ log.info("programId not found: {}", programId.toString());
+ return HttpResponse.notFound();
+ }
+
+ SearchRequest searchRequest = genotypeImportQuery.constructSearchRequest();
+
+ return ResponseUtils.getQueryResponse(
+ genoService.getGenotypeImports(programId),
+ genotypeImportQueryMapper,
+ searchRequest,
+ genotypeImportQuery
+ );
+ }
+
+ @Get("programs/{programId}/geno/imports/{genotypeImportId}/download")
+ @ProgramSecured(roleGroups = ProgramSecuredRoleGroup.PROGRAM_SCOPED_ROLES)
+ @Produces(value = {"application/octet-stream"})
+ public HttpResponse downloadGenotypeImport(@PathVariable UUID programId, @PathVariable UUID genotypeImportId) {
+ Optional program = programService.getById(programId);
+ if (program.isEmpty()) {
+ log.info("programId not found: {}", programId.toString());
+ return HttpResponse.notFound();
+ }
+
+ try {
+ Optional downloadFile = genoService.downloadGenotypeImport(programId, genotypeImportId);
+ if (downloadFile.isEmpty()) {
+ return HttpResponse.notFound();
+ }
+
+ return HttpResponse.ok(downloadFile.get().getStreamedFile())
+ .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + downloadFile.get().getFileName());
+ } catch (IOException e) {
+ log.error("Error downloading genotype import", e);
+ return HttpResponse.status(HttpStatus.INTERNAL_SERVER_ERROR, "Error downloading genotype import");
+ }
+ }
+
+ @Post("programs/{programId}/submissions/{submissionId}/geno/import")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
@AddMetadata
@ProgramSecured(roles = {ProgramSecuredRole.PROGRAM_ADMIN})
- public HttpResponse> uploadData(@PathVariable UUID programId, @PathVariable UUID experimentId, @Part("file") CompletedFileUpload upload) {
+ public HttpResponse> uploadData(@PathVariable UUID programId, @PathVariable UUID submissionId, @Part("file") CompletedFileUpload upload) {
AuthenticatedUser actingUser = securityService.getUser();
try {
- ImportResponse result = genoService.submitGenotypeData(actingUser.getId(), programId, experimentId, upload);
+ ImportResponse result = genoService.submitGenotypeData(actingUser.getId(), programId, submissionId, upload);
Response response = new Response<>(result);
return HttpResponse.ok(response);
} catch (DoesNotExistException e) {
diff --git a/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java b/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java
index e01ab2c08..741d533a5 100644
--- a/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java
+++ b/src/main/java/org/breedinginsight/api/v1/controller/geno/SampleSubmissionController.java
@@ -54,6 +54,11 @@
@Secured(SecurityRule.IS_AUTHENTICATED)
public class SampleSubmissionController {
+ public static final String DELETE_STATUS_NOT_ALLOWED_ERROR_MESSAGE =
+ "Sample submission cannot be deleted because status is submitted or completed";
+ public static final String DELETE_GENOTYPE_DATA_NOT_ALLOWED_ERROR_MESSAGE =
+ "Sample submission cannot be deleted because associated genotype data exists";
+
private final boolean brapiSubmissionEnabled;
private final SampleSubmissionService sampleSubmissionService;
private final ProgramService programService;
@@ -303,20 +308,20 @@ public HttpResponse deleteSubmissionById(@PathVariable UUID programId, @PathVari
return HttpResponse.notFound();
}
- // sample status validation
- Optional submissionOpt = sampleSubmissionService.getSampleSubmission(program.get(), submissionId, false);
-
- if(submissionOpt.isEmpty()) {
- return HttpResponse.notFound();
- }
- SampleSubmission submission = submissionOpt.get();
- if (!submission.isDeletable()) {
- return HttpResponse.notAllowed();
+ SampleSubmissionService.DeleteResult result = sampleSubmissionService.deleteSampleSubmission(program.get(), submissionId);
+ switch (result) {
+ case NOT_FOUND:
+ return HttpResponse.notFound();
+ case STATUS_NOT_ALLOWED:
+ return HttpResponse.notAllowed()
+ .body(DELETE_STATUS_NOT_ALLOWED_ERROR_MESSAGE);
+ case GENOTYPE_DATA_NOT_ALLOWED:
+ return HttpResponse.notAllowed()
+ .body(DELETE_GENOTYPE_DATA_NOT_ALLOWED_ERROR_MESSAGE);
+ case DELETED:
+ default:
+ return HttpResponse.ok();
}
-
- sampleSubmissionService.deleteSampleSubmission(program.get(), submissionId);
-
- return HttpResponse.ok();
}
}
diff --git a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java
index 850ed8138..b26abf7ea 100644
--- a/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java
+++ b/src/main/java/org/breedinginsight/brapi/v2/BrAPIGermplasmController.java
@@ -439,7 +439,8 @@ public HttpResponse> getGermplasmGenotype(@PathVaria
try {
BrAPIGermplasm germplasm = germplasmDAO.getGermplasmByUUID(germplasmId, programId);
- GermplasmGenotype germplasmGenotype = genoService.retrieveGenotypeData(programId, germplasm);
+
+ GermplasmGenotype germplasmGenotype = genoService.retrieveGenotypeData(programId, UUID.fromString(germplasmId));
Response response = new Response(germplasmGenotype);
return HttpResponse.ok(response);
diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIGermplasmDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIGermplasmDAO.java
index 478c99bcf..58910396b 100644
--- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIGermplasmDAO.java
+++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIGermplasmDAO.java
@@ -204,8 +204,7 @@ private Map processGermplasmForDisplay(List processGermplasmForDisplay(List
- private String processBreedbasePedigree(String pedigree) {
-
- if (pedigree != null) {
- if (pedigree.equals("NA/NA")) {
- return "";
- }
-
- // Technically processGermplasmForDisplay should handle ok without stripping these NAs but will strip anyways
- // for consistency.
- // We only allow the /NA case for single parent as we require a female parent in the pedigree
- // keep the leading slash, will be handled by processGermplasmForDisplay
- if (pedigree.endsWith("/NA")) {
- return pedigree.substring(0, pedigree.length()-2);
- }
-
- // shouldn't have this case in our data but just in case
- if (pedigree.startsWith("NA/")) {
- return pedigree.substring(2);
- }
- }
- return pedigree;
- }
-
public List createBrAPIGermplasm(List postBrAPIGermplasmList, UUID programId, ImportUpload upload) {
GermplasmApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), GermplasmApi.class);
var program = programDAO.fetchOneById(programId);
diff --git a/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java b/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java
index 81039b7ef..464634d54 100644
--- a/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java
+++ b/src/main/java/org/breedinginsight/brapi/v2/model/request/query/GermplasmQuery.java
@@ -22,6 +22,7 @@ public class GermplasmQuery extends BrapiQuery {
private String femaleParentGID;
private String maleParentGID;
private String createdDate;
+ private String externalUID;
private String createdByUserName;
private String synonym;
// This is a meta-parameter, it describes the display format of any date fields.
@@ -56,9 +57,15 @@ public SearchRequest constructSearchRequest() {
if (!StringUtils.isBlank(getMaleParentGID())) {
filters.add(constructFilterRequest("maleParentGID", getMaleParentGID()));
}
+
if (!StringUtils.isBlank(getCreatedDate())) {
filters.add(constructFilterRequest("createdDate", getCreatedDate()));
}
+
+ if (!StringUtils.isBlank(getExternalUID())) {
+ filters.add(constructFilterRequest("externalUID", getExternalUID()));
+ }
+
if (!StringUtils.isBlank(getCreatedByUserName())) {
filters.add(constructFilterRequest("createdByUserName", getCreatedByUserName()));
}
diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java
index 7e4a01e67..b7ba4a5eb 100644
--- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java
+++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIGermplasmService.java
@@ -119,24 +119,31 @@ public List