Skip to content

Commit

Permalink
Merge branch 'staging' into OCD-4734
Browse files Browse the repository at this point in the history
  • Loading branch information
kekey1 committed Dec 19, 2024
2 parents 7620526 + aef1fe8 commit 2b519c7
Show file tree
Hide file tree
Showing 14 changed files with 234 additions and 8 deletions.
17 changes: 17 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Release Notes

## Version 47.6.0
_18 December 2024_

### Features
* Add endpoint to return data for Service Base URL List report
* Allow existing user to be granted access to additional Organizations
* Return generic msg if invitation token is bad
* Create /developers/<id>/insights endpoint to fetch insights data

### Bug Fixes
* Add all required standards to listing as of cert day + current day
* Give appropriate error if addt'l software group name too long
* Give pending change request report user correct cognito group
* Send API Key deletion warning if key was created and never used

---

## Version 47.5.0
_9 December 2024_

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,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,11 @@
<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,16 @@
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,7 @@
<Logger name="updatedSedFriendlyIdsJobLogger" level="INFO" additivity="false">
<AppenderRef ref="updatedSedFriendlyIdsJob" />
</Logger>
<Logger name="fixDatadogUrlUptimeAssertionsJobLogger" level="INFO" additivity="false">
<AppenderRef ref="fixDatadogUrlUptimeAssertionsJob" />
</Logger>
</Loggers>
4 changes: 4 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,8 @@
<AppenderRef ref="updatedSedFriendlyIdsJob" />
<AppenderRef ref="updatedSedFriendlyIdsJobJson" />
</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.5.0
api.version=47.6.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
9 changes: 9 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,14 @@
<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>
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 @@ -60,6 +60,10 @@ public DatadogSyntheticsTestService(DatadogSyntheticsTestApiProvider apiProvider
this.datadogTestLocation = datadogTestLocation;
}

protected DatadogSyntheticsTestApiProvider getApiProvider() {
return apiProvider;
}

