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(lamdba): add admin function for NOSO 2 #1060

Merged
merged 23 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2174b09
rewriting noso admin to be a function for an empty package
andieswift Jan 24, 2025
b544f2c
we adding some tests up in this b
andieswift Jan 24, 2025
ab37e48
making asharon fixes
andieswift Jan 24, 2025
2727577
merged main
andieswift Jan 24, 2025
d199239
changed back the string check for event.body
andieswift Jan 24, 2025
432b0b1
putting back the .and zod thing
andieswift Jan 24, 2025
873d26b
Merge branch 'main' into admin-noso2
andieswift Jan 24, 2025
41e9caa
needed to adjust logic so that if the package is in SeaTool we still …
andieswift Jan 25, 2025
b15ea13
updated tests to reflect changes for checking for seaTool packages
andieswift Jan 25, 2025
3281fbd
:( i have to remove this extra `s`
andieswift Jan 25, 2025
50e26b3
setting the item origin to SEATool on line 78 was redundant
andieswift Jan 27, 2025
6c3c29f
removed .and on zod, and created a new zod schema for sinkMain
andieswift Jan 27, 2025
006e7fc
i spelled like all the properties wrong
andieswift Jan 27, 2025
f2e18a5
Merge branch 'main' into admin-noso2
andieswift Jan 27, 2025
a84a00b
in changing the sinkmain schema i also have to change the changelog s…
andieswift Jan 28, 2025
61ec66a
change the admin change title
andieswift Jan 28, 2025
8c39498
forgt the status on the extended schema
andieswift Jan 28, 2025
0d68302
added mockEvent prop]
andieswift Jan 28, 2025
13eb23a
fixed the ui part
andieswift Jan 28, 2025
2fd47b5
fixed test and other errors
andieswift Jan 28, 2025
4a6b82c
removed unused code, adde3d example json comment
andieswift Jan 28, 2025
9deee3d
embarassing left andie console logs
andieswift Jan 29, 2025
c684300
Merge branch 'main' into admin-noso2
andieswift Jan 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion lib/lambda/sinkChangelog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
transformUpdateValuesSchema,
transformDeleteSchema,
transformedUpdateIdSchema,
transformSubmitValuesSchema,
} from "./update/adminChangeSchemas";
import { getPackageChangelog } from "libs/api/package";

