From 5012a6ac68d487c0629326c13139f015c62631d0 Mon Sep 17 00:00:00 2001 From: Dustin Goodman Date: Sun, 15 Jan 2023 15:29:31 -0600 Subject: [PATCH] address PR feedback --- .../serverless-framework-sqs-dynamodb/.env.example | 2 ++ starters/serverless-framework-sqs-dynamodb/README.md | 10 ++++++---- .../serverless-framework-sqs-dynamodb/serverless.ts | 3 ++- .../src/handlers/example_job_processor.test.ts | 2 +- .../src/handlers/example_stream_processor.test.ts | 5 ++++- .../src/handlers/example_stream_processor.ts | 10 +++++++--- .../src/handlers/generate_job.ts | 3 ++- .../src/handlers/technology_create.test.ts | 5 ----- .../src/models/technology/getAll.ts | 3 +-- .../src/types/environment.d.ts | 1 + .../src/utils/cache/constants.ts | 2 +- .../src/utils/dynamodb/deleteItem.test.ts | 1 + .../src/utils/dynamodb/deleteItem.ts | 1 + .../src/utils/isOffline/isOffline.ts | 2 +- 14 files changed, 30 insertions(+), 20 deletions(-) diff --git a/starters/serverless-framework-sqs-dynamodb/.env.example b/starters/serverless-framework-sqs-dynamodb/.env.example index 65240b684..bbef04e5a 100644 --- a/starters/serverless-framework-sqs-dynamodb/.env.example +++ b/starters/serverless-framework-sqs-dynamodb/.env.example @@ -2,3 +2,5 @@ IS_OFFLINE=true # connection info for Redis used for caching REDIS_CACHE_URL=redis://:sOmE_sEcUrE_pAsS@localhost:6379 +# how long should items last in the cache by default +DEFAULT_CACHE_TIME=300000 diff --git a/starters/serverless-framework-sqs-dynamodb/README.md b/starters/serverless-framework-sqs-dynamodb/README.md index ca21415af..a4f1a17ad 100644 --- a/starters/serverless-framework-sqs-dynamodb/README.md +++ b/starters/serverless-framework-sqs-dynamodb/README.md @@ -60,7 +60,7 @@ git clone https://github.com/thisdot/starter.dev.git This README uses `npm` for commands. If you're using `yarn` or `pnpm`, utilize the equivalent version of the commands. -1. Create a `.env` file: +1. Create a `.env` file. This is to support any variable in the Serverless Configuration being read from `env:` and test running: ```bash cp .env.example .env @@ -92,11 +92,11 @@ npm start ### General Commands -- `build` bundles the project using the serverless packaging serverless. The produced artifacts will ship bundles shipped to AWS on deployment. You can optionally pass `--analyze ` to run the bundle analyzer and visualize the results to understand your handler bundles. +- `build` bundles the project using the Serverless Framework's out of the box `package` command. The produced artifacts will be shipped to AWS on deployment. You can optionally pass `--analyze ` to run the bundle analyzer and visualize the results to understand your handler bundles. - `deploy` ships the project to the configured AWS account using the Serverless Framework CLI command. - `start` runs the `serverless-offline` provided server for local development and testing. Be sure to have the local docker infrastructure running to emulate the related services. - `test` runs `jest` under the hood. -- `lint` runs `eslint` under the hood. You can use all the eslint available command line arguments. To lint the entire project, run `npm run lint .`, or equivalent. You can affix `--fix` to auto-correct linting issues that eslint can handle. +- `lint` runs `eslint` under the hood. You can use all the available eslint command line arguments. To lint the entire project, run `npm run lint .`, or equivalent. You can affix `--fix` to auto-correct linting issues that eslint can handle. - `format:check` runs prettier format checking on all project files. - `format:write` runs prettier format writing on all project files. @@ -141,7 +141,7 @@ This starter kit ships with a set of RESTful APIs. All routes are served via `ht ## Serverless Configuration -This kit uses the TypeScript option for configuration. It is type checked using the `@serverless/typescript` definitions over the DefinitelyTyped definitions because DefinitelyTyped is currently behind on its definition. However, the `@serverless/typescript` types have known issues with certain fields are noted directly in the configuration. +This kit uses the TypeScript option for configuration. It is type checked using the `@serverless/typescript` definitions over the DefinitelyTyped definitions because DefinitelyTyped is currently behind on its definition. However, the `@serverless/typescript` types have known issues with certain fields, which are noted directly in the configuration. It is not compatible with the automated CI/CD of the Serverless Dashboard as they only support the default YAML format. You can read more about [setting up CI/CD using GitHub for this project on the This Dot blog](https://www.thisdot.co/blog/github-actions-for-serverless-framework-deployments). @@ -227,6 +227,8 @@ There is an existing [DynamoDB Local preset for Jest](https://github.com/shelfio ## Deployment + + As a serverless implementation, most of the infrastructure will be deployed and configured correctly simply utilizing the `deploy` script provided by this kit which is just an alias for [`serverless deploy`](https://www.serverless.com/framework/docs/providers/aws/cli-reference/deploy). However, the Redis instance is not configurable via the Serverless Configuration and will need to be set up ahead of your first deploy and configured via environment variables. We recommend using [Serverless Framework's interface for AWS Secret Manager](https://www.serverless.com/blog/aws-secrets-management/) for security purposes. This entire stack can be deployed via CI tools such as GitHub Actions, CircleCI, etc. and is our recommended approach as this kit is incompatible with the Serverless Dashboard. The Serverless Dashboard CI only works with the configuration in the YAML format which we do not use to give developers type-safety in the config file. diff --git a/starters/serverless-framework-sqs-dynamodb/serverless.ts b/starters/serverless-framework-sqs-dynamodb/serverless.ts index 6c2057744..413c424cb 100644 --- a/starters/serverless-framework-sqs-dynamodb/serverless.ts +++ b/starters/serverless-framework-sqs-dynamodb/serverless.ts @@ -98,8 +98,9 @@ const serverlessConfiguration: AWS = { environment: { REGION: '${aws:region}', SLS_STAGE: '${sls:stage}', - // DynamoDB Tables + DEFAULT_CACHE_TIME: '${env:DEFAULT_CACHE_TIME}', REDIS_CACHE_URL: '${env:REDIS_CACHE_URL}', + // DynamoDB Tables TECHNOLOGIES_TABLE: '${param:technologiesTable}', }, iam: { diff --git a/starters/serverless-framework-sqs-dynamodb/src/handlers/example_job_processor.test.ts b/starters/serverless-framework-sqs-dynamodb/src/handlers/example_job_processor.test.ts index 90e9a313d..16981912a 100644 --- a/starters/serverless-framework-sqs-dynamodb/src/handlers/example_job_processor.test.ts +++ b/starters/serverless-framework-sqs-dynamodb/src/handlers/example_job_processor.test.ts @@ -6,7 +6,7 @@ describe('demo', () => { let logMock: jest.SpyInstance; beforeAll(async () => { - logMock = jest.spyOn(console, 'log').mockImplementation(() => ({})); + logMock = jest.spyOn(console, 'log').mockImplementation(() => {}); subject = await handler( { Records: [ diff --git a/starters/serverless-framework-sqs-dynamodb/src/handlers/example_stream_processor.test.ts b/starters/serverless-framework-sqs-dynamodb/src/handlers/example_stream_processor.test.ts index 70472cc10..64763354d 100644 --- a/starters/serverless-framework-sqs-dynamodb/src/handlers/example_stream_processor.test.ts +++ b/starters/serverless-framework-sqs-dynamodb/src/handlers/example_stream_processor.test.ts @@ -6,10 +6,13 @@ describe('demo', () => { let logMock: jest.SpyInstance; beforeAll(async () => { - logMock = jest.spyOn(console, 'log').mockImplementation(() => ({})); + logMock = jest.spyOn(console, 'log').mockImplementation(() => {}); subject = await handler( { Records: [ + { + eventName: 'UNKNOWN', + }, { eventName: 'INSERT', dynamodb: { diff --git a/starters/serverless-framework-sqs-dynamodb/src/handlers/example_stream_processor.ts b/starters/serverless-framework-sqs-dynamodb/src/handlers/example_stream_processor.ts index 8c14eb95b..c6e6cdf7d 100644 --- a/starters/serverless-framework-sqs-dynamodb/src/handlers/example_stream_processor.ts +++ b/starters/serverless-framework-sqs-dynamodb/src/handlers/example_stream_processor.ts @@ -1,13 +1,17 @@ import type { DynamoDBStreamHandler, DynamoDBRecord } from 'aws-lambda'; const recordHandler = async (record: DynamoDBRecord) => { - if (record.eventName === 'INSERT' && record.dynamodb) { + if (!record.dynamodb) { + return; + } + + if (record.eventName === 'INSERT') { console.log('Inserted Record', record.dynamodb.NewImage); - } else if (record.eventName === 'MODIFY' && record.dynamodb) { + } else if (record.eventName === 'MODIFY') { console.log('Updated Record'); console.log('New Values', record.dynamodb.NewImage); console.log('Old Values', record.dynamodb.OldImage); - } else if (record.eventName === 'REMOVE' && record.dynamodb) { + } else if (record.eventName === 'REMOVE') { console.log('Removed Record', record.dynamodb.OldImage); } }; diff --git a/starters/serverless-framework-sqs-dynamodb/src/handlers/generate_job.ts b/starters/serverless-framework-sqs-dynamodb/src/handlers/generate_job.ts index 498b20477..5deed8fdc 100644 --- a/starters/serverless-framework-sqs-dynamodb/src/handlers/generate_job.ts +++ b/starters/serverless-framework-sqs-dynamodb/src/handlers/generate_job.ts @@ -1,4 +1,5 @@ import type { APIGatewayProxyHandler } from 'aws-lambda'; +import { StatusCodes } from 'http-status-codes'; import { sendMessage } from '@/utils/sqs/sendMessage'; export const handler: APIGatewayProxyHandler = async () => { @@ -8,7 +9,7 @@ export const handler: APIGatewayProxyHandler = async () => { }); return { - statusCode: resp.success ? 201 : 400, + statusCode: resp.success ? StatusCodes.CREATED : StatusCodes.BAD_REQUEST, body: JSON.stringify(resp.data), }; }; diff --git a/starters/serverless-framework-sqs-dynamodb/src/handlers/technology_create.test.ts b/starters/serverless-framework-sqs-dynamodb/src/handlers/technology_create.test.ts index 01fea1a71..f4eb77e23 100644 --- a/starters/serverless-framework-sqs-dynamodb/src/handlers/technology_create.test.ts +++ b/starters/serverless-framework-sqs-dynamodb/src/handlers/technology_create.test.ts @@ -2,9 +2,7 @@ import type { APIGatewayProxyResult, APIGatewayProxyEvent, Context, Callback } f import { PutItemCommand, ServiceInputTypes, ServiceOutputTypes } from '@aws-sdk/client-dynamodb'; import { AwsStub, mockClient } from 'aws-sdk-client-mock'; import { getClient } from '@/utils/dynamodb/getClient'; -import { removeFromCache } from '@/utils/cache/removeFromCache'; import * as technologyCreate from '@/models/technology/create'; -import { getCacheKey } from '@/models/technology/getCacheKey'; import { handler } from './technology_create'; describe('POST /technology', () => { @@ -33,7 +31,6 @@ describe('POST /technology', () => { afterAll(async () => { ddbMock.restore(); - await removeFromCache(getCacheKey('87af19b1-aa0d-4178-a30c-2fa8cd1f2cff')); }); it('returns 201 status', () => { @@ -63,7 +60,6 @@ describe('POST /technology', () => { afterAll(async () => { jest.restoreAllMocks(); - await removeFromCache(getCacheKey('87af19b1-aa0d-4178-a30c-2fa8cd1f2cff')); }); it('returns 400 status', () => { @@ -97,7 +93,6 @@ describe('POST /technology', () => { afterAll(async () => { jest.restoreAllMocks(); - await removeFromCache(getCacheKey('87af19b1-aa0d-4178-a30c-2fa8cd1f2cff')); }); it('returns 500 status', () => { diff --git a/starters/serverless-framework-sqs-dynamodb/src/models/technology/getAll.ts b/starters/serverless-framework-sqs-dynamodb/src/models/technology/getAll.ts index 418cebea2..943d98455 100644 --- a/starters/serverless-framework-sqs-dynamodb/src/models/technology/getAll.ts +++ b/starters/serverless-framework-sqs-dynamodb/src/models/technology/getAll.ts @@ -1,6 +1,5 @@ import { scan } from '@/utils/dynamodb/scan'; export const getAll = async () => { - const items = await scan(process.env.TECHNOLOGIES_TABLE); - return items; + return await scan(process.env.TECHNOLOGIES_TABLE); }; diff --git a/starters/serverless-framework-sqs-dynamodb/src/types/environment.d.ts b/starters/serverless-framework-sqs-dynamodb/src/types/environment.d.ts index d022fbe97..ecffbdf71 100644 --- a/starters/serverless-framework-sqs-dynamodb/src/types/environment.d.ts +++ b/starters/serverless-framework-sqs-dynamodb/src/types/environment.d.ts @@ -4,6 +4,7 @@ declare global { REGION: string; SLS_STAGE: string; + DEFAULT_CACHE_TIME: string; REDIS_CACHE_URL: string; TECHNOLOGIES_TABLE: string; } diff --git a/starters/serverless-framework-sqs-dynamodb/src/utils/cache/constants.ts b/starters/serverless-framework-sqs-dynamodb/src/utils/cache/constants.ts index fa92b08fa..868d5ab0f 100644 --- a/starters/serverless-framework-sqs-dynamodb/src/utils/cache/constants.ts +++ b/starters/serverless-framework-sqs-dynamodb/src/utils/cache/constants.ts @@ -1 +1 @@ -export const DEFAULT_CACHE_TIME = 300_000; // 5 minutes = 1000 ms/s * 60 s/min * 5 min +export const DEFAULT_CACHE_TIME = parseInt(process.env.DEFAULT_CACHE_TIME, 10); diff --git a/starters/serverless-framework-sqs-dynamodb/src/utils/dynamodb/deleteItem.test.ts b/starters/serverless-framework-sqs-dynamodb/src/utils/dynamodb/deleteItem.test.ts index c49a88324..93cad37ba 100644 --- a/starters/serverless-framework-sqs-dynamodb/src/utils/dynamodb/deleteItem.test.ts +++ b/starters/serverless-framework-sqs-dynamodb/src/utils/dynamodb/deleteItem.test.ts @@ -42,6 +42,7 @@ describe('dynamodb.deleteItem()', () => { describe('when item does not exist', () => { beforeAll(async () => { + jest.spyOn(console, 'warn').mockImplementation(() => {}); jest.spyOn(console, 'error').mockImplementation(() => {}); ddbMock.on(DeleteItemCommand).resolves({ Attributes: undefined, diff --git a/starters/serverless-framework-sqs-dynamodb/src/utils/dynamodb/deleteItem.ts b/starters/serverless-framework-sqs-dynamodb/src/utils/dynamodb/deleteItem.ts index be56381a0..92eee28d8 100644 --- a/starters/serverless-framework-sqs-dynamodb/src/utils/dynamodb/deleteItem.ts +++ b/starters/serverless-framework-sqs-dynamodb/src/utils/dynamodb/deleteItem.ts @@ -15,6 +15,7 @@ export const deleteItem = async (tableName: string, key: Record const response = await client.send(command); if (!response || !response.Attributes) { + console.warn('dynamodb.deleteItem Warning - ${response}'); return null; } return unmarshall(response.Attributes); diff --git a/starters/serverless-framework-sqs-dynamodb/src/utils/isOffline/isOffline.ts b/starters/serverless-framework-sqs-dynamodb/src/utils/isOffline/isOffline.ts index fe226e57c..c0ba0549f 100644 --- a/starters/serverless-framework-sqs-dynamodb/src/utils/isOffline/isOffline.ts +++ b/starters/serverless-framework-sqs-dynamodb/src/utils/isOffline/isOffline.ts @@ -1,5 +1,5 @@ /** - * Utility function for checking where functions are being run locally via serverless offline + * Utility function for checking if functions are being run locally via serverless offline * or if they're running on infrastructure. Helpful for detecting which connection string to use. * * @returns boolean are we running locally or on infra?