From dc13bdb98f523ccdb1c0bf8efc0036f8b0708875 Mon Sep 17 00:00:00 2001 From: Ross Williams Date: Thu, 27 Jul 2023 12:12:52 +0100 Subject: [PATCH 1/2] fix: do not delete inbound edi file when source is core artifact bucket when using Core's remote sftp poller, the poller adds the document to Core's artifact bucket and sets this as the input when sending transaction processed events. In these cases the edi-inbound function should not attempt to delete the input since Core handles the work and the input is read-only. Fix tests which use the artifact bucket as the source input by default. --- .../edi/inbound/__fixtures__/events.ts | 2 +- .../handler.no-transaction-set-configured.ts | 2 +- .../handler.source-is-artifact-bucket.ts | 145 ++++++++++++++++++ .../edi/inbound/__tests__/handler.success.ts | 2 +- src/functions/edi/inbound/handler.ts | 4 + 5 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 src/functions/edi/inbound/__tests__/handler.source-is-artifact-bucket.ts 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) { From b57d3d2f2f34174e295f27c6f580db04205eb363 Mon Sep 17 00:00:00 2001 From: Ross Williams Date: Thu, 27 Jul 2023 12:25:06 +0100 Subject: [PATCH 2/2] fix: set pr github runner to node 18 matching other runners and project --- .github/workflows/pr.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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