Skip to content

Commit

Permalink
DT-945: Handle Update Study Registration Better (#2420)
Browse files Browse the repository at this point in the history
  • Loading branch information
rushtong authored Oct 31, 2024
1 parent 4b03bde commit 81fe1c0
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
Expand Down Expand Up @@ -851,23 +850,4 @@ public static ConsentGroup.AccessManagement fromValue(String value) {
}
}

public boolean isInvalidForUpdate() {
return Objects.nonNull(this.accessManagement) ||
Objects.nonNull(this.generalResearchUse) ||
Objects.nonNull(this.hmb) ||
(Objects.nonNull(this.diseaseSpecificUse) && this.diseaseSpecificUse.size() > 0) ||
Objects.nonNull(this.poa) ||
Objects.nonNull(this.otherPrimary) ||
Objects.nonNull(this.nmds) ||
Objects.nonNull(this.gso) ||
Objects.nonNull(this.pub) ||
Objects.nonNull(this.col) ||
Objects.nonNull(this.irb) ||
Objects.nonNull(this.gs) ||
Objects.nonNull(this.mor) ||
Objects.nonNull(this.morDate) ||
Objects.nonNull(this.npu) ||
Objects.nonNull(this.otherSecondary);
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package org.broadinstitute.consent.http.models.dataset_registration_v1;

import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import jakarta.ws.rs.BadRequestException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
Expand All @@ -10,7 +16,9 @@
import org.broadinstitute.consent.http.models.Dataset;
import org.broadinstitute.consent.http.models.Study;
import org.broadinstitute.consent.http.models.dataset_registration_v1.DatasetRegistrationSchemaV1.NihAnvilUse;
import org.broadinstitute.consent.http.models.dataset_registration_v1.builder.DatasetRegistrationSchemaV1Builder;
import org.broadinstitute.consent.http.service.DatasetService;
import org.broadinstitute.consent.http.util.gson.GsonUtil;

public class DatasetRegistrationSchemaV1UpdateValidator {

Expand All @@ -20,6 +28,89 @@ public DatasetRegistrationSchemaV1UpdateValidator(DatasetService datasetService)
this.datasetService = datasetService;
}

private final ExclusionStrategy studyExclusionStrategy = new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes fieldAttributes) {
return fieldAttributes.getName().equalsIgnoreCase("dataSubmitterUserId");
}

@Override
public boolean shouldSkipClass(Class<?> aClass) {
return aClass.getSimpleName().equalsIgnoreCase("ConsentGroup");
}
};

private final ExclusionStrategy consentGroupExclusionStrategy = new ExclusionStrategy() {
@Override
public boolean shouldSkipField(FieldAttributes fieldAttributes) {
final HashSet<String> exclusions = new HashSet<>(List.of(
DatasetRegistrationSchemaV1Builder.accessManagement,
DatasetRegistrationSchemaV1Builder.col,
DatasetRegistrationSchemaV1Builder.dataAccessCommitteeId,
DatasetRegistrationSchemaV1Builder.datasetIdentifier,
DatasetRegistrationSchemaV1Builder.diseaseSpecificUse,
DatasetRegistrationSchemaV1Builder.generalResearchUse,
DatasetRegistrationSchemaV1Builder.gs,
DatasetRegistrationSchemaV1Builder.gso,
DatasetRegistrationSchemaV1Builder.hmb,
DatasetRegistrationSchemaV1Builder.irb,
DatasetRegistrationSchemaV1Builder.nmds,
DatasetRegistrationSchemaV1Builder.mor,
DatasetRegistrationSchemaV1Builder.morDate,
DatasetRegistrationSchemaV1Builder.npu,
DatasetRegistrationSchemaV1Builder.otherPrimary,
DatasetRegistrationSchemaV1Builder.otherSecondary,
DatasetRegistrationSchemaV1Builder.pub,
DatasetRegistrationSchemaV1Builder.poa
));
return exclusions.contains(fieldAttributes.getName());
}

@Override
public boolean shouldSkipClass(Class<?> aClass) {
return false;
}
};

/**
* Create a registration object suitable for the Update operation.
*
* @param json DatasetRegistrationSchemaV1 in JSON format
* @return DatasetRegistrationSchemaV1
*/
public DatasetRegistrationSchemaV1 deserializeRegistration(String json) {
Gson studyGson = GsonUtil.gsonBuilderWithAdapters()
.addDeserializationExclusionStrategy(studyExclusionStrategy)
.create();
// Create the registration without any ConsentGroups
DatasetRegistrationSchemaV1 registration = studyGson.fromJson(json,
DatasetRegistrationSchemaV1.class);
// Ensure that we have no null entries before parsing them.
registration.setConsentGroups(new ArrayList<>());

// Conditionally parse the consent groups
Gson gson = GsonUtil.getInstance();
Gson filteredCGGson = GsonUtil.gsonBuilderWithAdapters()
.addDeserializationExclusionStrategy(consentGroupExclusionStrategy).create();
JsonObject jsonObject = gson.fromJson(json, JsonObject.class);
JsonArray jsonArray = jsonObject.getAsJsonArray("consentGroups");
if (jsonArray != null) {
jsonArray.asList().forEach(jsonElement -> {
JsonObject cgJson = jsonElement.getAsJsonObject();
if (cgJson.has("datasetId")) {
// If we have a dataset id, we're updating. Filter out non-updatable fields
ConsentGroup cg = filteredCGGson.fromJson(cgJson, ConsentGroup.class);
registration.getConsentGroups().add(cg);
} else {
// If we have don't have a dataset id, we're trying to add a new one to the study.
ConsentGroup cg = gson.fromJson(cgJson, ConsentGroup.class);
registration.getConsentGroups().add(cg);
}
});
}
return registration;
}

