diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 49a25fa..17a3075 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3 - uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3 with: - node-version: 16 + node-version: "18.7" cache: "npm" - run: npm ci - run: npm test diff --git a/src/functions/edi/inbound/__fixtures__/events.ts b/src/functions/edi/inbound/__fixtures__/events.ts index b88747f..fd807ca 100644 --- a/src/functions/edi/inbound/__fixtures__/events.ts +++ b/src/functions/edi/inbound/__fixtures__/events.ts @@ -45,7 +45,7 @@ export const sampleTransactionProcessedEvent = TransactionEventSchema.parse({ }, input: { type: "EDI/X12", - bucketName: "stedi-default-core-artifacts-217851219840", + bucketName: "account_id-sftp", key: "1f1b129a-9b86-04ea-3815-2d0f2b271c19/1746-1746-1.edi", }, output: { diff --git a/src/functions/edi/inbound/__tests__/handler.no-transaction-set-configured.ts b/src/functions/edi/inbound/__tests__/handler.no-transaction-set-configured.ts index 79bfc10..0f509a9 100644 --- a/src/functions/edi/inbound/__tests__/handler.no-transaction-set-configured.ts +++ b/src/functions/edi/inbound/__tests__/handler.no-transaction-set-configured.ts @@ -71,7 +71,7 @@ test("throws runtime error when no configuration is found for transaction set", }); const expectedErrorMessage = - "execution failed [id=7e5ceff7d64033820ab4fed8285328b4272369b7]: no transaction set configured"; + "execution failed [id=725f84a020bdfdd61f597aaf4a1a8d5dfaa5b38d]: no transaction set configured"; const errorWebhook = nock("https://example.com") .post("/error-webhook", (body: any) => { return body.error.message === expectedErrorMessage; diff --git a/src/functions/edi/inbound/__tests__/handler.source-is-artifact-bucket.ts b/src/functions/edi/inbound/__tests__/handler.source-is-artifact-bucket.ts new file mode 100644 index 0000000..964c6d8 --- /dev/null +++ b/src/functions/edi/inbound/__tests__/handler.source-is-artifact-bucket.ts @@ -0,0 +1,145 @@ +import test from "ava"; +import { handler } from "../handler.js"; +import nock from "nock"; +import { sampleTransactionProcessedEvent } from "../__fixtures__/events.js"; +import { sdkStreamMixin } from "@aws-sdk/util-stream-node"; +import { + mockBucketClient, + mockExecutionTracking, + mockGuideClient, + mockStashClient, + mockTranslateClient, +} from "../../../../lib/testing/testHelpers.js"; +import { + DeleteObjectCommand, + GetObjectCommand, +} from "@stedi/sdk-client-buckets"; +import { Readable } from "stream"; +import { GetValueCommand } from "@stedi/sdk-client-stash"; +import guideJSON855 from "../__fixtures__/855-guide.json" assert { type: "json" }; + +const buckets = mockBucketClient(); +const translate = mockTranslateClient(); +const stash = mockStashClient(); +const guides = mockGuideClient(); + +const partnershipId = "this-is-me_another-merchant"; + +test.beforeEach(() => { + nock.disableNetConnect(); + mockExecutionTracking(buckets); +}); + +test.afterEach.always(() => { + buckets.reset(); + guides.reset(); + stash.reset(); + translate.reset(); +}); + +test.serial("deletes input file", async (t) => { + // loading incoming EDI file from S3 + buckets.on(GetObjectCommand, {}).resolves({ + body: sdkStreamMixin( + Readable.from([new TextEncoder().encode(JSON.stringify(guideJSON855))]) + ), + }); + + stash + .on(GetValueCommand, { + key: `destinations|${partnershipId}|855`, + }) // mock destinations lookup + .resolvesOnce({ + value: { + description: + "Purchase Order Acknowledgments received from ANOTHERMERCH", + destinations: [ + { + destination: { + type: "webhook", + url: "https://webhook.site/TESTING", + verb: "POST", + }, + }, + ], + }, + }); + + // mock destination webhook delivery + const webhookRequest = nock("https://webhook.site") + .post("/TESTING", (body) => t.deepEqual(body, guideJSON855)) + .reply(200, { thank: "you" }); + + const result = await handler(sampleTransactionProcessedEvent); + + t.assert( + webhookRequest.isDone(), + "delivered guide JSON to destination webhook" + ); + + const bucketDestinationCall = buckets.commandCalls(DeleteObjectCommand, { + bucketName: "account_id-sftp", + }); + + t.is(bucketDestinationCall.length, 1); + + t.deepEqual(result, {}); +}); + +test.serial( + "does not delete input when source is artifact bucket", + async (t) => { + // loading incoming EDI file from S3 + buckets.on(GetObjectCommand, {}).resolves({ + body: sdkStreamMixin( + Readable.from([new TextEncoder().encode(JSON.stringify(guideJSON855))]) + ), + }); + + stash + .on(GetValueCommand, { + key: `destinations|${partnershipId}|855`, + }) // mock destinations lookup + .resolvesOnce({ + value: { + description: + "Purchase Order Acknowledgments received from ANOTHERMERCH", + destinations: [ + { + destination: { + type: "webhook", + url: "https://webhook.site/TESTING", + verb: "POST", + }, + }, + ], + }, + }); + + // mock destination webhook delivery + const webhookRequest = nock("https://webhook.site") + .post("/TESTING", (body) => t.deepEqual(body, guideJSON855)) + .reply(200, { thank: "you" }); + + const transactionWithInputasAritfactBucket = structuredClone( + sampleTransactionProcessedEvent + ); + + const inputBucketName = "stedi-default-core-artifacts-1234"; + sampleTransactionProcessedEvent.detail.input.bucketName = inputBucketName; + const result = await handler(transactionWithInputasAritfactBucket); + + t.assert( + webhookRequest.isDone(), + "delivered guide JSON to destination webhook" + ); + + const bucketDestinationCall = buckets.commandCalls(DeleteObjectCommand, { + bucketName: inputBucketName, + }); + + t.is(bucketDestinationCall.length, 0); + + t.deepEqual(result, {}); + } +); diff --git a/src/functions/edi/inbound/__tests__/handler.success.ts b/src/functions/edi/inbound/__tests__/handler.success.ts index 3f3b8dd..8d7e11f 100644 --- a/src/functions/edi/inbound/__tests__/handler.success.ts +++ b/src/functions/edi/inbound/__tests__/handler.success.ts @@ -100,7 +100,7 @@ test.serial( } ); -test.serial.only( +test.serial( `delivers to webhook destination with edi when includeSource is set`, async (t) => { // loading incoming EDI file from S3 diff --git a/src/functions/edi/inbound/handler.ts b/src/functions/edi/inbound/handler.ts index e3a3e1e..5107f6b 100644 --- a/src/functions/edi/inbound/handler.ts +++ b/src/functions/edi/inbound/handler.ts @@ -96,6 +96,7 @@ export const handler = async ( await processDeliveries(processDeliveriesInput); // Delete the input file (it is archived by core) + // unless the file source is the core artifact bucket await ensureFileIsDeleted( transactionEvent.detail.input.bucketName, transactionEvent.detail.input.key @@ -113,6 +114,9 @@ export const handler = async ( }; export const ensureFileIsDeleted = async (bucketName: string, key: string) => { + if (bucketName.startsWith("stedi-default-core-artifacts")) { + return; + } try { await buckets.send(new DeleteObjectCommand({ bucketName, key })); } catch (error) {