Expand Down Expand Up @@ -67,7 +68,9 @@ const processAndIndex = async ({
// query all changelog entries for this ID and create copies of all entries with new ID
if (record.isAdminChange) {
const schema = transformDeleteSchema(offset).or(
transformUpdateValuesSchema(offset).or(transformedUpdateIdSchema),
transformUpdateValuesSchema(offset)
.or(transformedUpdateIdSchema)
.or(transformSubmitValuesSchema),
);

const result = schema.safeParse(record);
Expand Down
4 changes: 3 additions & 1 deletion lib/lambda/sinkMainProcessors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import {
deleteAdminChangeSchema,
updateValuesAdminChangeSchema,
updateIdAdminChangeSchema,
extendSubmitNOSOAdminSchema,
} from "./update/adminChangeSchemas";

const removeDoubleQuotesSurroundingString = (str: string) => str.replace(/^"|"$/g, "");
const adminRecordSchema = deleteAdminChangeSchema
.or(updateValuesAdminChangeSchema)
.or(updateIdAdminChangeSchema);
.or(updateIdAdminChangeSchema)
.or(extendSubmitNOSOAdminSchema);

type OneMacRecord = {
id: string;
Expand Down
34 changes: 34 additions & 0 deletions lib/lambda/update/adminChangeSchemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,37 @@ export const transformedUpdateIdSchema = updateIdAdminChangeSchema.transform((da
id: `${data.id}`,
timestamp: Date.now(),
}));

export const submitNOSOAdminSchema = z.object({
id: z.string(),
authority: z.string(),
status: z.string(),
submitterEmail: z.string(),
submitterName: z.string(),
adminChangeType: z.literal("NOSO"),
mockEvent: z.string(),
changeMade: z.string(),
changeReason: z.string(),
});

export const extendSubmitNOSOAdminSchema = submitNOSOAdminSchema.extend({
packageId: z.string(),
origin: z.string(),
makoChangedDate: z.number(),
changedDate: z.number(),
statusDate: z.number(),
isAdminChange: z.boolean(),
state: z.string(),
event: z.string(),
stateStatus: z.string(),
cmsStatus: z.string(),
});

export const transformSubmitValuesSchema = extendSubmitNOSOAdminSchema.transform((data) => ({
...data,
adminChangeType: "NOSO",
event: "NOSO",
id: data.id,
packageId: data.id,
timestamp: Date.now(),
}));
114 changes: 114 additions & 0 deletions lib/lambda/update/submitNOSO.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { handler } from "./submitNOSO";
import { APIGatewayEvent } from "node_modules/shared-types";

import { NOT_EXISTING_ITEM_ID, TEST_ITEM_ID } from "mocks";

vi.mock("libs/handler-lib", () => ({
response: vi.fn((data) => data),
}));

describe("handler", () => {
beforeEach(() => {
vi.clearAllMocks();
process.env.topicName = "test-topic";
});

it("should return 400 if event body is missing", async () => {
const event = {} as APIGatewayEvent;
const result = await handler(event);
const expectedResult = { statusCode: 400, body: { message: "Event body required" } };

expect(result).toStrictEqual(expectedResult);
});

it("should return 400 if package ID is not found", async () => {
const noActionevent = {
body: JSON.stringify({ packageId: "123", changeReason: "Nunya", authority: "test" }),
} as APIGatewayEvent;

const resultPackage = await handler(noActionevent);

expect(resultPackage?.statusCode).toBe(400);
});
it("should return 400 if admingChangeType is not found", async () => {
const noApackageEvent = {
body: JSON.stringify({ action: "123", changeReason: "Nunya" }),
} as APIGatewayEvent;

const resultAction = await handler(noApackageEvent);

expect(resultAction?.statusCode).toBe(400);
});
it("should return 400 if existing item is entered", async () => {
const noActionevent = {
body: JSON.stringify({
id: TEST_ITEM_ID,
adminChangeType: "NOSO",
authority: "SPA",
submitterEmail: "[email protected]",
submitterName: "Name",
status: "submitted",
changeMade: "change",
mockEvent: "mock-event",
changeReason: "reason",
}),
} as APIGatewayEvent;

const result = await handler(noActionevent);

const expectedResult = {
statusCode: 400,
body: { message: `Package with id: ${TEST_ITEM_ID} already exists.` },
};
expect(result).toStrictEqual(expectedResult);
});

it("should submit a new item", async () => {
const validItem = {
body: JSON.stringify({
id: NOT_EXISTING_ITEM_ID,
authority: "Medicaid SPA",
status: "submitted",
submitterEmail: "[email protected]",
submitterName: "Name",
adminChangeType: "NOSO",
changeMade: "change",
mockEvent: "mock-event",
changeReason: "reason",
}),
} as APIGatewayEvent;

const result = await handler(validItem);

const expectedResult = {
statusCode: 200,
body: { message: `${NOT_EXISTING_ITEM_ID} has been submitted.` },
};
expect(result).toStrictEqual(expectedResult);
});

it("should fail to create a package ID with no topic name", async () => {
process.env.topicName = "";
const validItem = {
body: JSON.stringify({
id: NOT_EXISTING_ITEM_ID,
authority: "Medicaid SPA",
status: "submitted",
submitterEmail: "[email protected]",
submitterName: "Name",
adminChangeType: "NOSO",
mockEvent: "mock-event",
changeMade: "change",
changeReason: "reason",
}),
} as APIGatewayEvent;

const result = await handler(validItem);
const expectedResult = {
statusCode: 500,
body: { message: "Topic name is not defined" },
};
expect(result).toStrictEqual(expectedResult);
});
});
115 changes: 115 additions & 0 deletions lib/lambda/update/submitNOSO.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { response } from "libs/handler-lib";
import { APIGatewayEvent } from "aws-lambda";
import { produceMessage } from "libs/api/kafka";
import { getPackage } from "libs/api/package";
import { ItemResult } from "shared-types/opensearch/main";
import { submitNOSOAdminSchema } from "./adminChangeSchemas";
import { z } from "zod";

import { getStatus } from "shared-types";

/*
EXAMPLE EVENT JSON:

{
"body": {
"id": "CO-34304.R00.01",
"authority": "1915(c)",
"status": "Submitted",
"submitterEmail": "[email protected]",
"submitterName": "George Harrison",
"adminChangeType": "NOSO",
"mockEvent": "app-k", //needed for future actions
"changeMade": "CO-34304.R00.01 added to OneMAC.Package not originally submitted in OneMAC. Contact your CPOC to verify the initial submission documents.",
"changeReason": "Per request from CMS, this package was added to OneMAC."
}
}

*/

interface submitMessageType {
id: string;
authority: string;
status: string;
submitterEmail: string;
submitterName: string;
adminChangeType: string;
stateStatus: string;
cmsStatus: string;
}

const sendSubmitMessage = async (item: submitMessageType) => {
const topicName = process.env.topicName as string;
if (!topicName) {
throw new Error("Topic name is not defined");
}

const currentTime = Date.now();

await produceMessage(
topicName,
item.id,
JSON.stringify({
...item,
packageId: item.id,
origin: "SEATool",
isAdminChange: true,
adminChangeType: "NOSO",
description: null,
event: "NOSO",
state: item.id.substring(0, 2),
makoChangedDate: currentTime,
changedDate: currentTime,
statusDate: currentTime,
}),
);

return response({
statusCode: 200,
body: { message: `${item.id} has been submitted.` },
});
};

export const handler = async (event: APIGatewayEvent) => {
if (!event.body) {
return response({
statusCode: 400,
body: { message: "Event body required" },
});
}

try {
const item = submitNOSOAdminSchema.parse(
typeof event.body === "string" ? JSON.parse(event.body) : event.body,
);
andieswift marked this conversation as resolved.
Show resolved Hide resolved

const { stateStatus, cmsStatus } = getStatus(item.status);
// check if it already exsists
const currentPackage: ItemResult | undefined = await getPackage(item.id);

if (currentPackage && currentPackage.found == true) {
// if it exists and has origin OneMAC we shouldn't override it
if (currentPackage._source.origin === "OneMAC") {
return response({
statusCode: 400,
body: { message: `Package with id: ${item.id} already exists.` },
});
}
}

return await sendSubmitMessage({ ...item, stateStatus, cmsStatus });
} catch (err) {
console.error("Error has occured submitting package:", err);
if (err instanceof z.ZodError) {
return response({
statusCode: 400,
body: { message: err.errors },
});
}

return response({
statusCode: 500,
body: { message: err.message || "Internal Server Error" },
});
}
};
3 changes: 2 additions & 1 deletion lib/packages/shared-types/opensearch/changelog/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ export type Document = Omit<AppkDocument, "event"> &
| "withdraw-rai"
| "update-values"
| "update-id"
| "delete";
| "delete"
| "NOSO";
};

export type Response = Res<Document>;
Expand Down
1 change: 1 addition & 0 deletions lib/packages/shared-types/opensearch/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export type Document = AppkDocument &
adminChangeType?: string;
changeMade?: string;
idToBeUpdated?: string;
mockEvent?: string;
};

export type Response = Res<Document>;
Expand Down
11 changes: 11 additions & 0 deletions lib/stacks/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,17 @@ export class Api extends cdk.NestedStack {
indexNamespace,
},
},
{
id: "submitNOSO",
entry: join(__dirname, "../lambda/update/submitNOSO.ts"),
environment: {
dbInfoSecretName,
topicName,
brokerString,
osDomain: `https://${openSearchDomainEndpoint}`,
indexNamespace,
},
},
{
id: "getSystemNotifs",
entry: join(__dirname, "../lambda/getSystemNotifs.ts"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,13 @@ export const UploadSubsequentDocuments = () => {
return <Navigate to="/dashboard" />;
}

const originalSubmissionEvent = (submission._source.changelog ?? []).reduce<string | null>(
let originalSubmissionEvent = (submission._source.changelog ?? []).reduce<string | null>(
(acc, { _source }) => (_source?.event ? _source?.event : acc),
null,
);
if (originalSubmissionEvent === "NOSO") {
originalSubmissionEvent = submission._source.mockEvent;
}

const schema: SchemaWithEnforcableProps | undefined = formSchemas[originalSubmissionEvent];

Expand Down
4 changes: 3 additions & 1 deletion react-app/src/features/package/admin-changes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,14 @@ export const AdminChange: FC<opensearch.changelog.Document> = (props) => {
}
return ["Disable Formal RAI Response Withdraw", AC_WithdrawDisabled];
}
case "NOSO":
return [props.changeType || "Package Added", AC_LegacyAdminChange];
case "legacy-admin-change":
return [props.changeType || "Manual Update", AC_LegacyAdminChange];
default:
return [BLANK_VALUE, AC_Update];
}
}, [props.actionType, props.changeType]);
}, [props.event, props.changeType, props.raiWithdrawEnabled]);

return (
<AccordionItem key={props.id} value={props.id}>
Expand Down
Loading