From c72a4f7bf99d18d96c23f22d587c13b4a5955de5 Mon Sep 17 00:00:00 2001 From: Andrea Amorosi Date: Mon, 25 Mar 2024 11:16:24 +0100 Subject: [PATCH] feat: add envelopes & entrypoint --- packages/jmespath/src/envelopes.ts | 103 +++++++++++++++++++++++++++++ packages/jmespath/src/search.ts | 60 +++++++++++++++++ packages/jmespath/typedoc.json | 3 +- 3 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 packages/jmespath/src/envelopes.ts create mode 100644 packages/jmespath/src/search.ts diff --git a/packages/jmespath/src/envelopes.ts b/packages/jmespath/src/envelopes.ts new file mode 100644 index 0000000000..84cf06c383 --- /dev/null +++ b/packages/jmespath/src/envelopes.ts @@ -0,0 +1,103 @@ +import { search } from './search.js'; +import { PowertoolsFunctions } from './PowertoolsFunctions.js'; +import type { ParsingOptions, JSONObject } from './types.js'; + +/** + * Searches and extracts data using JMESPath + * + * Envelope being the JMESPath expression to extract the data you're after + * + * Built-in JMESPath functions include: `powertools_json`, `powertools_base64`, `powertools_base64_gzip` + * + * @example + * ```typescript + * import { extractDataFromEnvelope } from '@aws-lambda-powertools/jmespath/envelopes'; + * + * type CustomEvent = { + * body: string; // "{\"customerId\":\"dd4649e6-2484-4993-acb8-0f9123103394\"}" + * }; + * + * type EventBody = { + * customerId: string; + * }; + * + * export const handler = async (event: CustomEvent): Promise => { + * const payload = extractDataFromEnvelope(event, "powertools_json(body)"); + * const { customerId } = payload; // now deserialized + * // ... + * }; + * ``` + * + * We provide built-in envelopes for popular AWS Lambda event sources to easily decode and/or deserialize JSON objects. + * + * @example + * ```typescript + * import { + * extractDataFromEnvelope, + * SQS, + * } from '@aws-lambda-powertools/jmespath/envelopes'; + * import type { SQSEvent } from 'aws-lambda'; + * + * type MessageBody = { + * customerId: string; + * }; + * + * export const handler = async (event: SQSEvent): Promise => { + * const records = extractDataFromEnvelope>(event, SQS); + * for (const record in records) { // records is now a list containing the deserialized body of each message + * const { customerId } = record; + * } + * }; + * ``` + * + * @param data The JSON object to search + * @param envelope The JMESPath expression to use + * @param options The parsing options to use + */ +const extractDataFromEnvelope = ( + data: JSONObject, + envelope: string, + options?: ParsingOptions +): T => { + if (!options) { + options = { customFunctions: new PowertoolsFunctions() }; + } + + return search(envelope, data, options) as T; +}; + +const API_GATEWAY_REST = 'powertools_json(body)'; +const API_GATEWAY_HTTP = 'powertools_json(body)'; +const SQS = 'Records[*].powertools_json(body)'; +const SNS = 'Records[0].Sns.Message | powertools_json(@)'; +const EVENTBRIDGE = 'detail'; +const CLOUDWATCH_EVENTS_SCHEDULED = 'detail'; +const KINESIS_DATA_STREAM = + 'Records[*].kinesis.powertools_json(powertools_base64(data))'; +const CLOUDWATCH_LOGS = + 'awslogs.powertools_base64_gzip(data) | powertools_json(@).logEvents[*]'; +const S3_SNS_SQS = + 'Records[*].powertools_json(body).powertools_json(Message).Records[0]'; +const S3_SQS = 'Records[*].powertools_json(body).Records[0]'; +const S3_SNS_KINESIS_FIREHOSE = + 'records[*].powertools_json(powertools_base64(data)).powertools_json(Message).Records[0]'; +const S3_KINESIS_FIREHOSE = + 'records[*].powertools_json(powertools_base64(data)).Records[0]'; +const S3_EVENTBRIDGE_SQS = 'Records[*].powertools_json(body).detail'; + +export { + extractDataFromEnvelope, + API_GATEWAY_REST, + API_GATEWAY_HTTP, + SQS, + SNS, + EVENTBRIDGE, + CLOUDWATCH_EVENTS_SCHEDULED, + KINESIS_DATA_STREAM, + CLOUDWATCH_LOGS, + S3_SNS_SQS, + S3_SQS, + S3_SNS_KINESIS_FIREHOSE, + S3_KINESIS_FIREHOSE, + S3_EVENTBRIDGE_SQS, +}; diff --git a/packages/jmespath/src/search.ts b/packages/jmespath/src/search.ts new file mode 100644 index 0000000000..8f53775fa5 --- /dev/null +++ b/packages/jmespath/src/search.ts @@ -0,0 +1,60 @@ +import { Parser } from './Parser.js'; +import type { ParsingOptions, JSONObject } from './types.js'; + +const parser = new Parser(); + +/** + * Search for data in a JSON object using a JMESPath expression. + * + * @example + * ```typescript + * import { search } from '@aws-lambda-powertools/jmespath'; + * + * const data = { + * foo: { + * bar: { + * baz: 1 + * } + * } + * }; + * + * const result = search('foo.bar.baz', data); + * console.log(result); // 1 + * ``` + * + * By default the search function will use all the built-in functions + * present in the [JMESPath specification](https://jmespath.org/specification.html). + * + * Powertools for AWS Lambda provides some additional functions that can be used + * by passing them in the `customFunctions` option. + * + * @example + * ```typescript + * import { search } from '@aws-lambda-powertools/jmespath'; + * import { PowertoolsFunctions } from '@aws-lambda-powertools/jmespath/functions'; + * + * const data = { + * body: "{\"foo\": \"bar\"}" + * }; + * + * const result = search( + * 'powertools_json(body)', + * data, + * { customFunctions: new PowertoolsFunctions() } + * ); + * console.log(result); // { foo: 'bar' } + * ``` + * + * @param expression The JMESPath expression to use + * @param data The JSON object to search + * @param options The parsing options to use + */ +const search = ( + expression: string, + data: JSONObject, + options?: ParsingOptions +): unknown => { + return parser.parse(expression).search(data, options); +}; + +export { search }; diff --git a/packages/jmespath/typedoc.json b/packages/jmespath/typedoc.json index da81672090..4471cee375 100644 --- a/packages/jmespath/typedoc.json +++ b/packages/jmespath/typedoc.json @@ -3,7 +3,8 @@ "../../typedoc.base.json" ], "entryPoints": [ - "./src/index.ts", + "./src/search.ts", + "./src/errors.ts", "./src/types.ts", "./src/envelopes.ts", "./src/Functions.ts",