Skip to content

Commit

Permalink
Merge branch 'ver-47.7.0' into production
Browse files Browse the repository at this point in the history
  • Loading branch information
tmy1313 committed Jan 6, 2025
2 parents 12f22e5 + 15640fd commit 88038a5
Show file tree
Hide file tree
Showing 31 changed files with 807 additions and 13 deletions.
14 changes: 14 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -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_

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import gov.healthit.chpl.report.surveillance.CapCounts;
import gov.healthit.chpl.report.surveillance.NonconformityCounts;
import gov.healthit.chpl.report.surveillance.SurveillanceActivityCounts;
import gov.healthit.chpl.scheduler.job.report.attestation.AttestationReport;
import gov.healthit.chpl.scheduler.job.summarystatistics.data.CertificationBodyStatistic;
import gov.healthit.chpl.search.domain.ListingSearchResult;
import gov.healthit.chpl.util.SwaggerSecurityRequirement;
Expand Down Expand Up @@ -462,4 +463,14 @@ public ReportDataController(ReportDataManager reportDataManager, DeveloperSearch
return reportDataManager.getDirectReviewCounts();
}

@Operation(summary = "Retrieves the data used to generate the Attestations report.",
description = "Retrieves the data used to generate the Attestations report.",
security = {
@SecurityRequirement(name = SwaggerSecurityRequirement.API_KEY)
})
@RequestMapping(value = "/attestations", method = RequestMethod.GET, produces = "application/json; charset=utf-8")
public @ResponseBody List<AttestationReport> getAttestationReports() {
return reportDataManager.getAttestationReports();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ public CognitoUserInvitation inviteUser(@RequestBody CognitoUserInvitation invit
case CognitoGroups.CHPL_CMS_STAFF:
createdInvitiation = cognitoInvitationManager.inviteCmsUser(invitation);
break;
default:
LOGGER.error("Invitation group name not handled: " + invitation.getGroupName());
}
return createdInvitiation;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -475,4 +475,18 @@
<KeyValuePair key="dd.span_id" value="%X{dd.span_id}" />
</JsonLayout>
</Console>
<Console name="attestationReportCreatorJobJson" target="SYSTEM_OUT">
<JsonLayout compact="true" eventEol="true" properties="true" stacktraceAsString="true">
<KeyValuePair key="service" value="attestationReportCreatorJob" />
<KeyValuePair key="dd.trace_id" value="%X{dd.trace_id}" />
<KeyValuePair key="dd.span_id" value="%X{dd.span_id}" />
</JsonLayout>
</Console>
<Console name="fixDatadogUrlUptimeAssertionsJobJson" target="SYSTEM_OUT">
<JsonLayout compact="true" eventEol="true" properties="true" stacktraceAsString="true">
<KeyValuePair key="service" value="fixDatadogUrlUptimeAssertionsJob" />
<KeyValuePair key="dd.trace_id" value="%X{dd.trace_id}" />
<KeyValuePair key="dd.span_id" value="%X{dd.span_id}" />
</JsonLayout>
</Console>
</Appenders>
Original file line number Diff line number Diff line change
Expand Up @@ -808,4 +808,28 @@
interval="1" modulate="true" />
</Policies>
</RollingFile>
<RollingFile name="attestationReportCreatorJob"
fileName="${logDir}/scheduler/attestationReportCreatorJob.log"
filePattern="${logDir}/scheduler/history/attestationReportCreatorJob-%d{yyyy-MM-dd}.log"
filePermissions="rw-rw-r--">
<PatternLayout>
<Pattern>%d{ISO8601} %-5p (%t) [%C{1}(%M:%L)] %m%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy
interval="1" modulate="true" />
</Policies>
</RollingFile>
<RollingFile name="fixDatadogUrlUptimeAssertionsJob"
fileName="${logDir}/scheduler/fixDatadogUrlUptimeAssertionsJob.log"
filePattern="${logDir}/scheduler/history/fixDatadogUrlUptimeAssertionsJob-%d{yyyy-MM-dd}.log"
filePermissions="rw-rw-r--">
<PatternLayout>
<Pattern>%d{ISO8601} %-5p (%t) [%C{1}(%M:%L)] %m%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy
interval="1" modulate="true" />
</Policies>
</RollingFile>
</Appenders>
Original file line number Diff line number Diff line change
Expand Up @@ -205,4 +205,10 @@
<Logger name="updatedSedFriendlyIdsJobLogger" level="INFO" additivity="false">
<AppenderRef ref="updatedSedFriendlyIdsJob" />
</Logger>
<Logger name="attestationReportCreatorJobLogger" level="INFO" additivity="false">
<AppenderRef ref="attestationReportCreatorJob" />
</Logger>
<Logger name="fixDatadogUrlUptimeAssertionsJobLogger" level="INFO" additivity="false">
<AppenderRef ref="fixDatadogUrlUptimeAssertionsJob" />
</Logger>
</Loggers>
8 changes: 8 additions & 0 deletions chpl/chpl-api/src/main/resources/log4j2-xinclude-loggers.xml
Original file line number Diff line number Diff line change
Expand Up @@ -262,4 +262,12 @@
<AppenderRef ref="updatedSedFriendlyIdsJob" />
<AppenderRef ref="updatedSedFriendlyIdsJobJson" />
</Logger>
<Logger name="attestationReportCreatorJobLogger" level="INFO" additivity="false">
<AppenderRef ref="attestationReportCreatorJob" />
<AppenderRef ref="attestationReportCreatorJobJson" />
</Logger>
<Logger name="fixDatadogUrlUptimeAssertionsJobLogger" level="INFO" additivity="false">
<AppenderRef ref="fixDatadogUrlUptimeAssertionsJob" />
<AppenderRef ref="FixDatadogUrlUptimeAssertionsJobJson" />
</Logger>
</Loggers>
Original file line number Diff line number Diff line change
Expand Up @@ -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. <br/>\
See more at <a href="%s" target="_blank">%s</a>
Expand Down Expand Up @@ -360,6 +360,7 @@ apiCriteriaKeys=criterion.170_315_g_7,\

###### Attestations ######
attestationExceptionWindowInDays=5
attestationApprovalWindowInDays=30
####################################

###### Redis Connection Properties ######
Expand Down
2 changes: 1 addition & 1 deletion chpl/chpl-resources/src/main/resources/errors.properties
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ pendingListing.alreadyProcessing=The listing is already being processed.
upload.emptyFile=You cannot upload an empty file!
upload.notCSV=File must be a CSV document.
listing.upload.badMerge=The CHPL Product Number in the uploaded file, '%s', does not match the expected CHPL Product Number of \
the listing after updates are applied: '%s'. ACB code, ATL code, Product code, Version code, and Certified Date code \
the listing after updates are applied: '%s'. ACB code, Product code, Version code, and Certified Date code \
may not be changed via upload file.
listing.upload.noHeadingFound=No records with allowed heading values were found in the file.
listing.upload.unrecognizedHeading=The heading '%s' was found in the upload file but is not recognized.
Expand Down
18 changes: 18 additions & 0 deletions chpl/chpl-resources/src/main/resources/jobs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -921,5 +921,23 @@
<durability>true</durability>
<recover>false</recover>
</job>

<job>
<name>attestationReportCreatorJob</name>
<group>systemJobs</group>
<description>Generate data for the Attestation Report (Power BI)</description>
<job-class>gov.healthit.chpl.scheduler.job.report.attestation.AttestationReportCreatorJob</job-class>
<durability>true</durability>
<recover>false</recover>
</job>

<job>
<name>fixDatadogUrlUptimeAssertionsJob</name>
<group>systemJobs</group>
<description>Fix Datadog Url Uptime Assertions Job (BEWARE - THIS JOB CAN ONLY BE RUN ONCE IN EACH DATADOG ENVITRONMENT!)</description>
<job-class>gov.healthit.chpl.scheduler.job.urluptime.FixDatadogUrlUptimeAssertionsJob</job-class>
<durability>true</durability>
<recover>false</recover>
</job>
</schedule>
</job-scheduling-data>
10 changes: 10 additions & 0 deletions chpl/chpl-resources/src/main/resources/system-triggers.xml
Original file line number Diff line number Diff line change
Expand Up @@ -245,5 +245,15 @@
<cron-expression>0 30 6 * * ?</cron-expression> <!-- At 0630 UTC every day -->
</cron>
</trigger>
<trigger>
<cron>
<name>attestationReportCreator</name>
<group>AttestationReportCreatorJobTrigger</group>
<job-name>attestationReportCreatorJob</job-name>
<job-group>systemJobs</job-group>
<misfire-instruction>MISFIRE_INSTRUCTION_DO_NOTHING</misfire-instruction>
<cron-expression>0 30 10 * * ?</cron-expression> <!-- At 1030 UTC every day -->
</cron>
</trigger>
</schedule>
</job-scheduling-data>
2 changes: 1 addition & 1 deletion chpl/chpl-service/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>10.1.30</version>
<version>10.1.34</version>
<scope>provided</scope>
</dependency>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ private DecodedJWT decodeJwt(String jwt) {
RSAKeyProvider keyProvider = new CognitoRsaKeyProvider(region, userPoolId, tokenizeRsaKeyUrl);
Algorithm algorithm = Algorithm.RSA256(keyProvider);
JWTVerifier jwtVerifier = JWT.require(algorithm)
//.withAudience(clientId)
.acceptLeeway(30000) //allows for the CHPL server clock and AWS server clock to be off by 30 seconds
.build();

DecodedJWT decodedJwt = jwtVerifier.verify(jwt);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ public void mergeWithListingFromChpl(CertifiedProductSearchDetails updatedListin
updatedListing.setEdition(currentListing.getEdition());
updatedListing.setCuresUpdate(currentListing.getCuresUpdate());
updatedListing.setSurveillance(currentListing.getSurveillance());
updatedListing.setTestingLabs(currentListing.getTestingLabs());
updatedListing.setCountClosedNonconformities(currentListing.getCountClosedNonconformities());
updatedListing.setCountClosedSurveillance(currentListing.getCountClosedSurveillance());
updatedListing.setCountOpenNonconformities(currentListing.getCountOpenNonconformities());
Expand Down Expand Up @@ -123,6 +122,7 @@ public void mergeWithListingFromChpl(CertifiedProductSearchDetails updatedListin
private String applyUpdatesToChplProductNumber(CertifiedProductSearchDetails updatedListing, CertifiedProductSearchDetails currentListing) {
ChplProductNumberParts currChplProductNumberParts
= chplProductNumberUtil.parseChplProductNumber(currentListing.getChplProductNumber());
currChplProductNumberParts.setAtlCode(chplProductNumberUtil.deriveTestingLabCodeFromListing(updatedListing));
currChplProductNumberParts.setIcsCode(chplProductNumberUtil.deriveIcsCodeFromListing(updatedListing));
currChplProductNumberParts.setAdditionalSoftwareCode(chplProductNumberUtil.deriveAdditionalSoftwareCodeFromListing(updatedListing));
return chplProductNumberUtil.getChplProductNumber(currChplProductNumberParts);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package gov.healthit.chpl.changerequest.dao;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.stereotype.Repository;
Expand Down Expand Up @@ -47,4 +50,26 @@ public List<Developer> getDevelopersForCertificationBody(Long certificationBodyI
.map(item -> item.getDeveloper().toDomain())
.collect(Collectors.<Developer>toList());
}

public Map<Long, List<CertificationBody>> getCertificationBodiesForAllDeveloper() {
String hql = "SELECT main "
+ "FROM DeveloperCertificationBodyMapEntity main "
+ "JOIN FETCH main.developer dev "
+ "JOIN FETCH main.certificationBody cb "
+ "LEFT JOIN FETCH cb.address ";

List<DeveloperCertificationBodyMapEntity> entities = entityManager
.createQuery(hql, DeveloperCertificationBodyMapEntity.class)
.getResultList();

Map<Long, List<CertificationBody>> map = new HashMap<Long, List<CertificationBody>>();

entities.forEach(e -> {
if (!map.containsKey(e.getDeveloperId())) {
map.put(e.getDeveloperId(), new ArrayList<CertificationBody>());
}
map.get(e.getDeveloperId()).add(e.getCertificationBody().toDomain());
});
return map;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public enum ActivityConcept implements Serializable {
CORRECTIVE_ACTION_PLAN,
DEVELOPER,
FUNCTIONALITY_TESTED,
INVITATION,
LISTING_UPLOAD,
PENDING_CERTIFIED_PRODUCT,
PENDING_SURVEILLANCE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
import gov.healthit.chpl.permissions.domains.listingUpload.GetByIdActionPermissions;
import gov.healthit.chpl.permissions.domains.listingUpload.GetListingUploadAsListingPermissions;
import gov.healthit.chpl.permissions.domains.listingUpload.GetUploadedCsvActionPermissions;
import gov.healthit.chpl.permissions.domains.listingUpload.ParseActionPermissions;
import gov.healthit.chpl.permissions.domains.listingUpload.ValidateByIdsActionPermissions;

@Component
public class ListingUploadDomainPerissions extends DomainPermissions {
public static final String CREATE = "CREATE";
public static final String PARSE = "PARSE";
public static final String GET_ALL = "GET_ALL";
public static final String GET_BY_ID = "GET_BY_ID";
public static final String GET_UPLOAD_AS_LISTING = "GET_UPLOAD_AS_LISTING";
Expand All @@ -27,6 +29,7 @@ public class ListingUploadDomainPerissions extends DomainPermissions {
@Autowired
public ListingUploadDomainPerissions(
@Qualifier("createListingUploadActionPermissions") CreateActionPermissions createActionPermissions,
@Qualifier("parseListingUploadActionPermissions") ParseActionPermissions parseActionPermissions,
@Qualifier("getAllListingUploadsActionPermissions") GetAllActionPermissions getAllActionPermissions,
@Qualifier("getListingUploadByIdActionPermissions") GetByIdActionPermissions getByIdActionPermissions,
@Qualifier("getListingUploadAsListingActionPermissions") GetListingUploadAsListingPermissions getListingUploadAsListingPermissions,
Expand All @@ -35,6 +38,7 @@ public ListingUploadDomainPerissions(
@Qualifier("deleteListingUploadActionPermissions") DeleteActionPermissions deleteActionPermissions,
@Qualifier("getUploadedCsvActionPermissions") GetUploadedCsvActionPermissions getUploadedCsvActionPermissions) {
getActionPermissions().put(CREATE, createActionPermissions);
getActionPermissions().put(PARSE, parseActionPermissions);
getActionPermissions().put(GET_ALL, getAllActionPermissions);
getActionPermissions().put(GET_BY_ID, getByIdActionPermissions);
getActionPermissions().put(GET_UPLOAD_AS_LISTING, getListingUploadAsListingPermissions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public boolean hasAccess(Object obj) {
}

private boolean hasAccess(ListingUpload uploadedMetadata) {
if (getResourcePermissions().isUserRoleAdmin()) {
if (getResourcePermissions().isUserRoleAdmin()
|| getResourcePermissions().isUserRoleOnc()) {
return true;
} else if (getResourcePermissions().isUserRoleAcbAdmin()) {
return isAcbValidForCurrentUser(uploadedMetadata.getAcb().getId());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package gov.healthit.chpl.permissions.domains.listingUpload;

import java.util.List;

import org.springframework.stereotype.Component;

import gov.healthit.chpl.domain.ListingUpload;
import gov.healthit.chpl.permissions.domains.ActionPermissions;

@Component("parseListingUploadActionPermissions")
public class ParseActionPermissions extends ActionPermissions {

@Override
public boolean hasAccess() {
return getResourcePermissions().isUserRoleAdmin()
|| getResourcePermissions().isUserRoleOnc()
|| getResourcePermissions().isUserRoleAcbAdmin();
}

@Override
public boolean hasAccess(Object obj) {
if (obj instanceof ListingUpload) {
return hasAccess((ListingUpload) obj);
} else if (obj instanceof List<?>) {
boolean hasAccessToAll = true;
for (Object listItem : (List<?>) obj) {
if (listItem instanceof ListingUpload) {
hasAccessToAll = hasAccessToAll && hasAccess((ListingUpload) listItem);
} else {
hasAccessToAll = false;
}
}
return hasAccessToAll;
}
return false;
}

private boolean hasAccess(ListingUpload uploadedMetadata) {
if (getResourcePermissions().isUserRoleAdmin()) {
return true;
} else if (getResourcePermissions().isUserRoleAcbAdmin() && uploadedMetadata.getAcb() != null) {
return isAcbValidForCurrentUser(uploadedMetadata.getAcb().getId());
}
return false;
}
}
Loading

0 comments on commit 88038a5

Please sign in to comment.