Skip to content

Commit

Permalink
feat(parser): add helper function to handle JSON stringified fields (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
am29d authored Aug 8, 2024
1 parent b064408 commit 806b884
Show file tree
Hide file tree
Showing 9 changed files with 353 additions and 5 deletions.
4 changes: 2 additions & 2 deletions docs/core/event-handler/api-gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ This is the sample infrastructure for API Gateway and Lambda Function URLs we ar
=== "API Gateway SAM Template"

```yaml title="AWS Serverless Application Model (SAM) example"
--8<-- "examples/snippets/event-handler/rest/templates/template.yaml"
[//]: # ( --8<-- "examples/snippets/event-handler/rest/templates/template.yaml")
```

=== "Lambda Function URL SAM Template"

```yaml title="AWS Serverless Application Model (SAM) example"
--8<-- "examples/event_handler_lambda_function_url/sam/template.yaml"
[//]: # ( --8<-- "examples/event_handler_lambda_function_url/sam/template.yaml")
```

<!-- remove line below while editing this doc & put it back until the doc has reached its first draft -->
Expand Down
28 changes: 28 additions & 0 deletions docs/utilities/parser.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,34 @@ You can extend every built-in schema to include your own schema, and yet have al
--8<-- "examples/snippets/parser/examplePayload.json"
```

If you want to extend a schema and transform a JSON stringified payload to an object, you can use helper function `JSONStringified`:

=== "AlbSchema with JSONStringified"
```typescript hl_lines="12"
--8<-- "examples/snippets/parser/extendAlbSchema.ts"
```

1. Extend built-in `AlbSchema` using JSONStringified function to transform your payload

=== "Alb exmaple payload"

```json hl_lines="26"
--8<-- "examples/snippets/parser/exampleAlbPayload.json"
```

=== "SQS Schema with JSONStringified"
```typescript hl_lines="23-25 30 34"
--8<-- "examples/snippets/parser/extendSqsSchema.ts"
```

1. make sure to set your schema to the correct key in the JSON payload

=== "SQS exmaple payload"

```json hl_lines="6 28"
--8<-- "examples/snippets/parser/exampleSqsPayload.json"
```

## Envelopes

When trying to parse your payload you might encounter the following situations:
Expand Down
28 changes: 28 additions & 0 deletions examples/snippets/parser/exampleAlbPayload.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"requestContext": {
"elb": {
"targetGroupArn": "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/lambda-279XGJDqGZ5rsrHC2Fjr/49e9d65c45c6791a"
}
},
"httpMethod": "GET",
"path": "/lambda",
"queryStringParameters": {
"query": "1234ABCD"
},
"headers": {
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"accept-encoding": "gzip",
"accept-language": "en-US,en;q=0.9",
"connection": "keep-alive",
"host": "lambda-alb-123578498.us-east-2.elb.amazonaws.com",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
"x-amzn-trace-id": "Root=1-5c536348-3d683b8b04734faae651f476",
"x-forwarded-for": "72.12.164.125",
"x-forwarded-port": "80",
"x-forwarded-proto": "http",
"x-imforwards": "20"
},
"body": "{\"name\":\"Walter\", \"age\": 50}",
"isBase64Encoded": false
}
43 changes: 43 additions & 0 deletions examples/snippets/parser/exampleSqsPayload.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"Records": [
{
"messageId": "059f36b4-87a3-44ab-83d2-661975830a7d",
"receiptHandle": "AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...",
"body": "{\"name\": \"John Doe\", \"age\": 30}",
"attributes": {
"ApproximateReceiveCount": "1",
"SentTimestamp": "1545082649183",
"SenderId": "AIDAIENQZJOLO23YVJ4VO",
"ApproximateFirstReceiveTimestamp": "1545082649185"
},
"messageAttributes": {
"testAttr": {
"stringValue": "100",
"binaryValue": "base64Str",
"dataType": "Number"
}
},
"md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:us-east-2:123456789012:my-queue",
"awsRegion": "us-east-2"
},
{
"messageId": "2e1424d4-f796-459a-8184-9c92662be6da",
"receiptHandle": "AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...",
"body": "{\"name\": \"foo\", \"age\": 10}",
"attributes": {
"ApproximateReceiveCount": "1",
"SentTimestamp": "1545082650636",
"SenderId": "AIDAIENQZJOLO23YVJ4VO",
"ApproximateFirstReceiveTimestamp": "1545082650649",
"DeadLetterQueueSourceArn": "arn:aws:sqs:us-east-2:123456789012:my-queue-dead"
},
"messageAttributes": {},
"md5OfBody": "e4e68fb7bd0e697a0ae8f1bb342846b3",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:us-east-2:123456789012:my-queue",
"awsRegion": "us-east-2"
}
]
}
12 changes: 12 additions & 0 deletions examples/snippets/parser/extendAlbSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { JSONStringified } from '@aws-lambda-powertools/parser/helpers';
import { AlbSchema } from '@aws-lambda-powertools/parser/schemas';
import { z } from 'zod';

const customSchema = z.object({
name: z.string(),
age: z.number(),
});

const extendedSchema = AlbSchema.extend({
body: JSONStringified(customSchema), // (1)!
});
19 changes: 19 additions & 0 deletions examples/snippets/parser/extendSqsSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { JSONStringified } from '@aws-lambda-powertools/parser/helpers';
import {
SqsRecordSchema,
SqsSchema,
} from '@aws-lambda-powertools/parser/schemas';
import { z } from 'zod';

const customSchema = z.object({
name: z.string(),
age: z.number(),
});

const extendedSchema = SqsSchema.extend({
Records: z.array(
SqsRecordSchema.extend({
body: JSONStringified(customSchema), // (1)!
})
),
});
22 changes: 19 additions & 3 deletions packages/parser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,14 +167,21 @@
"require": "./lib/cjs/envelopes/vpc-latticev2.js",
"import": "./lib/esm/envelopes/vpc-latticev2.js"
},
"./helpers": {
"require": "./lib/cjs/helpers.js",
"import": "./lib/esm/helpers.js"
},
"./types": {
"require": "./lib/cjs/types/index.js",
"import": "./lib/esm/types/index.js"
}
},
"typesVersions": {
"*": {
"types": ["./lib/cjs/types/index.d.ts", "./lib/esm/types/index.d.ts"],
"types": [
"./lib/cjs/types/index.d.ts",
"./lib/esm/types/index.d.ts"
],
"middleware": [
"./lib/cjs/middleware/parser.d.ts",
"./lib/esm/middleware/parser.d.ts"
Expand Down Expand Up @@ -227,7 +234,10 @@
"./lib/cjs/schemas/lambda.d.ts",
"./lib/esm/schemas/lambda.d.ts"
],
"schemas/s3": ["./lib/cjs/schemas/s3.d.ts", "./lib/esm/schemas/s3.d.ts"],
"schemas/s3": [
"./lib/cjs/schemas/s3.d.ts",
"./lib/esm/schemas/s3.d.ts"
],
"schemas/ses": [
"./lib/cjs/schemas/ses.d.ts",
"./lib/esm/schemas/ses.d.ts"
Expand Down Expand Up @@ -303,12 +313,18 @@
"envelopes/vpc-latticev2": [
"./lib/cjs/envelopes/vpc-latticev2.d.ts",
"./lib/esm/envelopes/vpc-latticev2.d.ts"
],
"helpers": [
"./lib/cjs/helpers.d.ts",
"./lib/esm/helpers.d.ts"
]
}
},
"main": "./lib/cjs/index.js",
"types": "./lib/cjs/index.d.ts",
"files": ["lib"],
"files": [
"lib"
],
"repository": {
"type": "git",
"url": "git+https://github.com/aws-powertools/powertools-lambda-typescript.git"
Expand Down
41 changes: 41 additions & 0 deletions packages/parser/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { type ZodSchema, z } from 'zod';

/**
* A helper function to parse a JSON string and validate it against a schema.
* Use it for built-in schemas like `AlbSchema`, `ApiGatewaySchema`, etc. to extend them with your customer schema.
*
* @example
* ```typescript
* import { JSONStringified } from '@aws-lambda-powertools/parser/helpers';
* import { AlbSchema } from '@aws-lambda-powertools/parser/schemas';
* import { z } from 'zod';
*
* const customSchema = z.object({
* name: z.string(),
* age: z.number(),
* });
*
* const extendedSchema = AlbSchema.extend({
* body: JSONStringified(customSchema),
* });
*
* ```
*
* @param schema - The schema to validate the JSON string against
*/
const JSONStringified = (schema: ZodSchema) =>
z
.string()
.transform((str, ctx) => {
try {
return JSON.parse(str);
} catch (err) {
ctx.addIssue({
code: 'custom',
message: 'Invalid JSON',
});
}
})
.pipe(schema);

export { JSONStringified };
Loading

0 comments on commit 806b884

Please sign in to comment.