Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BI-2476 Create Single and Batch Delete End Points for Germplasm #47

Merged
merged 1 commit into from
Mar 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/main/java/io/swagger/api/germ/GermplasmApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import io.swagger.model.BrAPIResponse;
import io.swagger.model.Model202AcceptedSearchResponse;
import io.swagger.model.core.ListsListResponse;
import io.swagger.model.core.ListsSingleResponse;
import io.swagger.model.germ.GermplasmListResponse;
import io.swagger.model.germ.GermplasmMCPDResponse;
import io.swagger.model.germ.GermplasmNewRequest;
Expand Down Expand Up @@ -45,6 +47,20 @@ ResponseEntity<GermplasmSingleResponse> germplasmGermplasmDbIdGet(
@ApiParam(value = "HTTP HEADER - Token used for Authorization <strong> Bearer {token_string} </strong>") @RequestHeader(value = "Authorization", required = false) String authorization)
throws BrAPIServerException;

@ApiOperation(value = "Delete an existing germplasm", nickname = "germplasmGermplasmDbIdDelete", notes = "Delete an existing germplasm", response = GermplasmSingleResponse.class, authorizations = {
@Authorization(value = "AuthorizationToken") }, tags = { "Germplasm", })
@ApiResponses(value = { @ApiResponse(code = 204, message = "OK", response = GermplasmSingleResponse.class),
@ApiResponse(code = 400, message = "Bad Request", response = String.class),
@ApiResponse(code = 401, message = "Unauthorized", response = String.class),
@ApiResponse(code = 403, message = "Forbidden", response = String.class),
@ApiResponse(code = 404, message = "Not Found", response = String.class) })
@RequestMapping(value = "/germplasm/{germplasmDbId}", produces = { "application/json" }, method = RequestMethod.DELETE)
ResponseEntity<GermplasmSingleResponse> germplasmGermplasmDbIdDelete(
@ApiParam(value = "The unique ID of this germplasm", required = true) @PathVariable("germplasmDbId") String germplasmDbId,
@ApiParam(value = "hardDelete") @Valid @RequestParam(value = "hardDelete", defaultValue = "false", required = false) boolean hardDelete,
@ApiParam(value = "HTTP HEADER - Token used for Authorization <strong> Bearer {token_string} </strong>") @RequestHeader(value = "Authorization", required = false) String authorization)
throws BrAPIServerException;

@ApiOperation(value = "Get the details of a specific Germplasm in MCPD format", nickname = "germplasmGermplasmDbIdMcpdGet", notes = "Get all MCPD details of a germplasm <a target=\"_blank\" href=\"https://www.bioversityInternational.org/fileadmin/user_upload/online_library/publications/pdfs/FAOBIOVERSITY_MULTI-CROP_PASSPORT_DESCRIPTORS_V.2.1_2015_2020.pdf\"> MCPD v2.1 spec can be found here </a> Implementation Notes - When the MCPD spec identifies a field which can have multiple values returned, the JSON response should be an array instead of a semi-colon separated string.", response = GermplasmMCPDResponse.class, authorizations = {
@Authorization(value = "AuthorizationToken") }, tags = { "Germplasm", })
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = GermplasmMCPDResponse.class),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.swagger.model.BrAPIResponse;
import io.swagger.model.Metadata;
import io.swagger.model.core.ListsSingleResponse;
import io.swagger.model.germ.Germplasm;
import io.swagger.model.germ.GermplasmListResponse;
import io.swagger.model.germ.GermplasmListResponseResult;
Expand All @@ -16,6 +17,7 @@
import io.swagger.model.germ.GermplasmProgenyResponse;
import io.swagger.api.germ.GermplasmApi;

import jakarta.validation.Valid;
import org.brapi.test.BrAPITestServer.controller.core.BrAPIController;
import org.brapi.test.BrAPITestServer.exceptions.BrAPIServerException;
import org.brapi.test.BrAPITestServer.model.entity.SearchRequestEntity;
Expand Down Expand Up @@ -72,6 +74,26 @@ public ResponseEntity<GermplasmSingleResponse> germplasmGermplasmDbIdGet(
return responseOK(new GermplasmSingleResponse(), data);
}

@CrossOrigin
@Override
public ResponseEntity<GermplasmSingleResponse> germplasmGermplasmDbIdDelete(
@PathVariable("germplasmDbId") String germplasmDbId,
@Valid @RequestParam(value = "hardDelete", defaultValue = "false" ,required = false) boolean hardDelete,
@RequestHeader(value = "Authorization", required = false) String authorization) throws BrAPIServerException {

log.debug("Request: " + request.getRequestURI());
validateSecurityContext(request, "ROLE_USER");
validateAcceptHeader(request);

if (hardDelete) {
germplasmService.deleteGermplasm(germplasmDbId);
return responseNoContent();
}

germplasmService.softDeleteGermplasm(germplasmDbId);
return responseNoContent();
}

