Skip to content

Commit

Permalink
temporary extension support
Browse files Browse the repository at this point in the history
  • Loading branch information
mdial89f committed Sep 13, 2024
1 parent daa2980 commit 3a9af20
Show file tree
Hide file tree
Showing 14 changed files with 233 additions and 10 deletions.
2 changes: 2 additions & 0 deletions lib/lambda/submit/submissionPayloads/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { capitatedRenewal } from "./capitated-renewal";
import { contractingAmendment } from "./contracting-amendment";
import { contractingInitial } from "./contracting-initial";
import { contractingRenewal } from "./contracting-renewal";
import { temporaryExtension } from "./temporary-extension";

export const submissionPayloads = {
"capitated-amendment": capitatedAmendment,
Expand All @@ -16,4 +17,5 @@ export const submissionPayloads = {
"contracting-renewal": contractingRenewal,
"new-chip-submission": newChipSubmission,
"new-medicaid-submission": newMedicaidSubmission,
"temporary-extension": temporaryExtension,
};
52 changes: 52 additions & 0 deletions lib/lambda/submit/submissionPayloads/temporary-extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// can/should add the additional frontend checks here

import { events } from "shared-types/events";
import {
isAuthorized,
getAuthDetails,
lookupUserAttributes,
} from "../../../libs/api/auth/user";
import { type APIGatewayEvent } from "aws-lambda";
import { itemExists } from "libs/api/package";

export const temporaryExtension = async (event: APIGatewayEvent) => {
if (!event.body) return;

const parsedResult = events["temporary-extension"].baseSchema.safeParse(
JSON.parse(event.body),
);
if (!parsedResult.success) {
throw parsedResult.error;
}

// This is the backend check for auth
if (!(await isAuthorized(event, parsedResult.data.id.slice(0, 2)))) {
throw "Unauthorized";
}

// This is the backend check for the item already existing
if (await itemExists({ id: parsedResult.data.id })) {
throw "Item Already Exists";
}

if (!(await itemExists({ id: parsedResult.data.waiverNumber }))) {
throw "Original Waiver does not exist";
}

const authDetails = getAuthDetails(event);
const userAttr = await lookupUserAttributes(
authDetails.userId,
authDetails.poolId,
);
const submitterEmail = userAttr.email;
const submitterName = `${userAttr.given_name} ${userAttr.family_name}`;

const transformedData = events["temporary-extension"].schema.parse({
...parsedResult.data,
submitterName,
submitterEmail,
timestamp: Date.now(),
});

return transformedData;
};
3 changes: 2 additions & 1 deletion lib/packages/shared-types/events/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as capitatedRenewal from "./capitated-renewal";
import * as contractingAmendment from "./contracting-amendment";
import * as contractingInitial from "./contracting-initial";
import * as contractingRenewal from "./contracting-renewal";
import * as temporaryExtension from "./temporary-extension";

export * from "./toggle-withdraw-rai-enabled";
export * from "./issue-rai";
Expand All @@ -21,7 +22,6 @@ export * from "./seatool";
export * from "./remove-appk-child";
export * from "./update-id";
export * from "./complete-intake";
export * from "./temporary-extension";

export const events = {
"capitated-amendment": capitatedAmendment,
Expand All @@ -32,6 +32,7 @@ export const events = {
"contracting-renewal": contractingRenewal,
"new-chip-submission": newChipSubmission,
"new-medicaid-submission": newMedicaidSubmission,
"temporary-extension": temporaryExtension,
};

export type BaseSchemas = z.infer<typeof newMedicaidSubmission.baseSchema>;
30 changes: 25 additions & 5 deletions lib/packages/shared-types/events/temporary-extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,23 @@ import {
attachmentArraySchemaOptional,
} from "../attachments";

export const temporaryExtensionFeSchema = z.object({
id: z.string(),
authority: z.string(),
seaActionType: z.string().default("Extend"),
originalWaiverNumber: z.number(),
export const baseSchema = z.object({
event: z.literal("temporary-extension").default("temporary-extension"),
id: z
.string()
.min(1, { message: "Required" })
.refine((id) => /^[A-Z]{2}-\d{4,5}\.R\d{2}\.TE\d{2}$/.test(id), {
message:
"The Temporary Extension Request Number must be in the format of SS-####.R##.TE## or SS-#####.R##.TE##",
}),
waiverNumber: z
.string()
.min(1, { message: "Required" })
.refine((id) => /^[A-Z]{2}-\d{4,5}\.R\d{2}\.00$/.test(id), {
message:
"The Approved Initial or Renewal Waiver Number must be in the format of SS-####.R##.00 or SS-#####.R##.00.",
}),
authority: z.string(), // z.enum?
additionalInformation: z.string().max(4000).nullable().default(null),
attachments: z.object({
waiverExtensionRequest: z.object({
Expand All @@ -21,3 +33,11 @@ export const temporaryExtensionFeSchema = z.object({
}),
}),
});

export const schema = baseSchema.extend({
actionType: z.string().default("Extend"),
origin: z.literal("mako").default("mako"),
submitterName: z.string(),
submitterEmail: z.string().email(),
timestamp: z.number(),
});
3 changes: 3 additions & 0 deletions lib/packages/shared-types/opensearch/changelog/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
contractingRenewal,
newChipSubmission,
newMedicaidSubmission,
temporaryExtension,
} from "./transforms";

// legacy
Expand All @@ -30,6 +31,7 @@ export type Document = z.infer<capitatedAmendment.Schema> &
z.infer<contractingRenewal.Schema> &
z.infer<newChipSubmission.Schema> &
z.infer<newMedicaidSubmission.Schema> &
z.infer<temporaryExtension.Schema> &
z.infer<legacyEvent.Schema> &
z.infer<legacyAdminChange.Schema>;

Expand Down Expand Up @@ -60,4 +62,5 @@ export const transforms = {
"contracting-renewal": contractingRenewal,
"new-chip-submission": newChipSubmission,
"new-medicaid-submission": newMedicaidSubmission,
"temporary-extension": temporaryExtension,
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * as contractingInitial from "./contracting-initial";
export * as contractingRenewal from "./contracting-renewal";
export * as newChipSubmission from "./new-chip-submission";
export * as newMedicaidSubmission from "./new-medicaid-submission";
export * as temporaryExtension from "./temporary-extension";
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { events } from "shared-types";
export const transform = (offset: number) => {
return events["temporary-extension"].schema.transform((data) => {
const attachments = Object.entries(data.attachments).flatMap(
//eslint-disable-next-line
([_title, attachment]) => {
// Check if 'attachment' exists and has a non-empty 'files' array
if (
attachment &&
Array.isArray(attachment.files) &&
attachment.files.length > 0
) {
// Map each file in 'files' array to the desired shape
return attachment.files.map((file) => ({
filename: file.filename,
title: attachment.label,
bucket: file.bucket,
key: file.key,
uploadDate: file.uploadDate,
}));
}
// If there are no files or the files array is empty, return an empty array
return [];
},
);
return {
...data,
attachments,
packageId: data.id,
id: `${data.id}-${offset}`,
};
});
};

export type Schema = ReturnType<typeof transform>;
3 changes: 3 additions & 0 deletions lib/packages/shared-types/opensearch/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
toggleWithdrawEnabled,
seatool,
changedDate,
temporaryExtension,
} from "./transforms";

export type Document = z.infer<capitatedAmendment.Schema> &
Expand All @@ -34,6 +35,7 @@ export type Document = z.infer<capitatedAmendment.Schema> &
z.infer<contractingRenewal.Schema> &
z.infer<newChipSubmission.Schema> &
z.infer<newMedicaidSubmission.Schema> &
z.infer<temporaryExtension.Schema> &
z.infer<legacyPackageView.Schema> &
z.infer<issueRai.Schema> &
z.infer<respondToRai.Schema> &
Expand Down Expand Up @@ -69,4 +71,5 @@ export const transforms = {
"contracting-amendment": contractingAmendment,
"contracting-initial": contractingInitial,
"contracting-renewal": contractingRenewal,
"temporary-extension": temporaryExtension,
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ export * as withdrawRai from "./withdraw-rai-response";
export * as seatool from "./seatool";
export * as removeAppkChild from "./remove-appk-child";
export * as changedDate from "./changedDate";
export * as temporaryExtension from "./temporary-extension";
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { events, SEATOOL_STATUS, getStatus } from "shared-types";
import {
getNextBusinessDayTimestamp,
seaToolFriendlyTimestamp,
} from "../../../../shared-utils/seatool-date-helper";

export const transform = () => {
// any adhoc logic
return events["temporary-extension"].schema.transform((data) => {
const { stateStatus, cmsStatus } = getStatus(SEATOOL_STATUS.PENDING);
const timestampDate = new Date(data.timestamp);
const todayEpoch = seaToolFriendlyTimestamp(timestampDate);
const nextBusinessDayEpoch = getNextBusinessDayTimestamp(timestampDate);

return {
additionalInformation: data.additionalInformation,
authority: data.authority,
changedDate: new Date(data.timestamp).toISOString(),
cmsStatus,
description: null,
id: data.id,
makoChangedDate: new Date(data.timestamp).toISOString(),
origin: "OneMAC",
raiWithdrawEnabled: false, // Set to false for new submissions
seatoolStatus: SEATOOL_STATUS.PENDING,
state: data.id.split("-")[0],
stateStatus,
statusDate: new Date(todayEpoch).toISOString(),
subject: null,
submissionDate: new Date(nextBusinessDayEpoch).toISOString(),
submitterEmail: data.submitterEmail,
submitterName: data.submitterName,
actionType: data.actionType,
};
});
};

export type Schema = ReturnType<typeof transform>;
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ import {
SelectValue,
} from "@/components";
import { Link } from "react-router-dom";
import { temporaryExtensionFeSchema } from "shared-types";
import { formSchemas } from "@/formSchemas";

export const TemporaryExtensionForm = () => (
<ActionForm
schema={temporaryExtensionFeSchema}
schema={formSchemas["temporary-extension"]}
title="Temporary Extension Request Details"
fields={(form) => (
<>
Expand Down Expand Up @@ -49,7 +49,7 @@ export const TemporaryExtensionForm = () => (
)}
/>
<FormField
name="originalWaiverNumber"
name="waiverNumber"
control={form.control}
render={({ field }) => {
return (
Expand Down Expand Up @@ -125,7 +125,7 @@ export const TemporaryExtensionForm = () => (
}}
documentPollerArgs={{
property: "id",
documentChecker: (data) => data !== undefined,
documentChecker: (check) => check.recordExists,
}}
bannerPostSubmission={{
header: "Temporary extension request submitted",
Expand Down
1 change: 1 addition & 0 deletions react-app/src/features/package/package-activity/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ export const PackageActivity: FC<opensearch.changelog.Document> = (props) => {
case "contracting-renewal":
case "new-chip-submission":
case "new-medicaid-submission":
case "temporary-extension":
return ["Initial package submitted", PA_InitialSubmission];

// case "withdraw-rai":
Expand Down
2 changes: 2 additions & 0 deletions react-app/src/formSchemas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as contractingAmendment from "./contracting-amendment";
import * as contractingRenewal from "./contracting-renewal";
import * as newMedicaidSubmission from "./new-medicaid-submission";
import * as newChipSubmission from "./new-chip-submission";
import * as temporaryExtension from "./temporary-extension";

export const formSchemas = {
"capitated-amendment": capitatedAmendment.formSchema,
Expand All @@ -16,4 +17,5 @@ export const formSchemas = {
"contracting-renewal": contractingRenewal.formSchema,
"new-chip-submission": newChipSubmission.formSchema,
"new-medicaid-submission": newMedicaidSubmission.formSchema,
"temporary-extension": temporaryExtension.formSchema,
};
64 changes: 64 additions & 0 deletions react-app/src/formSchemas/temporary-extension.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { events } from "shared-types/events";
import { isAuthorizedState } from "@/utils";
import { getItem, idIsApproved, itemExists } from "@/api";
import { z } from "zod";

export const formSchema = events["temporary-extension"].baseSchema
.extend({
id: events["temporary-extension"].baseSchema.shape.id
.refine(isAuthorizedState, {
message:
"You can only submit for a state you have access to. If you need to add another state, visit your IDM user profile to request access.",
})
.refine(async (value) => !(await itemExists(value)), {
message:
"According to our records, this Temporary Extension Request Number already exists. Please check the Temporary Extension Request Number and try entering it again.",
}),
waiverNumber: events["temporary-extension"].baseSchema.shape.waiverNumber
.refine(async (value) => await itemExists(value), {
message:
"According to our records, this Approved Initial or Renewal Waiver Number does not yet exist. Please check the Approved Initial or Renewal Waiver Number and try entering it again.",
})
.refine(async (value) => idIsApproved(value), {
message:
"According to our records, this Approved Initial or Renewal Waiver Number is not approved. You must supply an approved Initial or Renewal Waiver Number.",
}),
})
.superRefine(async (data, ctx) => {
// Check that the authorities match
try {
const originalWaiverData = await getItem(data.waiverNumber);
if (originalWaiverData._source.authority !== data.authority) {
ctx.addIssue({
message:
"The selected Temporary Extension Type does not match the Approved Initial or Renewal Waiver's type.",
code: z.ZodIssueCode.custom,
fatal: true,
path: ["authority"],
});
}

// Check that the original waiver and temp extension have the same id up to the last period
const waiverNumberPrefix = data.waiverNumber.substring(
0,
data.waiverNumber.lastIndexOf("."),
);
const idPrefix = data.id.substring(0, data.id.lastIndexOf("."));
if (waiverNumberPrefix !== idPrefix) {
ctx.addIssue({
message:
"The Approved Initial or Renewal Waiver Number and the Temporary Extension Request Number must be identical until the last period.",
code: z.ZodIssueCode.custom,
fatal: true,
path: ["id"],
});
}
return z.never;
} catch (error) {
// If we've failed here, the item does not exist, and the originalWaiverNumberSchema validation will throw the correct errors.
console.error(error);
return z.never;
}
});

// export type Schema = Awaited<ReturnType<typeof transform>>;

0 comments on commit 3a9af20

Please sign in to comment.