Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(xbdc): Remove SEATool connection for new submissions #697

Closed
wants to merge 14 commits into from
Binary file modified bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions lib/lambda/sinkMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,5 +296,6 @@ const changed_date = async (
});
}
}

await bulkUpdateDataWrapper(osDomain, index, docs);
};
151 changes: 12 additions & 139 deletions lib/lambda/submit.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { response } from "libs/handler-lib";
import { APIGatewayEvent } from "aws-lambda";
import * as sql from "mssql";
import {
getAuthDetails,
isAuthorized,
Expand All @@ -14,16 +13,13 @@ import {
onemacSchema,
} from "shared-types";
import {
getSecret,
getAvailableActions,
getNextBusinessDayTimestamp,
seaToolFriendlyTimestamp,
} from "shared-utils";
import { buildStatusMemoQuery } from "../libs/api/statusMemo";
import { produceMessage } from "../libs/api/kafka";
import { getPackage } from "../libs/api/package";

let config: sql.config;
const secretName = process.env.dbInfoSecretName;
if (!secretName) {
throw new Error("Environment variable dbInfoSecretName is not set");
Expand All @@ -48,16 +44,6 @@ export const submit = async (event: APIGatewayEvent) => {
});
}

const secret = JSON.parse(await getSecret(secretName));
const { ip, port, user, password } = secret;
config = {
user,
password,
server: ip,
port: parseInt(port as string),
database: "SEA",
} as sql.config;

const activeSubmissionTypes = [
Authority.CHIP_SPA,
Authority.MED_SPA,
Expand All @@ -68,7 +54,7 @@ export const submit = async (event: APIGatewayEvent) => {
return response({
statusCode: 400,
body: {
message: `OneMAC (micro) Submissions API does not support the following authority: ${body.authority}`,
message: `OneMAC Submissions API does not support the following authority: ${body.authority}`,
},
});
}
Expand All @@ -79,7 +65,6 @@ export const submit = async (event: APIGatewayEvent) => {
authDetails.poolId,
);

// I think we need to break this file up. A switch maybe
if (
[Authority["1915b"], Authority["1915c"]].includes(body.authority) &&
body.seaActionType === "Extend"
Expand All @@ -106,9 +91,17 @@ export const submit = async (event: APIGatewayEvent) => {
},
});
}
const authorityId = findAuthorityIdByName(body.authority);