public List<SyntheticsTestDetails> getAllSyntheticsTests() {
try {
return apiProvider.getApiInstance().listTests().getTests();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package gov.healthit.chpl.scheduler.job.urluptime;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

import com.datadog.api.client.ApiException;
import com.datadog.api.client.v1.model.SyntheticsAPITest;
import com.datadog.api.client.v1.model.SyntheticsAPITestConfig;
import com.datadog.api.client.v1.model.SyntheticsAPITestType;
import com.datadog.api.client.v1.model.SyntheticsAssertion;
import com.datadog.api.client.v1.model.SyntheticsAssertionOperator;
import com.datadog.api.client.v1.model.SyntheticsAssertionTarget;
import com.datadog.api.client.v1.model.SyntheticsAssertionType;
import com.datadog.api.client.v1.model.SyntheticsTestOptions;
import com.datadog.api.client.v1.model.SyntheticsTestOptionsHTTPVersion;
import com.datadog.api.client.v1.model.SyntheticsTestOptionsScheduling;
import com.datadog.api.client.v1.model.SyntheticsTestOptionsSchedulingTimeframe;
import com.datadog.api.client.v1.model.SyntheticsTestRequest;

import gov.healthit.chpl.scheduler.job.QuartzJob;
import gov.healthit.chpl.util.DateUtil;
import lombok.extern.log4j.Log4j2;

@Log4j2(topic = "fixDatadogUrlUptimeAssertionsJobLogger")
public class FixDatadogUrlUptimeAssertionsJob extends QuartzJob {

private static final Integer HTTP_STATUS_OK = 200;
private static final String HTTP_METHOD_GET = "GET";
private static final Long SECONDS_IN_A_MINUTE = 60L;


@Autowired
private DatadogSyntheticsTestService datadogSyntheticsTestService;

@Value("${datadog.syntheticsTest.startTime}")
private String datadogTestStartTime;

@Value("${datadog.syntheticsTest.endTime}")
private String datadogTestEndTime;

@Value("${datadog.syntheticsTest.checkEveryMinutes}")
private Long datadogCheckEveryMinutes;

@Value("${datadog.syntheticsTest.timeout}")
private Integer datadogTestTimeout;

@Value("${datadog.syntheticsTest.location}")
private String datadogTestLocation;

@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
LOGGER.info("********* Starting the Fix Datadog Url Uptime Assertions Job *********");

datadogSyntheticsTestService.getAllSyntheticsTests().forEach(test -> {
Optional<SyntheticsAssertion> contentHeaderAssertion = getContentHeaderAssertionExist(test.getConfig().getAssertions());
String url = test.getConfig().getRequest().getUrl();

if (contentHeaderAssertion.isPresent()) {

SyntheticsAPITest body = new SyntheticsAPITest()
.config(new SyntheticsAPITestConfig()
.assertions(Arrays.asList(
new SyntheticsAssertion(new SyntheticsAssertionTarget()
.operator(SyntheticsAssertionOperator.LESS_THAN)
.target(datadogTestTimeout)
.type(SyntheticsAssertionType.RESPONSE_TIME)),
new SyntheticsAssertion(new SyntheticsAssertionTarget()
.operator(SyntheticsAssertionOperator.IS)
.target(HTTP_STATUS_OK)
.type(SyntheticsAssertionType.STATUS_CODE)),
new SyntheticsAssertion(new SyntheticsAssertionTarget()
.operator(SyntheticsAssertionOperator.MATCHES)
.target("/[\\S\s]+[\\S]+/")
.type(SyntheticsAssertionType.BODY))))
.request(new SyntheticsTestRequest()
.url(url)
.method(HTTP_METHOD_GET)))
.options(new SyntheticsTestOptions()
.httpVersion(SyntheticsTestOptionsHTTPVersion.ANY)
.minFailureDuration(0L)
.minLocationFailed(1L)
.scheduling(new SyntheticsTestOptionsScheduling()
.timezone(DateUtil.ET_ZONE_ID)
.addTimeframesItem(new SyntheticsTestOptionsSchedulingTimeframe()
.day(DatadogDayOfWeek.MONDAY)
.from(datadogTestStartTime)
.to(datadogTestEndTime))
.addTimeframesItem(new SyntheticsTestOptionsSchedulingTimeframe()
.day(DatadogDayOfWeek.TUESDAY)
.from(datadogTestStartTime)
.to(datadogTestEndTime))
.addTimeframesItem(new SyntheticsTestOptionsSchedulingTimeframe()
.day(DatadogDayOfWeek.WEDNESDAY)
.from(datadogTestStartTime)
.to(datadogTestEndTime))
.addTimeframesItem(new SyntheticsTestOptionsSchedulingTimeframe()
.day(DatadogDayOfWeek.THURSDAY)
.from(datadogTestStartTime)
.to(datadogTestEndTime))
.addTimeframesItem(new SyntheticsTestOptionsSchedulingTimeframe()
.day(DatadogDayOfWeek.FRIDAY)
.from(datadogTestStartTime)
.to(datadogTestEndTime)))
.tickEvery(convertMinutesToSeconds(datadogCheckEveryMinutes)))
.locations(Collections.singletonList(datadogTestLocation))
.message("Failed: " + url)
.type(SyntheticsAPITestType.API)
.name(url)
.tags(test.getTags());

try {
datadogSyntheticsTestService.getApiProvider().getApiInstance().updateAPITest(test.getPublicId(), body);
LOGGER.info("Test updated: {}", url);
} catch (ApiException e) {
LOGGER.error("Could not update test for URL: {}", url, e);
}
} else {
LOGGER.info("Test NOT updated: {}", url);
}
});

LOGGER.info("********* Completed the Fix Datadog Url Uptime Assertions Job *********");
}


private Optional<SyntheticsAssertion> getContentHeaderAssertionExist(List<SyntheticsAssertion> assertions) {
return assertions.stream()
.filter(assertion -> assertion.getSyntheticsAssertionTarget().getType().equals(SyntheticsAssertionType.HEADER)
&& assertion.getSyntheticsAssertionTarget().getProperty().equals("content-length"))
.findAny();

}

private Long convertMinutesToSeconds(Long minutes) {
return minutes * SECONDS_IN_A_MINUTE;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ public CertifiedProductSearchDetails getListingUploadAsListingForUpdate(ListingU
LOGGER.debug("Converting listing upload into CertifiedProductSearchDetails object");
CertifiedProductSearchDetails listing = listingDetailsHandler.parseAsListing(headingRecord, allListingRecords);
LOGGER.debug("Converted listing upload into CertifiedProductSearchDetails object");
listingNormalizer.normalize(listing, List.of(baselineStandardAsOfTodayNormalizer));
listingNormalizer.normalize(listing, List.of(baselineStandardAsOfCertificationDayNormalizer, baselineStandardAsOfTodayNormalizer));
LOGGER.debug("Normalized listing upload");
return listing;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ private CognitoCredentials reenableUser(CreateUserFromInvitationRequest userInfo
}

cognitoApiWrapper.updateUser(existingUser);
cognitoInvitationManager.deleteToken(UUID.fromString(userInfo.getHash()));
cognitoInvitationManager.deleteInvitation(invitation);

User reenabledUser = cognitoApiWrapper.getUserNoCache(existingUser.getCognitoId());
activityManager.addUserActivity(reenabledUser.getCognitoId(),
Expand All @@ -239,7 +239,7 @@ private CognitoCredentials createNewUser(CreateUserFromInvitationRequest userInf
} else {
cognitoApiWrapper.addUserToGroup(userInfo.getUser().getEmail(), groupNameForEnvironment);
}
cognitoInvitationManager.deleteToken(UUID.fromString(userInfo.getHash()));
cognitoInvitationManager.deleteInvitation(invitation);

User createdUser = cognitoApiWrapper.getUserNoCache(credentials.getCognitoId());
activityManager.addUserActivity(createdUser.getCognitoId(),
Expand Down Expand Up @@ -293,7 +293,7 @@ public User addOrganizationToUser(UUID invitationToken, String accessToken) thro
}

cognitoApiWrapper.addOrgToUser(originalUser, invitation.getOrganizationId());
cognitoInvitationManager.deleteToken(invitationToken);
cognitoInvitationManager.deleteInvitation(invitation);

User updatedUser = cognitoApiWrapper.getUserNoCache(originalUser.getCognitoId());
activityManager.addUserActivity(updatedUser.getCognitoId(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import gov.healthit.chpl.domain.activity.ActivityConcept;
import gov.healthit.chpl.exception.ActivityException;
import gov.healthit.chpl.exception.UserCreationException;
import gov.healthit.chpl.exception.UserPermissionRetrievalException;
import gov.healthit.chpl.exception.UserRetrievalException;
import gov.healthit.chpl.exception.ValidationException;
import gov.healthit.chpl.manager.ActivityManager;
import gov.healthit.chpl.service.InvitationEmailer;
import lombok.extern.log4j.Log4j2;

Expand All @@ -23,14 +26,17 @@ public class CognitoInvitationManager {
private CognitoUserInvitationDAO userInvitationDAO;
private InvitationEmailer invitationEmailer;
private CognitoInvitationValidator cognitoInvitationValidator;
private ActivityManager activityManager;

@Autowired
public CognitoInvitationManager(CognitoUserInvitationDAO userInvitationDAO, InvitationEmailer invitationEmailer,
CognitoInvitationValidator cognitoInvitationValidator) {
CognitoInvitationValidator cognitoInvitationValidator,
ActivityManager activityManager) {

this.userInvitationDAO = userInvitationDAO;
this.invitationEmailer = invitationEmailer;
this.cognitoInvitationValidator = cognitoInvitationValidator;
this.activityManager = activityManager;
}

@Transactional
Expand Down Expand Up @@ -111,8 +117,15 @@ public CognitoUserInvitation inviteCmsUser(CognitoUserInvitation invitation)
}

@Transactional
public void deleteToken(UUID invitationToken) {
userInvitationDAO.deleteByToken(invitationToken);
public void deleteInvitation(CognitoUserInvitation invitation) {
userInvitationDAO.deleteByToken(invitation.getInvitationToken());

try {
activityManager.addActivity(ActivityConcept.INVITATION, invitation.getId(),
"Deleted invitation for " + invitation.getEmail(), invitation, null);
} catch (ActivityException ex) {
LOGGER.error("Could not write activity about creating an invitation for " + invitation.getEmail());
}
}

public CognitoUserInvitation getByToken(UUID invitationToken) {
Expand All @@ -124,6 +137,13 @@ private CognitoUserInvitation createUserInvitation(CognitoUserInvitation origInv

CognitoUserInvitation invitation = userInvitationDAO.create(origInvitation);

try {
activityManager.addActivity(ActivityConcept.INVITATION, invitation.getId(),
"Created invitation for " + invitation.getEmail(), null, invitation);
} catch (ActivityException ex) {
LOGGER.error("Could not write activity about creating an invitation for " + invitation.getEmail());
}

invitationEmailer.emailInvitedUser(invitation);
return invitation;
}
Expand Down

0 comments on commit 2b519c7

Please sign in to comment.