Skip to content

Commit

Permalink
Merge pull request #3399 from NCI-Agency/fix-future-engagement-worker
Browse files Browse the repository at this point in the history
Fix future engagement worker
  • Loading branch information
VassilIordanov authored Jan 15, 2021
2 parents 370196d + 662702d commit 7401a9b
Show file tree
Hide file tree
Showing 7 changed files with 293 additions and 123 deletions.
4 changes: 4 additions & 0 deletions src/main/java/mil/dds/anet/beans/ApprovalStep.java
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,8 @@ protected ApprovalStep clone() {
return clone;
}

public static boolean isPlanningStep(ApprovalStep step) {
return step != null && ApprovalStepType.PLANNING_APPROVAL.equals(step.getType());
}

}
58 changes: 46 additions & 12 deletions src/main/java/mil/dds/anet/database/ReportDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.google.common.collect.ObjectArrays;
import io.leangen.graphql.annotations.GraphQLRootContext;
import java.lang.invoke.MethodHandles;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
Expand Down Expand Up @@ -60,10 +61,15 @@
import org.jdbi.v3.sqlobject.customizer.Bind;
import org.jdbi.v3.sqlobject.customizer.BindBean;
import org.jdbi.v3.sqlobject.statement.SqlBatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.vyarus.guicey.jdbi3.tx.InTransaction;