const item = {
...body,
authorityId, // TODO: is this actually used?
submissionDate: getNextBusinessDayTimestamp(),
statusDate: seaToolFriendlyTimestamp(),
changedDate: Date.now(),
};
// Safe parse the body
const eventBody = onemacSchema.safeParse(body);
const eventBody = onemacSchema.safeParse(item);
if (!eventBody.success) {
return console.log(
"MAKO Validation Error. The following record failed to parse: ",
Expand All @@ -124,15 +117,7 @@ export const submit = async (event: APIGatewayEvent) => {
await produceMessage(
process.env.topicName as string,
body.id,
JSON.stringify({
...eventBody.data,
submissionDate: getNextBusinessDayTimestamp(),
statusDate: seaToolFriendlyTimestamp(),
changedDate: Date.now(),
notificationMetadata: {
submissionDate: getNextBusinessDayTimestamp(),
},
}),
JSON.stringify(eventBody.data),
);

return response({
Expand All @@ -141,20 +126,7 @@ export const submit = async (event: APIGatewayEvent) => {
});
}

const today = seaToolFriendlyTimestamp();
const submissionDate = getNextBusinessDayTimestamp();
console.log(
"Initial Submission Date determined to be: " +
new Date(submissionDate).toISOString(),
);

// Open the connection pool and transaction outside of the try/catch/finally
const pool = await sql.connect(config);
const transaction = new sql.Transaction(pool);

// Begin writes
try {
await transaction.begin();
// We first parse the event; if it's malformed, this will throw an error before we touch seatool or kafka
const eventBody = onemacSchema.safeParse(body);
if (!eventBody.success) {
Expand All @@ -166,121 +138,22 @@ export const submit = async (event: APIGatewayEvent) => {
);
}

// Resolve the the Plan_Type_ID
const authorityId = findAuthorityIdByName(body.authority);
// Resolve the actionTypeID, if applicable
const actionTypeSelect = [Authority["1915b"], Authority.CHIP_SPA].includes(
body.authority,
)
? `
SELECT @ActionTypeID = Action_ID FROM SEA.dbo.Action_Types
WHERE Plan_Type_ID = '${authorityId}'
AND Action_Name = '${body.seaActionType}';
`
: "SET @ActionTypeID = NULL;";

// perhaps someday... but for now... it is but a memory.

// // Generate INSERT statements for typeIds
// const typeIdsValues = body.typeIds
// .map((typeId: number) => `('${body.id}', '${typeId}')`)
// .join(",\n");

// const typeIdsInsert = typeIdsValues
// ? `INSERT INTO SEA.dbo.State_Plan_Service_Types (ID_Number, Service_Type_ID) VALUES ${typeIdsValues};`
// : "";

// // Generate INSERT statements for subTypeIds
// const subTypeIdsValues = body.subTypeIds
// .map((subTypeId: number) => `('${body.id}', '${subTypeId}')`)
// .join(",\n");

// const subTypeIdsInsert = subTypeIdsValues
// ? `INSERT INTO SEA.dbo.State_Plan_Service_SubTypes (ID_Number, Service_SubType_ID) VALUES ${subTypeIdsValues};`
// : "";

const query = `
DECLARE @RegionID INT;
DECLARE @SPWStatusID INT;
DECLARE @ActionTypeID INT;
DECLARE @SubmissionDate DATETIME;
DECLARE @StatusDate DATETIME;
DECLARE @ProposedDate DATETIME;
DECLARE @TitleName NVARCHAR(MAX) = ${
body.subject ? `'${body.subject.replace("'", "''")}'` : "NULL"
};
DECLARE @SummaryMemo NVARCHAR(MAX) = ${
body.description ? `'${body.description.replace("'", "''")}'` : "NULL"
};
DECLARE @StatusMemo NVARCHAR(MAX) = ${buildStatusMemoQuery(
body.id,
"Package Submitted",
"insert",
)}
DECLARE @PlanTypeID INT = ${authorityId}

-- Set your variables
SELECT @RegionID = Region_ID FROM SEA.dbo.States WHERE State_Code = '${
body.state
}';
SELECT @SPWStatusID = SPW_Status_ID FROM SEA.dbo.SPW_Status WHERE SPW_Status_DESC = 'Pending';
-- Set ActionTypeID if applicale, using the conditionally set statement generated previously
${actionTypeSelect}

SET @SubmissionDate = DATEADD(s, CONVERT(INT, LEFT(${submissionDate}, 10)), CAST('19700101' as DATETIME));
SET @StatusDate = DATEADD(s, CONVERT(INT, LEFT(${today}, 10)), CAST('19700101' as DATETIME));
SET @ProposedDate = DATEADD(s, CONVERT(INT, LEFT(${
body.proposedEffectiveDate
}, 10)), CAST('19700101' as DATETIME));

-- Main insert into State_Plan
INSERT INTO SEA.dbo.State_Plan (ID_Number, State_Code, Title_Name, Summary_Memo, Region_ID, Plan_Type, Submission_Date, Status_Date, Proposed_Date, SPW_Status_ID, Budget_Neutrality_Established_Flag, Status_Memo, Action_Type)
VALUES ('${body.id}', '${
body.state
}', @TitleName, @SummaryMemo, @RegionID, @PlanTypeID, @SubmissionDate, @StatusDate, @ProposedDate, @SPWStatusID, 0, @StatusMemo, @ActionTypeID);
`;

// -- Insert all types into State_Plan_Service_Types
// ${typeIdsInsert}

// -- Insert all types into State_Plan_Service_SubTypes
// ${subTypeIdsInsert}

// data for emails
eventBody.data.notificationMetadata = {
submissionDate: submissionDate,
proposedEffectiveDate: body.proposedEffectiveDate,
};

const result = await transaction.request().query(query);
console.log(result);

// Write to kafka, before we commit our seatool transaction.
// This way, if we have an error making the kafka write, the seatool changes are rolled back.
await produceMessage(
process.env.topicName as string,
body.id,
JSON.stringify(eventBody.data),
);

// Commit transaction if we've made it this far
await transaction.commit();

return response({
statusCode: 200,
body: { message: "success" },
});
} catch (err) {
// Rollback and log
await transaction.rollback();
console.error("Error when interacting with seatool or kafka:", err);
console.error("Error whilst interacting with kafka:", err);
return response({
statusCode: 500,
body: { message: "Internal server error" },
});
} finally {
// Close pool
await pool.close();
}
};

Expand Down
1 change: 1 addition & 0 deletions lib/libs/opensearch-lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export async function bulkUpdateData(
): Promise<void> {
try {
const response = await client.bulk({ refresh: true, body: body });

if (response.body.errors) {
// Check for 429 status within response errors
const hasRateLimitErrors = response.body.items.some(
Expand Down
8 changes: 6 additions & 2 deletions lib/packages/shared-types/action-types/new-submission.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { z } from "zod";
import { attachmentSchema } from "../attachments";
import { notificationMetadataSchema } from "../notification-metadata";
import { SEATOOL_STATUS } from "../statusHelper";

// This is the event schema for ne submissions from our system
// This is the event schema for new submissions from our system
export const onemacSchema = z.object({
authority: z.string(),
seaActionType: z.string().optional(), // Used by waivers and chip spas
Expand All @@ -18,10 +19,13 @@ export const onemacSchema = z.object({
raiWithdrawEnabled: z.boolean().default(false),
notificationMetadata: notificationMetadataSchema.nullish(),
timestamp: z.number().optional(),
// these are specific to TEs... should be broken into its own schema
statusDate: z.number().optional(),
submissionDate: z.number().optional(),
changedDate: z.number().optional(),
//
seatoolStatus: z.literal(SEATOOL_STATUS.PENDING),
proposedEffectiveDate: z.number().optional(),
state: z.string(),
});

export type OneMac = z.infer<typeof onemacSchema>;
7 changes: 7 additions & 0 deletions lib/packages/shared-types/authority.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,10 @@ export type AuthorityUnion =
| "CHIP SPA"
| "1915(b)"
| "1915(c)";

export const authorityStringToSeatoolAuthority = {
"medicaid spa": "Medicaid SPA",
"chip spa": "CHIP SPA",
"1915(b)": "1915(b)",
"1915(c)": "1915(c)",
};
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import {
SEATOOL_AUTHORITIES,
SEATOOL_STATUS,
authorityStringToSeatoolAuthority,
getStatus,
onemacSchema,
} from "shared-types";
import { seaToolFriendlyTimestamp } from "shared-utils";

const getIdByAuthorityName = (authorityName: string) => {
try {
const authorityId = Object.keys(SEATOOL_AUTHORITIES).find(
(key) => SEATOOL_AUTHORITIES[key] === authorityName,
(key) =>
SEATOOL_AUTHORITIES[key].toLowerCase() === authorityName.toLowerCase(),
);
return authorityId ? parseInt(authorityId, 10) : null;
} catch (error) {
Expand All @@ -22,6 +26,21 @@ const getDateStringOrNullFromEpoc = (epocDate: number | null | undefined) =>
? new Date(epocDate).toISOString()
: null;

type Flavor = "SPA" | "WAIVER" | "MEDICAID" | "CHIP";

const flavorLookup = (val: string | null): null | string => {
if (!val) return null;

const lookup: Record<string, Flavor> = {
["1915(b)"]: "WAIVER",
["1915(c)"]: "WAIVER",
["chip spa"]: "CHIP",
["medicaid spa"]: "MEDICAID",
};

return lookup[val];
};

export const transform = (id: string) => {
return onemacSchema.transform((data) => {
if (data.seaActionType === "Extend") {
Expand All @@ -31,6 +50,8 @@ export const transform = (id: string) => {
id,
attachments: data.attachments,
appkParentId: data.appkParentId,
appkParent: data.appkParent,
appkTitle: data.appkTitle,
raiWithdrawEnabled: data.raiWithdrawEnabled,
additionalInformation: data.additionalInformation,
submitterEmail: data.submitterEmail,
Expand All @@ -44,10 +65,11 @@ export const transform = (id: string) => {
state: id.split("-")[0],
actionType: data.seaActionType,
actionTypeId: 9999,
authorityId: getIdByAuthorityName(data.authority),
authorityId: getIdByAuthorityName(data.authority.toUpperCase()),
authority: data.authority,
stateStatus: "Submitted",
cmsStatus: "Requested",
proposedDate: data.proposedEffectiveDate,
seatoolStatus: SEATOOL_STATUS.PENDING,
statusDate: getDateStringOrNullFromEpoc(data.statusDate),
submissionDate: getDateStringOrNullFromEpoc(data.submissionDate),
Expand All @@ -61,7 +83,12 @@ export const transform = (id: string) => {
// ----------
};
} else {
return {
const { stateStatus, cmsStatus } = getStatus(data.seatoolStatus);
// eslint-disable-next-line
const currentDate = !!data.timestamp
? new Date(data.timestamp).toISOString()
: seaToolFriendlyTimestamp();
const result = {
id,
attachments: data.attachments,
appkParentId: data.appkParentId,
Expand All @@ -73,11 +100,26 @@ export const transform = (id: string) => {
submitterName:
data.submitterName === "-- --" ? null : data.submitterName,
origin: "OneMAC",
makoChangedDate:
typeof data.timestamp === "number"
? new Date(data.timestamp).toISOString()
: null,
makoChangedDate: currentDate,
flavor: flavorLookup(data.authority.toLowerCase()),
proposedDate: data.proposedEffectiveDate,
actionType: data.seaActionType,
actionTypeId: 9999,
authorityId: getIdByAuthorityName(data.authority.toUpperCase()),
seatoolStatus: SEATOOL_STATUS.PENDING,
stateStatus,
cmsStatus,
statusDate: currentDate,
submissionDate: seaToolFriendlyTimestamp(),
changedDate: currentDate,
subject: null,
description: null,
state: data.state,
authority: authorityStringToSeatoolAuthority[data.authority],
};
console.log("SENDING TO OPENSEARCH:");
console.log(JSON.stringify(result));
return result;
}
});
};
Expand Down
Loading
Loading