Skip to content

Commit

Permalink
Merge branch 'develop' into MAT-6267
Browse files Browse the repository at this point in the history
  • Loading branch information
ethankaplan authored Nov 1, 2023
2 parents f3bdd7b + d3a1428 commit 6abcbdf
Show file tree
Hide file tree
Showing 14 changed files with 699 additions and 59 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
<dependency>
<groupId>gov.cms.madie</groupId>
<artifactId>madie-java-models</artifactId>
<version>0.6.6-SNAPSHOT</version>
<version>0.6.10-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
Expand Down
32 changes: 27 additions & 5 deletions src/main/java/cms/gov/madie/measure/resources/AdminController.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
import cms.gov.madie.measure.dto.MeasureTestCaseValidationReport;
import cms.gov.madie.measure.dto.MeasureTestCaseValidationReportSummary;
import cms.gov.madie.measure.dto.TestCaseValidationReport;
import cms.gov.madie.measure.exceptions.ResourceNotFoundException;
import cms.gov.madie.measure.repositories.MeasureRepository;
import cms.gov.madie.measure.services.ActionLogService;
import cms.gov.madie.measure.services.MeasureService;
import cms.gov.madie.measure.services.MeasureSetService;
import cms.gov.madie.measure.services.TestCaseService;
import gov.cms.madie.models.common.ActionType;
import gov.cms.madie.models.measure.Measure;
import gov.cms.madie.models.measure.MeasureSet;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
Expand All @@ -15,11 +20,7 @@
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

import java.security.Principal;
import java.util.ArrayList;
Expand All @@ -34,6 +35,9 @@ public class AdminController {
private final MeasureService measureService;
private final TestCaseService testCaseService;
private final MeasureSetService measureSetService;
private final ActionLogService actionLogService;

private final MeasureRepository measureRepository;

@Value("${madie.admin.concurrency-limit}")
private int concurrencyLimit;
Expand Down Expand Up @@ -104,6 +108,24 @@ public ResponseEntity<MeasureTestCaseValidationReportSummary> validateAllMeasure
.build());
}

@DeleteMapping("/measures/{id}")
@PreAuthorize("#request.getHeader('api-key') == #apiKey")
public ResponseEntity<Measure> permDeleteMeasure(
HttpServletRequest request,
@Value("${admin-api-key}") String apiKey,
Principal principal,
@RequestHeader("Authorization") String accessToken,
@PathVariable String id) {

Measure measureToDelete = measureService.findMeasureById(id);
if (measureToDelete != null) {
measureRepository.delete(measureToDelete);
actionLogService.logAction(id, Measure.class, ActionType.DELETED, principal.getName());
return ResponseEntity.ok(measureToDelete);
}
throw new ResourceNotFoundException(id);
}