public class ReportDao extends AnetBaseDao<Report, ReportSearchQuery> {

private static final Logger logger =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

// Must always retrieve these e.g. for ORDER BY or search post-processing
public static final String[] minimalFields = {"uuid", "approvalStepUuid",
"advisorOrganizationUuid", "createdAt", "updatedAt", "engagementDate", "releasedAt", "state"};
Expand Down Expand Up @@ -821,14 +827,51 @@ public void sendReportPublishedEmail(Report r) {
AnetEmailWorker.sendEmailAsync(email);
}

public int submit(final Report r, final Person user) {
final AnetObjectEngine engine = AnetObjectEngine.getInstance();
// Get all the approval steps for this report
final List<ApprovalStep> steps = r.computeApprovalSteps(engine.getContext(), engine).join();

// Write the submission action
final ReportAction action = new ReportAction();
action.setReportUuid(r.getUuid());
action.setPersonUuid(user.getUuid());
action.setType(ActionType.SUBMIT);
action.setPlanned(r.isFutureEngagement());
engine.getReportActionDao().insert(action);

if (r.isFutureEngagement() && Utils.isEmptyOrNull(steps)) {
// Future engagements without planning approval chain will be approved directly
// Write the approval action
final ReportAction approval = new ReportAction();
approval.setReportUuid(r.getUuid());
approval.setPersonUuid(user.getUuid());
approval.setType(ActionType.APPROVE);
approval.setPlanned(true); // so the FutureEngagementWorker can find this
engine.getReportActionDao().insert(approval);
r.setState(ReportState.APPROVED);
} else {
// Push the report into the first step of this workflow
r.setApprovalStep(steps.get(0));
r.setState(ReportState.PENDING_APPROVAL);
}
final int numRows = update(r, user);
if (numRows != 0 && !Utils.isEmptyOrNull(steps)) {
sendApprovalNeededEmail(r, steps.get(0));
logger.info("Putting report {} into step {}", r.getUuid(), steps.get(0).getUuid());
}
return numRows;
}

public int approve(Report r, Person user, ApprovalStep step) {
// Write the approval action
final ReportAction action = new ReportAction();
action.setReportUuid(r.getUuid());
action.setStepUuid(step.getUuid());
// User could be null when the publication action is being done automatically by a worker
// User could be null when the approval action is being done automatically by a worker
action.setPersonUuid(DaoUtils.getUuid(user));
action.setType(ActionType.APPROVE);
action.setPlanned(ApprovalStep.isPlanningStep(step));
AnetObjectEngine.getInstance().getReportActionDao().insert(action);

// Update the report
Expand Down Expand Up @@ -915,6 +958,7 @@ public List<Report> getFutureToPastReports(Instant end) {
sql.append("/* getFutureToPastReports */");
sql.append(" SELECT r.uuid AS reports_uuid");
sql.append(" FROM reports r");
// Get the last report action
// FIXME: Hard-coded MS SQL or PostgreSQL specific query stanza
if (DaoUtils.isMsSql()) {
sql.append(" OUTER APPLY (SELECT TOP (1)");
Expand All @@ -937,19 +981,9 @@ public List<Report> getFutureToPastReports(Instant end) {
sql.append(" )");
// Get past reports relative to the endDate argument
sql.append(" AND r.\"engagementDate\" <= :endDate");
sql.append(" AND (");
// Get reports for engagements which just became past engagements during or
// after the planning approval process, but which are not in the report approval process yet
sql.append(" ra.planned = :planned");
sql.append(" OR ra.\"approvalStepUuid\" IN (");
sql.append(" SELECT a.uuid FROM \"approvalSteps\" a");
sql.append(" WHERE a.type = :planningApprovalStepType");
sql.append(" )");
// Also get reports pending planning approval when the approval action was not taken yet
sql.append(" OR r.\"approvalStepUuid\" IN (");
sql.append(" SELECT a.uuid FROM \"approvalSteps\" a");
sql.append(" WHERE a.type = :planningApprovalStepType");
sql.append(" ))");
sql.append(" AND ra.planned = :planned");
DaoUtils.addInstantAsLocalDateTime(sqlArgs, "endDate", end);
sqlArgs.put("reportApproved", DaoUtils.getEnumId(ReportState.APPROVED));
sqlArgs.put("reportRejected", DaoUtils.getEnumId(ReportState.REJECTED));
Expand Down
37 changes: 4 additions & 33 deletions src/main/java/mil/dds/anet/resources/ReportResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -398,41 +398,11 @@ public Report submitReport(@GraphQLRootContext Map<String, Object> context,
throw new WebApplicationException("Missing engagement date", Status.BAD_REQUEST);
}

// Get all the approval steps for this report
final List<ApprovalStep> steps = r.computeApprovalSteps(engine.getContext(), engine).join();

// Write the submission action
final ReportAction action = new ReportAction();
action.setReportUuid(r.getUuid());
action.setPersonUuid(user.getUuid());
action.setType(ActionType.SUBMIT);
engine.getReportActionDao().insert(action);

if (r.isFutureEngagement() && Utils.isEmptyOrNull(steps)) {
// Future engagements without planning approval chain will be approved directly
// Write the approval action
final ReportAction approval = new ReportAction();
approval.setReportUuid(r.getUuid());
approval.setPersonUuid(user.getUuid());
approval.setType(ActionType.APPROVE);
approval.setPlanned(true); // so the FutureEngagementWorker can find this
engine.getReportActionDao().insert(approval);
r.setState(ReportState.APPROVED);
} else {
// Push the report into the first step of this workflow
r.setApprovalStep(steps.get(0));
r.setState(ReportState.PENDING_APPROVAL);
}
final int numRows = dao.update(r, user);
if (numRows != 1) {
final int numRows = dao.submit(r, user);
if (numRows == 0) {
throw new WebApplicationException("No records updated", Status.BAD_REQUEST);
}

if (!Utils.isEmptyOrNull(steps)) {
dao.sendApprovalNeededEmail(r, steps.get(0));
logger.info("Putting report {} into step {}", r.getUuid(), steps.get(0).getUuid());
}

AnetAuditLogger.log("report {} submitted by author {}", r.getUuid(), user.getUuid());
// GraphQL mutations *have* to return something, we return the report
return r;
Expand Down Expand Up @@ -522,14 +492,15 @@ public Report rejectReport(@GraphQLRootContext Map<String, Object> context,
}

// Write the rejection action
ReportAction rejection = new ReportAction();
final ReportAction rejection = new ReportAction();
rejection.setReportUuid(r.getUuid());
if (step != null) {
// Step is null when an approved report is being rejected by an admin
rejection.setStepUuid(step.getUuid());
}
rejection.setPersonUuid(approver.getUuid());
rejection.setType(ActionType.REJECT);
rejection.setPlanned(ApprovalStep.isPlanningStep(step) || r.isFutureEngagement());
engine.getReportActionDao().insert(rejection);

// Update the report
Expand Down
114 changes: 114 additions & 0 deletions src/main/resources/migrations.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3655,4 +3655,118 @@
<column name="customFields" type="${long_text_type}" />
</addColumn>
</changeSet>

<changeSet id="set-planned-reportActions" author="gjvoosten">
<!-- any report action linked to a planning step is itself planned -->
<sql dbms="postgresql">
<![CDATA[
UPDATE "reportActions"
SET planned = true
WHERE "approvalStepUuid" IN (
SELECT uuid
FROM "approvalSteps"
WHERE type = 0
)
AND "createdAt" >= '2021-01-01';
]]>
</sql>
<sql dbms="mssql">
<![CDATA[
UPDATE "reportActions"
SET planned = 1
WHERE "approvalStepUuid" IN (
SELECT uuid
FROM "approvalSteps"
WHERE type = 0
)
AND "createdAt" >= '2021-01-01';
]]>
</sql>

<!-- any report submit action directly followed by a planned action is itself planned -->
<sql dbms="postgresql">
<![CDATA[
UPDATE "reportActions"
SET planned = true
WHERE "createdAt" IN (
SELECT ra2."createdAt"
FROM "reportActions" ra
LEFT JOIN LATERAL (
SELECT ra2.*
FROM "reportActions" ra2
WHERE ra2."reportUuid" = ra."reportUuid"
AND ra2."createdAt" < ra."createdAt"
ORDER BY ra2."createdAt" DESC
LIMIT 1
) ra2 ON TRUE
WHERE ra2.type = 2
AND ra.planned = true
)
AND "createdAt" >= '2021-01-01';
]]>
</sql>
<sql dbms="mssql">
<![CDATA[
UPDATE "reportActions"
SET planned = 1
WHERE "createdAt" IN (
SELECT ra2."createdAt"
FROM "reportActions" ra
OUTER APPLY (
SELECT TOP(1) ra2.*
FROM "reportActions" ra2
WHERE ra2."reportUuid" = ra."reportUuid"
AND ra2."createdAt" < ra."createdAt"
ORDER BY ra2."createdAt" DESC
) ra2
WHERE ra2.type = 2
AND ra.planned = 1
)
AND "createdAt" >= '2021-01-01';
]]>
</sql>

<!-- any report publish action directly preceded by a planned action is itself planned -->
<sql dbms="postgresql">
<![CDATA[
UPDATE "reportActions"
SET planned = true
WHERE "createdAt" IN (
SELECT ra2."createdAt"
FROM "reportActions" ra
LEFT JOIN LATERAL (
SELECT ra2.*
FROM "reportActions" ra2
WHERE ra2."reportUuid" = ra."reportUuid"
AND ra2."createdAt" > ra."createdAt"
ORDER BY ra2."createdAt"
LIMIT 1
) ra2 ON TRUE
WHERE ra2.type = 3
AND ra.planned = true
);
]]>
</sql>
<sql dbms="mssql">
<![CDATA[
UPDATE "reportActions"
SET planned = 1
WHERE "createdAt" IN (
SELECT ra2."createdAt"
FROM "reportActions" ra
OUTER APPLY (
SELECT TOP(1) ra2.*
FROM "reportActions" ra2
WHERE ra2."reportUuid" = ra."reportUuid"
AND ra2."createdAt" > ra."createdAt"
ORDER BY ra2."createdAt"
) ra2
WHERE ra2.type = 3
AND ra.planned = 1
)
AND "createdAt" >= '2021-01-01';
]]>
</sql>
</changeSet>

</databaseChangeLog>
Loading

0 comments on commit 7401a9b

Please sign in to comment.