@CrossOrigin
@Override
public ResponseEntity<GermplasmMCPDResponse> germplasmGermplasmDbIdMcpdGet(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ public List<String> collectDbIds(List<Germplasm> entities) {

@Override
public void deleteBatchDeleteData(List<String> dbIds) {

germplasmService.deleteGermplasmBatch(dbIds);
}

@Override
public void softDeleteBatchDeleteData(List<String> dbIds) {

germplasmService.softDeleteGermplasmBatch(dbIds);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
import io.swagger.model.germ.GermplasmMCPD.AcquisitionSourceCodeEnum;
import io.swagger.model.germ.GermplasmMCPD.MlsStatusEnum;
import io.swagger.model.germ.GermplasmStorageTypesEnum;
import org.hibernate.annotations.Where;

@Entity
@Table(name = "germplasm")
@Where(clause = "soft_deleted = false")
public class GermplasmEntity extends BrAPIPrimaryEntity {
@Column
private String accessionNumber;
Expand Down Expand Up @@ -84,6 +86,8 @@ public class GermplasmEntity extends BrAPIPrimaryEntity {
private List<ObservationUnitEntity> observationUnits;
@ElementCollection
private List<GermplasmStorageTypesEnum> typeOfGermplasmStorageCode;
@Column(name = "soft_deleted")
private boolean softDeleted;

public GermplasmInstituteEntity getHostInstitute() {
if (getInstitutes() != null) {
Expand Down Expand Up @@ -352,4 +356,8 @@ public void setTypeOfGermplasmStorageCode(List<GermplasmStorageTypesEnum> typeOf
this.typeOfGermplasmStorageCode = typeOfGermplasmStorageCode;
}

public boolean getSoftDeleted() { return softDeleted; }

public void setSoftDeleted(boolean sofDeleted) { this.softDeleted = sofDeleted; }

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class PedigreeEdgeEntity extends BrAPIPrimaryEntity{
@ManyToOne(fetch = FetchType.LAZY)
private PedigreeNodeEntity thisNode;
@ManyToOne(fetch = FetchType.LAZY)
private PedigreeNodeEntity conncetedNode;
private PedigreeNodeEntity connectedNode;
@Column
private ParentType parentType;
@Column
Expand All @@ -31,11 +31,11 @@ public EdgeType getEdgeType() {
public void setEdgeType(EdgeType edgeType) {
this.edgeType = edgeType;
}
public PedigreeNodeEntity getConncetedNode() {
return conncetedNode;
public PedigreeNodeEntity getConnectedNode() {
return connectedNode;
}
public void setConncetedNode(PedigreeNodeEntity conncetedNode) {
this.conncetedNode = conncetedNode;
public void setConnectedNode(PedigreeNodeEntity connectedNode) {
this.connectedNode = connectedNode;
}
public ParentType getParentType() {
return parentType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public List<PedigreeEdgeEntity> getParentEdges() {
public List<PedigreeNodeEntity> getParentNodes() {
return edges.stream().filter(e -> {
return e.getEdgeType() == EdgeType.parent;
}).map(edge -> edge.getConncetedNode()).collect(Collectors.toList());
}).map(edge -> edge.getConnectedNode()).collect(Collectors.toList());
}

public List<PedigreeEdgeEntity> getProgenyEdges() {
Expand All @@ -106,13 +106,13 @@ public List<PedigreeEdgeEntity> getProgenyEdges() {
public List<PedigreeNodeEntity> getProgenyNodes() {
return edges.stream().filter(e -> {
return e.getEdgeType() == EdgeType.child;
}).map(edge -> edge.getConncetedNode()).collect(Collectors.toList());
}).map(edge -> edge.getConnectedNode()).collect(Collectors.toList());
}

public void addParent(PedigreeNodeEntity node, ParentType type) {
PedigreeEdgeEntity edge = new PedigreeEdgeEntity();
edge.setThisNode(this);
edge.setConncetedNode(node);
edge.setConnectedNode(node);
edge.setParentType(type);
edge.setEdgeType(EdgeType.parent);
if(edges == null)
Expand All @@ -123,7 +123,7 @@ public void addParent(PedigreeNodeEntity node, ParentType type) {
public void addProgeny(PedigreeNodeEntity node, ParentType type) {
PedigreeEdgeEntity edge = new PedigreeEdgeEntity();
edge.setThisNode(this);
edge.setConncetedNode(node);
edge.setConnectedNode(node);
edge.setParentType(type);
edge.setEdgeType(EdgeType.child);
if(edges == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,21 @@

import org.brapi.test.BrAPITestServer.model.entity.germ.GermplasmEntity;
import org.brapi.test.BrAPITestServer.repository.BrAPIRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

public interface GermplasmRepository extends BrAPIRepository<GermplasmEntity, String> {
@Modifying
@Transactional
@Query("UPDATE GermplasmEntity g SET g.softDeleted = :softDeleted WHERE g.id = :germplasmId")
int updateSoftDeletedStatus(@Param("germplasmId") String listId, @Param("softDeleted") boolean softDeleted);

@Modifying
@Transactional
@Query("UPDATE GermplasmEntity g SET g.softDeleted = :softDeleted WHERE g.id IN :germplasmIds")
int updateSoftDeletedStatusBatch(@Param("germplasmIds") List<String> germplasmIds, @Param("softDeleted") boolean softDeleted);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ public class PedigreeRepositoryCustomImpl implements PedigreeRepositoryCustom {
public List<PedigreeNodeEntity> findPedigreeSiblings(PedigreeNodeEntity sourceNode) {
String siblingSearchSQL = "select distinct siblingNode from PedigreeNodeEntity siblingNode "
+ "join siblingNode.edges parentEdge "
+ "join parentEdge.conncetedNode.edges childEdge "
+ "join childEdge.conncetedNode sourceNode "
+ "where sourceNode = :sourceId and parentEdge.edgeType = 0 and childEdge.edgeType = 1 and siblingNode.id != childEdge.conncetedNode.id";
+ "join parentEdge.connectedNode.edges childEdge "
+ "join childEdge.connectedNode sourceNode "
+ "where sourceNode = :sourceId and parentEdge.edgeType = 0 and childEdge.edgeType = 1 and siblingNode.id != childEdge.connectedNode.id";

TypedQuery<PedigreeNodeEntity> query = em.createQuery(siblingSearchSQL, PedigreeNodeEntity.class);
query.setParameter("sourceId", sourceNode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ private void fetchPedigreeEdges(Page<GermplasmEntity> page) {
searchQuery.leftJoinFetch("pedigree", "pedigree")
.leftJoinFetch("*pedigree.crossingProject", "crossingProject")
.leftJoinFetch("*pedigree.edges", "pedigreeEdges")
.leftJoinFetch("*pedigreeEdges.conncetedNode", "connectedNode")
.leftJoinFetch("*pedigreeEdges.connectedNode", "connectedNode")
.appendList(page.stream()
.map(BrAPIBaseEntity::getId)
.collect(Collectors.toList()), "id");
Expand Down Expand Up @@ -326,6 +326,29 @@ public Germplasm updateGermplasm(String germplasmDbId, GermplasmNewRequest body)
return convertFromEntity(savedEntity);
}

public void deleteGermplasmBatch(List<String> germplasmDbIds) {
germplasmRepository.deleteAllByIdInBatch(germplasmDbIds);
}

public void softDeleteGermplasmBatch(List<String> germplasmDbIds) {
germplasmRepository.updateSoftDeletedStatusBatch(germplasmDbIds, true);
}

public void deleteGermplasm(String germplasmDbId) throws BrAPIServerException {
// Soft delete the germplasm first since the method throws a 404 exception if the germplasm is not found
softDeleteGermplasm(germplasmDbId);

// Hard delete the list
germplasmRepository.deleteAllByIdInBatch(Arrays.asList(germplasmDbId));
}

public void softDeleteGermplasm(String germplasmDbId) throws BrAPIServerException {
int updatedCount = germplasmRepository.updateSoftDeletedStatus(germplasmDbId, true);
if (updatedCount == 0) {
throw new BrAPIServerDbIdNotFoundException("list", germplasmDbId, "list database ID", HttpStatus.NOT_FOUND);
}
}

public List<Germplasm> saveGermplasm(@Valid List<GermplasmNewRequest> body) throws BrAPIServerException {
List<GermplasmEntity> toSave = new ArrayList<>();
for (GermplasmNewRequest germplasm : body) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import org.brapi.test.BrAPITestServer.model.entity.germ.CrossingProjectEntity;
import org.brapi.test.BrAPITestServer.model.entity.germ.GermplasmEntity;
import org.brapi.test.BrAPITestServer.model.entity.germ.PedigreeEdgeEntity;
import org.brapi.test.BrAPITestServer.model.entity.germ.PedigreeEdgeEntity.EdgeType;
import org.brapi.test.BrAPITestServer.model.entity.germ.PedigreeNodeEntity;
import org.brapi.test.BrAPITestServer.repository.germ.PedigreeEdgeRepository;
import org.brapi.test.BrAPITestServer.repository.germ.PedigreeRepository;
Expand Down Expand Up @@ -408,17 +407,17 @@ private PedigreeNode convertFromEntity(PedigreeNodeEntity entity, PedigreeSearch
if (entity.getParentEdges() != null && request.isIncludeParents()) {
node.setParents(entity.getParentEdges().stream().map(edge -> {
PedigreeNodeParents parent = new PedigreeNodeParents();
parent.setGermplasmDbId(edge.getConncetedNode().getGermplasm().getId());
parent.setGermplasmName(edge.getConncetedNode().getGermplasm().getGermplasmName());
parent.setGermplasmDbId(edge.getConnectedNode().getGermplasm().getId());
parent.setGermplasmName(edge.getConnectedNode().getGermplasm().getGermplasmName());
parent.setParentType(edge.getParentType());
return parent;
}).collect(Collectors.toList()));
}
if (entity.getProgenyEdges() != null && request.isIncludeProgeny()) {
node.setProgeny(entity.getProgenyEdges().stream().map(edge -> {
PedigreeNodeParents progeny = new PedigreeNodeParents();
progeny.setGermplasmDbId(edge.getConncetedNode().getGermplasm().getId());
progeny.setGermplasmName(edge.getConncetedNode().getGermplasm().getGermplasmName());
progeny.setGermplasmDbId(edge.getConnectedNode().getGermplasm().getId());
progeny.setGermplasmName(edge.getConnectedNode().getGermplasm().getGermplasmName());
progeny.setParentType(edge.getParentType());
return progeny;
}).collect(Collectors.toList()));
Expand All @@ -442,10 +441,10 @@ static public String getPedigreeString(PedigreeNodeEntity entity) {
if (entity.getParentEdges() != null && !entity.getParentEdges().isEmpty()) {
Optional<PedigreeNodeEntity> mother = entity.getParentEdges().stream().filter(parentEdge -> {
return ParentType.FEMALE == parentEdge.getParentType();
}).map(PedigreeEdgeEntity::getConncetedNode).findFirst();
}).map(PedigreeEdgeEntity::getConnectedNode).findFirst();
Optional<PedigreeNodeEntity> father = entity.getParentEdges().stream().filter(parentEdge -> {
return ParentType.MALE == parentEdge.getParentType();
}).map(PedigreeEdgeEntity::getConncetedNode).findFirst();
}).map(PedigreeEdgeEntity::getConnectedNode).findFirst();

if (mother.isPresent()) {
pedStr += mother.get().getGermplasm().getGermplasmName() + "/";
Expand Down Expand Up @@ -493,7 +492,7 @@ private void updateEntityWithEdges(PedigreeNodeEntity entity, PedigreeNode node)
if (node.getParents() != null) {

SearchQueryBuilder<PedigreeEdgeEntity> search = new SearchQueryBuilder<PedigreeEdgeEntity>(PedigreeEdgeEntity.class);
search.appendSingle(node.getGermplasmDbId(), "conncetedNode.germplasm.id");
search.appendSingle(node.getGermplasmDbId(), "connectedNode.germplasm.id");
search.appendEnum(PedigreeEdgeEntity.EdgeType.child, "edgeType");
Pageable defaultPageSize = PagingUtility.getPageRequest(new Metadata().pagination(new IndexPagination().pageSize(10000000)));
Page<PedigreeEdgeEntity> existingParentEdges = pedigreeEdgeRepository.findAllBySearch(search, defaultPageSize);
Expand All @@ -516,7 +515,7 @@ private void updateEntityWithEdges(PedigreeNodeEntity entity, PedigreeNode node)
if (node.getProgeny() != null) {

SearchQueryBuilder<PedigreeEdgeEntity> search = new SearchQueryBuilder<PedigreeEdgeEntity>(PedigreeEdgeEntity.class);
search.appendSingle(node.getGermplasmDbId(), "conncetedNode.germplasm.id");
search.appendSingle(node.getGermplasmDbId(), "connectedNode.germplasm.id");
search.appendEnum(PedigreeEdgeEntity.EdgeType.parent, "edgeType");
Pageable defaultPageSize = PagingUtility.getPageRequest(new Metadata().pagination(new IndexPagination().pageSize(10000000)));
Page<PedigreeEdgeEntity> existingProgenyEdges = pedigreeEdgeRepository.findAllBySearch(search, defaultPageSize);
Expand Down
Loading