From 90fcfa00a8fd43b38749c4a4fa158dbd6e33b463 Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Thu, 9 Jan 2025 10:22:20 -0500 Subject: [PATCH 01/11] Delete sample submission initial work --- .../geno/SampleSubmissionController.java | 39 +++++++++ .../brapps/importer/daos/BrAPISampleDAO.java | 85 ++++++++++++++++++- .../brapps/importer/daos/logo.svg | 0 .../model/SampleSubmission.java | 10 +++ .../services/SampleSubmissionService.java | 30 +++++++ .../utilities/BrAPIDAOUtil.java | 23 +++++ 6 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/breedinginsight/brapps/importer/daos/logo.svg 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 e99bb5e70..d859a7cbf 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 @@ -281,4 +281,43 @@ public HttpResponse> checkVendorStatus(@PathVariable return HttpResponse.serverError(); } } + + /** + * Removes + * @param programId + * @param submissionId + * @return + * @throws ApiException + */ + @Delete("programs/{programId}/submissions/{submissionId}") + @Produces(MediaType.APPLICATION_JSON) + // only sys admin allowed on post & put so kept same permissions for delete + @ProgramSecured(roles = {ProgramSecuredRole.SYSTEM_ADMIN}) + public HttpResponse deleteSubmissionById(@PathVariable UUID programId, @PathVariable UUID submissionId) throws ApiException { + + // program validation + Optional program = programService.getById(programId); + if(program.isEmpty()) { + log.info(String.format("programId not found: %s", programId.toString())); + 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 has status of submitted do not allow deletion + // if the user changes the status to not submitted then they can delete + if (!submission.isDeletable()) { + return HttpResponse.notAllowed(); + } + + sampleSubmissionService.deleteSampleSubmission(program.get(), submissionId); + + return HttpResponse.ok(); + } + } diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java index 1cb9a531a..8bb0993c2 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java +++ b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java @@ -17,8 +17,14 @@ package org.breedinginsight.brapps.importer.daos; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import io.micronaut.context.annotation.Property; import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.brapi.client.v2.JSON; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.client.v2.modules.genotype.SamplesApi; import org.brapi.v2.model.geno.BrAPISample; @@ -33,9 +39,9 @@ import javax.inject.Inject; import javax.inject.Singleton; +import java.io.IOException; import java.util.Collections; import java.util.List; -import java.util.UUID; @Slf4j @Singleton @@ -47,6 +53,7 @@ public class BrAPISampleDAO { private final ImportDAO importDAO; private final BrAPIDAOUtil brAPIDAOUtil; private final BrAPIEndpointProvider brAPIEndpointProvider; + private final Gson gson = new JSON().getGson(); @Inject public BrAPISampleDAO(ProgramDAO programDAO, @@ -102,4 +109,80 @@ public List readSamplesBySubmissionIds(Program program, List sampleDbIds) throws ApiException { + // create batch of samples, not yet included in brapi client TODO: switch to brapi client when available + String programBrAPIBaseUrl = brAPIDAOUtil.getProgramBrAPIBaseUrl(program.getId()); + String batchDbId = postSamplesBatch(programBrAPIBaseUrl, sampleDbIds); + + // delete samples specified in batch + deleteSamplesBatch(programBrAPIBaseUrl, batchDbId); + + // TODO: delete plates associated with submission, could potentially only require brapi server side change if deleting a sample cascades + } + + private String postSamplesBatch(String programBrAPIBaseUrl, List sampleDbIds) throws ApiException { + HttpUrl.Builder requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/batchDeletes").newBuilder(); + //requestUrl.addQueryParameter("hardDelete", Boolean.toString(hard)); + + BatchDeleteRequest requestBody = new BatchDeleteRequest(sampleDbIds); + String json = gson.toJson(requestBody); + RequestBody body = RequestBody.create(json, MediaType.get("application/json")); + + HttpUrl url = requestUrl.build(); + Request brapiRequest = new Request.Builder() + .url(url) + .post(body) + .addHeader("Content-Type", "application/json") + .build(); + + String jsonResponse = brAPIDAOUtil.makeCallWithResponse(brapiRequest); + JsonElement rootElement = JsonParser.parseString(jsonResponse); + JsonObject rootObject = rootElement.getAsJsonObject(); + JsonObject resultObject = rootObject.getAsJsonObject("result"); + String batchDeleteDbId = resultObject.get("batchDeleteDbId").getAsString(); + return batchDeleteDbId; + } + + private void deleteSamplesBatch(String programBrAPIBaseUrl, String batchDbId) throws ApiException { + HttpUrl.Builder requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/batchDeletes/" + batchDbId).newBuilder(); + requestUrl.addQueryParameter("hardDelete", "true"); + + HttpUrl url = requestUrl.build(); + Request brapiRequest = new Request.Builder() + .url(url) + .method("DELETE", null) + .addHeader("Content-Type", "application/json") + .build(); + + brAPIDAOUtil.makeCall(brapiRequest); + } + + /** + * TODO: temporary minimal model here until brapi client is updated with delete models + */ + public class BatchDeleteRequest { + private String batchDeleteType; + private Search search; + + public BatchDeleteRequest(List sampleDbIds) { + this.batchDeleteType = "samples"; + this.search = new Search(sampleDbIds); + } + + private class Search { + private List sampleDbIds; + + public Search(List sampleDbIds) { + this.sampleDbIds = sampleDbIds; + } + } + } + } diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/logo.svg b/src/main/java/org/breedinginsight/brapps/importer/daos/logo.svg new file mode 100644 index 000000000..e69de29bb diff --git a/src/main/java/org/breedinginsight/model/SampleSubmission.java b/src/main/java/org/breedinginsight/model/SampleSubmission.java index 6202a5a6f..020f66352 100644 --- a/src/main/java/org/breedinginsight/model/SampleSubmission.java +++ b/src/main/java/org/breedinginsight/model/SampleSubmission.java @@ -17,6 +17,7 @@ package org.breedinginsight.model; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -79,6 +80,15 @@ private void parseShipmentForms(JSONB shipmentforms) { } } + /** + * Should only be deleted when status is not submitted and has no vendor status + */ + @JsonIgnore + public boolean isDeletable() { + return (this.getSubmitted() == null || (this.getSubmitted() != null && !this.getSubmitted())) + && this.getVendorStatus() == null; + } + public enum Status { NOT_SUBMITTED("NOT SUBMITTED"), SUBMITTED("SUBMITTED"), diff --git a/src/main/java/org/breedinginsight/services/SampleSubmissionService.java b/src/main/java/org/breedinginsight/services/SampleSubmissionService.java index 2c629da55..134f690dd 100644 --- a/src/main/java/org/breedinginsight/services/SampleSubmissionService.java +++ b/src/main/java/org/breedinginsight/services/SampleSubmissionService.java @@ -427,4 +427,34 @@ public Optional updateSubmissionStatus(Program program, UUID s return submissionOptional; } + + /** + * Deletes BrAPI plates and submission objects as well as sample submission record in bidb + * We do not currently cache plates or samples so don't need to worry about that + * @param submissionId sample submission UUID to delete + * @exception ApiException if a BrAPI call fails + */ + public void deleteSampleSubmission(Program program, UUID submissionId) throws ApiException { + + // delete BrAPI data + // delete samples + // delete plates + + // create a batch of sampleIds to delete + + // get samples with the samble submission xref + List samples = sampleDAO.readSamplesBySubmissionIds(program, List.of(submissionId.toString())); + + // extract sampleDbIds to include in batch + List sampleDbIds = samples.stream().map(BrAPISample::getSampleDbId).collect(Collectors.toList()); + + // create batch of samples, not yet included in brapi client TODO: switch to brapi client when available + sampleDAO.deleteSamples(program, sampleDbIds); + + // TODO: delete plates, not yet supported in brapi server + + // delete sample submission record from bidb + submissionDAO.deleteById(submissionId); + } + } diff --git a/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java b/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java index 075d6bb2b..c3882457b 100644 --- a/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java +++ b/src/main/java/org/breedinginsight/utilities/BrAPIDAOUtil.java @@ -25,6 +25,7 @@ import lombok.extern.slf4j.Slf4j; import okhttp3.OkHttpClient; import okhttp3.Request; +import okhttp3.Response; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.brapi.client.v2.ApiResponse; @@ -389,6 +390,28 @@ public void makeCall(Request brapiRequest) throws ApiException { } } + /** + * TODO: replace with brapi client methods when available, will do timeout spec from config at that point + * @param brapiRequest + * @return + * @throws ApiException + */ + public String makeCallWithResponse(Request brapiRequest) throws ApiException { + OkHttpClient client = new OkHttpClient.Builder() + .readTimeout(5, TimeUnit.MINUTES) + .build(); + + // autoclose Response + try (Response response = client.newCall(brapiRequest).execute()) { + if (!response.isSuccessful()) { + throw new ApiException("Request failed with status code: " + response.code()); + } + return response.body().string(); + } catch (IOException e) { + throw new ApiException(e); + } + } + public String getProgramBrAPIBaseUrl(UUID programId) { ProgramBrAPIEndpoints programBrAPIEndpoints; try { From 3ff0eb4655668b967854ac2a795c1b8998fabd3d Mon Sep 17 00:00:00 2001 From: rob-ouser-bi Date: Wed, 15 Jan 2025 16:05:57 +0000 Subject: [PATCH 02/11] [autocommit] bumping build number --- src/main/resources/version.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties index 4e43777d2..4ef6916c8 100644 --- a/src/main/resources/version.properties +++ b/src/main/resources/version.properties @@ -15,5 +15,5 @@ # -version=v1.1.0+899 -versionInfo=https://github.com/Breeding-Insight/bi-api/commit/1d19a829223e99b09324a69b51fa47c6b965de64 +version=v1.1.0+901 +versionInfo=https://github.com/Breeding-Insight/bi-api/commit/9c0fdb5b160215a40e5f2df57a7e922ec0036052 From b58768c7fb8cbdf8e5e4112bf25b1c53ac0f51bb Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:16:50 -0500 Subject: [PATCH 03/11] Cleanup --- src/main/java/org/breedinginsight/brapps/importer/daos/logo.svg | 0 .../breedinginsight/brapi/v2/ListControllerIntegrationTest.java | 1 - 2 files changed, 1 deletion(-) delete mode 100644 src/main/java/org/breedinginsight/brapps/importer/daos/logo.svg diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/logo.svg b/src/main/java/org/breedinginsight/brapps/importer/daos/logo.svg deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java index 025179f2e..b0a55b3b1 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java @@ -240,6 +240,5 @@ public void deleteListSuccess() { } catch(HttpClientResponseException e) { assertEquals(HttpStatus.NOT_FOUND, e.getStatus()); } - } } From 8de90d9ae50e3cc6a5e705b824c5001f2a5f2236 Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:19:28 -0500 Subject: [PATCH 04/11] Removed added spacing --- .../breedinginsight/brapi/v2/ListControllerIntegrationTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java index b0a55b3b1..029c16594 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java @@ -228,6 +228,7 @@ public void deleteListSuccess() { HttpResponse deleteResponse = deleteCall.blockingFirst(); assertEquals(HttpStatus.NO_CONTENT, deleteResponse.getStatus()); + // A DELETE request to the brapi/v2/lists/ endpoint with invalid dbId. Flowable> invalidDeleteCall = client.exchange( DELETE(String.format("/programs/%s/brapi/v2/lists/%s", program.getId().toString(), "NOT-VALID-DBID")) From 4ead129fdb504517449784eea502ca2b433cced2 Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:28:13 -0500 Subject: [PATCH 05/11] Cleanup comments --- .../v1/controller/geno/SampleSubmissionController.java | 8 +++++--- .../brapi/v2/ListControllerIntegrationTest.java | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) 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 d859a7cbf..be6377870 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 @@ -283,9 +283,11 @@ public HttpResponse> checkVendorStatus(@PathVariable } /** - * Removes - * @param programId - * @param submissionId + * Delete sample submission. + * Currently deletes the bidb submission record and BrAPI samples, TODO: delete BrAPI plates once supported + * in BrAPI server, see BI-2431 + * @param programId bi-api id of program + * @param submissionId bi-api id of submission * @return * @throws ApiException */ diff --git a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java index 029c16594..2d421d306 100644 --- a/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java +++ b/src/test/java/org/breedinginsight/brapi/v2/ListControllerIntegrationTest.java @@ -228,7 +228,7 @@ public void deleteListSuccess() { HttpResponse deleteResponse = deleteCall.blockingFirst(); assertEquals(HttpStatus.NO_CONTENT, deleteResponse.getStatus()); - + // A DELETE request to the brapi/v2/lists/ endpoint with invalid dbId. Flowable> invalidDeleteCall = client.exchange( DELETE(String.format("/programs/%s/brapi/v2/lists/%s", program.getId().toString(), "NOT-VALID-DBID")) From bc2b09cc5c4030c88917ddee4b0387ce8c26221b Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Mon, 27 Jan 2025 17:17:03 -0500 Subject: [PATCH 06/11] Handle non-immediate response --- .../brapps/importer/daos/BrAPISampleDAO.java | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java index 8bb0993c2..fe4ad57c2 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java +++ b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java @@ -22,6 +22,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import io.micronaut.context.annotation.Property; +import io.micronaut.http.server.exceptions.InternalServerException; import lombok.extern.slf4j.Slf4j; import okhttp3.*; import org.brapi.client.v2.JSON; @@ -146,8 +147,35 @@ private String postSamplesBatch(String programBrAPIBaseUrl, List sampleD JsonElement rootElement = JsonParser.parseString(jsonResponse); JsonObject rootObject = rootElement.getAsJsonObject(); JsonObject resultObject = rootObject.getAsJsonObject("result"); - String batchDeleteDbId = resultObject.get("batchDeleteDbId").getAsString(); - return batchDeleteDbId; + + // check to see if immediate response or searchResultId + if(resultObject.has("batchDeleteDbId")) { + return resultObject.get("batchDeleteDbId").getAsString(); + } else if (resultObject.has("searchResultsDbId")) { + // TODO: once api stuff is in client use BrAPIDAOUtil::search to handle retries, for now just request once + // could maybe be an issue for large number of samples + return getBatchDeleteDbIdFromSearchResult(programBrAPIBaseUrl, resultObject.get("searchResultsDbId").getAsString()); + } else { + throw new InternalServerException("Expected batchDeleteDbId or searchResultsDbId but got " + resultObject); + } + + } + + private String getBatchDeleteDbIdFromSearchResult(String programBrAPIBaseUrl, String searchResultDbId) throws ApiException { + HttpUrl.Builder requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/search/batchDeletes/" + searchResultDbId).newBuilder(); + + HttpUrl url = requestUrl.build(); + Request brapiRequest = new Request.Builder() + .url(url) + .method("GET", null) + .addHeader("Content-Type", "application/json") + .build(); + + String jsonResponse = brAPIDAOUtil.makeCallWithResponse(brapiRequest); + JsonElement rootElement = JsonParser.parseString(jsonResponse); + JsonObject rootObject = rootElement.getAsJsonObject(); + JsonObject resultObject = rootObject.getAsJsonObject("result"); + return resultObject.get("batchDeleteDbId").getAsString(); } private void deleteSamplesBatch(String programBrAPIBaseUrl, String batchDbId) throws ApiException { From a35eb5fd464f47fbc607754cd3a0dfd71754bf7b Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Tue, 4 Feb 2025 15:39:21 -0500 Subject: [PATCH 07/11] Added plate deletion --- .../brapps/importer/daos/BrAPISampleDAO.java | 62 ++++++++++++++++--- .../services/SampleSubmissionService.java | 11 ++-- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java index fe4ad57c2..13f2cc723 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java +++ b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java @@ -123,20 +123,45 @@ public void deleteSamples(Program program, List sampleDbIds) throws ApiE String batchDbId = postSamplesBatch(programBrAPIBaseUrl, sampleDbIds); // delete samples specified in batch - deleteSamplesBatch(programBrAPIBaseUrl, batchDbId); + deleteBatch(programBrAPIBaseUrl, batchDbId); + } + + /** + * Deletes all plates specified in the brapi server + * @param program + * @param plateDbIds + * @throws ApiException + */ + public void deletePlates(Program program, List plateDbIds) throws ApiException { + // create batch of samples, not yet included in brapi client TODO: switch to brapi client when available + String programBrAPIBaseUrl = brAPIDAOUtil.getProgramBrAPIBaseUrl(program.getId()); + String batchDbId = postPlatesBatch(programBrAPIBaseUrl, plateDbIds); - // TODO: delete plates associated with submission, could potentially only require brapi server side change if deleting a sample cascades + // delete plates specified in batch + deleteBatch(programBrAPIBaseUrl, batchDbId); } + private String postSamplesBatch(String programBrAPIBaseUrl, List sampleDbIds) throws ApiException { HttpUrl.Builder requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/batchDeletes").newBuilder(); - //requestUrl.addQueryParameter("hardDelete", Boolean.toString(hard)); - - BatchDeleteRequest requestBody = new BatchDeleteRequest(sampleDbIds); + SampleBatchDeleteRequest requestBody = new SampleBatchDeleteRequest(sampleDbIds); String json = gson.toJson(requestBody); RequestBody body = RequestBody.create(json, MediaType.get("application/json")); + HttpUrl url = requestUrl.build(); + return postBatch(url, body, programBrAPIBaseUrl); + } + private String postPlatesBatch(String programBrAPIBaseUrl, List plateDbIds) throws ApiException { + HttpUrl.Builder requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/batchDeletes").newBuilder(); + PlateBatchDeleteRequest requestBody = new PlateBatchDeleteRequest(plateDbIds); + String json = gson.toJson(requestBody); + RequestBody body = RequestBody.create(json, MediaType.get("application/json")); HttpUrl url = requestUrl.build(); + return postBatch(url, body, programBrAPIBaseUrl); + } + + private String postBatch(HttpUrl url, RequestBody body, String programBrAPIBaseUrl) throws ApiException { + Request brapiRequest = new Request.Builder() .url(url) .post(body) @@ -153,12 +178,11 @@ private String postSamplesBatch(String programBrAPIBaseUrl, List sampleD return resultObject.get("batchDeleteDbId").getAsString(); } else if (resultObject.has("searchResultsDbId")) { // TODO: once api stuff is in client use BrAPIDAOUtil::search to handle retries, for now just request once - // could maybe be an issue for large number of samples + // could be an issue for large number of samples return getBatchDeleteDbIdFromSearchResult(programBrAPIBaseUrl, resultObject.get("searchResultsDbId").getAsString()); } else { throw new InternalServerException("Expected batchDeleteDbId or searchResultsDbId but got " + resultObject); } - } private String getBatchDeleteDbIdFromSearchResult(String programBrAPIBaseUrl, String searchResultDbId) throws ApiException { @@ -178,7 +202,7 @@ private String getBatchDeleteDbIdFromSearchResult(String programBrAPIBaseUrl, St return resultObject.get("batchDeleteDbId").getAsString(); } - private void deleteSamplesBatch(String programBrAPIBaseUrl, String batchDbId) throws ApiException { + private void deleteBatch(String programBrAPIBaseUrl, String batchDbId) throws ApiException { HttpUrl.Builder requestUrl = HttpUrl.parse(programBrAPIBaseUrl + "/batchDeletes/" + batchDbId).newBuilder(); requestUrl.addQueryParameter("hardDelete", "true"); @@ -195,11 +219,11 @@ private void deleteSamplesBatch(String programBrAPIBaseUrl, String batchDbId) th /** * TODO: temporary minimal model here until brapi client is updated with delete models */ - public class BatchDeleteRequest { + public class SampleBatchDeleteRequest { private String batchDeleteType; private Search search; - public BatchDeleteRequest(List sampleDbIds) { + public SampleBatchDeleteRequest(List sampleDbIds) { this.batchDeleteType = "samples"; this.search = new Search(sampleDbIds); } @@ -213,4 +237,22 @@ public Search(List sampleDbIds) { } } + public class PlateBatchDeleteRequest { + private String batchDeleteType; + private Search search; + + public PlateBatchDeleteRequest(List plateDbIds) { + this.batchDeleteType = "plates"; + this.search = new Search(plateDbIds); + } + + private class Search { + private List plateDbIds; + + public Search(List plateDbIds) { + this.plateDbIds = plateDbIds; + } + } + } + } diff --git a/src/main/java/org/breedinginsight/services/SampleSubmissionService.java b/src/main/java/org/breedinginsight/services/SampleSubmissionService.java index 134f690dd..e0ccc49fd 100644 --- a/src/main/java/org/breedinginsight/services/SampleSubmissionService.java +++ b/src/main/java/org/breedinginsight/services/SampleSubmissionService.java @@ -440,18 +440,19 @@ public void deleteSampleSubmission(Program program, UUID submissionId) throws Ap // delete samples // delete plates - // create a batch of sampleIds to delete + // create a batch of sampleIds and plateIds to delete - // get samples with the samble submission xref + // get samples with the sample submission xref List samples = sampleDAO.readSamplesBySubmissionIds(program, List.of(submissionId.toString())); - // extract sampleDbIds to include in batch + // extract sampleDbIds and plateDbIds to include in batches List sampleDbIds = samples.stream().map(BrAPISample::getSampleDbId).collect(Collectors.toList()); + List platesDbIds = samples.stream().map(BrAPISample::getPlateDbId).collect(Collectors.toList()); // create batch of samples, not yet included in brapi client TODO: switch to brapi client when available + // TODO: uncomment when brapi server is working sampleDAO.deleteSamples(program, sampleDbIds); - - // TODO: delete plates, not yet supported in brapi server + sampleDAO.deletePlates(program, platesDbIds); // delete sample submission record from bidb submissionDAO.deleteById(submissionId); From 530967651d0d4c52be047fd6956025905948b4c8 Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Mon, 10 Feb 2025 15:06:54 -0500 Subject: [PATCH 08/11] Add program admin permission --- .../api/v1/controller/geno/SampleSubmissionController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 be6377870..539391c73 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 @@ -293,8 +293,8 @@ public HttpResponse> checkVendorStatus(@PathVariable */ @Delete("programs/{programId}/submissions/{submissionId}") @Produces(MediaType.APPLICATION_JSON) - // only sys admin allowed on post & put so kept same permissions for delete - @ProgramSecured(roles = {ProgramSecuredRole.SYSTEM_ADMIN}) + // sys admin and program admin roles to match file import permissions + @ProgramSecured(roles = {ProgramSecuredRole.SYSTEM_ADMIN, ProgramSecuredRole.PROGRAM_ADMIN}) public HttpResponse deleteSubmissionById(@PathVariable UUID programId, @PathVariable UUID submissionId) throws ApiException { // program validation From 10c6adf645fc16551e452746c68232ce24736fb8 Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Tue, 11 Feb 2025 14:58:06 -0500 Subject: [PATCH 09/11] Cleaned up comments --- .../v1/controller/geno/SampleSubmissionController.java | 5 +---- .../brapps/importer/daos/BrAPISampleDAO.java | 4 ++-- .../services/SampleSubmissionService.java | 9 +-------- 3 files changed, 4 insertions(+), 14 deletions(-) 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 539391c73..6c0e79bc3 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 @@ -284,8 +284,7 @@ public HttpResponse> checkVendorStatus(@PathVariable /** * Delete sample submission. - * Currently deletes the bidb submission record and BrAPI samples, TODO: delete BrAPI plates once supported - * in BrAPI server, see BI-2431 + * Deletes the bidb submission record and BrAPI samples & plates * @param programId bi-api id of program * @param submissionId bi-api id of submission * @return @@ -311,8 +310,6 @@ public HttpResponse deleteSubmissionById(@PathVariable UUID programId, @PathVari return HttpResponse.notFound(); } SampleSubmission submission = submissionOpt.get(); - // if submission has status of submitted do not allow deletion - // if the user changes the status to not submitted then they can delete if (!submission.isDeletable()) { return HttpResponse.notAllowed(); } diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java index 13f2cc723..372bb63be 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java +++ b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPISampleDAO.java @@ -133,7 +133,7 @@ public void deleteSamples(Program program, List sampleDbIds) throws ApiE * @throws ApiException */ public void deletePlates(Program program, List plateDbIds) throws ApiException { - // create batch of samples, not yet included in brapi client TODO: switch to brapi client when available + // create batch of plates, not yet included in brapi client TODO: switch to brapi client when available String programBrAPIBaseUrl = brAPIDAOUtil.getProgramBrAPIBaseUrl(program.getId()); String batchDbId = postPlatesBatch(programBrAPIBaseUrl, plateDbIds); @@ -178,7 +178,7 @@ private String postBatch(HttpUrl url, RequestBody body, String programBrAPIBaseU return resultObject.get("batchDeleteDbId").getAsString(); } else if (resultObject.has("searchResultsDbId")) { // TODO: once api stuff is in client use BrAPIDAOUtil::search to handle retries, for now just request once - // could be an issue for large number of samples + // brapi server only returns immediate response for batchDeletes so this case won't happen return getBatchDeleteDbIdFromSearchResult(programBrAPIBaseUrl, resultObject.get("searchResultsDbId").getAsString()); } else { throw new InternalServerException("Expected batchDeleteDbId or searchResultsDbId but got " + resultObject); diff --git a/src/main/java/org/breedinginsight/services/SampleSubmissionService.java b/src/main/java/org/breedinginsight/services/SampleSubmissionService.java index e0ccc49fd..c7f4acef1 100644 --- a/src/main/java/org/breedinginsight/services/SampleSubmissionService.java +++ b/src/main/java/org/breedinginsight/services/SampleSubmissionService.java @@ -435,13 +435,7 @@ public Optional updateSubmissionStatus(Program program, UUID s * @exception ApiException if a BrAPI call fails */ public void deleteSampleSubmission(Program program, UUID submissionId) throws ApiException { - - // delete BrAPI data - // delete samples - // delete plates - // create a batch of sampleIds and plateIds to delete - // get samples with the sample submission xref List samples = sampleDAO.readSamplesBySubmissionIds(program, List.of(submissionId.toString())); @@ -449,8 +443,7 @@ public void deleteSampleSubmission(Program program, UUID submissionId) throws Ap List sampleDbIds = samples.stream().map(BrAPISample::getSampleDbId).collect(Collectors.toList()); List platesDbIds = samples.stream().map(BrAPISample::getPlateDbId).collect(Collectors.toList()); - // create batch of samples, not yet included in brapi client TODO: switch to brapi client when available - // TODO: uncomment when brapi server is working + // delete samples and plates BrAPI objects in brapi server sampleDAO.deleteSamples(program, sampleDbIds); sampleDAO.deletePlates(program, platesDbIds); From c9de0444ac3f921226976b50ddcfbe4e40c5945c Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Thu, 13 Feb 2025 10:57:22 -0500 Subject: [PATCH 10/11] Update src/main/java/org/breedinginsight/services/SampleSubmissionService.java Co-authored-by: mlm483 <128052931+mlm483@users.noreply.github.com> --- .../org/breedinginsight/services/SampleSubmissionService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/breedinginsight/services/SampleSubmissionService.java b/src/main/java/org/breedinginsight/services/SampleSubmissionService.java index c7f4acef1..3309dd0dc 100644 --- a/src/main/java/org/breedinginsight/services/SampleSubmissionService.java +++ b/src/main/java/org/breedinginsight/services/SampleSubmissionService.java @@ -440,8 +440,8 @@ public void deleteSampleSubmission(Program program, UUID submissionId) throws Ap List samples = sampleDAO.readSamplesBySubmissionIds(program, List.of(submissionId.toString())); // extract sampleDbIds and plateDbIds to include in batches - List sampleDbIds = samples.stream().map(BrAPISample::getSampleDbId).collect(Collectors.toList()); - List platesDbIds = samples.stream().map(BrAPISample::getPlateDbId).collect(Collectors.toList()); + List sampleDbIds = samples.stream().map(BrAPISample::getSampleDbId).distinct().collect(Collectors.toList()); + List platesDbIds = samples.stream().map(BrAPISample::getPlateDbId).distinct().collect(Collectors.toList()); // delete samples and plates BrAPI objects in brapi server sampleDAO.deleteSamples(program, sampleDbIds); From 21ce4be20a75dfcad8c02381f9242b69d1504da4 Mon Sep 17 00:00:00 2001 From: Nick <53413353+nickpalladino@users.noreply.github.com> Date: Thu, 13 Feb 2025 11:10:48 -0500 Subject: [PATCH 11/11] Added return type to docs --- .../api/v1/controller/geno/SampleSubmissionController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6c0e79bc3..e01ab2c08 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 @@ -287,7 +287,7 @@ public HttpResponse> checkVendorStatus(@PathVariable * Deletes the bidb submission record and BrAPI samples & plates * @param programId bi-api id of program * @param submissionId bi-api id of submission - * @return + * @return HttpResponse * @throws ApiException */ @Delete("programs/{programId}/submissions/{submissionId}")