diff --git a/lib/lambda/deleteIndex.ts b/lib/lambda/deleteIndex.ts index 67492361a4..0a7238d951 100644 --- a/lib/lambda/deleteIndex.ts +++ b/lib/lambda/deleteIndex.ts @@ -8,19 +8,20 @@ export const handler: Handler = async (event, __, callback) => { }; let errorResponse = null; try { - if (!event.osDomain) throw "process.env.osDomain cannot be undefined"; + const { osDomain, indexNamespace = "" } = event; + if (!osDomain) throw "osDomain cannot be undefined"; const indices: Index[] = [ - `${event.indexNamespace}main`, - `${event.indexNamespace}changelog`, - `${event.indexNamespace}insights`, - `${event.indexNamespace}types`, - `${event.indexNamespace}subtypes`, - `${event.indexNamespace}legacyinsights`, - `${event.indexNamespace}cpocs`, + `${indexNamespace}main`, + `${indexNamespace}changelog`, + `${indexNamespace}insights`, + `${indexNamespace}types`, + `${indexNamespace}subtypes`, + `${indexNamespace}legacyinsights`, + `${indexNamespace}cpocs`, ]; for (const index of indices) { - await os.deleteIndex(event.osDomain, index); + await os.deleteIndex(osDomain, index); } } catch (error: any) { response.statusCode = 500; diff --git a/lib/lambda/getAttachmentUrl.test.ts b/lib/lambda/getAttachmentUrl.test.ts index 0ee3102e18..26a7932d39 100644 --- a/lib/lambda/getAttachmentUrl.test.ts +++ b/lib/lambda/getAttachmentUrl.test.ts @@ -1,57 +1,36 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; +import { describe, it, expect, vi, afterEach } from "vitest"; import { APIGatewayEvent } from "aws-lambda"; import { handler } from "./getAttachmentUrl"; -import { response } from "libs/handler-lib"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; -import { getStateFilter } from "../libs/api/auth/user"; -import { getPackage, getPackageChangelog } from "../libs/api/package"; - -vi.mock("libs/handler-lib", () => ({ - response: vi.fn(), -})); - -vi.mock("@aws-sdk/client-sts", () => ({ - STSClient: vi.fn().mockImplementation(() => ({ - send: vi.fn(), - })), - AssumeRoleCommand: vi.fn(), -})); - -vi.mock("@aws-sdk/client-s3", () => ({ - S3Client: vi.fn().mockImplementation(() => ({ - send: vi.fn(), - })), - GetObjectCommand: vi.fn(), -})); +import { + OPENSEARCH_DOMAIN, + getRequestContext, + NOT_FOUND_ITEM_ID, + HI_TEST_ITEM_ID, + TEST_ITEM_ID, + WITHDRAWN_CHANGELOG_ITEM_ID, + GET_ERROR_ITEM_ID, + ATTACHMENT_BUCKET_NAME, + ATTACHMENT_BUCKET_REGION, +} from "mocks"; vi.mock("@aws-sdk/s3-request-presigner", () => ({ getSignedUrl: vi.fn(), })); -vi.mock("../libs/api/auth/user", () => ({ - getStateFilter: vi.fn(), -})); - -vi.mock("../libs/api/package", () => ({ - getPackage: vi.fn(), - getPackageChangelog: vi.fn(), -})); - describe("Lambda Handler", () => { - beforeEach(() => { + afterEach(() => { vi.clearAllMocks(); - process.env.osDomain = "test-domain"; // Set the environment variable before each test }); it("should return 400 if event body is missing", async () => { const event = {} as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 400, - body: { message: "Event body required" }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(400); + expect(res.body).toEqual(JSON.stringify({ message: "Event body required" })); }); it("should return 500 if osDomain is missing", async () => { @@ -60,155 +39,117 @@ describe("Lambda Handler", () => { const event = { body: JSON.stringify({ id: "test-id", - bucket: "test-bucket", + bucket: ATTACHMENT_BUCKET_NAME, key: "test-key", filename: "test-file", }), } as APIGatewayEvent; - await handler(event); + const res = await handler(event); + + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(500); + expect(res.body).toEqual( + JSON.stringify({ message: "ERROR: process.env.osDomain must be defined" }), + ); - expect(response).toHaveBeenCalledWith({ - statusCode: 500, - body: { message: "ERROR: osDomain env variable is required" }, - }); + process.env.osDomain = OPENSEARCH_DOMAIN; }); it("should return 404 if no package is found", async () => { - vi.mocked(getPackage).mockResolvedValueOnce(null); - const event = { body: JSON.stringify({ - id: "test-id", - bucket: "test-bucket", + id: NOT_FOUND_ITEM_ID, + bucket: ATTACHMENT_BUCKET_NAME, key: "test-key", filename: "test-file", }), + requestContext: getRequestContext(), } as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 404, - body: { message: "No record found for the given id" }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(404); + expect(res.body).toEqual(JSON.stringify({ message: "No record found for the given id" })); }); it("should return 404 if state access is not permitted", async () => { - vi.mocked(getPackage).mockResolvedValueOnce({ - _source: { state: "test-state" }, - }); - vi.mocked(getStateFilter).mockResolvedValueOnce({ - terms: { state: ["other-state"] }, - }); - const event = { body: JSON.stringify({ - id: "test-id", - bucket: "test-bucket", + id: HI_TEST_ITEM_ID, + bucket: ATTACHMENT_BUCKET_NAME, key: "test-key", filename: "test-file", }), + requestContext: getRequestContext(), } as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 404, - body: { message: "state access not permitted for the given id" }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(403); + expect(res.body).toEqual( + JSON.stringify({ message: "state access not permitted for the given id" }), + ); }); it("should return 500 if attachment details are not found", async () => { - vi.mocked(getPackage).mockResolvedValueOnce({ - _source: { state: "test-state" }, - }); - vi.mocked(getStateFilter).mockResolvedValueOnce({ - terms: { state: ["test-state"] }, - }); - vi.mocked(getPackageChangelog).mockResolvedValueOnce({ - hits: { - hits: [ - { - _source: { - attachments: [{ bucket: "other-bucket", key: "other-key" }], - }, - }, - ], - }, - }); - const event = { body: JSON.stringify({ - id: "test-id", - bucket: "test-bucket", + id: TEST_ITEM_ID, + bucket: ATTACHMENT_BUCKET_NAME, key: "test-key", filename: "test-file", }), + requestContext: getRequestContext(), } as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 500, - body: { message: "Attachment details not found for given record id." }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(500); + expect(res.body).toEqual( + JSON.stringify({ message: "Attachment details not found for given record id." }), + ); }); it("should return 200 with the presigned URL if all checks pass", async () => { - vi.mocked(getPackage).mockResolvedValueOnce({ - _source: { state: "test-state" }, - }); - vi.mocked(getStateFilter).mockResolvedValueOnce({ - terms: { state: ["test-state"] }, - }); - vi.mocked(getPackageChangelog).mockResolvedValueOnce({ - hits: { - hits: [ - { - _source: { - attachments: [{ bucket: "test-bucket", key: "test-key" }], - }, - }, - ], - }, - }); - vi.mocked(getSignedUrl).mockResolvedValueOnce("test-presigned-url"); + const mockUrl = `https://${ATTACHMENT_BUCKET_NAME}.s3.${ATTACHMENT_BUCKET_REGION}.amazonaws.com/123e4567-e89b-12d3-a456-426614174000`; + vi.mocked(getSignedUrl).mockResolvedValueOnce(mockUrl); const event = { body: JSON.stringify({ - id: "test-id", - bucket: "test-bucket", - key: "test-key", - filename: "test-file", + id: WITHDRAWN_CHANGELOG_ITEM_ID, + bucket: ATTACHMENT_BUCKET_NAME, + key: "doc001", + filename: "contract_amendment_2024.pdf", }), + requestContext: getRequestContext(), } as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 200, - body: { url: "test-presigned-url" }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(200); + expect(res.body).toEqual(JSON.stringify({ url: mockUrl })); }); it("should handle errors during processing", async () => { - vi.mocked(getPackage).mockRejectedValueOnce(new Error("Test error")); - const event = { body: JSON.stringify({ - id: "test-id", - bucket: "test-bucket", + id: GET_ERROR_ITEM_ID, + bucket: ATTACHMENT_BUCKET_NAME, key: "test-key", filename: "test-file", }), + requestContext: getRequestContext(), } as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 500, - body: { message: "Internal server error" }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(500); + expect(res.body).toEqual(JSON.stringify({ message: "Internal server error" })); }); }); diff --git a/lib/lambda/getAttachmentUrl.ts b/lib/lambda/getAttachmentUrl.ts index 7f94231bd9..e35139e72c 100644 --- a/lib/lambda/getAttachmentUrl.ts +++ b/lib/lambda/getAttachmentUrl.ts @@ -7,33 +7,31 @@ import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; import { getStateFilter } from "../libs/api/auth/user"; import { getPackage, getPackageChangelog } from "../libs/api/package"; +import { getDomain } from "libs/utils"; // Handler function to get Seatool data export const handler = async (event: APIGatewayEvent) => { - if (!process.env.osDomain) { + try { + getDomain(); + } catch (error) { return response({ statusCode: 500, - body: { message: "ERROR: osDomain env variable is required" }, + body: { message: `ERROR: ${error?.message || error}` }, }); } + if (!event.body) { return response({ statusCode: 400, body: { message: "Event body required" }, }); } - if (!process.env.osDomain) { - return response({ - statusCode: 500, - body: { message: "Handler is missing process.env.osDomain env var" }, - }); - } try { const body = JSON.parse(event.body); const mainResult = await getPackage(body.id); - if (!mainResult) { + if (!mainResult || !mainResult.found) { return response({ statusCode: 404, body: { message: "No record found for the given id" }, @@ -48,7 +46,7 @@ export const handler = async (event: APIGatewayEvent) => { if (!stateAccessAllowed) { return response({ - statusCode: 404, + statusCode: 403, body: { message: "state access not permitted for the given id" }, }); } @@ -72,12 +70,7 @@ export const handler = async (event: APIGatewayEvent) => { } // Now we can generate the presigned url - const url = await generatePresignedUrl( - body.bucket, - body.key, - body.filename, - 60, - ); + const url = await generatePresignedUrl(body.bucket, body.key, body.filename, 60); return response({ statusCode: 200, @@ -104,7 +97,7 @@ async function getClient(bucket: string) { const assumedCredentials = assumedRoleResponse.Credentials; if (!assumedCredentials) { - throw new Error("No assumed redentials"); + throw new Error("No assumed credentials"); } // Create S3 client using the assumed role's credentials diff --git a/lib/lambda/getCpocs.test.ts b/lib/lambda/getCpocs.test.ts index 7765ca6b9a..79c86c3491 100644 --- a/lib/lambda/getCpocs.test.ts +++ b/lib/lambda/getCpocs.test.ts @@ -12,10 +12,10 @@ describe("getCpocs Handler", () => { const res = await handler(event); expect(res.statusCode).toEqual(400); - expect(res.body).toEqual(JSON.stringify({ message: "Event body required" })) + expect(res.body).toEqual(JSON.stringify({ message: "Event body required" })); }); - // TODO - should this be removed? when will the result be empty and not + // TODO - should this be removed? when will the result be empty and not // just a result with an empty hit array it("should return 400 if no Cpocs are found", async () => { mockedServiceServer.use(emptyCpocSearchHandler); @@ -25,7 +25,7 @@ describe("getCpocs Handler", () => { const res = await handler(event); expect(res.statusCode).toEqual(400); - expect(res.body).toEqual(JSON.stringify({ message: "No Cpocs found" })) + expect(res.body).toEqual(JSON.stringify({ message: "No Cpocs found" })); }); it("should return 200 with the result if Cpocs are found", async () => { @@ -46,6 +46,6 @@ describe("getCpocs Handler", () => { const res = await handler(event); expect(res.statusCode).toEqual(500); - expect(res.body).toEqual(JSON.stringify({ error: "Internal server error", message: "Response Error" })) + expect(res.body).toEqual(JSON.stringify({ message: "Internal server error" })); }); }); diff --git a/lib/lambda/getCpocs.ts b/lib/lambda/getCpocs.ts index 5efc373159..c98d5a5867 100644 --- a/lib/lambda/getCpocs.ts +++ b/lib/lambda/getCpocs.ts @@ -2,13 +2,12 @@ import { handleOpensearchError } from "./utils"; import { APIGatewayEvent } from "aws-lambda"; import * as os from "libs/opensearch-lib"; import { response } from "libs/handler-lib"; +import { getDomainAndNamespace } from "libs/utils"; // type GetCpocsBody = object; export const queryCpocs = async () => { - if (!process.env.osDomain) { - throw new Error("process.env.osDomain must be defined"); - } + const { index, domain } = getDomainAndNamespace("cpocs"); const query = { size: 1000, @@ -20,11 +19,7 @@ export const queryCpocs = async () => { }, ], }; - return await os.search( - process.env.osDomain, - `${process.env.indexNamespace}cpocs`, - query, - ); + return await os.search(domain, index, query); }; export const getCpocs = async (event: APIGatewayEvent) => { @@ -47,7 +42,7 @@ export const getCpocs = async (event: APIGatewayEvent) => { body: result, }); } catch (err) { - return response(handleOpensearchError(err)) + return response(handleOpensearchError(err)); } }; diff --git a/lib/lambda/getPackageActions.test.ts b/lib/lambda/getPackageActions.test.ts index 7edb00baa9..3d77dbdfce 100644 --- a/lib/lambda/getPackageActions.test.ts +++ b/lib/lambda/getPackageActions.test.ts @@ -70,8 +70,6 @@ describe("getPackageActions Handler", () => { expect(res).toBeTruthy(); expect(res.statusCode).toEqual(500); - expect(res.body).toEqual( - JSON.stringify({ error: "Internal server error", message: "Response Error" }), - ); + expect(res.body).toEqual(JSON.stringify({ message: "Internal server error" })); }); }); diff --git a/lib/lambda/getSubTypes.test.ts b/lib/lambda/getSubTypes.test.ts index 1f8dd3fc65..7c34e22793 100644 --- a/lib/lambda/getSubTypes.test.ts +++ b/lib/lambda/getSubTypes.test.ts @@ -1,18 +1,18 @@ import { describe, it, expect } from "vitest"; import { APIGatewayEvent } from "aws-lambda"; import { handler } from "./getSubTypes"; -import { - MEDICAID_SPA_AUTHORITY_ID, +import { + MEDICAID_SPA_AUTHORITY_ID, CHIP_SPA_AUTHORITY_ID, NOT_FOUND_AUTHORITY_ID, - TYPE_ONE_ID, - TYPE_TWO_ID, + TYPE_ONE_ID, + TYPE_TWO_ID, TYPE_THREE_ID, DO_NOT_USE_TYPE_ID, ERROR_AUTHORITY_ID, medicaidSubtypes, - chipSubtypes -} from "mocks/data/types" + chipSubtypes, +} from "mocks/data/types"; import { TestSubtypeItemResult } from "mocks"; describe("getSubTypes Handler", () => { @@ -22,10 +22,10 @@ describe("getSubTypes Handler", () => { const res = await handler(event); expect(res.statusCode).toEqual(400); - expect(res.body).toEqual(JSON.stringify({ message: "Event body required" })) + expect(res.body).toEqual(JSON.stringify({ message: "Event body required" })); }); - // TODO - should this be removed? when will the result be empty and not + // TODO - should this be removed? when will the result be empty and not // just a result with an empty hit array it.skip("should return 400 if no subtypes are found", async () => { const event = { @@ -38,7 +38,9 @@ describe("getSubTypes Handler", () => { const res = await handler(event); expect(res.statusCode).toEqual(400); - expect(res.body).toEqual(JSON.stringify({ message: "No record found for the given authority" })); + expect(res.body).toEqual( + JSON.stringify({ message: "No record found for the given authority" }), + ); }); it("should return 200 with the result if subtypes are found", async () => { @@ -53,15 +55,15 @@ describe("getSubTypes Handler", () => { const body = JSON.parse(res.body); expect(res.statusCode).toEqual(200); - expect(body.hits.hits).toEqual(medicaidSubtypes) + expect(body.hits.hits).toEqual(medicaidSubtypes); }); it("should filter out types with names that include Do Not Use", async () => { const event = { - body: JSON.stringify({ + body: JSON.stringify({ authorityId: CHIP_SPA_AUTHORITY_ID, - typeIds: [TYPE_THREE_ID, DO_NOT_USE_TYPE_ID ] - }) + typeIds: [TYPE_THREE_ID, DO_NOT_USE_TYPE_ID], + }), } as APIGatewayEvent; const res = await handler(event); @@ -70,10 +72,10 @@ describe("getSubTypes Handler", () => { expect(res.statusCode).toEqual(200); expect(body.hits.hits).toEqual(chipSubtypes); body.hits.hits.forEach((type: TestSubtypeItemResult) => { - expect(type?._source?.name).toBeTruthy() - expect(type?._source?.name?.match(/Do Not Use/)).toBeFalsy() - }) - }) + expect(type?._source?.name).toBeTruthy(); + expect(type?._source?.name?.match(/Do Not Use/)).toBeFalsy(); + }); + }); it("should return 500 if an error occurs during processing", async () => { const event = { @@ -86,6 +88,6 @@ describe("getSubTypes Handler", () => { const res = await handler(event); expect(res.statusCode).toEqual(500); - expect(res.body).toEqual(JSON.stringify({ error: "Internal server error", message: "Response Error" })) + expect(res.body).toEqual(JSON.stringify({ message: "Internal server error" })); }); }); diff --git a/lib/lambda/getSubTypes.ts b/lib/lambda/getSubTypes.ts index 3f1d44f56e..95ae6ff9f3 100644 --- a/lib/lambda/getSubTypes.ts +++ b/lib/lambda/getSubTypes.ts @@ -1,7 +1,8 @@ -import { handleOpensearchError } from "./utils"; +import { handleOpensearchError } from "./utils"; import { APIGatewayEvent } from "aws-lambda"; import { response } from "libs/handler-lib"; import * as os from "libs/opensearch-lib"; +import { getDomainAndNamespace } from "libs/utils"; type GetSubTypesBody = { authorityId: string; @@ -9,9 +10,7 @@ type GetSubTypesBody = { }; export const querySubTypes = async (authorityId: string, typeIds: string[]) => { - if (!process.env.osDomain) { - throw new Error("process.env.osDomain must be defined"); - } + const { index, domain } = getDomainAndNamespace("subtypes"); const query = { size: 200, @@ -49,7 +48,7 @@ export const querySubTypes = async (authorityId: string, typeIds: string[]) => { ], }; - return await os.search(process.env.osDomain, `${process.env.indexNamespace}subtypes`, query); + return await os.search(domain, index, query); }; export const getSubTypes = async (event: APIGatewayEvent) => { @@ -74,7 +73,7 @@ export const getSubTypes = async (event: APIGatewayEvent) => { body: result, }); } catch (err) { - return response(handleOpensearchError(err)) + return response(handleOpensearchError(err)); } }; diff --git a/lib/lambda/getTypes.test.ts b/lib/lambda/getTypes.test.ts index a6036c8795..4cfce9211f 100644 --- a/lib/lambda/getTypes.test.ts +++ b/lib/lambda/getTypes.test.ts @@ -1,14 +1,14 @@ import { describe, it, expect } from "vitest"; import { APIGatewayEvent } from "aws-lambda"; import { handler } from "./getTypes"; -import { - CHIP_SPA_AUTHORITY_ID, - MEDICAID_SPA_AUTHORITY_ID, - NOT_FOUND_AUTHORITY_ID, +import { + CHIP_SPA_AUTHORITY_ID, + MEDICAID_SPA_AUTHORITY_ID, + NOT_FOUND_AUTHORITY_ID, ERROR_AUTHORITY_ID, medicaidTypes, - chipTypes -} from "mocks/data/types" + chipTypes, +} from "mocks/data/types"; import { TestTypeItemResult } from "mocks"; describe("getTypes Handler", () => { @@ -21,7 +21,7 @@ describe("getTypes Handler", () => { expect(res.body).toEqual(JSON.stringify({ message: "Event body required" })); }); - // TODO - should this be removed? when will the result be empty and not + // TODO - should this be removed? when will the result be empty and not // just a result with an empty hit array it.skip("should return 400 if no types are found", async () => { const event = { @@ -31,7 +31,9 @@ describe("getTypes Handler", () => { const res = await handler(event); expect(res.statusCode).toEqual(400); - expect(res.body).toEqual(JSON.stringify({ message: "No record found for the given authority" })); + expect(res.body).toEqual( + JSON.stringify({ message: "No record found for the given authority" }), + ); }); it("should return 200 with the result if types are found", async () => { @@ -43,7 +45,7 @@ describe("getTypes Handler", () => { const body = JSON.parse(res.body); expect(res.statusCode).toEqual(200); - expect(body.hits.hits).toEqual(medicaidTypes) + expect(body.hits.hits).toEqual(medicaidTypes); }); it("should filter out types with names that include Do Not Use", async () => { @@ -55,11 +57,11 @@ describe("getTypes Handler", () => { const body = JSON.parse(res.body); expect(res.statusCode).toEqual(200); - expect(body.hits.hits).toEqual(chipTypes) + expect(body.hits.hits).toEqual(chipTypes); body.hits.hits.forEach((type: TestTypeItemResult) => { - expect(type?._source?.name).toBeTruthy() - expect(type?._source?.name?.match(/Do Not Use/)).toBeFalsy() - }) + expect(type?._source?.name).toBeTruthy(); + expect(type?._source?.name?.match(/Do Not Use/)).toBeFalsy(); + }); }); it("should return 500 if an error occurs during processing", async () => { @@ -70,6 +72,6 @@ describe("getTypes Handler", () => { const res = await handler(event); expect(res.statusCode).toEqual(500); - expect(res.body).toEqual(JSON.stringify({ error: "Internal server error", message: "Response Error" })); + expect(res.body).toEqual(JSON.stringify({ message: "Internal server error" })); }); }); diff --git a/lib/lambda/getTypes.ts b/lib/lambda/getTypes.ts index 21eb17d7ab..ff3f5c0e2f 100644 --- a/lib/lambda/getTypes.ts +++ b/lib/lambda/getTypes.ts @@ -2,15 +2,14 @@ import { handleOpensearchError } from "./utils"; import { APIGatewayEvent } from "aws-lambda"; import * as os from "libs/opensearch-lib"; import { response } from "libs/handler-lib"; +import { getDomainAndNamespace } from "libs/utils"; type GetTypesBody = { authorityId: string; }; export const queryTypes = async (authorityId: string) => { - if (!process.env.osDomain) { - throw new Error("process.env.osDomain must be defined"); - } + const { index, domain } = getDomainAndNamespace("types"); const query = { size: 200, @@ -42,11 +41,7 @@ export const queryTypes = async (authorityId: string) => { }, ], }; - return await os.search( - process.env.osDomain, - `${process.env.indexNamespace}types`, - query, - ); + return await os.search(domain, index, query); }; export const getTypes = async (event: APIGatewayEvent) => { diff --git a/lib/lambda/getUploadUrl.test.ts b/lib/lambda/getUploadUrl.test.ts index 4c12a045fe..125dfe62fb 100644 --- a/lib/lambda/getUploadUrl.test.ts +++ b/lib/lambda/getUploadUrl.test.ts @@ -1,18 +1,9 @@ -import { describe, it, expect, vi, beforeEach, Mock } from "vitest"; +import { describe, it, expect, vi, beforeEach, afterEach, Mock } from "vitest"; import { APIGatewayEvent } from "aws-lambda"; import { handler } from "./getUploadUrl"; -import { response } from "libs/handler-lib"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; import { v4 as uuidv4 } from "uuid"; - -vi.mock("libs/handler-lib", () => ({ - response: vi.fn(), -})); - -vi.mock("@aws-sdk/client-s3", () => ({ - S3Client: vi.fn().mockImplementation(() => ({})), - PutObjectCommand: vi.fn(), -})); +import { ATTACHMENT_BUCKET_NAME, ATTACHMENT_BUCKET_REGION } from "mocks"; vi.mock("@aws-sdk/s3-request-presigner", () => ({ getSignedUrl: vi.fn(), @@ -23,43 +14,41 @@ vi.mock("uuid", () => ({ })); describe("Handler for generating signed URL", () => { + const TEST_UUID = "123e4567-e89b-12d3-a456-426614174000"; + beforeEach(() => { + (uuidv4 as Mock).mockReturnValue(TEST_UUID); + }); + + afterEach(() => { vi.clearAllMocks(); - process.env.attachmentsBucketName = "test-bucket"; - process.env.attachmentsBucketRegion = "test-region"; - (uuidv4 as Mock).mockReturnValue("123e4567-e89b-12d3-a456-426614174000"); }); it("should return 400 if event body is missing", async () => { const event = {} as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 400, - body: { message: "Event body required" }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(400); + expect(res.body).toEqual(JSON.stringify({ message: "Event body required" })); }); it("should return 200 with signed URL, bucket, and key", async () => { - const mockUrl = "https://example.com/signed-url"; + const mockUrl = `https://${ATTACHMENT_BUCKET_NAME}.s3.${ATTACHMENT_BUCKET_REGION}.amazonaws.com/${TEST_UUID}`; (getSignedUrl as Mock).mockResolvedValueOnce(mockUrl); const event = { body: JSON.stringify({ fileName: "test-file.pdf" }), } as APIGatewayEvent; - await handler(event); - - expect(response).toHaveBeenCalledWith({ - statusCode: 200, - body: { - url: mockUrl, - bucket: "test-bucket", - key: "123e4567-e89b-12d3-a456-426614174000.pdf", - }, - }); - expect(getSignedUrl).toHaveBeenCalled(); + const res = await handler(event); + + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(200); + expect(res.body).toEqual( + JSON.stringify({ url: mockUrl, bucket: ATTACHMENT_BUCKET_NAME, key: `${TEST_UUID}.pdf` }), + ); }); it("should return 500 if an error occurs during processing", async () => { @@ -69,22 +58,22 @@ describe("Handler for generating signed URL", () => { body: JSON.stringify({ fileName: "test-file.pdf" }), } as APIGatewayEvent; - await handler(event); + const res = await handler(event); - expect(response).toHaveBeenCalledWith({ - statusCode: 500, - body: { message: "Internal server error" }, - }); + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(500); + expect(res.body).toEqual(JSON.stringify({ message: "Internal server error" })); }); it("should throw an error if required environment variables are missing", async () => { delete process.env.attachmentsBucketName; - await handler({} as APIGatewayEvent); + const res = await handler({} as APIGatewayEvent); + + expect(res).toBeTruthy(); + expect(res.statusCode).toEqual(500); + expect(res.body).toEqual(JSON.stringify({ message: "Internal server error" })); - expect(response).toHaveBeenCalledWith({ - statusCode: 500, - body: { message: "Internal server error" }, - }); + process.env.attachmentsBucketName = ATTACHMENT_BUCKET_NAME; }); }); diff --git a/lib/lambda/item.test.ts b/lib/lambda/item.test.ts index a874595faf..b28cba08ad 100644 --- a/lib/lambda/item.test.ts +++ b/lib/lambda/item.test.ts @@ -73,8 +73,6 @@ describe("getItemData Handler", () => { expect(res).toBeTruthy(); expect(res.statusCode).toEqual(500); - expect(res.body).toEqual( - JSON.stringify({ error: "Internal server error", message: "Response Error" }), - ); + expect(res.body).toEqual(JSON.stringify({ message: "Internal server error" })); }); }); diff --git a/lib/lambda/itemExists.test.ts b/lib/lambda/itemExists.test.ts index fba774d836..e1613b2980 100644 --- a/lib/lambda/itemExists.test.ts +++ b/lib/lambda/itemExists.test.ts @@ -55,7 +55,7 @@ describe("Handler for checking if record exists", () => { ); }); - it("should return 500 error occurs during processing", async () => { + it("should return 200 and exists: false if an error occurs during processing", async () => { const event = { body: JSON.stringify({ id: GET_ERROR_ITEM_ID }), } as APIGatewayEvent; @@ -63,9 +63,9 @@ describe("Handler for checking if record exists", () => { const res = await handler(event); expect(res).toBeTruthy(); - expect(res.statusCode).toEqual(500); + expect(res.statusCode).toEqual(200); expect(res.body).toEqual( - JSON.stringify({ error: "Internal server error", message: "Response Error" }), + JSON.stringify({ message: "No record found for the given id", exists: false }), ); }); }); diff --git a/lib/lambda/processEmails.test.ts b/lib/lambda/processEmails.test.ts index cb608b48ab..e78f67e834 100644 --- a/lib/lambda/processEmails.test.ts +++ b/lib/lambda/processEmails.test.ts @@ -2,7 +2,7 @@ import { describe, it, expect, vi } from "vitest"; import { Context } from "aws-lambda"; import { SESClient } from "@aws-sdk/client-ses"; import { sendEmail, validateEmailTemplate, handler } from "./processEmails"; -import { KafkaRecord, KafkaEvent } from "node_modules/shared-types"; +import { KafkaRecord, KafkaEvent } from "shared-types"; describe("process emails Handler", () => { it("should return 200 with a proper email", async () => { diff --git a/lib/lambda/processEmails.ts b/lib/lambda/processEmails.ts index e6ee16d07d..efe4f96fde 100644 --- a/lib/lambda/processEmails.ts +++ b/lib/lambda/processEmails.ts @@ -3,7 +3,7 @@ import { EmailAddresses, KafkaEvent, KafkaRecord } from "shared-types"; import { decodeBase64WithUtf8, getSecret } from "shared-utils"; import { Handler } from "aws-lambda"; import { getEmailTemplates, getAllStateUsers } from "libs/email"; -import * as os from "./../libs/opensearch-lib"; +import * as os from "libs/opensearch-lib"; import { EMAIL_CONFIG, getCpocEmail, getSrtEmails } from "libs/email/content/email-components"; import { htmlToText, HtmlToTextOptions } from "html-to-text"; import pLimit from "p-limit"; diff --git a/lib/lambda/search.test.ts b/lib/lambda/search.test.ts index 2d51b35d21..124064852e 100644 --- a/lib/lambda/search.test.ts +++ b/lib/lambda/search.test.ts @@ -29,7 +29,7 @@ describe("getSearchData Handler", () => { const body = JSON.parse(res.body); expect(body).toBeTruthy(); expect(body?.hits?.hits).toBeTruthy(); - expect(body?.hits?.hits?.length).toEqual(12); + expect(body?.hits?.hits?.length).toEqual(13); }); it("should handle errors during processing", async () => { @@ -43,8 +43,6 @@ describe("getSearchData Handler", () => { expect(res).toBeTruthy(); expect(res.statusCode).toEqual(500); - expect(res.body).toEqual( - JSON.stringify({ error: "Internal server error", message: "Response Error" }), - ); + expect(res.body).toEqual(JSON.stringify({ message: "Internal server error" })); }); }); diff --git a/lib/lambda/search.ts b/lib/lambda/search.ts index f823579122..6e4a4aa8d1 100644 --- a/lib/lambda/search.ts +++ b/lib/lambda/search.ts @@ -1,21 +1,26 @@ import { handleOpensearchError } from "./utils"; import { APIGatewayEvent } from "aws-lambda"; import { response } from "libs/handler-lib"; -import { Index } from "shared-types/opensearch"; +import { BaseIndex } from "shared-types/opensearch"; import { validateEnvVariable } from "shared-utils"; import { getStateFilter } from "../libs/api/auth/user"; import { getAppkChildren } from "../libs/api/package"; import * as os from "../libs/opensearch-lib"; +import { getDomainAndNamespace } from "libs/utils"; // Handler function to search index export const getSearchData = async (event: APIGatewayEvent) => { validateEnvVariable("osDomain"); + if (!event.pathParameters || !event.pathParameters.index) { return response({ statusCode: 400, body: { message: "Index path parameter required" }, }); } + + const { domain, index } = getDomainAndNamespace(event.pathParameters.index as BaseIndex); + try { let query: any = {}; if (event.body) { @@ -42,11 +47,7 @@ export const getSearchData = async (event: APIGatewayEvent) => { query.from = query.from || 0; query.size = query.size || 100; - const results = await os.search( - process.env.osDomain as string, - `${process.env.indexNamespace}${event.pathParameters.index}` as Index, - query, - ); + const results = await os.search(domain, index, query); for (let i = 0; i < results?.hits?.hits?.length; i++) { if (results.hits.hits[i]._source?.appkParent) { diff --git a/lib/lambda/sinkChangelog.ts b/lib/lambda/sinkChangelog.ts index d2fe78a00c..038c003afd 100644 --- a/lib/lambda/sinkChangelog.ts +++ b/lib/lambda/sinkChangelog.ts @@ -1,13 +1,13 @@ import { Handler } from "aws-lambda"; import { decodeBase64WithUtf8 } from "shared-utils"; import { KafkaEvent, KafkaRecord, opensearch } from "shared-types"; -import { ErrorType, bulkUpdateDataWrapper, getTopic, logError } from "../libs/sink-lib"; +import { ErrorType, bulkUpdateDataWrapper, getTopic, logError } from "libs/sink-lib"; import { transformUpdateValuesSchema, transformDeleteSchema, transformedUpdateIdSchema, } from "./update/adminChangeSchemas"; -import { getPackageChangelog } from "lib/libs/api/package"; +import { getPackageChangelog } from "libs/api/package"; // One notable difference between this handler and sinkMain's... // The order in which records are processed for the changelog doesn't matter. diff --git a/lib/lambda/sinkMain.test.ts b/lib/lambda/sinkMain.test.ts index 1efa64a139..54516cd6ff 100644 --- a/lib/lambda/sinkMain.test.ts +++ b/lib/lambda/sinkMain.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, vi, afterEach } from "vitest"; import { handler } from "./sinkMain"; import * as sinkMainProcessors from "./sinkMainProcessors"; -import { KafkaEvent } from "lib/packages/shared-types"; +import { KafkaEvent } from "shared-types"; const createKafkaEvent = (records: KafkaEvent["records"]) => ({ eventSource: "SelfManagedKafka", diff --git a/lib/lambda/sinkMainProcessors.test.ts b/lib/lambda/sinkMainProcessors.test.ts index 4e4f31f5ee..c40744308a 100644 --- a/lib/lambda/sinkMainProcessors.test.ts +++ b/lib/lambda/sinkMainProcessors.test.ts @@ -7,7 +7,7 @@ import { import * as sinkLib from "libs"; import { Document, seatool } from "shared-types/opensearch/main"; import { offsetToUtc } from "shared-utils"; -import { KafkaRecord } from "lib/packages/shared-types"; +import { KafkaRecord } from "shared-types"; const convertObjToBase64 = (obj: object) => Buffer.from(JSON.stringify(obj)).toString("base64"); diff --git a/lib/lambda/submit/indexUnauthorized.test.ts b/lib/lambda/submit/indexUnauthorized.test.ts new file mode 100644 index 0000000000..bb91124b5f --- /dev/null +++ b/lib/lambda/submit/indexUnauthorized.test.ts @@ -0,0 +1,107 @@ +import { submit } from "./index"; +import { APIGatewayEvent } from "node_modules/shared-types"; +import { describe, it, expect, vi, afterEach, beforeEach } from "vitest"; +import { getRequestContext } from "mocks"; +import { automatedStateSubmitterUsername } from "mocks/data/users/stateSubmitters"; +import { + capitatedAmendmentBase, + appkBase, + capitatedInitial, + capitatedRenewal, + contractingAmmendment, + contractingInitial, + contractingRenewal, + newChipSubmission, + newMedicaidSubmission, + respondToRai, + temporaryExtension, + toggleWithdrawRai, + withdrawPackage, + withdrawRai, +} from "mocks/data/submit/base"; + +vi.mock("kafkajs", () => { + const producer = { + connect: vi.fn(), + send: vi.fn(), + disconnect: vi.fn(), + }; + const kafka = { + producer: () => producer, + }; + return { + Kafka: vi.fn(() => kafka), + Producer: vi.fn(() => producer), + }; +}); +describe("submit Lambda function", () => { + let brokerString: string | undefined; + beforeEach(() => { + brokerString = process.env.brokerString; + process.env.brokerString = "broker1,broker2"; + }); + + afterEach(() => { + process.env.brokerString = brokerString; + }); + it.each([ + [ + "should not have authorization to create a capitated ammendment event", + JSON.stringify(capitatedAmendmentBase), + ], + ["should not have authorization to create a appk event", JSON.stringify(appkBase)], + [ + "should not have authorization to create a capitated initial event", + JSON.stringify(capitatedInitial), + ], + [ + "should not have authorization to create a capitated renewal event", + JSON.stringify(capitatedRenewal), + ], + [ + "should not have authorization to create a contracting ammendment event", + JSON.stringify(contractingAmmendment), + ], + [ + "should not have authorization to create a contracting renewal event", + JSON.stringify(contractingRenewal), + ], + [ + "should not have authorization to create a contracting initial event", + JSON.stringify(contractingInitial), + ], + [ + "should not have authorization to create a new medicaid submission event", + JSON.stringify(newMedicaidSubmission), + ], + [ + "should not have authorization to create a new chip submission event", + JSON.stringify(newChipSubmission), + ], + [ + "should not have authorization to create a respond to rai event", + JSON.stringify(respondToRai), + ], + [ + "should not have authorization to create a temporary extension event", + JSON.stringify(temporaryExtension), + ], + [ + "should not have authorization to create a toggle withdraw rai event", + JSON.stringify(toggleWithdrawRai), + ], + [ + "should not have authorization to create a withdraw package event", + JSON.stringify(withdrawPackage), + ], + ["should not have authorization to create a withdraw rai event", JSON.stringify(withdrawRai)], + ])("%s", async (_, base) => { + const event = { + body: base, + requestContext: getRequestContext(automatedStateSubmitterUsername), + } as APIGatewayEvent; + const result = await submit(event); + expect(result.statusCode).toEqual(500); + expect(result.body).toEqual('{"message":"Internal server error"}'); + }); +}); diff --git a/lib/lambda/update/getPackageType.ts b/lib/lambda/update/getPackageType.ts index 7383efae94..f1eca6e296 100644 --- a/lib/lambda/update/getPackageType.ts +++ b/lib/lambda/update/getPackageType.ts @@ -1,6 +1,6 @@ -import { response } from "lib/libs/handler-lib"; -import { events } from "lib/packages/shared-types"; -import { getPackageChangelog } from "lib/libs/api/package"; +import { response } from "libs/handler-lib"; +import { events } from "shared-types"; +import { getPackageChangelog } from "libs/api/package"; export const getPackageType = async (packageId: string) => { // use event of current package to determine how ID should be formatted diff --git a/lib/lambda/update/updatePackage.ts b/lib/lambda/update/updatePackage.ts index 24592a66a6..dd9c4109f5 100644 --- a/lib/lambda/update/updatePackage.ts +++ b/lib/lambda/update/updatePackage.ts @@ -4,7 +4,7 @@ import { getPackage } from "libs/api/package"; import { produceMessage } from "libs/api/kafka"; import { ItemResult } from "shared-types/opensearch/main"; import { getPackageType } from "./getPackageType"; -import { events } from "lib/packages/shared-types"; +import { events } from "shared-types"; import { z } from "zod"; const sendDeleteMessage = async (packageId: string) => { diff --git a/lib/lambda/utils.ts b/lib/lambda/utils.ts index 683a4fa0a0..d6302887a8 100644 --- a/lib/lambda/utils.ts +++ b/lib/lambda/utils.ts @@ -3,10 +3,9 @@ import { errors as OpensearchErrors } from "@opensearch-project/opensearch"; export type ErrorResponse = { statusCode: number; body: { - error?: string; message?: string; - } -} + }; +}; export const handleOpensearchError = (error: unknown): ErrorResponse => { console.error({ error }); @@ -14,9 +13,8 @@ export const handleOpensearchError = (error: unknown): ErrorResponse => { return { statusCode: error.statusCode || error.meta?.statusCode || 500, body: { - error: error.body || error.meta?.body || error, - message: error.message, - } + message: error.body || error.meta?.body, + }, }; } @@ -24,4 +22,4 @@ export const handleOpensearchError = (error: unknown): ErrorResponse => { statusCode: 500, body: { message: "Internal server error" }, }; -} +}; diff --git a/lib/libs/api/package/appk.test.ts b/lib/libs/api/package/appk.test.ts index 8beb6c179a..64717451b0 100644 --- a/lib/libs/api/package/appk.test.ts +++ b/lib/libs/api/package/appk.test.ts @@ -1,87 +1,75 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; -import * as os from "../../opensearch-lib"; +import { describe, it, expect, vi, afterEach } from "vitest"; import { getAppkChildren } from "./appk"; -import { opensearch } from "shared-types"; - -vi.mock("../../opensearch-lib"); +import { + OPENSEARCH_DOMAIN, + INITIAL_RELEASE_APPK_ITEM_ID, + EXISTING_ITEM_APPROVED_APPK_ITEM_ID, +} from "mocks"; describe("getAppkChildren", () => { - const mockOsDomain = "mock-os-domain"; - const mockIndexNamespace = "mock-index-namespace"; - const mockPackageId = "mock-package-id"; - const mockFilter = [{ term: { status: "active" } }]; - const mockResponse = { - hits: { - hits: [ - { - _source: { - timestamp: "2024-01-01T00:00:00Z", - change: "Initial release", - }, - }, - ], - }, - } as unknown as opensearch.main.Response; - - beforeEach(() => { - vi.resetModules(); - process.env.osDomain = mockOsDomain; - process.env.indexNamespace = mockIndexNamespace; - }); - afterEach(() => { vi.clearAllMocks(); }); it("should throw an error if osDomain is not defined", async () => { delete process.env.osDomain; - await expect(getAppkChildren(mockPackageId)).rejects.toThrow( + await expect(getAppkChildren(INITIAL_RELEASE_APPK_ITEM_ID)).rejects.toThrow( "process.env.osDomain must be defined", ); + process.env.osDomain = OPENSEARCH_DOMAIN; }); it("should return the children with the specified packageId and no additional filters", async () => { - vi.mocked(os.search).mockResolvedValue(mockResponse); + const result = await getAppkChildren(INITIAL_RELEASE_APPK_ITEM_ID); - const result = await getAppkChildren(mockPackageId); - - expect(os.search).toHaveBeenCalledWith( - mockOsDomain, - `${mockIndexNamespace}main`, - { - from: 0, - size: 200, - query: { - bool: { - must: [{ term: { "appkParentId.keyword": mockPackageId } }], + expect(result).toEqual( + expect.objectContaining({ + hits: { + total: { + value: 1, + relation: "eq", }, + max_score: null, + hits: [ + { + _source: { + changedDate: "2024-01-01T00:00:00Z", + title: "Initial release", + cmsStatus: "Pending", + stateStatus: "Under Review", + }, + }, + ], }, - }, + }), ); - expect(result).toEqual(mockResponse); }); it("should return the children with the specified packageId and additional filters", async () => { - vi.mocked(os.search).mockResolvedValue(mockResponse); - - const result = await getAppkChildren(mockPackageId, mockFilter); + const result = await getAppkChildren(EXISTING_ITEM_APPROVED_APPK_ITEM_ID, [ + { term: { cmsStatus: "Approved" } }, + ]); - expect(os.search).toHaveBeenCalledWith( - mockOsDomain, - `${mockIndexNamespace}main`, - { - from: 0, - size: 200, - query: { - bool: { - must: [ - { term: { "appkParentId.keyword": mockPackageId } }, - ...mockFilter, - ], + expect(result).toEqual( + expect.objectContaining({ + hits: { + total: { + value: 1, + relation: "eq", }, + max_score: null, + hits: [ + { + _source: { + changedDate: "2025-01-08T00:00:00Z", + title: "Approved release", + cmsStatus: "Approved", + stateStatus: "Approved", + }, + }, + ], }, - }, + }), ); - expect(result).toEqual(mockResponse); }); }); diff --git a/lib/libs/api/package/appk.ts b/lib/libs/api/package/appk.ts index 72b417935c..cb49354e39 100644 --- a/lib/libs/api/package/appk.ts +++ b/lib/libs/api/package/appk.ts @@ -1,29 +1,19 @@ import * as os from "../../opensearch-lib"; import { opensearch } from "shared-types"; +import { getDomainAndNamespace } from "../../utils"; -export const getAppkChildren = async ( - packageId: string, - filter: any[] = [], -) => { - if (!process.env.osDomain) { - throw new Error("process.env.osDomain must be defined"); - } +export const getAppkChildren = async (packageId: string, filter: any[] = []) => { + const { domain, index } = getDomainAndNamespace("main"); - const response = (await os.search( - process.env.osDomain, - `${process.env.indexNamespace}main`, - { - from: 0, - size: 200, - query: { - bool: { - must: [{ term: { "appkParentId.keyword": packageId } }].concat( - filter, - ), - }, + const response = (await os.search(domain, index, { + from: 0, + size: 200, + query: { + bool: { + must: [{ term: { "appkParentId.keyword": packageId } }].concat(filter), }, }, - )) as opensearch.main.Response; + })) as opensearch.main.Response; return response; }; diff --git a/lib/libs/api/package/changelog.ts b/lib/libs/api/package/changelog.ts index 83682b5856..8f1f81c2c4 100644 --- a/lib/libs/api/package/changelog.ts +++ b/lib/libs/api/package/changelog.ts @@ -1,26 +1,18 @@ import * as os from "libs/opensearch-lib"; import { opensearch } from "shared-types"; +import { getDomainAndNamespace } from "../../utils"; -export const getPackageChangelog = async ( - packageId: string, - filter: any[] = [], -) => { - if (!process.env.osDomain) { - throw new Error("process.env.osDomain must be defined"); - } +export const getPackageChangelog = async (packageId: string, filter: any[] = []) => { + const { domain, index } = getDomainAndNamespace("changelog"); - return (await os.search( - process.env.osDomain, - `${process.env.indexNamespace}changelog`, - { - from: 0, - size: 200, - sort: [{ timestamp: "desc" }], - query: { - bool: { - must: [{ term: { "packageId.keyword": packageId } }].concat(filter), - }, + return (await os.search(domain, index, { + from: 0, + size: 200, + sort: [{ timestamp: "desc" }], + query: { + bool: { + must: [{ term: { "packageId.keyword": packageId } }].concat(filter), }, }, - )) as opensearch.changelog.Response; + })) as opensearch.changelog.Response; }; diff --git a/lib/libs/api/package/getPackage.ts b/lib/libs/api/package/getPackage.ts index cf66117315..5ca6d28c20 100644 --- a/lib/libs/api/package/getPackage.ts +++ b/lib/libs/api/package/getPackage.ts @@ -1,18 +1,14 @@ -import { ItemResult } from "lib/packages/shared-types/opensearch/main"; +import { ItemResult } from "shared-types/opensearch/main"; import * as os from "libs/opensearch-lib"; +import { getDomainAndNamespace } from "../../utils"; export interface ExtendedItemResult extends ItemResult { appkChildren?: any[]; } export const getPackage = async (id: string): Promise => { - if (!process.env.osDomain) { - throw new Error("process.env.osDomain must be defined"); - } - const packageResult = await os.getItem( - process.env.osDomain, - `${process.env.indexNamespace}main`, - id, - ); + const { domain, index } = getDomainAndNamespace("main"); + + const packageResult = await os.getItem(domain, index, id); return packageResult; }; diff --git a/lib/libs/api/package/itemExists.ts b/lib/libs/api/package/itemExists.ts index 694315baa0..c5172f161b 100644 --- a/lib/libs/api/package/itemExists.ts +++ b/lib/libs/api/package/itemExists.ts @@ -1,14 +1,22 @@ import * as os from "../../../libs/opensearch-lib"; +import { getDomain, getNamespace } from "libs/utils"; +import { BaseIndex } from "lib/packages/shared-types/opensearch"; export async function itemExists(params: { id: string; osDomain?: string; indexNamespace?: string; }): Promise { - const packageResult = await os.getItem( - params.osDomain || process.env.osDomain!, - `${params.indexNamespace || process.env.indexNamespace!}main`, - params.id, - ); - return !!packageResult?._source; + try { + const domain = params.osDomain || getDomain(); + const index: `${string}${BaseIndex}` = params.indexNamespace + ? `${params.indexNamespace}main` + : getNamespace("main"); + + const packageResult = await os.getItem(domain, index, params.id); + return !!packageResult?._source; + } catch (error) { + console.error(error); + return false; + } } diff --git a/lib/libs/email/content/withdrawPackage/emailTemplates/ChipSpaCMS.tsx b/lib/libs/email/content/withdrawPackage/emailTemplates/ChipSpaCMS.tsx index af88ecdedd..755e587165 100644 --- a/lib/libs/email/content/withdrawPackage/emailTemplates/ChipSpaCMS.tsx +++ b/lib/libs/email/content/withdrawPackage/emailTemplates/ChipSpaCMS.tsx @@ -1,4 +1,4 @@ -import { CommonEmailVariables, Events } from "lib/packages/shared-types"; +import { CommonEmailVariables, Events } from "shared-types"; import { BasicFooter, Divider, PackageDetails } from "../../email-components"; import { BaseEmailTemplate } from "../../email-templates"; diff --git a/lib/libs/email/content/withdrawPackage/emailTemplates/ChipSpaState.tsx b/lib/libs/email/content/withdrawPackage/emailTemplates/ChipSpaState.tsx index e7e3ed7110..a60d8fb927 100644 --- a/lib/libs/email/content/withdrawPackage/emailTemplates/ChipSpaState.tsx +++ b/lib/libs/email/content/withdrawPackage/emailTemplates/ChipSpaState.tsx @@ -1,4 +1,4 @@ -import { CommonEmailVariables, Events } from "lib/packages/shared-types"; +import { CommonEmailVariables, Events } from "shared-types"; import { BasicFooter, FollowUpNotice, Divider, PackageDetails } from "../../email-components"; import { BaseEmailTemplate } from "../../email-templates"; diff --git a/lib/libs/opensearch-lib.ts b/lib/libs/opensearch-lib.ts index 25ef6020c6..521b17d969 100644 --- a/lib/libs/opensearch-lib.ts +++ b/lib/libs/opensearch-lib.ts @@ -4,9 +4,9 @@ import { Client, Connection, errors as OpensearchErrors } from "@opensearch-proj import * as aws4 from "aws4"; import { aws4Interceptor } from "aws4-axios"; import axios from "axios"; -import { ItemResult, Document as OSDocument } from "lib/packages/shared-types/opensearch/main"; +import { ItemResult, Document as OSDocument } from "shared-types/opensearch/main"; import { opensearch } from "shared-types"; -import { getDomainAndNamespace } from "./sink-lib"; +import { getDomainAndNamespace } from "./utils"; let client: Client; @@ -185,9 +185,12 @@ export async function getItem( const response = await client.get({ id, index }); return decodeUtf8(response).body; } catch (error) { - if (error instanceof OpensearchErrors.ResponseError && error.statusCode === 404 || error.meta?.statusCode === 404) { + if ( + (error instanceof OpensearchErrors.ResponseError && error.statusCode === 404) || + error.meta?.statusCode === 404 + ) { console.log("Error (404) retrieving in OpenSearch:", error); - return undefined + return undefined; } throw error; } diff --git a/lib/libs/sink-lib.ts b/lib/libs/sink-lib.ts index 491387ff01..524e8a3cb8 100644 --- a/lib/libs/sink-lib.ts +++ b/lib/libs/sink-lib.ts @@ -2,7 +2,8 @@ import pino from "pino"; const logger = pino(); import * as os from "./opensearch-lib"; -import { BaseIndex } from "lib/packages/shared-types/opensearch"; +import { BaseIndex } from "shared-types/opensearch"; +import { getDomainAndNamespace } from "./utils"; export function getTopic(topicPartition: string) { return topicPartition.split("--").pop()?.split("-").slice(0, -1)[0]; @@ -82,32 +83,6 @@ const prettyPrintJsonInObject = (obj: any): any => { return obj; }; -/** - * Returns the `osDomain` and `indexNamespace` env variables. Passing `baseIndex` appends the arg to the `index` variable - * @throws if env variables are not defined, `getDomainAndNamespace` throws error indicating which variable is missing - * @returns - */ -export function getDomainAndNamespace( - baseIndex: T, -): { domain: string; index: `${string}${T}` }; - -export function getDomainAndNamespace(baseIndex?: BaseIndex) { - const domain = process.env.osDomain; - const indexNamespace = process.env.indexNamespace ?? ""; - - if (domain === undefined) { - throw new Error("osDomain is undefined in environment variables"); - } - - if (indexNamespace == "" && process.env.isDev == "true") { - throw new Error("indexName is undefined in environment variables"); - } - - const index = `${indexNamespace}${baseIndex}`; - - return { index, domain }; -} - export async function bulkUpdateDataWrapper( docs: { id: string; [key: string]: unknown }[], baseIndex: BaseIndex, diff --git a/lib/libs/utils.ts b/lib/libs/utils.ts new file mode 100644 index 0000000000..fb7809d806 --- /dev/null +++ b/lib/libs/utils.ts @@ -0,0 +1,52 @@ +import { BaseIndex, Index } from "lib/packages/shared-types/opensearch"; + +/** + * Returns the `osDomain` + * @throws if env variables are not defined, `getDomain` throws error indicating if variable is missing + * @returns the value of `osDomain` + */ +export function getDomain(): string; +export function getDomain(): string { + const domain = process.env.osDomain; + + if (domain === undefined) { + throw new Error("process.env.osDomain must be defined"); + } + + return domain; +} + +/** + * Returns the `indexNamespace` env variables. Passing `baseIndex` appends the arg to the `index` variable + * @throws if env variables are not defined, `getNamespace` throws error indicating if variable is missing and + * the environment the application is running on `isDev` + * @returns the value of `indexNamespace` or empty string if not in development + */ +export function getNamespace(baseIndex?: T): Index; +export function getNamespace(baseIndex?: BaseIndex) { + const indexNamespace = process.env.indexNamespace ?? ""; + + if (indexNamespace == "" && process.env.isDev == "true") { + throw new Error("process.env.indexNamespace must be defined"); + } + + const index = `${indexNamespace}${baseIndex}`; + + return index; +} + +/** + * Returns the `osDomain` and `indexNamespace` env variables. Passing `baseIndex` appends the arg to the `index` variable + * @throws if env variables are not defined, `getDomainAndNamespace` throws error indicating which variable is missing + * @returns + */ +export function getDomainAndNamespace( + baseIndex: T, +): { domain: string; index: Index }; + +export function getDomainAndNamespace(baseIndex: BaseIndex) { + const domain = getDomain(); + const index = getNamespace(baseIndex); + + return { index, domain }; +} diff --git a/lib/packages/shared-types/events/capitated-renewal.test.ts b/lib/packages/shared-types/events/capitated-renewal.test.ts deleted file mode 100644 index 3fbeb89ca6..0000000000 --- a/lib/packages/shared-types/events/capitated-renewal.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { events } from "shared-types/events"; -import { describe, expect, test } from "vitest"; - -describe("Capitated Renewal", () => { - const schema = events["capitated-renewal"].baseSchema.pick({ id: true }); - const formatErrorMessage = "Renewal Number must be in the format of"; - - test("VALID ID", () => { - const validId = "MD-2024.R01.00"; - const result = schema.safeParse({ id: validId }); - - expect(result.success).toBe(true); - }); - - test("EMPTY ID", () => { - const validId = ""; - const result = schema.safeParse({ id: validId }); - - expect(result.success).toBe(false); - expect(result.error.issues[0].message).toBe("Required"); - }); - - test("TOO SHORT ID FORMAT", () => { - const invalidId = "MD-202.R01.00"; - const result = schema.safeParse({ id: invalidId }); - - expect(result.success).toBe(false); - expect(result.error.issues[0].message).toMatch(formatErrorMessage); - }); - - test("TOO LONG ID FORMAT", () => { - const invalidId = "MD-123456.R01.00"; - const result = schema.safeParse({ id: invalidId }); - - expect(result.success).toBe(false); - expect(result.error.issues[0].message).toMatch(formatErrorMessage); - }); - - test("INVALID CHARACTERS ID FORMAT", () => { - const invalidId = "MD-1234.R0a.00"; - const result = schema.safeParse({ id: invalidId }); - - expect(result.success).toBe(false); - expect(result.error.issues[0].message).toMatch(formatErrorMessage); - }); - - test("INVALID R## ID FORMAT", () => { - const invalidId = "NY-1234.R00.00"; - const result = schema.safeParse({ id: invalidId }); - - expect(result.success).toBe(false); - expect(result.error.issues[0].message).toMatch(formatErrorMessage); - }); - - test("INVALID AMENDMENT ID", () => { - const invalidId = "NY-1234.R01.01"; - const result = schema.safeParse({ id: invalidId }); - - expect(result.success).toBe(false); - expect(result.error.issues[0].message).toMatch(formatErrorMessage); - }); -}); diff --git a/lib/packages/shared-types/events/contracting-renewal.test.ts b/lib/packages/shared-types/events/contracting-renewal.test.ts deleted file mode 100644 index d24086c2ff..0000000000 --- a/lib/packages/shared-types/events/contracting-renewal.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { events } from "shared-types/events"; -import { describe, expect, test } from "vitest"; - -describe("Contracting Renewal", () => { - const schema = events["contracting-renewal"].baseSchema.pick({ id: true }); - const formatErrorMessage = "Renewal Number must be in the format of"; - - test(" VALID ID", () => { - const validId = "MD-2024.R01.00"; - const result = schema.safeParse({ id: validId }); - - expect(result.success).toBe(true); - }); - - test("EMPTY ID", () => { - const validId = ""; - const result = schema.safeParse({ id: validId }); - - expect(result.success).toBe(false); - expect(result.error.issues[0].message).toBe("Required"); - }); - - test("TOO SHORT ID FORMAT", () => { - const invalidId = "MD-202.R01.00"; - const result = schema.safeParse({ id: invalidId }); - - expect(result.success).toBe(false); - expect(result.error.issues[0].message).toMatch(formatErrorMessage); - }); - - test("TOO LONG ID FORMAT", () => { - const invalidId = "MD-123456.R01.00"; - const result = schema.safeParse({ id: invalidId }); - - expect(result.success).toBe(false); - expect(result.error.issues[0].message).toMatch(formatErrorMessage); - }); - - test("INVALID CHARACTERS ID FORMAT", () => { - const invalidId = "MD-1234.R0a.00"; - const result = schema.safeParse({ id: invalidId }); - - expect(result.success).toBe(false); - expect(result.error.issues[0].message).toMatch(formatErrorMessage); - }); - - test("INVALID R## ID FORMAT", () => { - const invalidId = "NY-1234.R00.00"; - const result = schema.safeParse({ id: invalidId }); - - expect(result.success).toBe(false); - expect(result.error.issues[0].message).toMatch(formatErrorMessage); - }); - - test("INVALID AMENDMENT ID", () => { - const invalidId = "NY-1234.R01.01"; - const result = schema.safeParse({ id: invalidId }); - - expect(result.success).toBe(false); - expect(result.error.issues[0].message).toMatch(formatErrorMessage); - }); -}); diff --git a/lib/vitest.setup.ts b/lib/vitest.setup.ts index 3b94b69953..e354c3157b 100644 --- a/lib/vitest.setup.ts +++ b/lib/vitest.setup.ts @@ -12,43 +12,13 @@ import { USER_POOL_CLIENT_DOMAIN, USER_POOL_CLIENT_ID, USER_POOL_ID, + ATTACHMENT_BUCKET_NAME, + ATTACHMENT_BUCKET_REGION, setDefaultStateSubmitter, } from "mocks"; import { mockedServiceServer as mockedServer } from "mocks/server"; import { afterAll, afterEach, beforeAll, beforeEach, vi } from "vitest"; -// TODO to mock -// defaultApiTokenHandler: {} -// [MSW] Warning: intercepted a request without a matching request handler: -// • GET http://169.254.169.254/latest/meta-data/iam/security-credentials/ - -// starting MSW listener for lib tests -// stdout | local-constructs/manage-users/src/manageUsers.test.ts > Cognito User Lambda Handler > should handle errors and send FAILED response -// Error: Failed to get secret -// at /home/runner/work/macpro-mako/macpro-mako/lib/local-constructs/manage-users/src/manageUsers.test.ts:126:37 -// at file:///home/runner/work/macpro-mako/macpro-mako/node_modules/@vitest/runner/dist/index.js:146:14 -// at file:///home/runner/work/macpro-mako/macpro-mako/node_modules/@vitest/runner/dist/index.js:529:11 -// at runWithTimeout (file:///home/runner/work/macpro-mako/macpro-mako/node_modules/@vitest/runner/dist/index.js:61:7) -// at runTest (file:///home/runner/work/macpro-mako/macpro-mako/node_modules/@vitest/runner/dist/index.js:982:17) -// at processTicksAndRejections (node:internal/process/task_queues:95:5) -// at runSuite (file:///home/runner/work/macpro-mako/macpro-mako/node_modules/@vitest/runner/dist/index.js:1131:15) -// at runSuite (file:///home/runner/work/macpro-mako/macpro-mako/node_modules/@vitest/runner/dist/index.js:1131:15) -// at runFiles (file:///home/runner/work/macpro-mako/macpro-mako/node_modules/@vitest/runner/dist/index.js:1188:5) -// at startTests (file:///home/runner/work/macpro-mako/macpro-mako/node_modules/@vitest/runner/dist/index.js:1197:3) -// ✓ |lib| local-constructs/manage-users/src/manageUsers.test.ts (2 tests) 15ms - -// { username: '53832e35-1fbe-4c74-9111-4a0cd29ce2cf' } -// getAuthDetails event: {"requestContext":{"identity":{"cognitoAuthenticationProvider":"https://cognito-idp.us-east-1.amazonaws.com/us-east-1_userPool1,https://cognito-idp.us-east-1.amazonaws.com/us-east-1_userPool1:CognitoSignIn:53832e35-1fbe-4c74-9111-4a0cd29ce2cf"}}} -// { -// authDetails: { -// userId: '53832e35-1fbe-4c74-9111-4a0cd29ce2cf', -// poolId: 'us-east-1_userPool1' -// } -// } -// defaultApiTokenHandler: {} -// defaultSecurityCredentialsHandler: {} -// defaultSecurityCredentialsHandler: {} - Amplify.configure({ API: API_CONFIG, Auth: AUTH_CONFIG, @@ -80,6 +50,8 @@ beforeEach(() => { process.env.idmClientIssuer = USER_POOL_CLIENT_DOMAIN; process.env.osDomain = OPENSEARCH_DOMAIN; process.env.indexNamespace = OPENSEARCH_INDEX_NAMESPACE; + process.env.attachmentsBucketName = ATTACHMENT_BUCKET_NAME; + process.env.attachmentsBucketRegion = ATTACHMENT_BUCKET_REGION; process.env.emailAddressLookupSecretName = "mock-email-secret"; // pragma: allowlist secret process.env.DLQ_URL = "https://sqs.us-east-1.amazonaws.com/123/test"; process.env.configurationSetName = "SES"; diff --git a/mocks/consts.ts b/mocks/consts.ts index 976633c77c..67c78c1b92 100644 --- a/mocks/consts.ts +++ b/mocks/consts.ts @@ -10,6 +10,8 @@ export const COGNITO_IDP_DOMAIN = `https://cognito-idp.${REGION}.amazonaws.com/$ export const OPENSEARCH_DOMAIN = `https://vpc-opensearchdomain-mock-domain.${REGION}.es.amazonaws.com`; export const OPENSEARCH_INDEX_NAMESPACE = "test-namespace-"; export const CLOUDFORMATION_NOTIFICATION_DOMAIN = "https://test-cfn.amazonaws.com"; +export const ATTACHMENT_BUCKET_NAME = "test-bucket"; +export const ATTACHMENT_BUCKET_REGION = REGION; export const ACCESS_KEY_ID = "ASIAZHXA3XOU7XZ53M36"; // pragma: allowlist secret export const SECRET_KEY = "UWKCFxhrgbPnixgLnL1JKwFEwiK9ZKvTAtpk8cGa"; // pragma: allowlist secret diff --git a/mocks/data/items.ts b/mocks/data/items.ts index 9bb2742052..e07e4e30fe 100644 --- a/mocks/data/items.ts +++ b/mocks/data/items.ts @@ -1,5 +1,6 @@ import { SEATOOL_STATUS } from "shared-types"; import type { TestItemResult } from "../index.d"; +import { ATTACHMENT_BUCKET_NAME } from "../consts"; export const EXISTING_ITEM_PENDING_ID = "MD-0002.R00.00"; export const EXISTING_ITEM_APPROVED_NEW_ID = "MD-0000.R00.00"; @@ -20,6 +21,7 @@ export const CONTRACTING_AMEND_ITEM_ID = "MD-007.R00.01"; export const MISSING_CHANGELOG_ITEM_ID = "MD-008.R00.00"; export const WITHDRAWN_CHANGELOG_ITEM_ID = "VA-11-2020"; export const INITIAL_RELEASE_APPK_ITEM_ID = "MD-010.R00.01"; +export const EXISTING_ITEM_APPROVED_APPK_ITEM_ID = "MD-012.R00.01"; export const SUBMISSION_ERROR_ITEM_ID = "Throw Submission Error"; export const GET_ERROR_ITEM_ID = "Throw Get Item Error"; @@ -242,6 +244,7 @@ const items: Record = { key: "doc001", title: "Contract Amendment", filename: "contract_amendment_2024.pdf", + bucket: ATTACHMENT_BUCKET_NAME, }, ], additionalInformation: "Amendment to the capitated contract terms for 2024.", @@ -259,6 +262,7 @@ const items: Record = { key: "rai002", title: "Response to RAI", filename: "rai_response.docx", + bucket: ATTACHMENT_BUCKET_NAME, }, ], additionalInformation: "Detailed response to the request for additional information.", @@ -276,6 +280,7 @@ const items: Record = { key: "subdoc003", title: "Follow-Up Documents", filename: "followup_docs.zip", + bucket: ATTACHMENT_BUCKET_NAME, }, ], additionalInformation: "Supporting documents uploaded as follow-up.", @@ -293,6 +298,7 @@ const items: Record = { key: "subdoc004", title: "Compliance Files", filename: "compliance_documents.xlsx", + bucket: ATTACHMENT_BUCKET_NAME, }, ], additionalInformation: "Compliance review files uploaded.", @@ -310,6 +316,7 @@ const items: Record = { key: "withdraw005", title: "Withdrawal Notice", filename: "rai_withdrawal_notice.pdf", + bucket: ATTACHMENT_BUCKET_NAME, }, ], additionalInformation: "Official notice of RAI withdrawal submitted.", @@ -327,6 +334,7 @@ const items: Record = { key: "withdraw006", title: "Package Withdrawal", filename: "package_withdrawal_request.docx", + bucket: ATTACHMENT_BUCKET_NAME, }, ], additionalInformation: "Package has been withdrawn from submission pipeline.", @@ -344,6 +352,7 @@ const items: Record = { key: "misc007", title: "Miscellaneous File", filename: "miscellaneous_info.txt", + bucket: ATTACHMENT_BUCKET_NAME, }, ], additionalInformation: "Uncategorized file upload.", @@ -368,6 +377,38 @@ const items: Record = { _source: { changedDate: "2024-01-01T00:00:00Z", title: "Initial release", + cmsStatus: "Pending", + stateStatus: "Under Review", + }, + }, + ], + }, + }, + [EXISTING_ITEM_APPROVED_APPK_ITEM_ID]: { + _id: EXISTING_ITEM_APPROVED_APPK_ITEM_ID, + found: true, + _source: { + id: EXISTING_ITEM_APPROVED_APPK_ITEM_ID, + seatoolStatus: SEATOOL_STATUS.APPROVED, + actionType: "New", + authority: "1915(c)", + state: "MD", + origin: "OneMAC", + appkChildren: [ + { + _source: { + changedDate: "2024-01-01T00:00:00Z", + title: "Initial release", + cmsStatus: "Pending", + stateStatus: "Under Review", + }, + }, + { + _source: { + changedDate: "2025-01-08T00:00:00Z", + title: "Approved release", + cmsStatus: "Approved", + stateStatus: "Approved", }, }, ], diff --git a/mocks/data/users/stateSubmitters.ts b/mocks/data/users/stateSubmitters.ts index f391b9fcf3..9b0d30e608 100644 --- a/mocks/data/users/stateSubmitters.ts +++ b/mocks/data/users/stateSubmitters.ts @@ -1,5 +1,6 @@ import { TestUserData } from "../../index.d"; +export const automatedStateSubmitterUsername = "f3a1b6d6-3bc9-498d-ac22-41a6d46982c9"; export const makoStateSubmitter: TestUserData = { UserAttributes: [ { @@ -232,10 +233,10 @@ export const automatedStateSubmitter: TestUserData = { }, { Name: "sub", - Value: "f3a1b6d6-3bc9-498d-ac22-41a6d46982c9", + Value: automatedStateSubmitterUsername, }, ], - Username: "f3a1b6d6-3bc9-498d-ac22-41a6d46982c9", + Username: automatedStateSubmitterUsername, }; export const testNewStateSubmitter: TestUserData = { diff --git a/mocks/handlers/aws/cloudFormation.ts b/mocks/handlers/aws/cloudFormation.ts index ba87334946..557d5dd30e 100644 --- a/mocks/handlers/aws/cloudFormation.ts +++ b/mocks/handlers/aws/cloudFormation.ts @@ -4,7 +4,8 @@ import exports from "../../data/cloudFormationsExports"; const defaultCloudFormationHandler = http.post( `https://cloudformation.us-east-1.amazonaws.com/`, - async () => { + async ({ request }) => { + console.log("defaultCloudFormationHandler", { request, headers: request.headers }); let xmlResponse = ` diff --git a/mocks/handlers/aws/cognito.ts b/mocks/handlers/aws/cognito.ts index e830dfc187..026dbc017e 100644 --- a/mocks/handlers/aws/cognito.ts +++ b/mocks/handlers/aws/cognito.ts @@ -141,7 +141,8 @@ export const getRequestContext = (user?: TestUserData | string): APIGatewayEvent } as APIGatewayEventRequestContext; }; -export const signInHandler = http.post(/amazoncognito.com\/oauth2\/token/, async () => { +export const signInHandler = http.post(/amazoncognito.com\/oauth2\/token/, async ({ request }) => { + console.log("signInHandler", { request, headers: request.headers }); if (process.env.MOCK_USER_USERNAME) { const user = findUserByUsername(process.env.MOCK_USER_USERNAME); if (user) { @@ -163,10 +164,7 @@ export const signInHandler = http.post(/amazoncognito.com\/oauth2\/token/, async export const identityServiceHandler = http.post( /cognito-identity/, async ({ request }) => { - console.log("identityServiceHandler", { - request, - headers: request.headers, - }); + console.log("identityServiceHandler", { request, headers: request.headers }); const target = request.headers.get("x-amz-target"); if (target) { if (target == "AWSCognitoIdentityService.GetId") { @@ -238,10 +236,7 @@ export const identityProviderServiceHandler = http.post< PathParams, IdpRequestSessionBody | IdpRefreshRequestBody | IdpListUsersRequestBody | AdminGetUserRequestBody >(/https:\/\/cognito-idp.\S*.amazonaws.com\//, async ({ request }) => { - console.log("identityProviderServiceHandler", { - request, - headers: request.headers, - }); + console.log("identityProviderServiceHandler", { request, headers: request.headers }); const target = request.headers.get("x-amz-target"); if (target) { if (target == "AWSCognitoIdentityProviderService.InitiateAuth") { diff --git a/mocks/handlers/aws/credentials.ts b/mocks/handlers/aws/credentials.ts index 0ea1feed75..a5ab5d5385 100644 --- a/mocks/handlers/aws/credentials.ts +++ b/mocks/handlers/aws/credentials.ts @@ -10,24 +10,32 @@ const generateSessionToken = (): string | null => { return null; }; -const defaultApiTokenHandler = http.put(/\/api\/token/, () => { +const defaultApiTokenHandler = http.put(/\/api\/token/, ({ request }) => { + console.log("defaultApiTokenHandler", { request, headers: request.headers }); return HttpResponse.text(generateSessionToken()); }); -const defaultSecurityCredentialsHandler = http.get(/\/meta-data\/iam\/security-credentials/, () => { - return HttpResponse.json({ - Code: "Success", - LastUpdated: new Date().toISOString(), - Type: "AWS-HMAC", - AccessKeyId: ACCESS_KEY_ID, - SecretAccessKey: SECRET_KEY, - Token: generateSessionToken(), - Expiration: "2017-05-17T15:09:54Z", - }); -}); +const defaultSecurityCredentialsHandler = http.get( + /\/meta-data\/iam\/security-credentials/, + ({ request }) => { + console.log("defaultSecurityCredentialsHandler", { request, headers: request.headers }); + return HttpResponse.json({ + Code: "Success", + LastUpdated: new Date().toISOString(), + Type: "AWS-HMAC", + AccessKeyId: ACCESS_KEY_ID, + SecretAccessKey: SECRET_KEY, + Token: generateSessionToken(), + Expiration: "2017-05-17T15:09:54Z", + }); + }, +); -const defaultSecurityTokenServiceHandler = http.post("https://sts.us-east-1.amazonaws.com/", () => { - const xmlResponse = ` +const defaultSecurityTokenServiceHandler = http.post( + "https://sts.us-east-1.amazonaws.com/", + ({ request }) => { + console.log("defaultSecurityTokenServiceHandler", { request, headers: request.headers }); + const xmlResponse = ` DevUser123 @@ -49,8 +57,9 @@ const defaultSecurityTokenServiceHandler = http.post("https://sts.us-east-1.amaz `; - return HttpResponse.xml(xmlResponse); -}); + return HttpResponse.xml(xmlResponse); + }, +); export const errorSecurityTokenServiceHandler = http.post( "https://sts.us-east-1.amazonaws.com/", diff --git a/mocks/handlers/aws/lambda.ts b/mocks/handlers/aws/lambda.ts index 9eeea91e58..3c74d0d634 100644 --- a/mocks/handlers/aws/lambda.ts +++ b/mocks/handlers/aws/lambda.ts @@ -10,7 +10,7 @@ import { TestEventSourceMappingRequestBody } from "../../index.d"; const defaultListEventSourceMappingsHandler = http.get( "https://lambda.us-east-1.amazonaws.com/2015-03-31/event-source-mappings", async ({ request }) => { - console.log("get: ", { request }); + console.log("defaultListEventSourceMappingsHandler", { request, headers: request.headers }); const requestUrl = new URL(request.url); const functionName = requestUrl.searchParams.get("FunctionName") || ""; @@ -34,6 +34,7 @@ const defaultCreateEventSourceMappingsHandler = http.post< >( "https://lambda.us-east-1.amazonaws.com/2015-03-31/event-source-mappings", async ({ request }) => { + console.log("defaultCreateEventSourceMappingsHandler", { request, headers: request.headers }); const { FunctionName, Topics } = await request.json(); if (!FunctionName) { @@ -68,7 +69,12 @@ const defaultCreateEventSourceMappingsHandler = http.post< const defaultGetEventSourceMappingHandler = http.get( "https://lambda.us-east-1.amazonaws.com/2015-03-31/event-source-mappings/:uuid", - async ({ params }) => { + async ({ request, params }) => { + console.log("defaultGetEventSourceMappingHandler", { + request, + headers: request.headers, + params, + }); const { uuid } = params; if (!uuid) { @@ -89,7 +95,8 @@ const defaultGetEventSourceMappingHandler = http.get( const defaultDeleteEventSourceMappingHandler = http.delete( "https://lambda.us-east-1.amazonaws.com/2015-03-31/event-source-mappings/:uuid", - async ({ params }) => { + async ({ request, params }) => { + console.log("", { request, headers: request.headers, params }); const { uuid } = params; if (!uuid) { diff --git a/mocks/handlers/aws/secretsManager.ts b/mocks/handlers/aws/secretsManager.ts index 63fe010423..767d03b11f 100644 --- a/mocks/handlers/aws/secretsManager.ts +++ b/mocks/handlers/aws/secretsManager.ts @@ -5,6 +5,7 @@ import secrets, { TEST_SECRET_ERROR_ID } from "../../data/secrets"; const defaultSecretHandler = http.post( `https://secretsmanager.us-east-1.amazonaws.com`, async ({ request }) => { + console.log("defaultSecretHandler", { request, headers: request }); const { SecretId } = await request.json(); if (!SecretId) { return HttpResponse.json({ diff --git a/mocks/handlers/aws/stepFunctions.ts b/mocks/handlers/aws/stepFunctions.ts index a41d8adf5e..521cd41589 100644 --- a/mocks/handlers/aws/stepFunctions.ts +++ b/mocks/handlers/aws/stepFunctions.ts @@ -4,6 +4,7 @@ import { TestStepFunctionRequestBody } from "../../index.d"; const defaultStepFunctionHandler = http.post( "https://states.us-east-1.amazonaws.com/", async ({ request }) => { + console.log("defaultStepFunctionHandler", { request, headers: request.headers }); const { input } = await request.json(); const { cfnEvent: { diff --git a/mocks/handlers/index.ts b/mocks/handlers/index.ts index de40f65572..fe792b7f7c 100644 --- a/mocks/handlers/index.ts +++ b/mocks/handlers/index.ts @@ -24,24 +24,12 @@ export const postOnceHandler = (endpoint: string, status: number = 200, body?: B ); // Handlers that mock calls to the API -export const defaultApiHandlers = [ - ...apiHandlers, - ...countiesHandlers -] +export const defaultApiHandlers = [...apiHandlers, ...countiesHandlers]; // Handlers that mock calls to 3rd party services from the API -export const defaultServiceHandlers = [ - ...awsHandlers, - ...opensearchHandlers, - ...countiesHandlers -] +export const defaultServiceHandlers = [...awsHandlers, ...opensearchHandlers, ...countiesHandlers]; -export default [ - ...apiHandlers, - ...awsHandlers, - ...opensearchHandlers, - ...countiesHandlers, -]; +export default [...apiHandlers, ...awsHandlers, ...opensearchHandlers, ...countiesHandlers]; export { convertUserAttributes, diff --git a/mocks/handlers/opensearch/main.ts b/mocks/handlers/opensearch/main.ts index 8f4398987e..517f1154d0 100644 --- a/mocks/handlers/opensearch/main.ts +++ b/mocks/handlers/opensearch/main.ts @@ -31,16 +31,13 @@ const defaultMainSearchHandler = http.post( "https://vpc-opensearchdomain-mock-domain.us-east-1.es.amazonaws.com/test-namespace-main/_search", async ({ request }) => { const { query } = await request.json(); - console.log({ query }); if (query?.match_all?.id == "throw-error") { return new HttpResponse("Internal server error", { status: 500 }); } const must = query?.bool?.must; - console.log("must: ", JSON.stringify(must)); const mustTerms = getTermKeys(must); - console.log({ mustTerms }); const appkParentIdValue = getTermValues(must, "appkParentId.keyword") || getTermValues(must, "appkParentId"); @@ -61,7 +58,11 @@ const defaultMainSearchHandler = http.post( let appkChildren: TestAppkItemResult[] = (item._source?.appkChildren as TestAppkItemResult[]) || []; if (appkChildren.length > 0) { - mustTerms.forEach((term) => { + // TODO We don't have this field in the TypeScript, not sure what the parent field actually is + const filteredTerms = mustTerms.filter( + (term) => term !== "appkParentId.keyword" && term !== "appkParentId", + ); + filteredTerms.forEach((term) => { const filterValue = getTermValues(must, term); const filterTerm: keyof TestAppkDocument = term.replace( ".keyword", @@ -103,12 +104,10 @@ const defaultMainSearchHandler = http.post( if (itemHits.length > 0) { mustTerms.forEach((term) => { const filterValue = getTermValues(must, term); - console.log({ filterValue }); const filterTerm: keyof TestMainDocument = term.replace( ".keyword", "", ) as keyof TestMainDocument; - console.log({ filterTerm }); if (filterValue) { itemHits = filterItemsByTerm(itemHits, filterTerm, filterValue); } diff --git a/react-app/src/features/package/package-activity/index.test.tsx b/react-app/src/features/package/package-activity/index.test.tsx index 6b1c4e6ac6..d30fe430f2 100644 --- a/react-app/src/features/package/package-activity/index.test.tsx +++ b/react-app/src/features/package/package-activity/index.test.tsx @@ -9,6 +9,7 @@ import { NOT_FOUND_ITEM_ID, MISSING_CHANGELOG_ITEM_ID, WITHDRAWN_CHANGELOG_ITEM_ID, + ATTACHMENT_BUCKET_NAME, setDefaultStateSubmitter, } from "mocks"; @@ -61,36 +62,43 @@ describe("Package Activity", () => { filename: "contract_amendment_2024.pdf", key: "doc001", title: "Contract Amendment", + bucket: ATTACHMENT_BUCKET_NAME, }, { filename: "rai_response.docx", key: "rai002", title: "Response to RAI", + bucket: ATTACHMENT_BUCKET_NAME, }, { filename: "followup_docs.zip", key: "subdoc003", title: "Follow-Up Documents", + bucket: ATTACHMENT_BUCKET_NAME, }, { filename: "compliance_documents.xlsx", key: "subdoc004", title: "Compliance Files", + bucket: ATTACHMENT_BUCKET_NAME, }, { filename: "rai_withdrawal_notice.pdf", key: "withdraw005", title: "Withdrawal Notice", + bucket: ATTACHMENT_BUCKET_NAME, }, { filename: "package_withdrawal_request.docx", key: "withdraw006", title: "Package Withdrawal", + bucket: ATTACHMENT_BUCKET_NAME, }, { filename: "miscellaneous_info.txt", key: "misc007", title: "Miscellaneous File", + bucket: ATTACHMENT_BUCKET_NAME, }, ]); }); @@ -115,6 +123,7 @@ describe("Package Activity", () => { filename: "contract_amendment_2024.pdf", key: "doc001", title: "Contract Amendment", + bucket: ATTACHMENT_BUCKET_NAME, }, ]); }); @@ -141,6 +150,7 @@ describe("Package Activity", () => { filename: "contract_amendment_2024.pdf", key: "doc001", title: "Contract Amendment", + bucket: ATTACHMENT_BUCKET_NAME, }); expect(spiedWindowOpen).toBeCalledWith("hello world!"); });