public boolean validate(Study existingStudy, DatasetRegistrationSchemaV1 registration) {

// Validate that the new study name is unique
Expand All @@ -29,8 +120,7 @@ public boolean validate(Study existingStudy, DatasetRegistrationSchemaV1 registr
throw new BadRequestException("Invalid change to Study Name");
}

// Not modifiable: Data Submitter Name/Email, Primary Data Use,
// Secondary Data Use
// Not modifiable: Data Submitter Name/Email
if (registration.getDataSubmitterUserId() != null
&& !registration.getDataSubmitterUserId().equals(existingStudy.getCreateUserId())) {
throw new BadRequestException("Invalid change to Data Submitter");
Expand All @@ -41,16 +131,6 @@ public boolean validate(Study existingStudy, DatasetRegistrationSchemaV1 registr
throw new BadRequestException("Invalid number of Consent Groups");
}

// Data use changes are not allowed for existing datasets
List<ConsentGroup> invalidConsentGroups = registration.getConsentGroups()
.stream()
.filter(cg -> Objects.nonNull(cg.getDatasetId()))
.filter(ConsentGroup::isInvalidForUpdate)
.toList();
if (!invalidConsentGroups.isEmpty()) {
throw new BadRequestException("Invalid Data Use changes to existing Consent Groups");
}

// Ensure that all consent group changes are for datasets in the current study
List<ConsentGroup> nonStudyConsentGroups = registration.getConsentGroups()
.stream()
Expand Down Expand Up @@ -141,16 +221,9 @@ public boolean validate(Study existingStudy, DatasetRegistrationSchemaV1 registr
throw new BadRequestException("NIH Grant of Contract Number is required");
}
}
if (Objects.isNull(registration.getPhenotypeIndication())) {
throw new BadRequestException("Phenotype Indication is required");
}
if (Objects.isNull(registration.getPiName())) {
throw new BadRequestException("Principal Investigator is required");
}
if (Objects.isNull(registration.getDataCustodianEmail()) || registration.getDataCustodianEmail()
.isEmpty()) {
throw new BadRequestException("Data Custodian Email is required");
}

return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public class DatasetRegistrationSchemaV1Builder {
public static final String npu = "npu";
public static final String otherSecondary = "otherSecondary";
public static final String dataAccessCommitteeId = "dataAccessCommitteeId";
public static final String datasetIdentifier = "datasetIdentifier";
public static final String dataLocation = "dataLocation";
public static final String url = "url";
public static final String numberOfParticipants = "numberOfParticipants";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,7 @@ public Response updateStudyByRegistration(
// Manually validate the schema from an editing context. Validation with the schema tools
// enforces it in a creation context but doesn't work for editing purposes.
DatasetRegistrationSchemaV1UpdateValidator updateValidator = new DatasetRegistrationSchemaV1UpdateValidator(datasetService);
Gson gson = GsonUtil.gsonBuilderWithAdapters().create();
DatasetRegistrationSchemaV1 registration = gson.fromJson(json,
DatasetRegistrationSchemaV1.class);
DatasetRegistrationSchemaV1 registration = updateValidator.deserializeRegistration(json);

if (updateValidator.validate(existingStudy, registration)) {
// Update study from registration
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/assets/paths/datasetByIdDataUse.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
put:
summary: Admin Only API to update the Dataset's Data Use object from JSON
operationId: updateDatasetDataUse
description: |
Admin Only API to update the Dataset's Data Use object from JSON.
## Note
Expand Down
7 changes: 5 additions & 2 deletions src/main/resources/assets/paths/studyById.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,15 @@ put:
is also provided, then alternative data sharing information is required in the registration
schema. Study Names can be updated, but they must be unique in the system.
See [/api/dataset/studyNames](#/Study/findAllStudyNames) for a list of existing study names.
Data Use fields can only be modified using Admin permissions. See
[/api/dataset/{id}/datause](#/Admin/updateDatasetDataUse) to update a dataset's Data Use.
The following fields are not modifiable:
* Data Submitter Name/Email
* Primary Data Use
* Secondary Data Use
* All Data Use fields (if any are provided, they will be ignored)
* Access Management (if provided, it will be ignored)
* Consent Group Name
* Data Access Committee Id
Additionally, existing Consent Groups cannot be removed from the study, but new ones can be
added.
Expand Down
Loading

0 comments on commit 81fe1c0

Please sign in to comment.