private Callable<MeasureTestCaseValidationReport> buildCallableForMeasureId(
final String measureId, final String accessToken) {
return () -> testCaseService.updateTestCaseValidResourcesWithReport(measureId, accessToken);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
package cms.gov.madie.measure.resources;

import java.security.Principal;
import java.util.List;
import java.util.Optional;

import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import cms.gov.madie.measure.exceptions.ResourceNotFoundException;
import cms.gov.madie.measure.repositories.MeasureRepository;
import cms.gov.madie.measure.services.BundleService;
import cms.gov.madie.measure.services.FhirServicesClient;
import cms.gov.madie.measure.utils.ControllerUtil;
import cms.gov.madie.measure.utils.ExportFileNamesUtil;
import gov.cms.madie.models.measure.Measure;
import java.security.Principal;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@Slf4j
@RestController
Expand Down Expand Up @@ -58,6 +66,7 @@ public ResponseEntity<byte[]> getTestCaseExport(
Principal principal,
@RequestHeader("Authorization") String accessToken,
@PathVariable String measureId,
@RequestParam Optional<String> bundleType,
@RequestBody List<String> testCaseId) {

final String username = principal.getName();
Expand All @@ -70,7 +79,9 @@ public ResponseEntity<byte[]> getTestCaseExport(
}

Measure measure = measureOptional.get();
// change Measure Bundle Type to "type" for export

return fhirServicesClient.getTestCaseExports(measure, accessToken, testCaseId);
return fhirServicesClient.getTestCaseExports(
measure, accessToken, testCaseId, bundleType.orElse(("COLLECTION").toUpperCase()));
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package cms.gov.madie.measure.services;

import cms.gov.madie.measure.config.FhirServicesConfig;
import gov.cms.madie.models.common.BundleType;
import gov.cms.madie.models.dto.ExportDTO;
import gov.cms.madie.models.measure.HapiOperationOutcome;
import gov.cms.madie.models.measure.Measure;
import java.net.URI;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
Expand Down Expand Up @@ -71,7 +73,7 @@ public ResponseEntity<HapiOperationOutcome> validateBundle(
}

public ResponseEntity<byte[]> getTestCaseExports(
Measure measure, String accessToken, List<String> testCaseId) {
Measure measure, String accessToken, List<String> testCaseId, String bundleType) {
URI uri =
URI.create(
fhirServicesConfig.getMadieFhirServiceBaseUrl()
Expand All @@ -82,7 +84,12 @@ public ResponseEntity<byte[]> getTestCaseExports(
headers.set(HttpHeaders.ACCEPT, MediaType.ALL_VALUE);
headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);

ExportDTO dto = ExportDTO.builder().measure(measure).testCaseIds(testCaseId).build();
ExportDTO dto =
ExportDTO.builder()
.measure(measure)
.bundleType(BundleType.valueOf(bundleType))
.testCaseIds(testCaseId)
.build();

HttpEntity<ExportDTO> measureEntity = new HttpEntity<>(dto, headers);

Expand Down
80 changes: 58 additions & 22 deletions src/main/java/cms/gov/madie/measure/services/GroupService.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
Expand All @@ -47,6 +49,8 @@ public class GroupService {
private final CqlDefinitionReturnTypeService cqlDefinitionReturnTypeService;
private final CqlObservationFunctionService cqlObservationFunctionService;

@Autowired private ModelValidatorLocator modelLocator;

public Group createOrUpdateGroup(Group group, String measureId, String username) {

Measure measure = measureRepository.findById(measureId).orElse(null);
Expand Down Expand Up @@ -228,11 +232,16 @@ public void updateTestCaseGroupWithMeasureGroup(
measureGroup.getStratifications().stream()
.map(
measureStrata -> {
String strataName =
String.format(
"Strata-%d %s",
count.getAndIncrement(), measureStrata.getAssociation().getDisplay());
return updateTestCaseStratification(measureStrata, testCaseGroup, strataName);
if (null != measureStrata.getAssociation()) {
String strataName =
String.format(
"Strata-%d %s",
count.getAndIncrement(), measureStrata.getAssociation().getDisplay());
return updateTestCaseStratification(measureStrata, testCaseGroup, strataName);
} else {
String strataName = String.format("Strata-%d", count.getAndIncrement());
return updateTestCaseStratification(measureStrata, testCaseGroup, strataName);
}
})
.filter(Objects::nonNull)
.toList();
Expand All @@ -247,22 +256,9 @@ public void removeGroupFromTestCases(String groupId, List<TestCase> testCases) {
}

public void validateGroupAssociations(String model, Group group) {
boolean isQdmModel = model.equalsIgnoreCase(ModelType.QDM_5_6.getValue());
boolean isAssociated;
if (isQdmModel) {
isAssociated =
group.getStratifications().stream().anyMatch(map -> map.getAssociation() != null);

} else {
isAssociated =
group.getStratifications().stream().anyMatch(map -> map.getAssociation() == null);
}
if (isAssociated) {
throw new InvalidGroupException(
isQdmModel
? "QDM group stratifications cannot be associated."
: "QI-Core group stratifications should be associated to a valid population type.");
}
String shortModel = ModelType.valueOfName(model).getShortValue();
ModelValidator validator = modelLocator.get(shortModel);
validator.validateGroupAssociations(model, group);
}

private TestCasePopulationValue updateTestCasePopulation(
Expand All @@ -287,7 +283,7 @@ private TestCasePopulationValue updateTestCasePopulation(
return testCasePopulation;
}

private TestCaseStratificationValue updateTestCaseStratification(
protected TestCaseStratificationValue updateTestCaseStratification(
Stratification stratification, TestCaseGroupPopulation testCaseGroup, String strataName) {
// if no cql definition(optional), no need to consider stratification
if (StringUtils.isEmpty(stratification.getCqlDefinition())) {
Expand All @@ -313,9 +309,49 @@ private TestCaseStratificationValue updateTestCaseStratification(
.build();
}
testCaseStrata.setName(strataName);
handlePopulationChange(testCaseStrata, testCaseGroup);

return testCaseStrata;
}

private void handlePopulationChange(
TestCaseStratificationValue testCaseStrata, TestCaseGroupPopulation testCaseGroup) {
List<TestCasePopulationValue> testCasePopulationValues = testCaseStrata.getPopulationValues();
List<TestCasePopulationValue> testCasePopulationValuesFromGroup =
testCaseGroup.getPopulationValues();

if (!CollectionUtils.isEmpty(testCasePopulationValuesFromGroup)) {
if (!CollectionUtils.isEmpty(testCasePopulationValues)) {
for (TestCasePopulationValue testCasePopulationValueFromGroup :
testCasePopulationValuesFromGroup) {
// if there is new population value from testCasePopulationValuesFromGroup
if (!findExistsTestCasePopulationValue(
testCasePopulationValueFromGroup.getId(), testCasePopulationValues)) {
testCasePopulationValues.add(testCasePopulationValueFromGroup);
}
// delete any that is not in testCasePopulationValuesFromGroup
List<TestCasePopulationValue> tempTestCasePopulationValues = new ArrayList<>();
for (TestCasePopulationValue tempTestCasePopulationValue : testCasePopulationValues) {
if (findExistsTestCasePopulationValue(
tempTestCasePopulationValue.getId(), testCasePopulationValuesFromGroup)) {
tempTestCasePopulationValues.add(tempTestCasePopulationValue);
}
}
testCaseStrata.setPopulationValues(tempTestCasePopulationValues);
}
} // when there is new strat
else {
testCaseStrata.setPopulationValues(testCasePopulationValuesFromGroup);
}
}
}

private boolean findExistsTestCasePopulationValue(
String id, List<TestCasePopulationValue> testCasePopulationValues) {
return testCasePopulationValues.stream()
.anyMatch(testCasePopulationValue -> id.equalsIgnoreCase(testCasePopulationValue.getId()));
}

private TestCasePopulationValue findTestCasePopulation(
String populationId, TestCaseGroupPopulation testCaseGroup) {
return testCaseGroup.getPopulationValues().stream()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package cms.gov.madie.measure.services;

import gov.cms.madie.models.measure.Group;

public interface ModelValidator {
public void validateGroupAssociations(String model, Group group);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package cms.gov.madie.measure.services;

import org.springframework.beans.BeansException;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class ModelValidatorLocator implements ApplicationContextAware {
private ApplicationContext ctx;

public ModelValidator get(String model) {
return ctx.getBean(model + "ModelValidator", ModelValidator.class);
}

@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.ctx = ctx;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package cms.gov.madie.measure.services;

import org.springframework.stereotype.Service;

import cms.gov.madie.measure.exceptions.InvalidGroupException;
import gov.cms.madie.models.measure.Group;

@Service
public class QdmModelValidator implements ModelValidator {

@Override
public void validateGroupAssociations(String model, Group group) {

boolean isAssociated;

isAssociated =
group.getStratifications().stream().anyMatch(map -> map.getAssociation() != null);

if (isAssociated) {
throw new InvalidGroupException("QDM group stratifications cannot be associated.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package cms.gov.madie.measure.services;

import org.springframework.stereotype.Service;

import cms.gov.madie.measure.exceptions.InvalidGroupException;
import gov.cms.madie.models.measure.Group;

@Service
public class QicoreModelValidator implements ModelValidator {

@Override
public void validateGroupAssociations(String model, Group group) {

boolean isNotAssociated;

isNotAssociated =
group.getStratifications().stream().anyMatch(map -> map.getAssociation() == null);

if (isNotAssociated) {
throw new InvalidGroupException(
"QI-Core group stratifications should be associated to a valid population type.");
}
}
}
Loading

0 comments on commit 6abcbdf

Please sign in to comment.