From 6943f4567eb1637efb0d27eb2d4401cbedabd56c Mon Sep 17 00:00:00 2001 From: Katy Ekey Date: Mon, 2 Dec 2024 10:17:30 -0500 Subject: [PATCH 01/11] feat!: Remove deprecated activity metadata objectId field Breaking Change [#OCD-4740] --- .../gov/healthit/chpl/domain/activity/ActivityMetadata.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ActivityMetadata.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ActivityMetadata.java index 11a18b87fa..48e7bbe9d0 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ActivityMetadata.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ActivityMetadata.java @@ -33,15 +33,11 @@ public class ActivityMetadata implements Serializable { private ActivityConcept concept; @Singular private Set categories = new HashSet(); + @Setter(AccessLevel.NONE) @Getter(AccessLevel.NONE) private Date date; - @DeprecatedResponseField(message = "This field is deprecated and will be removed. Use object.id", - removalDate = "2024-10-31") - @Deprecated - private Long objectId; - private ActivityObject object; private User responsibleUser; private String description; From f32de64bf1cf1c9b67ce6e874935420823244202 Mon Sep 17 00:00:00 2001 From: Katy Ekey Date: Tue, 3 Dec 2024 14:22:34 -0500 Subject: [PATCH 02/11] feat!: Deprecate fields in dev, prod, listing activity metadata The additional fields being filled in for developer, product, and listing activity metadata are not going to be used any longer. There were issues parsing this data out because of work done in OCD-4554 (repurposing a field called 'statuses' which exists in old activity and gives errors when we tried to parse that old activity). We are basically just no longer doing work we didn't need to be doing in the first place, now that the individual Activity pages are going away. [#OCD-4740] --- .../web/controller/ActivityController.java | 18 -- .../activity/ActivityMetadataBuilder.java | 1 - .../DeveloperActivityMetadataBuilder.java | 126 ++----------- .../ListingActivityMetadataBuilder.java | 171 ++---------------- .../ProductActivityMetadataBuilder.java | 133 ++------------ .../domain/activity/ActivityMetadata.java | 6 +- .../domain/activity/ActivityMetadataPage.java | 6 - .../activity/DeveloperActivityMetadata.java | 6 + .../activity/ListingActivityMetadata.java | 24 +++ .../activity/ProductActivityMetadata.java | 6 + .../chpl/user/cognito/CognitoApiWrapper.java | 6 +- 11 files changed, 81 insertions(+), 422 deletions(-) diff --git a/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/ActivityController.java b/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/ActivityController.java index 2de2d21e8e..988190abfb 100644 --- a/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/ActivityController.java +++ b/chpl/chpl-api/src/main/java/gov/healthit/chpl/web/controller/ActivityController.java @@ -339,7 +339,6 @@ public List metadataForProductById(@PathVariable("id") final L }) @RequestMapping(value = "/metadata/versions", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - @DeprecatedApiResponseFields(friendlyUrl = "/activity/metadata/versions", responseClass = ActivityMetadataPage.class) public ActivityMetadataPage metadataForVersions(@RequestParam(required = false) Long start, @RequestParam(required = false) Long end, @RequestParam(required = false) Integer pageNum, @RequestParam(required = false) Integer pageSize) throws JsonParseException, IOException, ValidationException { @@ -354,7 +353,6 @@ public ActivityMetadataPage metadataForVersions(@RequestParam(required = false) }) @RequestMapping(value = "/metadata/versions/{id:^-?\\d+$}", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - @DeprecatedApiResponseFields(friendlyUrl = "/activity/metadata/versions/{id}", responseClass = ActivityMetadata.class) public List metadataForVersionById(@PathVariable("id") final Long id, @RequestParam(required = false) final Long start, @RequestParam(required = false) final Long end) throws JsonParseException, IOException, EntityRetrievalException, ValidationException { @@ -390,7 +388,6 @@ public List metadataForVersionById(@PathVariable("id") final L }) @RequestMapping(value = "/metadata/acbs", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - @DeprecatedApiResponseFields(friendlyUrl = "/activity/metadata/acbs", responseClass = ActivityMetadataPage.class) public ActivityMetadataPage metadataForAcbs(@RequestParam(required = false) Long start, @RequestParam(required = false) Long end, @RequestParam(required = false) Integer pageNum, @RequestParam(required = false) Integer pageSize) throws JsonParseException, IOException, ValidationException { @@ -405,7 +402,6 @@ public ActivityMetadataPage metadataForAcbs(@RequestParam(required = false) Long }) @RequestMapping(value = "/metadata/acbs/{id:^-?\\d+$}", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - @DeprecatedApiResponseFields(friendlyUrl = "/activity/metadata/acbs/{id}", responseClass = ActivityMetadata.class) public List metadataForAcbById(@PathVariable("id") final Long id, @RequestParam(required = false) final Long start, @RequestParam(required = false) final Long end) throws JsonParseException, IOException, EntityRetrievalException, ValidationException { @@ -438,7 +434,6 @@ public List metadataForAcbById(@PathVariable("id") final Long }) @RequestMapping(value = "/metadata/atls", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - @DeprecatedApiResponseFields(friendlyUrl = "/activity/metadata/atls", responseClass = ActivityMetadataPage.class) public ActivityMetadataPage metadataForAtls(@RequestParam(required = false) Long start, @RequestParam(required = false) Long end, @RequestParam(required = false) Integer pageNum, @RequestParam(required = false) Integer pageSize) throws JsonParseException, IOException, ValidationException { @@ -453,7 +448,6 @@ public ActivityMetadataPage metadataForAtls(@RequestParam(required = false) Long }) @RequestMapping(value = "/metadata/atls/{id:^-?\\d+$}", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - @DeprecatedApiResponseFields(friendlyUrl = "/activity/metadata/atls/{id}", responseClass = ActivityMetadata.class) public List metadataForAtlById(@PathVariable("id") final Long id, @RequestParam(required = false) final Long start, @RequestParam(required = false) final Long end) throws JsonParseException, IOException, EntityRetrievalException, ValidationException { @@ -485,7 +479,6 @@ public List metadataForAtlById(@PathVariable("id") final Long @SecurityRequirement(name = SwaggerSecurityRequirement.BEARER) }) @RequestMapping(value = "/metadata/users", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - @DeprecatedApiResponseFields(friendlyUrl = "/activity/metadata/users", responseClass = ActivityMetadataPage.class) public ActivityMetadataPage metadataForUsers(@RequestParam(required = false) Long start, @RequestParam(required = false) Long end, @RequestParam(required = false) Integer pageNum, @RequestParam(required = false) Integer pageSize) throws JsonParseException, IOException, ValidationException { @@ -504,7 +497,6 @@ public ActivityMetadataPage metadataForUsers(@RequestParam(required = false) Lon }) @RequestMapping(value = "/metadata/announcements", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - @DeprecatedApiResponseFields(friendlyUrl = "/activity/metadata/announcements", responseClass = ActivityMetadataPage.class) public ActivityMetadataPage metadataForAnnouncements(@RequestParam(required = false) Long start, @RequestParam(required = false) Long end, @RequestParam(required = false) Integer pageNum, @RequestParam(required = false) Integer pageSize) throws JsonParseException, IOException, ValidationException { @@ -520,7 +512,6 @@ public ActivityMetadataPage metadataForAnnouncements(@RequestParam(required = fa @SecurityRequirement(name = SwaggerSecurityRequirement.BEARER) }) @RequestMapping(value = "/metadata/complaints", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - @DeprecatedApiResponseFields(friendlyUrl = "/activity/metadata/complaints", responseClass = ActivityMetadata.class) public List metadataForComplaints(@RequestParam final Long start, @RequestParam final Long end) throws JsonParseException, IOException, ValidationException { Date startDate = new Date(start); @@ -538,7 +529,6 @@ public List metadataForComplaints(@RequestParam final Long sta @SecurityRequirement(name = SwaggerSecurityRequirement.BEARER) }) @RequestMapping(value = "/metadata/quarterly-reports", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - @DeprecatedApiResponseFields(friendlyUrl = "/activity/metadata/quarterly-reports", responseClass = ActivityMetadata.class) public List metadataForQuarterlyReports(@RequestParam final Long start, @RequestParam final Long end) throws JsonParseException, IOException, ValidationException { @@ -566,7 +556,6 @@ public List metadataForQuarterlyReports(@RequestParam final Lo @SecurityRequirement(name = SwaggerSecurityRequirement.BEARER) }) @RequestMapping(value = "/metadata/annual-reports", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - @DeprecatedApiResponseFields(friendlyUrl = "/activity/metadata/annual-reports", responseClass = ActivityMetadata.class) public List metadataForAnnualReports(@RequestParam final Long start, @RequestParam final Long end) throws JsonParseException, IOException, ValidationException { Date startDate = new Date(start); @@ -583,7 +572,6 @@ public List metadataForAnnualReports(@RequestParam final Long @SecurityRequirement(name = SwaggerSecurityRequirement.API_KEY) }) @RequestMapping(value = "/metadata/corrective-action-plans", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - @DeprecatedApiResponseFields(friendlyUrl = "/activity/metadata/corrective-action-plans", responseClass = ActivityMetadataPage.class) public ActivityMetadataPage metadataForCorrectiveActionPlans(@RequestParam(required = false) Long start, @RequestParam(required = false) Long end, @RequestParam(required = false) Integer pageNum, @RequestParam(required = false) Integer pageSize) throws JsonParseException, IOException, ValidationException { @@ -601,7 +589,6 @@ public ActivityMetadataPage metadataForCorrectiveActionPlans(@RequestParam(requi }) @RequestMapping(value = "/metadata/pending-surveillances", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - @DeprecatedApiResponseFields(friendlyUrl = "/activity/metadata/pending-surveillances", responseClass = ActivityMetadataPage.class) public ActivityMetadataPage metadataForPendingSurveillances(@RequestParam(required = false) Long start, @RequestParam(required = false) Long end, @RequestParam(required = false) Integer pageNum, @RequestParam(required = false) Integer pageSize) throws JsonParseException, IOException, ValidationException { @@ -618,7 +605,6 @@ public ActivityMetadataPage metadataForPendingSurveillances(@RequestParam(requir }) @RequestMapping(value = "/metadata/change-requests", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - @DeprecatedApiResponseFields(friendlyUrl = "/activity/metadata/change-requests", responseClass = ActivityMetadata.class) public List metadataForChangeRequests(@RequestParam final Long start, @RequestParam final Long end) throws JsonParseException, IOException, ValidationException { Date startDate = new Date(start); @@ -637,7 +623,6 @@ public List metadataForChangeRequests(@RequestParam final Long }) @RequestMapping(value = "/metadata/api-keys", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - @DeprecatedApiResponseFields(friendlyUrl = "/activity/metadata/api-keys", responseClass = ActivityMetadataPage.class) public ActivityMetadataPage metadataForApiKeys(@RequestParam(required = false) Long start, @RequestParam(required = false) Long end, @RequestParam(required = false) Integer pageNum, @RequestParam(required = false) Integer pageSize) throws JsonParseException, IOException, ValidationException { @@ -654,7 +639,6 @@ public ActivityMetadataPage metadataForApiKeys(@RequestParam(required = false) L }) @RequestMapping(value = "/metadata/functionalities-tested", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - @DeprecatedApiResponseFields(friendlyUrl = "/activity/metadata/functionalities-tested", responseClass = ActivityMetadataPage.class) public ActivityMetadataPage metadataForFunctionalitiesTested(@RequestParam(required = false) Long start, @RequestParam(required = false) Long end, @RequestParam(required = false) Integer pageNum, @RequestParam(required = false) Integer pageSize) throws JsonParseException, IOException, ValidationException { @@ -671,7 +655,6 @@ public ActivityMetadataPage metadataForFunctionalitiesTested(@RequestParam(requi }) @RequestMapping(value = "/metadata/standards", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - @DeprecatedApiResponseFields(friendlyUrl = "/activity/metadata/standards", responseClass = ActivityMetadataPage.class) public ActivityMetadataPage metadataForStandards(@RequestParam(required = false) Long start, @RequestParam(required = false) Long end, @RequestParam(required = false) Integer pageNum, @RequestParam(required = false) Integer pageSize) throws JsonParseException, IOException, ValidationException { @@ -688,7 +671,6 @@ public ActivityMetadataPage metadataForStandards(@RequestParam(required = false) }) @RequestMapping(value = "/metadata/svaps", method = RequestMethod.GET, produces = "application/json; charset=utf-8") - @DeprecatedApiResponseFields(friendlyUrl = "/activity/metadata/svaps", responseClass = ActivityMetadataPage.class) public ActivityMetadataPage metadataForSvaps(@RequestParam(required = false) Long start, @RequestParam(required = false) Long end, @RequestParam(required = false) Integer pageNum, @RequestParam(required = false) Integer pageSize) throws JsonParseException, IOException, ValidationException { diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/activity/ActivityMetadataBuilder.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/activity/ActivityMetadataBuilder.java index 112eff90d8..597eec1c7b 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/activity/ActivityMetadataBuilder.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/activity/ActivityMetadataBuilder.java @@ -45,7 +45,6 @@ protected void addConceptSpecificMetadata(ActivityDTO dto, ActivityMetadata meta protected void addGenericMetadata(ActivityDTO dto, ActivityMetadata metadata) { metadata.setId(dto.getId()); metadata.setDate(dto.getActivityDate()); - metadata.setObjectId(dto.getActivityObjectId()); metadata.setObject(ActivityObject.builder() .id(dto.getActivityObjectId()) .build()); diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/activity/DeveloperActivityMetadataBuilder.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/activity/DeveloperActivityMetadataBuilder.java index 0743b716d5..59da4d1923 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/activity/DeveloperActivityMetadataBuilder.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/activity/DeveloperActivityMetadataBuilder.java @@ -1,12 +1,9 @@ package gov.healthit.chpl.activity; -import java.util.List; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import com.fasterxml.jackson.databind.ObjectMapper; - +import gov.healthit.chpl.dao.DeveloperDAO; import gov.healthit.chpl.domain.Developer; import gov.healthit.chpl.domain.activity.ActivityCategory; import gov.healthit.chpl.domain.activity.ActivityMetadata; @@ -18,12 +15,14 @@ @Log4j2 @Component("developerActivityMetadataBuilder") public class DeveloperActivityMetadataBuilder extends ActivityMetadataBuilder { - private ObjectMapper jsonMapper; + + private DeveloperDAO developerDao; @Autowired - public DeveloperActivityMetadataBuilder(ChplUserToCognitoUserUtil chplUserToCognitoUserUtil) { + public DeveloperActivityMetadataBuilder(ChplUserToCognitoUserUtil chplUserToCognitoUserUtil, + DeveloperDAO developerDao) { super(chplUserToCognitoUserUtil); - jsonMapper = new ObjectMapper(); + this.developerDao = developerDao; } @Override @@ -31,114 +30,17 @@ protected void addConceptSpecificMetadata(final ActivityDTO activity, final Acti if (!(metadata instanceof DeveloperActivityMetadata)) { return; } - DeveloperActivityMetadata developerMetadata = (DeveloperActivityMetadata) metadata; - - //parse developer specific metadata - //for merges, the original data is a list of developers. - //for other developer activities it's just a single developer. - Developer origDeveloper = null; - List origDevelopers = null; - if (activity.getOriginalData() != null) { - try { - origDeveloper = - jsonMapper.readValue(activity.getOriginalData(), Developer.class); - } catch (final Exception ignore) { - } - - //if we couldn't parse it as a Developer - //try to parse it as a List. - if (origDeveloper == null) { - try { - origDevelopers = jsonMapper.readValue(activity.getOriginalData(), - jsonMapper.getTypeFactory().constructCollectionType(List.class, Developer.class)); - } catch (final Exception ignore) { - } - } - - //if the orig data is not a developer or a list, log an error - if (origDeveloper == null && origDevelopers == null) { - LOGGER.error("Could not parse activity ID " + activity.getId() + " original data as " - + "a Developer or List. JSON was: " + activity.getOriginalData()); - } - } - - Developer newDeveloper = null; - List newDevelopers = null; - if (activity.getNewData() != null) { - try { - newDeveloper = - jsonMapper.readValue(activity.getNewData(), Developer.class); - } catch (final Exception ignore) { - } - - //if we couldn't parse it as a Developer - //try to parse it as a List. - if (newDeveloper == null) { - try { - newDevelopers = jsonMapper.readValue(activity.getNewData(), - jsonMapper.getTypeFactory().constructCollectionType(List.class, Developer.class)); - } catch (final Exception ignore) { - } - } - - //if the new data is not a developer or a list, log an error - if (newDeveloper == null && newDevelopers == null) { - LOGGER.error("Could not parse activity ID " + activity.getId() + " new data as " - + "a Developer or List. JSON was: " + activity.getNewData()); - } - } - - if (newDeveloper != null && origDeveloper != null - && newDevelopers == null && origDevelopers == null) { - //if there is a single new developer and single original developer - //that means the activity was editing the developer - parseDeveloperMetadata(developerMetadata, newDeveloper); - } else if (origDeveloper != null && newDeveloper == null - && newDevelopers == null && origDevelopers == null) { - //if there is an original developer but no new developer - //then the developer was deleted - pull its info from the orig object - parseDeveloperMetadata(developerMetadata, origDeveloper); - } else if (newDeveloper != null && origDeveloper == null - && newDevelopers == null && origDevelopers == null) { - //if there is a new developer but no original developer - //then the developer was just created - parseDeveloperMetadata(developerMetadata, newDeveloper); - } else if (newDevelopers != null && origDeveloper != null - && newDeveloper == null && origDevelopers == null) { - //multiple new developers and a single original developer - //means the activity was a split - parseDeveloperMetadata(developerMetadata, activity, newDevelopers); - } else if (origDevelopers != null && newDeveloper != null - && origDeveloper == null && newDevelopers == null) { - //multiple original developers and a single new developer - //means the activity was a merge - parseDeveloperMetadata(developerMetadata, newDeveloper); - } + DeveloperActivityMetadata developerMetadata = (DeveloperActivityMetadata) metadata; developerMetadata.getCategories().add(ActivityCategory.DEVELOPER); - } - private void parseDeveloperMetadata(DeveloperActivityMetadata developerMetadata, Developer developer) { - developerMetadata.setDeveloperName(developer.getName()); - developerMetadata.getObject().setName(developer.getName()); - developerMetadata.setDeveloperCode(developer.getDeveloperCode()); - } - - /** - * Find the developer in the list that matches the id of the developer - * the activity was recorded for. Parse activity metadata from that developer. - * @param developerMetadata - * @param activity - * @param developers - */ - private void parseDeveloperMetadata( - DeveloperActivityMetadata developerMetadata, ActivityDTO activity, - List developers) { - Long idToFind = activity.getActivityObjectId(); - for (Developer currDev : developers) { - if (currDev != null && currDev.getId().equals(idToFind)) { - parseDeveloperMetadata(developerMetadata, currDev); - break; + if (metadata.getObject() != null && metadata.getObject().getId() != null) { + Developer dev = null; + try { + dev = developerDao.getById(metadata.getObject().getId(), true); + metadata.getObject().setName(dev.getName()); + } catch (Exception ex) { + LOGGER.error("Could not find developer " + metadata.getObject().getId() + " for activity metadata.", ex); } } } diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/activity/ListingActivityMetadataBuilder.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/activity/ListingActivityMetadataBuilder.java index ab0bcb68dc..098e2bfd04 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/activity/ListingActivityMetadataBuilder.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/activity/ListingActivityMetadataBuilder.java @@ -1,35 +1,28 @@ package gov.healthit.chpl.activity; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.collections4.MapUtils; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import com.fasterxml.jackson.databind.ObjectMapper; - -import gov.healthit.chpl.certifiedproduct.service.CertificationStatusEventsService; -import gov.healthit.chpl.domain.CertifiedProductSearchDetails; import gov.healthit.chpl.domain.activity.ActivityCategory; import gov.healthit.chpl.domain.activity.ActivityMetadata; import gov.healthit.chpl.domain.activity.ListingActivityMetadata; -import gov.healthit.chpl.domain.surveillance.Surveillance; import gov.healthit.chpl.dto.ActivityDTO; +import gov.healthit.chpl.search.ListingSearchService; +import gov.healthit.chpl.search.domain.ListingSearchResult; import gov.healthit.chpl.util.ChplUserToCognitoUserUtil; +import lombok.extern.log4j.Log4j2; +@Log4j2 @Component("listingActivityMetadataBuilder") public class ListingActivityMetadataBuilder extends ActivityMetadataBuilder { - private static final Logger LOGGER = LogManager.getLogger(ListingActivityMetadataBuilder.class); - private CertificationStatusEventsService cseService; - private ObjectMapper jsonMapper; + private ListingSearchService listingSearchService; @Autowired - public ListingActivityMetadataBuilder(CertificationStatusEventsService cseService, ChplUserToCognitoUserUtil chplUserToCognitoUserUtil) { + public ListingActivityMetadataBuilder(ChplUserToCognitoUserUtil chplUserToCognitoUserUtil, + ListingSearchService listingSearchService) { super(chplUserToCognitoUserUtil); - this.cseService = cseService; - jsonMapper = new ObjectMapper(); + this.listingSearchService = listingSearchService; } @Override @@ -38,149 +31,15 @@ protected void addConceptSpecificMetadata(final ActivityDTO dto, final ActivityM return; } ListingActivityMetadata listingMetadata = (ListingActivityMetadata) metadata; - - //parse listing specific metadata - CertifiedProductSearchDetails origListing = null; - if (dto.getOriginalData() != null) { - try { - origListing = - jsonMapper.readValue(dto.getOriginalData(), CertifiedProductSearchDetails.class); - } catch (final Exception ex) { - LOGGER.error("Could not parse activity ID " + dto.getId() + " original data. " - + "JSON was: " + dto.getOriginalData(), ex); - } - } - - CertifiedProductSearchDetails newListing = null; - if (dto.getNewData() != null) { - try { - newListing = - jsonMapper.readValue(dto.getNewData(), CertifiedProductSearchDetails.class); - } catch (final Exception ex) { - LOGGER.error("Could not parse activity ID " + dto.getId() + " new data. " - + "JSON was: " + dto.getNewData(), ex); - } - } - - if (newListing != null) { - //for listing activity newListing should really never be null since listings can't be deleted - parseListingMetadata(listingMetadata, newListing); - } else if (origListing != null) { - //adding this here just in case in some future circumstance the newListing could have been null - parseListingMetadata(listingMetadata, origListing); - } - - categorizeActivity(listingMetadata, origListing, newListing); - } - - private void parseListingMetadata( - final ListingActivityMetadata listingMetadata, final CertifiedProductSearchDetails listing) { - listingMetadata.setChplProductNumber(listing.getChplProductNumber()); - listingMetadata.getObject().setName(listing.getChplProductNumber()); - if (listing.getCertifyingBody() != null - && listing.getCertifyingBody().get(CertifiedProductSearchDetails.ACB_NAME_KEY) != null - && listing.getCertifyingBody().get(CertifiedProductSearchDetails.ACB_ID_KEY) != null) { - listingMetadata.setAcbName(listing.getCertifyingBody().get(CertifiedProductSearchDetails.ACB_NAME_KEY).toString()); - listingMetadata.setAcbId(Long.valueOf(listing.getCertifyingBody().get(CertifiedProductSearchDetails.ACB_ID_KEY).toString())); - } - - //there is at least one activity record for listing ID 4801 - //that has a null certification date field due to a bug in the system at the time of the activity - listingMetadata.setCertificationDate(listing.getCertificationDate()); - if (listing.getDeveloper() != null) { - listingMetadata.setDeveloperName(listing.getDeveloper().getName()); - } - if (listing.getEdition() != null) { - listingMetadata.setEdition(listing.getEdition().getName()); - } else if (listing.getCertificationEdition() != null - && MapUtils.getString(listing.getCertificationEdition(), CertifiedProductSearchDetails.EDITION_NAME_KEY) != null) { - listingMetadata.setEdition(MapUtils.getString(listing.getCertificationEdition(), CertifiedProductSearchDetails.EDITION_NAME_KEY)); - } - if (listing.getCuresUpdate() != null) { - listingMetadata.setCuresUpdate(listing.getCuresUpdate()); - } - if (listing.getProduct() != null) { - listingMetadata.setProductName(listing.getProduct().getName()); - } - } - - private void categorizeActivity(final ListingActivityMetadata listingMetadata, - final CertifiedProductSearchDetails origListing, final CertifiedProductSearchDetails newListing) { listingMetadata.getCategories().add(ActivityCategory.LISTING); - if (origListing == null && newListing != null) { - listingMetadata.getCategories().add(ActivityCategory.LISTING_UPLOAD); - } else if (origListing != null && newListing != null) { - //status change? - if (origListing.getCertificationStatus() != null && newListing.getCertificationStatus() != null - && origListing.getCertificationStatus().getId() != newListing.getCertificationStatus().getId()) { - //check the legacy certificationStatus field for older activities - listingMetadata.getCategories().add(ActivityCategory.LISTING_STATUS_CHANGE); - } else if (!CollectionUtils.isEmpty(origListing.getCertificationEvents()) - && !CollectionUtils.isEmpty(newListing.getCertificationEvents()) - && (!CollectionUtils.isEmpty(cseService.getAddedCertificationStatusEvents(origListing, newListing)) - || !CollectionUtils.isEmpty(cseService.getRemovedCertificationStatusEvents(origListing, newListing)))) { - listingMetadata.getCategories().add(ActivityCategory.LISTING_STATUS_CHANGE); - } - //surveillance change? - //check for surveillance added or removed - if ((origListing.getSurveillance() != null && newListing.getSurveillance() == null) - || (origListing.getSurveillance() == null && newListing.getSurveillance() != null)) { - listingMetadata.getCategories().add(ActivityCategory.SURVEILLANCE); - } else if (origListing.getSurveillance() != null && newListing.getSurveillance() != null) { - if (origListing.getSurveillance().size() != newListing.getSurveillance().size()) { - listingMetadata.getCategories().add(ActivityCategory.SURVEILLANCE); - } else { - //there are the same amount of surveillances for both orig and - //new listing activity; - //check for new surveillance, deleted surveillance, or any updates - - //look for surveillance added - for (Surveillance newSurv : newListing.getSurveillance()) { - boolean foundInOrigListing = false; - for (Surveillance origSurv : origListing.getSurveillance()) { - if (origSurv.getId().longValue() == newSurv.getId().longValue()) { - foundInOrigListing = true; - } - } - if (!foundInOrigListing) { - //surv is in the new listing but not the original one = was added - listingMetadata.getCategories().add(ActivityCategory.SURVEILLANCE); - } - } - - //if there's a surveillance change already detected we don't need to look any farther - //if not keep looking for one - look for surveillance deleted - if (!listingMetadata.getCategories().contains(ActivityCategory.SURVEILLANCE)) { - for (Surveillance origSurv : origListing.getSurveillance()) { - boolean foundInNewListing = false; - for (Surveillance newSurv : newListing.getSurveillance()) { - if (origSurv.getId().longValue() == newSurv.getId().longValue()) { - foundInNewListing = true; - } - } - if (!foundInNewListing) { - //surv is in the original listing but not the new one = was deleted - listingMetadata.getCategories().add(ActivityCategory.SURVEILLANCE); - } - } - } - - //if there's a surveillance change already detected we don't need to look any farther - //if not keep looking for one - look for surveillance updated - if (!listingMetadata.getCategories().contains(ActivityCategory.SURVEILLANCE)) { - for (Surveillance origSurv : origListing.getSurveillance()) { - for (Surveillance newSurv : newListing.getSurveillance()) { - if (origSurv.getId().longValue() == newSurv.getId().longValue() - && !origSurv.matches(newSurv)) { - listingMetadata.getCategories().add(ActivityCategory.SURVEILLANCE); - //if we add a surveillance category there's no need to keep looking - //for more differences. - return; - } - } - } - } + if (metadata.getObject() != null && metadata.getObject().getId() != null) { + try { + ListingSearchResult listingSearchResult = listingSearchService.findListing(metadata.getObject().getId()); + if (listingSearchResult != null) { + metadata.getObject().setName(listingSearchResult.getChplProductNumber()); } + } catch (Exception ex) { + LOGGER.error("Could not find listing " + metadata.getObject().getId() + " for activity metadata.", ex); } } } diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/activity/ProductActivityMetadataBuilder.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/activity/ProductActivityMetadataBuilder.java index 3a3683811e..e33acc7004 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/activity/ProductActivityMetadataBuilder.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/activity/ProductActivityMetadataBuilder.java @@ -1,15 +1,9 @@ package gov.healthit.chpl.activity; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import com.fasterxml.jackson.databind.ObjectMapper; - -import gov.healthit.chpl.dao.DeveloperDAO; -import gov.healthit.chpl.domain.Developer; +import gov.healthit.chpl.dao.ProductDAO; import gov.healthit.chpl.domain.Product; import gov.healthit.chpl.domain.activity.ActivityCategory; import gov.healthit.chpl.domain.activity.ActivityMetadata; @@ -21,14 +15,14 @@ @Log4j2 @Component("productActivityMetadataBuilder") public class ProductActivityMetadataBuilder extends ActivityMetadataBuilder { - private ObjectMapper jsonMapper; - private DeveloperDAO developerDao; + + private ProductDAO productDao; @Autowired - public ProductActivityMetadataBuilder(DeveloperDAO developerDao, ChplUserToCognitoUserUtil chplUserToCognitoUserUtil) { + public ProductActivityMetadataBuilder(ChplUserToCognitoUserUtil chplUserToCognitoUserUtil, + ProductDAO productDao) { super(chplUserToCognitoUserUtil); - jsonMapper = new ObjectMapper(); - this.developerDao = developerDao; + this.productDao = productDao; } @Override @@ -37,120 +31,15 @@ protected void addConceptSpecificMetadata(ActivityDTO activity, ActivityMetadata return; } ProductActivityMetadata productMetadata = (ProductActivityMetadata) metadata; - - //parse product specific metadata - //for merges, original data is a list of products - //for splits, new data is a list of products - //otherwise we expect orig/new data to be a single product - Product origProduct = null; - List origProducts = null; - if (activity.getOriginalData() != null) { - try { - origProduct = - jsonMapper.readValue(activity.getOriginalData(), Product.class); - } catch (Exception ignore) { } - - if (origProduct == null) { - try { - origProducts = jsonMapper.readValue(activity.getOriginalData(), - jsonMapper.getTypeFactory().constructCollectionType(List.class, Product.class)); - } catch (Exception ignore) { } - } - - if (origProduct == null && origProducts == null) { - LOGGER.error("Could not parse activity ID " + activity.getId() + " original data " - + " as ProductDTO or List. JSON was: " + activity.getOriginalData()); - } - } - - Product newProduct = null; - List newProducts = null; - if (activity.getNewData() != null) { - try { - newProduct = - jsonMapper.readValue(activity.getNewData(), Product.class); - } catch (Exception ignore) { } - - if (newProduct == null) { - try { - newProducts = jsonMapper.readValue(activity.getNewData(), - jsonMapper.getTypeFactory().constructCollectionType(List.class, Product.class)); - } catch (Exception ignore) { } - } - - if (newProduct == null && newProducts == null) { - LOGGER.error("Could not parse activity ID " + activity.getId() + " new data " - + "as ProductDTO or List. JSON was: " + activity.getNewData()); - } - } - - if (newProduct != null && origProduct != null - && newProducts == null && origProducts == null) { - //if there is a single new product and single original product - //that means the activity was editing the product - parseProductMetadata(productMetadata, newProduct); - } else if (origProduct != null && newProduct == null - && newProducts == null && origProducts == null) { - //if there is an original product but no new product - //then the product was deleted - pull its info from the orig object - parseProductMetadata(productMetadata, origProduct); - } else if (newProduct != null && origProduct == null - && newProducts == null && origProducts == null) { - //if there is a new product but no original product - //then the product was just created - parseProductMetadata(productMetadata, newProduct); - } else if (newProducts != null && origProduct != null - && newProduct == null && origProducts == null) { - //multiple new products and a single original product - //means the activity was a split - parseProductMetadata(productMetadata, activity, newProducts); - } else if (origProducts != null && newProduct != null - && origProduct == null && newProducts == null) { - //multiple original products and a single new product - //means the activity was a merge - parseProductMetadata(productMetadata, newProduct); - } - productMetadata.getCategories().add(ActivityCategory.PRODUCT); - } - private void parseProductMetadata( - ProductActivityMetadata productMetadata, Product product) { - //Developer id is always filled in the activity object - //but the name does not seem to be. If the name is available - //use it but if not look up the developer by ID - if (product.getOwner() != null && !StringUtils.isEmpty(product.getOwner().getName())) { - productMetadata.setDeveloperName(product.getOwner().getName()); - } else if (product.getOwner() != null && product.getOwner().getId() != null) { + if (metadata.getObject() != null && metadata.getObject().getId() != null) { + Product product = null; try { - Developer developer = developerDao.getSimpleDeveloperById(product.getOwner().getId(), true); - productMetadata.setDeveloperName(developer.getName()); + product = productDao.getById(metadata.getObject().getId(), false); + metadata.getObject().setName(product.getName()); } catch (Exception ex) { - LOGGER.error("Unable to find developer with ID " + product.getOwner().getId() + " referenced " - + "in activity for product " + product.getId()); - } - } else if (!StringUtils.isEmpty(product.getDeveloperName())) { - productMetadata.setDeveloperName(product.getDeveloperName()); - } else if (product.getDeveloperId() != null) { - try { - Developer developer = developerDao.getSimpleDeveloperById(product.getDeveloperId(), true); - productMetadata.setDeveloperName(developer.getName()); - } catch (Exception ex) { - LOGGER.error("Unable to find developer with ID " + product.getDeveloperId() + " referenced " - + "in activity for product " + product.getId()); - } - } - productMetadata.setProductName(product.getName()); - productMetadata.getObject().setName(product.getName()); - } - - private void parseProductMetadata(ProductActivityMetadata productMetadata, ActivityDTO activity, - List products) { - Long idToFind = activity.getActivityObjectId(); - for (Product currProduct : products) { - if (currProduct != null && currProduct.getId().equals(idToFind)) { - parseProductMetadata(productMetadata, currProduct); - break; + LOGGER.error("Could not find product " + metadata.getObject().getId() + " for activity metadata.", ex); } } } diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ActivityMetadata.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ActivityMetadata.java index 48e7bbe9d0..835a149faf 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ActivityMetadata.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ActivityMetadata.java @@ -6,7 +6,6 @@ import java.util.Objects; import java.util.Set; -import gov.healthit.chpl.api.deprecatedUsage.DeprecatedResponseField; import gov.healthit.chpl.domain.auth.User; import gov.healthit.chpl.util.Util; import lombok.AccessLevel; @@ -17,7 +16,6 @@ import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; import lombok.Setter; -import lombok.Singular; import lombok.ToString; @Builder @@ -31,7 +29,8 @@ public class ActivityMetadata implements Serializable { private Long id; private ActivityConcept concept; - @Singular + + @Builder.Default private Set categories = new HashSet(); @Setter(AccessLevel.NONE) @@ -39,6 +38,7 @@ public class ActivityMetadata implements Serializable { private Date date; private ActivityObject object; + private User responsibleUser; private String description; diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ActivityMetadataPage.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ActivityMetadataPage.java index 77f2bafadd..efc3e47360 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ActivityMetadataPage.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ActivityMetadataPage.java @@ -3,12 +3,6 @@ import java.io.Serializable; import java.util.Set; -/** - * A page of activity metadata including the page number, page size, - * and total result set size. - * @author kekey - * - */ public class ActivityMetadataPage implements Serializable { private static final long serialVersionUID = -3855142961571461535L; diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/DeveloperActivityMetadata.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/DeveloperActivityMetadata.java index 5971f212b5..5bfb967c2d 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/DeveloperActivityMetadata.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/DeveloperActivityMetadata.java @@ -1,5 +1,6 @@ package gov.healthit.chpl.domain.activity; +import gov.healthit.chpl.api.deprecatedUsage.DeprecatedResponseField; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -10,6 +11,11 @@ public class DeveloperActivityMetadata extends ActivityMetadata { private static final long serialVersionUID = 9069117187928313180L; + @Deprecated + @DeprecatedResponseField(message = "This field is deprecated and will be removed.", removalDate = "2025-06-01") private String developerName; + + @Deprecated + @DeprecatedResponseField(message = "This field is deprecated and will be removed.", removalDate = "2025-06-01") private String developerCode; } diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ListingActivityMetadata.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ListingActivityMetadata.java index dd1ed5e74c..bdb92526cd 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ListingActivityMetadata.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ListingActivityMetadata.java @@ -1,5 +1,6 @@ package gov.healthit.chpl.domain.activity; +import gov.healthit.chpl.api.deprecatedUsage.DeprecatedResponseField; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; @@ -12,12 +13,35 @@ public class ListingActivityMetadata extends ActivityMetadata { private static final long serialVersionUID = 5473773376581297578L; + @Deprecated + @DeprecatedResponseField(message = "This field is deprecated and will be removed.", removalDate = "2025-06-01") private String chplProductNumber; + + @Deprecated + @DeprecatedResponseField(message = "This field is deprecated and will be removed.", removalDate = "2025-06-01") private String acbName; + + @Deprecated + @DeprecatedResponseField(message = "This field is deprecated and will be removed.", removalDate = "2025-06-01") private Long acbId; + + @Deprecated + @DeprecatedResponseField(message = "This field is deprecated and will be removed.", removalDate = "2025-06-01") private String developerName; + + @Deprecated + @DeprecatedResponseField(message = "This field is deprecated and will be removed.", removalDate = "2025-06-01") private String productName; + + @Deprecated + @DeprecatedResponseField(message = "This field is deprecated and will be removed.", removalDate = "2025-06-01") private String edition; + + @Deprecated + @DeprecatedResponseField(message = "This field is deprecated and will be removed.", removalDate = "2025-06-01") private Boolean curesUpdate; + + @Deprecated + @DeprecatedResponseField(message = "This field is deprecated and will be removed.", removalDate = "2025-06-01") private Long certificationDate; } diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ProductActivityMetadata.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ProductActivityMetadata.java index 44044743b6..2b2a0f8b1c 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ProductActivityMetadata.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/domain/activity/ProductActivityMetadata.java @@ -1,5 +1,6 @@ package gov.healthit.chpl.domain.activity; +import gov.healthit.chpl.api.deprecatedUsage.DeprecatedResponseField; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -10,7 +11,12 @@ public class ProductActivityMetadata extends ActivityMetadata { private static final long serialVersionUID = 9069117187924463180L; + @Deprecated + @DeprecatedResponseField(message = "This field is deprecated and will be removed.", removalDate = "2025-06-01") private String developerName; + + @Deprecated + @DeprecatedResponseField(message = "This field is deprecated and will be removed.", removalDate = "2025-06-01") private String productName; } diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/user/cognito/CognitoApiWrapper.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/user/cognito/CognitoApiWrapper.java index 38a22b27a5..fe5a7142d3 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/user/cognito/CognitoApiWrapper.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/user/cognito/CognitoApiWrapper.java @@ -248,7 +248,7 @@ public CognitoCredentials createUser(CreateUserRequest userRequest) throws UserC public AuthenticationResultType refreshToken(String refreshToken, UUID cognitoId) { Map authParams = new LinkedHashMap(); authParams.put("REFRESH_TOKEN", refreshToken); - authParams.put("SECRET_HASH", calculateSecretHash(cognitoId.toString())); + authParams.put("SECRET_HASH", calculateSecretHash(cognitoId.toString())); AdminInitiateAuthRequest authRequest = AdminInitiateAuthRequest.builder() .authFlow(AuthFlowType.REFRESH_TOKEN_AUTH) @@ -261,9 +261,7 @@ public AuthenticationResultType refreshToken(String refreshToken, UUID cognitoId AdminInitiateAuthResponse authResult = cognitoClient.adminInitiateAuth(authRequest); return authResult.authenticationResult(); } catch (Exception e) { - //This is cluttering the logs when the SSO flag is on, and the user logs in using CHPL creds - //We might want to uncomment it when we move to only using Cognito creds - //LOGGER.error("Error refreshing token", e); + LOGGER.error("Error refreshing token", e); return null; } } From 0515ee5e9a6f961180343395809eb580f8e63bed Mon Sep 17 00:00:00 2001 From: Katy Ekey Date: Thu, 2 Jan 2025 12:26:28 -0500 Subject: [PATCH 03/11] feat!: Give ONC user access to CRUD ops on functest, testtools, stds [#OCD-4768] --- .../domains/functionalitytested/CreateActionPermissions.java | 3 ++- .../domains/functionalitytested/DeleteActionPermissions.java | 3 ++- .../domains/functionalitytested/UpdateActionPermissions.java | 3 ++- .../permissions/domains/standard/CreateActionPermissions.java | 3 ++- .../permissions/domains/standard/DeleteActionPermissions.java | 3 ++- .../permissions/domains/standard/UpdateActionPermissions.java | 3 ++- .../permissions/domains/testtool/CreateActionPermissions.java | 3 ++- .../permissions/domains/testtool/DeleteActionPermissions.java | 3 ++- .../permissions/domains/testtool/UpdateActionPermissions.java | 3 ++- 9 files changed, 18 insertions(+), 9 deletions(-) diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/functionalitytested/CreateActionPermissions.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/functionalitytested/CreateActionPermissions.java index 00c9697099..e76e27d106 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/functionalitytested/CreateActionPermissions.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/functionalitytested/CreateActionPermissions.java @@ -8,7 +8,8 @@ public class CreateActionPermissions extends ActionPermissions { @Override public boolean hasAccess() { - return getResourcePermissions().isUserRoleAdmin(); + return getResourcePermissions().isUserRoleAdmin() + || getResourcePermissions().isUserRoleOnc(); } @Override diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/functionalitytested/DeleteActionPermissions.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/functionalitytested/DeleteActionPermissions.java index b67478e7fd..bcffcf6f13 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/functionalitytested/DeleteActionPermissions.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/functionalitytested/DeleteActionPermissions.java @@ -9,7 +9,8 @@ public class DeleteActionPermissions extends ActionPermissions { @Override public boolean hasAccess() { - return getResourcePermissions().isUserRoleAdmin(); + return getResourcePermissions().isUserRoleAdmin() + || getResourcePermissions().isUserRoleOnc(); } @Override diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/functionalitytested/UpdateActionPermissions.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/functionalitytested/UpdateActionPermissions.java index 75a39f679a..ecd2365cf1 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/functionalitytested/UpdateActionPermissions.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/functionalitytested/UpdateActionPermissions.java @@ -9,7 +9,8 @@ public class UpdateActionPermissions extends ActionPermissions { @Override public boolean hasAccess() { - return getResourcePermissions().isUserRoleAdmin(); + return getResourcePermissions().isUserRoleAdmin() + || getResourcePermissions().isUserRoleOnc(); } @Override diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/standard/CreateActionPermissions.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/standard/CreateActionPermissions.java index 105b406779..b89d96ceba 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/standard/CreateActionPermissions.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/standard/CreateActionPermissions.java @@ -8,7 +8,8 @@ public class CreateActionPermissions extends ActionPermissions { @Override public boolean hasAccess() { - return getResourcePermissions().isUserRoleAdmin(); + return getResourcePermissions().isUserRoleAdmin() + || getResourcePermissions().isUserRoleOnc(); } @Override diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/standard/DeleteActionPermissions.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/standard/DeleteActionPermissions.java index fc68749436..4d5959e1f2 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/standard/DeleteActionPermissions.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/standard/DeleteActionPermissions.java @@ -9,7 +9,8 @@ public class DeleteActionPermissions extends ActionPermissions { @Override public boolean hasAccess() { - return getResourcePermissions().isUserRoleAdmin(); + return getResourcePermissions().isUserRoleAdmin() + || getResourcePermissions().isUserRoleOnc(); } @Override diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/standard/UpdateActionPermissions.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/standard/UpdateActionPermissions.java index 35e460648c..3856f606bb 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/standard/UpdateActionPermissions.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/standard/UpdateActionPermissions.java @@ -9,7 +9,8 @@ public class UpdateActionPermissions extends ActionPermissions { @Override public boolean hasAccess() { - return getResourcePermissions().isUserRoleAdmin(); + return getResourcePermissions().isUserRoleAdmin() + || getResourcePermissions().isUserRoleOnc(); } @Override diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/testtool/CreateActionPermissions.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/testtool/CreateActionPermissions.java index 4154876b9b..dec5835d1a 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/testtool/CreateActionPermissions.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/testtool/CreateActionPermissions.java @@ -8,7 +8,8 @@ public class CreateActionPermissions extends ActionPermissions { @Override public boolean hasAccess() { - return getResourcePermissions().isUserRoleAdmin(); + return getResourcePermissions().isUserRoleAdmin() + || getResourcePermissions().isUserRoleOnc(); } @Override diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/testtool/DeleteActionPermissions.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/testtool/DeleteActionPermissions.java index bd9aa519f5..2c19bd0188 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/testtool/DeleteActionPermissions.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/testtool/DeleteActionPermissions.java @@ -9,7 +9,8 @@ public class DeleteActionPermissions extends ActionPermissions { @Override public boolean hasAccess() { - return getResourcePermissions().isUserRoleAdmin(); + return getResourcePermissions().isUserRoleAdmin() + || getResourcePermissions().isUserRoleOnc(); } @Override diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/testtool/UpdateActionPermissions.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/testtool/UpdateActionPermissions.java index 32aa85b7df..7b0ceb7957 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/testtool/UpdateActionPermissions.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/testtool/UpdateActionPermissions.java @@ -9,7 +9,8 @@ public class UpdateActionPermissions extends ActionPermissions { @Override public boolean hasAccess() { - return getResourcePermissions().isUserRoleAdmin(); + return getResourcePermissions().isUserRoleAdmin() + || getResourcePermissions().isUserRoleOnc(); } @Override From 135ac9d96662199043f7625818868bd77110758b Mon Sep 17 00:00:00 2001 From: Katy Ekey Date: Thu, 2 Jan 2025 15:11:29 -0500 Subject: [PATCH 04/11] feat!: Give ACB user permission to see activity for functest, stds, svap [#OCD-4768] --- .../activity/GetActivityDetailsActionPermissions.java | 2 +- .../GetFunctionalityTestedMetadataActionPermissions.java | 6 ++++-- .../activity/GetStandardMetadataActionPermissions.java | 6 ++++-- .../domains/activity/GetSvapMetadataActionPermissions.java | 6 ++++-- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/activity/GetActivityDetailsActionPermissions.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/activity/GetActivityDetailsActionPermissions.java index 7db210c0c1..b587b36e8b 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/activity/GetActivityDetailsActionPermissions.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/activity/GetActivityDetailsActionPermissions.java @@ -130,7 +130,7 @@ public boolean hasAccess(Object obj) { case FUNCTIONALITY_TESTED: case SVAP: case STANDARD: - return false; + return getResourcePermissions().isUserRoleAcbAdmin(); default: // all other types of activity // are accessible to any logged-in or anonymous user diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/activity/GetFunctionalityTestedMetadataActionPermissions.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/activity/GetFunctionalityTestedMetadataActionPermissions.java index 89b5b172ec..7aaaa3cd75 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/activity/GetFunctionalityTestedMetadataActionPermissions.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/activity/GetFunctionalityTestedMetadataActionPermissions.java @@ -8,11 +8,13 @@ public class GetFunctionalityTestedMetadataActionPermissions extends ActionPermissions { @Override public boolean hasAccess() { - return getResourcePermissions().isUserRoleAdmin() || getResourcePermissions().isUserRoleOnc(); + return getResourcePermissions().isUserRoleAdmin() || getResourcePermissions().isUserRoleOnc() + || getResourcePermissions().isUserRoleAcbAdmin(); } @Override public boolean hasAccess(Object obj) { - return getResourcePermissions().isUserRoleAdmin() || getResourcePermissions().isUserRoleOnc(); + return getResourcePermissions().isUserRoleAdmin() || getResourcePermissions().isUserRoleOnc() + || getResourcePermissions().isUserRoleAcbAdmin(); } } diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/activity/GetStandardMetadataActionPermissions.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/activity/GetStandardMetadataActionPermissions.java index 8feb5a384d..65a0409f73 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/activity/GetStandardMetadataActionPermissions.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/activity/GetStandardMetadataActionPermissions.java @@ -8,11 +8,13 @@ public class GetStandardMetadataActionPermissions extends ActionPermissions { @Override public boolean hasAccess() { - return getResourcePermissions().isUserRoleAdmin() || getResourcePermissions().isUserRoleOnc(); + return getResourcePermissions().isUserRoleAdmin() || getResourcePermissions().isUserRoleOnc() + || getResourcePermissions().isUserRoleAcbAdmin(); } @Override public boolean hasAccess(Object obj) { - return getResourcePermissions().isUserRoleAdmin() || getResourcePermissions().isUserRoleOnc(); + return getResourcePermissions().isUserRoleAdmin() || getResourcePermissions().isUserRoleOnc() + || getResourcePermissions().isUserRoleAcbAdmin(); } } diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/activity/GetSvapMetadataActionPermissions.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/activity/GetSvapMetadataActionPermissions.java index 336967b735..5ebf1b47bf 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/activity/GetSvapMetadataActionPermissions.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/permissions/domains/activity/GetSvapMetadataActionPermissions.java @@ -8,11 +8,13 @@ public class GetSvapMetadataActionPermissions extends ActionPermissions { @Override public boolean hasAccess() { - return getResourcePermissions().isUserRoleAdmin() || getResourcePermissions().isUserRoleOnc(); + return getResourcePermissions().isUserRoleAdmin() || getResourcePermissions().isUserRoleOnc() + || getResourcePermissions().isUserRoleAcbAdmin(); } @Override public boolean hasAccess(Object obj) { - return getResourcePermissions().isUserRoleAdmin() || getResourcePermissions().isUserRoleOnc(); + return getResourcePermissions().isUserRoleAdmin() || getResourcePermissions().isUserRoleOnc() + || getResourcePermissions().isUserRoleAcbAdmin(); } } From 153873fe81b399d99fb31d3b03b33cac95005ab0 Mon Sep 17 00:00:00 2001 From: Katy Ekey Date: Fri, 3 Jan 2025 09:38:09 -0500 Subject: [PATCH 05/11] test[aqa]: Update tests to expect 200 for ACB activity [#OCD-4768] --- ...ctivity-controller.postman_collection.json | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/chpl/chpl-api/e2e/collections/activity-controller.postman_collection.json b/chpl/chpl-api/e2e/collections/activity-controller.postman_collection.json index 74e852613c..ffa5098f09 100644 --- a/chpl/chpl-api/e2e/collections/activity-controller.postman_collection.json +++ b/chpl/chpl-api/e2e/collections/activity-controller.postman_collection.json @@ -233,15 +233,16 @@ "name": "ROLE_ACB", "item": [ { - "name": "GET /activity/metadata/svaps - ACB user gets 401 status and no response", + "name": "GET /activity/metadata/svaps - ACB user gets 200 status and valid response", "event": [ { "listen": "test", "script": { "exec": [ - "pm.test(\"ACB user gets 401 status and valid response from /activity/metadata/svaps end point\", function () {\r", + "pm.test(\"ACB user gets 200 status and valid response from /activity/metadata/svaps end point\", function () {\r", " var actualResponseBody = pm.response.json();\r", - " pm.response.to.have.status(401);\r", + " pm.response.to.have.status(200);\r", + " pm.expect(actualResponseBody).not.eql(null);\r", "});" ], "type": "text/javascript", @@ -285,20 +286,21 @@ "svaps" ] }, - "description": "GET /activity/metadata/svaps - ACB user gets 401 status and no response" + "description": "GET /activity/metadata/svaps - ACB user gets 200 status and valid response" }, "response": [] }, { - "name": "GET /activity/metadata/standards - ACB user gets 401 status and no response", + "name": "GET /activity/metadata/standards - ACB user gets 200 status and valid response", "event": [ { "listen": "test", "script": { "exec": [ - "pm.test(\"ACB user gets 401 status and valid response from /activity/metadata/standards end point\", function () {\r", + "pm.test(\"ACB user gets 200 status and valid response from /activity/metadata/standards end point\", function () {\r", " var actualResponseBody = pm.response.json();\r", - " pm.response.to.have.status(401);\r", + " pm.response.to.have.status(200);\r", + " pm.expect(actualResponseBody).not.eql(null);\r", "});" ], "type": "text/javascript", @@ -342,20 +344,21 @@ "standards" ] }, - "description": "GET /activity/metadata/standards - ACB user gets 401 status and no response" + "description": "GET /activity/metadata/standards - ACB user gets 200 status and valid response" }, "response": [] }, { - "name": "GET /activity/metadata/functionalities-tested - ACB user gets 401 status and no response", + "name": "GET /activity/metadata/functionalities-tested - ACB user gets 200 status and valid response", "event": [ { "listen": "test", "script": { "exec": [ - "pm.test(\"ACB user gets 401 status and valid response from /activity/metadata/functionalities-tested end point\", function () {\r", + "pm.test(\"ACB user gets 200 status and valid response from /activity/metadata/functionalities-tested end point\", function () {\r", " var actualResponseBody = pm.response.json();\r", - " pm.response.to.have.status(401);\r", + " pm.response.to.have.status(200);\r", + " pm.expect(actualResponseBody).not.eql(null);\r", "});" ], "type": "text/javascript", @@ -399,7 +402,7 @@ "functionalities-tested" ] }, - "description": "GET /activity/metadata/functionalities-tested - ACB user gets 401 status and no response" + "description": "GET /activity/metadata/functionalities-tested - ACB user gets 200 status and valid response" }, "response": [] } From d41be666455f9cc134b121525a9004e1ff5e8729 Mon Sep 17 00:00:00 2001 From: Katy Ekey Date: Fri, 3 Jan 2025 09:49:15 -0500 Subject: [PATCH 06/11] test[aqa]: ONC users are allowed to add and update test tools [#OCD-4768] --- ...test-tools-controller.postman_collection.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/chpl/chpl-api/e2e/collections/test-tools-controller.postman_collection.json b/chpl/chpl-api/e2e/collections/test-tools-controller.postman_collection.json index 22711c9341..944fae2145 100644 --- a/chpl/chpl-api/e2e/collections/test-tools-controller.postman_collection.json +++ b/chpl/chpl-api/e2e/collections/test-tools-controller.postman_collection.json @@ -225,7 +225,7 @@ "name": "ROLE_ONC", "item": [ { - "name": "POST /test-tools - ROLE_ONC user should get 401 status and access denied error", + "name": "POST /test-tools - ROLE_ONC user should get 200 status", "event": [ { "listen": "prerequest", @@ -240,8 +240,8 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"create test tools request by ROLE_ONC user should return Status code 401\", function () {\r", - " pm.response.to.have.status(401);\r", + "pm.test(\"create test tools request by ROLE_ONC user should return Status code 200\", function () {\r", + " pm.response.to.have.status(200);\r", "});\r" ], "type": "text/javascript" @@ -286,19 +286,19 @@ "test-tools" ] }, - "description": "ROLE_ONC user should get 401 status and access denied error to create Test Tools" + "description": "ROLE_ONC user should get 200 status and access denied error to create Test Tools" }, "response": [] }, { - "name": "PUT /test-tools - ROLE_ONC user should get 401 status and access denied error", + "name": "PUT /test-tools - ROLE_ONC user should get 200 status and access denied error", "event": [ { "listen": "test", "script": { "exec": [ - "pm.test(\"edit test tools request by ROLE_ONC user should return Status code 401\", function () {\r", - " pm.response.to.have.status(401);\r", + "pm.test(\"edit test tools request by ROLE_ONC user should return Status code 200\", function () {\r", + " pm.response.to.have.status(200);\r", "});\r" ], "type": "text/javascript" @@ -343,7 +343,7 @@ "test-tools" ] }, - "description": "PUT /test-tools - ROLE_ONC user should get 401 status and access denied error" + "description": "PUT /test-tools - ROLE_ONC user should get 200 status and access denied error" }, "response": [] }, From f751a5a2054a96f490e6ab19a2db4dc2f453a1b3 Mon Sep 17 00:00:00 2001 From: Katy Ekey Date: Fri, 3 Jan 2025 11:33:15 -0500 Subject: [PATCH 07/11] test[aqa]: Make valid requests to PUT/POST test tools as ONC user [#OCD-4768] --- .../collections/test-tools-controller.postman_collection.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chpl/chpl-api/e2e/collections/test-tools-controller.postman_collection.json b/chpl/chpl-api/e2e/collections/test-tools-controller.postman_collection.json index 944fae2145..573bb00e0a 100644 --- a/chpl/chpl-api/e2e/collections/test-tools-controller.postman_collection.json +++ b/chpl/chpl-api/e2e/collections/test-tools-controller.postman_collection.json @@ -274,7 +274,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"id\":1,\r\n \"value\": \"test\",\r\n \"regulatoryTextCitation\": \"test1\",\r\n \"startDay\": \"2023-07-12\",\r\n \"endDay\": \"2023-07-12\",\r\n \"requiredDay\": \"2023-07-12\",\r\n \"criteria\": [\r\n {\r\n \"id\": 0,\r\n \"number\": \"string\",\r\n \"title\": \"string\",\r\n \"certificationEditionId\": 0,\r\n \"certificationEdition\": \"string\",\r\n \"description\": \"string\",\r\n \"removed\": true\r\n }\r\n ],\r\n \"rule\": {\r\n \"id\": 0,\r\n \"name\": \"string\"\r\n }\r\n}" + "raw": "{\r\n \"value\": \"new tt\",\r\n \"regulatoryTextCitation\": \"newtt\",\r\n \"startDay\": \"2025-01-01\",\r\n \"endDay\": \"\",\r\n \"criteria\": [\r\n {\r\n \"id\": 165,\r\n \"number\": \"170.315 (b)(1)\",\r\n \"title\": \"Transitions of Care\",\r\n \"certificationEditionId\": 3,\r\n \"certificationEdition\": \"2015\",\r\n \"description\": null,\r\n \"removed\": false\r\n }\r\n ],\r\n \"rule\": {\r\n \"id\": 5,\r\n \"name\": \"HTI-1\"\r\n }\r\n}" }, "url": { "raw": "{{url}}/rest/test-tools", @@ -331,7 +331,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"id\": 1,\r\n \"value\": \"test2\",\r\n \"regulatoryTextCitation\": \"test3\",\r\n \"startDay\": \"2023-07-13\",\r\n \"endDay\": \"2023-07-13\",\r\n \"requiredDay\": \"2023-07-13\",\r\n \"criteria\": [\r\n {\r\n \"id\": 0,\r\n \"number\": \"string\",\r\n \"title\": \"string\",\r\n \"certificationEditionId\": 0,\r\n \"certificationEdition\": \"string\",\r\n \"description\": \"string\",\r\n \"removed\": true\r\n }\r\n ],\r\n \"rule\": {\r\n \"id\": 0,\r\n \"name\": \"string\"\r\n }\r\n}" + "raw": "{\r\n \"id\":33,\r\n \"value\": \"Drummond G10+ FHIR API powered by Touchstone\",\r\n \"regulatoryTextCitation\": \"Touchstone\",\r\n \"startDay\": \"\",\r\n \"endDay\": \"\",\r\n \"criteria\": [\r\n {\r\n \"id\": 182,\r\n \"number\": \"170.315 (g)(10)\",\r\n \"title\": \"Standardized API for Patient and Population Services\",\r\n \"certificationEditionId\": 3,\r\n \"certificationEdition\": \"2015\",\r\n \"description\": null,\r\n \"removed\": false\r\n }\r\n ],\r\n \"rule\": {\r\n \"id\": 5,\r\n \"name\": \"HTI-1\"\r\n }\r\n}" }, "url": { "raw": "{{url}}/rest/test-tools", From cfec13a2f2655f97fb08eb25120432ed17fd0305 Mon Sep 17 00:00:00 2001 From: Katy Ekey Date: Fri, 3 Jan 2025 12:40:45 -0500 Subject: [PATCH 08/11] [#OCD-4768] --- .../collections/test-tools-controller.postman_collection.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chpl/chpl-api/e2e/collections/test-tools-controller.postman_collection.json b/chpl/chpl-api/e2e/collections/test-tools-controller.postman_collection.json index 573bb00e0a..9e09560a22 100644 --- a/chpl/chpl-api/e2e/collections/test-tools-controller.postman_collection.json +++ b/chpl/chpl-api/e2e/collections/test-tools-controller.postman_collection.json @@ -331,7 +331,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"id\":33,\r\n \"value\": \"Drummond G10+ FHIR API powered by Touchstone\",\r\n \"regulatoryTextCitation\": \"Touchstone\",\r\n \"startDay\": \"\",\r\n \"endDay\": \"\",\r\n \"criteria\": [\r\n {\r\n \"id\": 182,\r\n \"number\": \"170.315 (g)(10)\",\r\n \"title\": \"Standardized API for Patient and Population Services\",\r\n \"certificationEditionId\": 3,\r\n \"certificationEdition\": \"2015\",\r\n \"description\": null,\r\n \"removed\": false\r\n }\r\n ],\r\n \"rule\": {\r\n \"id\": 5,\r\n \"name\": \"HTI-1\"\r\n }\r\n}" + "raw": "{\r\n \"id\":33,\r\n \"value\": \"Drummond G10+ FHIR API powered by Touchstone\",\r\n \"regulatoryTextCitation\": \"Touchstone\",\r\n \"startDay\": \"\",\r\n \"endDay\": \"\",\r\n \"criteria\": [\r\n {\r\n \"id\": 182,\r\n \"number\": \"170.315 (g)(10)\",\r\n \"title\": \"Standardized API for Patient and Population Services\",\r\n \"certificationEditionId\": 3,\r\n \"certificationEdition\": \"2015\",\r\n \"description\": null,\r\n \"removed\": false,\r\n \"rule\": {\r\n \"id\": 4,\r\n \"name\": \"Cures\"\r\n },\r\n \"startDay\": \"2020-06-30\",\r\n \"endDay\": null\r\n }\r\n ],\r\n \"rule\": {\r\n \"id\": 5,\r\n \"name\": \"HTI-1\"\r\n }\r\n}" }, "url": { "raw": "{{url}}/rest/test-tools", From 4273060fba31dd51036d2c0b301a12ac5c351939 Mon Sep 17 00:00:00 2001 From: Katy Ekey Date: Fri, 3 Jan 2025 13:13:06 -0500 Subject: [PATCH 09/11] fix: Allow for null job in AQA env [#OCD-4768] --- ...t-tools-controller.postman_collection.json | 6 ++--- .../GenerateListingDownloadFilesAspect.java | 25 +++++++++++-------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/chpl/chpl-api/e2e/collections/test-tools-controller.postman_collection.json b/chpl/chpl-api/e2e/collections/test-tools-controller.postman_collection.json index 9e09560a22..deecb35652 100644 --- a/chpl/chpl-api/e2e/collections/test-tools-controller.postman_collection.json +++ b/chpl/chpl-api/e2e/collections/test-tools-controller.postman_collection.json @@ -286,12 +286,12 @@ "test-tools" ] }, - "description": "ROLE_ONC user should get 200 status and access denied error to create Test Tools" + "description": "ROLE_ONC user should get 200 status to create Test Tools" }, "response": [] }, { - "name": "PUT /test-tools - ROLE_ONC user should get 200 status and access denied error", + "name": "PUT /test-tools - ROLE_ONC user should get 200 status", "event": [ { "listen": "test", @@ -343,7 +343,7 @@ "test-tools" ] }, - "description": "PUT /test-tools - ROLE_ONC user should get 200 status and access denied error" + "description": "PUT /test-tools - ROLE_ONC user should get 200 status" }, "response": [] }, diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/job/downloadfile/GenerateListingDownloadFilesAspect.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/job/downloadfile/GenerateListingDownloadFilesAspect.java index d6c366a0de..eaf285ad87 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/job/downloadfile/GenerateListingDownloadFilesAspect.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/job/downloadfile/GenerateListingDownloadFilesAspect.java @@ -64,15 +64,20 @@ private void scheduleDownloadFileJob(ListingSet listingSet) { if (!isJobAlreadyScheduled(info)) { ChplOneTimeTrigger downloadFileTrigger = new ChplOneTimeTrigger(); - ChplJob downloadFileJob = getDownloadFileJob(listingSetToJobNameMap.get(listingSet).jobName); - downloadFileTrigger.setJob(downloadFileJob); - downloadFileTrigger.setRunDateMillis(listingSetToJobNameMap.get(listingSet).getRunDateTime().toInstant().toEpochMilli()); - downloadFileTrigger = addTriggerToScheduler(downloadFileTrigger); - - LOGGER.info("System job {}/{} has been scheduled for {}", - downloadFileTrigger.getJob().getGroup(), - downloadFileTrigger.getJob().getName(), - DateUtil.toLocalDateTime(downloadFileTrigger.getRunDateMillis()).toString()); + String downloadFileJobName = listingSetToJobNameMap.get(listingSet).jobName; + ChplJob downloadFileJob = getDownloadFileJob(downloadFileJobName); + if (downloadFileJob != null) { + downloadFileTrigger.setJob(downloadFileJob); + downloadFileTrigger.setRunDateMillis(listingSetToJobNameMap.get(listingSet).getRunDateTime().toInstant().toEpochMilli()); + downloadFileTrigger = addTriggerToScheduler(downloadFileTrigger); + + LOGGER.info("System job {}/{} has been scheduled for {}", + downloadFileTrigger.getJob().getGroup(), + downloadFileTrigger.getJob().getName(), + DateUtil.toLocalDateTime(downloadFileTrigger.getRunDateMillis()).toString()); + } else { + LOGGER.error("No job found with name " + downloadFileJobName); + } } } @@ -89,7 +94,7 @@ private ChplJob getDownloadFileJob(String jobName) { return getAllJobs().stream() .filter(job -> job.getName().equals(jobName)) .findAny() - .get(); + .orElse(null); } private List getAllJobs() { From 15640fd7138123840b313fc7d21b4767d8c3d61f Mon Sep 17 00:00:00 2001 From: Todd Young Date: Mon, 6 Jan 2025 13:36:22 -0500 Subject: [PATCH 10/11] release: deploy version 47.7.0 on 06 January 2025 --- RELEASE_NOTES.md | 14 ++++++++++++++ .../src/main/resources/environment.properties | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e65c30f59b..5f7cd1b567 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,19 @@ # Release Notes +## Version 47.7.0 +_6 January 2025_ + +### Features +* Add endpoints to retrieve data for the Developer Attestation PowerBI report +* Allow changing of ATL using upload-to-update +* Save activity for created and deleted invitations +* Add job to update Service Base URL report items + +### Bug Fixes +* Give ONC users ability to edit listing via upload + +--- + ## Version 47.6.0 _18 December 2024_ diff --git a/chpl/chpl-resources/src/main/resources/environment.properties b/chpl/chpl-resources/src/main/resources/environment.properties index 813443e0e7..0f1a00c34c 100644 --- a/chpl/chpl-resources/src/main/resources/environment.properties +++ b/chpl/chpl-resources/src/main/resources/environment.properties @@ -17,7 +17,7 @@ chplUrlBegin=https://chpl.healthit.gov developerUrlPart=/#/organizations/developers/%s jndiName=java:comp/env/jdbc/openchpl persistenceUnitName=openchpl -api.version=47.6.0 +api.version=47.7.0 api.description=Created by CHPL Development Team. Please submit any questions using the Health IT \ Feedback Form and select the "Certified Health IT Products List (CHPL)" category.
\ See more at %s From 7ecefb0d45c3f35898d56f9105aa51c54dcdfd9d Mon Sep 17 00:00:00 2001 From: Todd Young Date: Tue, 7 Jan 2025 09:19:57 -0500 Subject: [PATCH 11/11] feat!: Clear out security context when each Quartz job finishes OCD-4777 --- chpl/chpl-api/src/main/webapp/WEB-INF/web.xml | 22 --------------- .../gov/healthit/chpl/CHPLServiceConfig.java | 24 ++++++++++++++++ .../gov/healthit/chpl/QuartzJobFactory.java | 28 +++++++++++++++++++ .../java/gov/healthit/chpl/SpringContext.java | 3 +- .../scheduler/ChplSchedulerReference.java | 18 ++++-------- .../healthit/chpl/scheduler/JobAspect.java | 16 +++++++++++ .../chpl/scheduler/job/QuartzJob.java | 2 ++ .../chpl/user/cognito/CognitoApiWrapper.java | 14 ++++++---- 8 files changed, 86 insertions(+), 41 deletions(-) create mode 100644 chpl/chpl-service/src/main/java/gov/healthit/chpl/QuartzJobFactory.java create mode 100644 chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/JobAspect.java diff --git a/chpl/chpl-api/src/main/webapp/WEB-INF/web.xml b/chpl/chpl-api/src/main/webapp/WEB-INF/web.xml index fc9c765848..4cde1a8dd4 100644 --- a/chpl/chpl-api/src/main/webapp/WEB-INF/web.xml +++ b/chpl/chpl-api/src/main/webapp/WEB-INF/web.xml @@ -39,26 +39,4 @@ /ff4j-console/* /static/* - - - - quartz:config-file - quartz.properties - - - quartz:shutdown-on-unload - true - - - quartz:wait-on-shutdown - true - - - quartz:start-on-load - false - - - org.quartz.ee.servlet.QuartzInitializerListener - - diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/CHPLServiceConfig.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/CHPLServiceConfig.java index bbaf7aa89b..0e267f697c 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/CHPLServiceConfig.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/CHPLServiceConfig.java @@ -27,8 +27,10 @@ import org.apache.http.conn.ssl.TrustStrategy; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.quartz.spi.JobFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.MethodInvokingFactoryBean; +import org.springframework.context.ApplicationContext; import org.springframework.context.EnvironmentAware; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; @@ -39,6 +41,7 @@ import org.springframework.context.annotation.PropertySources; import org.springframework.context.support.ResourceBundleMessageSource; import org.springframework.core.env.Environment; +import org.springframework.core.io.ClassPathResource; import org.springframework.core.task.TaskExecutor; import org.springframework.http.HttpRequest; import org.springframework.http.MediaType; @@ -52,6 +55,7 @@ import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.web.client.RestTemplate; @@ -96,6 +100,9 @@ public class CHPLServiceConfig implements WebMvcConfigurer, EnvironmentAware { @Autowired private Environment env; + @Autowired + private ApplicationContext applicationContext; + @Override public void setEnvironment(final Environment environment) { this.env = environment; @@ -279,4 +286,21 @@ private int getRequestTimeout() { } return requestTimeout; } + + @Bean + public JobFactory jobFactory() { + QuartzJobFactory jobFactory = new QuartzJobFactory(applicationContext); + return jobFactory; + } + + @Bean + public SchedulerFactoryBean schedulerFactory() { + SchedulerFactoryBean factory = new SchedulerFactoryBean(); + factory.setAutoStartup(true); + factory.setConfigLocation(new ClassPathResource("quartz.properties")); + factory.setJobFactory(jobFactory()); + + return factory; + } + } diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/QuartzJobFactory.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/QuartzJobFactory.java new file mode 100644 index 0000000000..5991dfdb44 --- /dev/null +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/QuartzJobFactory.java @@ -0,0 +1,28 @@ +package gov.healthit.chpl; + +import org.quartz.spi.TriggerFiredBundle; +import org.springframework.aop.aspectj.annotation.AspectJProxyFactory; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.scheduling.quartz.SpringBeanJobFactory; + +import gov.healthit.chpl.scheduler.JobAspect; + +public class QuartzJobFactory extends SpringBeanJobFactory { + private AutowireCapableBeanFactory beanFactory; + + public QuartzJobFactory(ApplicationContext applicationContext) { + beanFactory = applicationContext.getAutowireCapableBeanFactory(); + } + + @Override + protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception { + final Object job = super.createJobInstance(bundle); + beanFactory.autowireBean(job); + + AspectJProxyFactory pFactory = new AspectJProxyFactory(job); + pFactory.addAspect(new JobAspect()); + + return pFactory.getProxy(); + } +} diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/SpringContext.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/SpringContext.java index 032b97bb2e..05294f4f84 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/SpringContext.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/SpringContext.java @@ -3,10 +3,11 @@ import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.scheduling.quartz.SpringBeanJobFactory; import org.springframework.stereotype.Component; @Component -public class SpringContext implements ApplicationContextAware { +public class SpringContext extends SpringBeanJobFactory implements ApplicationContextAware { private static ApplicationContext context; diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/ChplSchedulerReference.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/ChplSchedulerReference.java index 59b1880c51..9de02c5e93 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/ChplSchedulerReference.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/ChplSchedulerReference.java @@ -1,11 +1,9 @@ package gov.healthit.chpl.scheduler; -import jakarta.annotation.PostConstruct; - import org.quartz.Scheduler; -import org.quartz.SchedulerException; -import org.quartz.impl.StdSchedulerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; import org.springframework.stereotype.Component; /** @@ -18,15 +16,9 @@ public class ChplSchedulerReference { private Scheduler scheduler; - /** - * Initializes the Quartz Scheduler when this object is created. - * @throws SchedulerException if thrown - */ - @PostConstruct - public void init() throws SchedulerException { - StdSchedulerFactory sf = new StdSchedulerFactory(); - sf.initialize(); - this.scheduler = sf.getScheduler(); + @Autowired + public ChplSchedulerReference(SchedulerFactoryBean schedulerFactory) { + this.scheduler = schedulerFactory.getScheduler(); } public Scheduler getScheduler() { diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/JobAspect.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/JobAspect.java new file mode 100644 index 0000000000..d09bfda06d --- /dev/null +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/JobAspect.java @@ -0,0 +1,16 @@ +package gov.healthit.chpl.scheduler; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.After; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.security.core.context.SecurityContextHolder; + +@Aspect +public class JobAspect { + + @After("execution(public void org.quartz.Job.execute(..))") + public void afterExecute(JoinPoint joinPoint) { + SecurityContextHolder.clearContext(); + } + +} diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/job/QuartzJob.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/job/QuartzJob.java index 3677a0347e..e7838e1137 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/job/QuartzJob.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/scheduler/job/QuartzJob.java @@ -8,7 +8,9 @@ import org.springframework.core.env.Environment; import gov.healthit.chpl.scheduler.AuthenticatedUserAwareJob; +import lombok.extern.log4j.Log4j2; +@Log4j2 public abstract class QuartzJob extends AuthenticatedUserAwareJob implements Job { public static final String JOB_DATA_KEY_EMAIL = "email"; public static final String JOB_DATA_KEY_ACB = "acb"; diff --git a/chpl/chpl-service/src/main/java/gov/healthit/chpl/user/cognito/CognitoApiWrapper.java b/chpl/chpl-service/src/main/java/gov/healthit/chpl/user/cognito/CognitoApiWrapper.java index 39ab515f0e..4c4f5c8efd 100644 --- a/chpl/chpl-service/src/main/java/gov/healthit/chpl/user/cognito/CognitoApiWrapper.java +++ b/chpl/chpl-service/src/main/java/gov/healthit/chpl/user/cognito/CognitoApiWrapper.java @@ -168,18 +168,22 @@ public AuthenticationResultType respondToNewPasswordRequiredChallenge(CognitoNew } } - @Cacheable(CacheNames.COGNITO_USERS_BY_UUID) + @Cacheable(value = CacheNames.COGNITO_USERS_BY_UUID, unless = "#result == null") public User getUserInfo(UUID cognitoId) throws UserRetrievalException { AdminGetUserRequest request = AdminGetUserRequest.builder() .userPoolId(userPoolId) .username(cognitoId.toString()) .build(); - AdminGetUserResponse response = cognitoClient.adminGetUser(request); - if (response == null || response.sdkHttpResponse() == null || !response.sdkHttpResponse().isSuccessful()) { + try { + AdminGetUserResponse response = cognitoClient.adminGetUser(request); + if (response == null || response.sdkHttpResponse() == null || !response.sdkHttpResponse().isSuccessful()) { + return null; + } + return createUserFromGetUserResponse(response); + } catch (Exception e) { return null; } - return createUserFromGetUserResponse(response); } @Cacheable(value = CacheNames.COGNITO_USERS_BY_EMAIL, unless = "#result == null") @@ -244,7 +248,7 @@ public CognitoCredentials createUser(CreateUserRequest userRequest) throws UserC public AuthenticationResultType refreshToken(String refreshToken, UUID cognitoId) { Map authParams = new LinkedHashMap(); authParams.put("REFRESH_TOKEN", refreshToken); - authParams.put("SECRET_HASH", calculateSecretHash(cognitoId.toString())); + authParams.put("SECRET_HASH", calculateSecretHash(cognitoId.toString())); AdminInitiateAuthRequest authRequest = AdminInitiateAuthRequest.builder() .authFlow(AuthFlowType.REFRESH_TOKEN_AUTH)