diff --git a/src/process.ts b/src/process.ts index 8c548cb..73d30e2 100644 --- a/src/process.ts +++ b/src/process.ts @@ -1,9 +1,10 @@ import S3Provider from './providers/S3Provider' import SQSConsumer from './consumers/SQSConsumer' +import GovUkPayPaymentEventMessage from './transformers/GovUKPayPaymentEventMessage' const { PROVIDER_S3_BUCKET_NAME, PROVIDER_S3_SOURCE_FILE, CONSUMER_SQS_QUEUE_URL } = process.env const s3Provider = new S3Provider(PROVIDER_S3_BUCKET_NAME, PROVIDER_S3_SOURCE_FILE) -const sqs = new SQSConsumer(CONSUMER_SQS_QUEUE_URL).writer() +const sqs = new SQSConsumer(CONSUMER_SQS_QUEUE_URL, new GovUkPayPaymentEventMessage()).writer() s3Provider .stream() diff --git a/src/transformers/GovUKPayPaymentEventMessage.test.ts b/src/transformers/GovUKPayPaymentEventMessage.test.ts new file mode 100644 index 0000000..a0c5f5e --- /dev/null +++ b/src/transformers/GovUKPayPaymentEventMessage.test.ts @@ -0,0 +1,41 @@ +import GovUKPayPaymentEventMessage from './GovUKPayPaymentEventMessage' +describe('message formatter', () => { + const messageFromS3Csv = { + 'transaction_id': 'some-transaction-id', + 'event_date': 'some-event-date', + 'parent_transaction_id': 'some-parent-transaction-id', + 'event_name': 'some-event-type', + 'transaction_type': 'some-resource-type', + 'reference': 'some-reference', + 'amount': 'some-amount' + } + const messageBuilder = new GovUKPayPaymentEventMessage() + + test('correctly transforms known reserved columns', () => { + const formatted = messageBuilder.transform(messageFromS3Csv) + const body = JSON.parse(formatted.MessageBody) + + expect(body).toHaveProperty('resource_external_id') + expect(body).toHaveProperty('parent_resource_external_id') + expect(body).toHaveProperty('resource_type') + expect(body).toHaveProperty('timestamp') + expect(body).toHaveProperty('event_details') + expect(body).toHaveProperty('event_type') + }) + + test('ignores reserved properties if not needed on transaction', () => { + const formatted = messageBuilder.transform({ 'transaction_id': 'some-transaction-id' }) + const body = JSON.parse(formatted.MessageBody) + + expect(body).toHaveProperty('resource_external_id') + expect(body).not.toHaveProperty('parent_resource_external_id') + }) + + test('correctly formats well formatted flat message', () => { + const formatted = messageBuilder.transform(messageFromS3Csv) + const body = JSON.parse(formatted.MessageBody) + + expect(body.event_details.reference).toBe('some-reference') + expect(body.event_details.amount).toBe('some-amount') + }) +}) \ No newline at end of file diff --git a/src/transformers/GovUKPayPaymentEventMessage.ts b/src/transformers/GovUKPayPaymentEventMessage.ts new file mode 100644 index 0000000..7c5acfc --- /dev/null +++ b/src/transformers/GovUKPayPaymentEventMessage.ts @@ -0,0 +1,55 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import crypto from 'crypto' +import SQS = require("aws-sdk/clients/sqs") +import Transformer from './Transformer' +import { Message } from './../providers/Provider' + +interface PaymentEventMessage { + event_details: { [key: string]: string }; + resource_type?: string; + resource_external_id?: string; + event_date?: string; + event_type?: string; + parent_resource_external_id?: string; + [key: string]: string | { [key: string]: string }; +} + +// we can gaurantee the existence of required fields as anything with permissions +// to this resource will be validating data entry +function formatPaymentEventMessage(message: Message): PaymentEventMessage { + const reservedKeys = [ + { key: 'transaction_id', target: 'resource_external_id' }, + { key: 'parent_transaction_id', target: 'parent_resource_external_id' }, + { key: 'transaction_type', target: 'resource_type' }, + { key: 'event_date', target: 'timestamp' }, + { key: 'event_name', target: 'event_type' } + ] + const formatted: PaymentEventMessage = { event_details: {} } + + // initially extract the reserved properties + for (const reserved of reservedKeys) { + const reservedEntry = message[reserved.key] + if (reservedEntry) { + formatted[reserved.target] = reservedEntry + delete message[reserved.key] + } + } + + // any remaining properties will override attributes of the transaction itself + // put these in `event_data` + for (const paymentEventMessageKey in message) { + formatted.event_details[paymentEventMessageKey] = message[paymentEventMessageKey] + } + return formatted +} + +export default class GovUkPayPaymentEventMessage implements Transformer { + transform(message: Message): SQS.SendMessageBatchRequestEntry { + return { + Id: crypto.randomBytes(16).toString('hex'), + MessageBody: JSON.stringify( + formatPaymentEventMessage(message) + ) + } + } +} \ No newline at end of file