diff --git a/README.md b/README.md index 4dff916..b2d94d3 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,51 @@ yarn add @tnotifier/lamware npm install @tnotifier/lamware ``` -We maintain and ship various middlewares for public use - you can [install them too!](#TODO:) +We maintain and ship various middlewares for public use - you can [install them too!](https://github.com/tnotifier/lamware/tree/master/packages) ## Usage -You can check out the [`example` folder](#TODO:) for a fully-featured example. +You can check out the [`example` folder](https://github.com/tnotifier/lamware/tree/master/example) for a fully-featured example with the AWS CDK stack to deploy it. + +```typescript +import { powertoolsTracing } from '@tnotifier/lamware-powertools-tracing'; +import { powertoolsLogger } from '@tnotifier/lamware-powertools-logger'; +import type { APIGatewayProxyHandlerV2 } from 'aws-lambda'; +import { doNotWait } from '@tnotifier/lamware-do-not-wait'; +import { appconfig } from '@tnotifier/lamware-appconfig'; +import { sentry } from '@tnotifier/lamware-sentry'; +import { warmer } from '@tnotifier/lamware-warmer'; +import { lamware } from '@tnotifier/lamware'; + +const { handler } = lamware>() + .use(doNotWait()) + .use(powertoolsTracing({ + serviceName: 'lamware-example', + })) + .use(powertoolsLogger({ + serviceName: 'lamware-example', + logLevel: 'DEBUG', + })) + .use(appconfig<{ hello: string }>({ + app: 'tnotifier-lamware-example', + env: 'production', + config: 'production', + })) + .use(sentry({ + config: { + dsn: 'https://d99b0b438475869385706e70157c5e05@o1080839.ingest.sentry.io/6270000', + }, + })) + .use(warmer()) + .execute(async ({ state }) => { + return { + statusCode: 200, + body: JSON.stringify({ + hello: 'world', + appconfig: state.config, + }), + }; + }); + +export { handler }; +``` diff --git a/packages/appconfig/README.md b/packages/appconfig/README.md new file mode 100644 index 0000000..5ccc0c2 --- /dev/null +++ b/packages/appconfig/README.md @@ -0,0 +1,60 @@ +
+ + NPM + + + Discord + + Apache-2.0 +

Lamware - AWS AppConfig

+
+ +This [Lamware](https://github.com/tnotifier/lamware) Middleware utilizes an API exposed by the [AWS Lambda AppConfig Layer Extension](https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-integration-lambda-extensions.html) to pull-down a copy of an AppConfig configuration and allows you to easily provide TypeScript typings for it. + +## Installation + +This package is available via NPM: + +```bash +yarn add @tnotifier/lamware-appconfig + +# or + +npm install @tnotifier/lamware-appconfig +``` + +## Usage + +```typescript +import type { APIGatewayProxyHandlerV2 } from 'aws-lambda'; +import { appconfig } from '@tnotifier/lamware-appconfig'; +import { lamware } from '@tnotifier/lamware'; + +interface AppConfig { + helloWorld: string; +} + +const { handler } = lamware>() + /** + * You can provide an Interface to the middleware to automatically type + * the config in the handler `execute`. + **/ + .use(appconfig({ + // Ensure you provide the info required to pull down a configuration. + app: 'tnotifier-api', + env: 'production', + config: 'production', + // You can also optionally provide an override URL for the AppConfig API. + url: 'http://localhost:2772', // The default, provided by the AppConfig Lambda Extension. + })) + .execute(async ({ state }) => { + return { + statusCode: 200, + body: JSON.stringify({ + debug: state.config.helloWorld, + }), + }; + }); + +export { handler }; +``` diff --git a/packages/appconfig/package.json b/packages/appconfig/package.json index 2c1209b..a85138c 100644 --- a/packages/appconfig/package.json +++ b/packages/appconfig/package.json @@ -12,7 +12,7 @@ "license": "GPL-3.0-only", "author": { "name": "Evil Kiwi Limited", - "url": "https://tnotifier.app", + "url": "https://evil.kiwi", "email": "support@tnotifier.app" }, "homepage": "https://github.com/tnotifier/lamware", diff --git a/packages/core/README.md b/packages/core/README.md new file mode 100644 index 0000000..44ac929 --- /dev/null +++ b/packages/core/README.md @@ -0,0 +1,12 @@ +
+ + NPM + + + Discord + + Apache-2.0 +

Lamware - Core Functionality

+
+ +Refer to the [root README](https://github.com/tnotifier/lamware/blob/master/README.md). diff --git a/packages/core/package.json b/packages/core/package.json index 8491e11..95aa021 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -11,7 +11,7 @@ "license": "GPL-3.0-only", "author": { "name": "Evil Kiwi Limited", - "url": "https://tnotifier.app", + "url": "https://evil.kiwi", "email": "support@tnotifier.app" }, "homepage": "https://github.com/tnotifier/lamware", diff --git a/packages/do-not-wait/README.md b/packages/do-not-wait/README.md new file mode 100644 index 0000000..10c5346 --- /dev/null +++ b/packages/do-not-wait/README.md @@ -0,0 +1,40 @@ +
+ + NPM + + + Discord + + Apache-2.0 +

Lamware - Do Not Wait

+
+ +This [Lamware](https://github.com/tnotifier/lamware) Middleware implements a Lambda best-practice of making sure Lambda doesn't wait for the event loop to be empty prior to responding by ensuring the `callbackWaitsForEmptyEventLoop` context variable is set to `false`. + +## Installation + +This package is available via NPM: + +```bash +yarn add @tnotifier/lamware-do-not-wait + +# or + +npm install @tnotifier/lamware-do-not-wait +``` + +## Usage + +```typescript +import type { APIGatewayProxyHandlerV2 } from 'aws-lambda'; +import { doNotWait } from '@tnotifier/lamware-do-not-wait'; +import { lamware } from '@tnotifier/lamware'; + +const { handler } = lamware>() + .use(doNotWait()) + .execute(async () => { + return { statusCode: 200 }; + }); + +export { handler }; +``` diff --git a/packages/do-not-wait/package.json b/packages/do-not-wait/package.json index a65572f..6745c9d 100644 --- a/packages/do-not-wait/package.json +++ b/packages/do-not-wait/package.json @@ -12,7 +12,7 @@ "license": "GPL-3.0-only", "author": { "name": "Evil Kiwi Limited", - "url": "https://tnotifier.app", + "url": "https://evil.kiwi", "email": "support@tnotifier.app" }, "homepage": "https://github.com/tnotifier/lamware", diff --git a/packages/powertools-logger/README.md b/packages/powertools-logger/README.md new file mode 100644 index 0000000..e133377 --- /dev/null +++ b/packages/powertools-logger/README.md @@ -0,0 +1,49 @@ +
+ + NPM + + + Discord + + Apache-2.0 +

Lamware - AWS Powertools Logger

+
+ +This [Lamware](https://github.com/tnotifier/lamware) Middleware utilizes the official [Lambda TypeScript Powertools](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/logger/) provided by AWS to: + +- Set-up and memoize a root `Logger` instance +- Automatically add Lambda Context to all logging (can be disabled) +- Provide a logging interface to all further middleware & the handler itself + +## Installation + +This package is available via NPM: + +```bash +yarn add @tnotifier/lamware-powertools-logger + +# or + +npm install @tnotifier/lamware-powertools-logger +``` + +## Usage + +```typescript +import { powertoolsLogger } from '@tnotifier/lamware-powertools-logger'; +import type { APIGatewayProxyHandlerV2 } from 'aws-lambda'; +import { lamware } from '@tnotifier/lamware'; + +const { handler } = lamware>() + .use(powertoolsLogger({ + // Options are pass-through to the Logger instance. + serviceName: 'my-api', + })) + .execute(async ({ state, logger }) => { + logger.debug('Hello world!'); + + return { statusCode: 200 }; + }); + +export { handler }; +``` diff --git a/packages/powertools-logger/package.json b/packages/powertools-logger/package.json index 2a1078d..8254655 100644 --- a/packages/powertools-logger/package.json +++ b/packages/powertools-logger/package.json @@ -12,7 +12,7 @@ "license": "GPL-3.0-only", "author": { "name": "Evil Kiwi Limited", - "url": "https://tnotifier.app", + "url": "https://evil.kiwi", "email": "support@tnotifier.app" }, "homepage": "https://github.com/tnotifier/lamware", diff --git a/packages/powertools-metrics/.gitignore b/packages/powertools-metrics/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/packages/powertools-metrics/.gitignore @@ -0,0 +1 @@ +/build diff --git a/packages/powertools-metrics/README.md b/packages/powertools-metrics/README.md new file mode 100644 index 0000000..7956892 --- /dev/null +++ b/packages/powertools-metrics/README.md @@ -0,0 +1,54 @@ +
+ + NPM + + + Discord + + Apache-2.0 +

Lamware - AWS Powertools Metrics

+
+ +This [Lamware](https://github.com/tnotifier/lamware) Middleware utilizes the official [Lambda TypeScript Powertools](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/metrics/) provided by AWS to: + +- Set-up and memoize a root `Metrics` instance +- Publish Metrics automatically after the Function handler executes +- Optionally set-up default dimensions +- Automatically capture various metrics: + - Cold starts + - Function name + +## Installation + +This package is available via NPM: + +```bash +yarn add @tnotifier/lamware-powertools-metrics + +# or + +npm install @tnotifier/lamware-powertools-metrics +``` + +## Usage + +```typescript +import { powertoolsMetrics } from '@tnotifier/lamware-powertools-metrics'; +import { MetricUnits } from '@aws-lambda-powertools/metrics'; +import type { APIGatewayProxyHandlerV2 } from 'aws-lambda'; +import { lamware } from '@tnotifier/lamware'; + +const { handler } = lamware>() + .use(powertoolsMetrics({ + // Options are pass-through to the Tracing instance. + namespace: 'tnotifier', + serviceName: 'my-api', + })) + .execute(async ({ state }) => { + state.metrics.addMetric('successfulBooking', MetricUnits.Count, 1); + + return { statusCode: 200 }; + }); + +export { handler }; +``` diff --git a/packages/powertools-metrics/package.json b/packages/powertools-metrics/package.json new file mode 100644 index 0000000..25f2f58 --- /dev/null +++ b/packages/powertools-metrics/package.json @@ -0,0 +1,55 @@ +{ + "name": "@tnotifier/lamware-powertools-metrics", + "version": "1.0.0", + "description": "Lamware Middleware to utilize the official Metrics powertools", + "files": [ + "build" + ], + "sideEffects": false, + "main": "./build/index.cjs.js", + "module": "./build/index.es.js", + "types": "./build/src/index.d.ts", + "license": "GPL-3.0-only", + "author": { + "name": "Evil Kiwi Limited", + "url": "https://evil.kiwi", + "email": "support@tnotifier.app" + }, + "homepage": "https://github.com/tnotifier/lamware", + "bugs": { + "url": "https://github.com/tnotifier/lamware/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/tnotifier/lamware.git" + }, + "keywords": [ + "lambda", + "middleware", + "typescript" + ], + "publishConfig": { + "access": "public" + }, + "scripts": { + "clean": "rimraf build", + "prepack": "yarn build", + "build": "yarn clean && yarn compile", + "compile": "vite build", + "dev": "vite build --watch", + "lint": "eslint --ext .ts,vue --ignore-path .gitignore src" + }, + "dependencies": { + "@aws-lambda-powertools/metrics": "^0.7.1", + "@tnotifier/lamware": "^1.1.5" + }, + "devDependencies": { + "@types/node": "^14.18.12", + "eslint": "^8.11.0", + "rimraf": "^3.0.2", + "tslib": "^2.3.1", + "typescript": "^4.6.2", + "vite": "^2.8.6", + "vite-plugin-dts": "^0.9.10" + } +} diff --git a/packages/powertools-metrics/src/index.ts b/packages/powertools-metrics/src/index.ts new file mode 100755 index 0000000..48aadb3 --- /dev/null +++ b/packages/powertools-metrics/src/index.ts @@ -0,0 +1,44 @@ +import type { MetricsOptions } from '@aws-lambda-powertools/metrics/lib/types'; +import { Metrics } from '@aws-lambda-powertools/metrics'; +import type { Middleware } from '@tnotifier/lamware'; +import type { Handler } from 'aws-lambda'; + +export interface Options extends MetricsOptions { + captureColdStartMetric?: boolean; + captureFunctionName?: boolean; + throwOnEmptyMetrics?: boolean; +} + +export const powertoolsMetrics = (options: Options): Middleware => { + return { + id: 'powertools-metrics', + pure: true, + init: async () => ({ metrics: new Metrics(options) }), + before: async (payload) => { + if (options.captureFunctionName !== false) { + payload.state.metrics.setFunctionName(payload.context.functionName); + } + + const { throwOnEmptyMetrics, defaultDimensions, captureColdStartMetric } = options; + + if (throwOnEmptyMetrics !== false) { + payload.state.metrics.throwOnEmptyMetrics(); + } + + if (defaultDimensions !== undefined) { + payload.state.metrics.setDefaultDimensions(defaultDimensions); + } + + if (captureColdStartMetric !== false) { + payload.state.metrics.captureColdStartMetric(); + } + + return payload; + }, + after: async (payload) => { + payload.state.metrics.publishStoredMetrics(); + + return payload; + }, + }; +}; diff --git a/packages/powertools-metrics/tsconfig.json b/packages/powertools-metrics/tsconfig.json new file mode 100644 index 0000000..48f049d --- /dev/null +++ b/packages/powertools-metrics/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../build/tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "./build/", + "paths": { + "*": ["./src/types/*.d.ts"], + "@/*": ["./src/*"] + } + }, + "include": [ + "./src/**/*" + ] +} diff --git a/packages/powertools-metrics/vite.config.ts b/packages/powertools-metrics/vite.config.ts new file mode 100644 index 0000000..330246d --- /dev/null +++ b/packages/powertools-metrics/vite.config.ts @@ -0,0 +1,54 @@ +import { builtinModules } from 'module'; +import { join, resolve } from 'path'; +import { defineConfig } from 'vite'; +import dts from 'vite-plugin-dts'; +import pkg from './package.json'; + +export default defineConfig({ + root: __dirname, + plugins: [ + dts({ + outputDir: join(__dirname, 'build'), + tsConfigFilePath: join(__dirname, 'tsconfig.json'), + staticImport: true, + skipDiagnostics: false, + logDiagnostics: true, + }), + ], + resolve: { + alias: [ + { find: /^@\/(.*)/, replacement: `${resolve(__dirname, 'src')}/$1` }, + ], + }, + json: { + namedExports: false, + stringify: true, + }, + build: { + sourcemap: process.env.MODE === 'development' ? true : false, + outDir: 'build', + assetsDir: '.', + minify: process.env.MODE === 'development' ? false : 'terser', + target: ['chrome91', 'node14'], + terserOptions: { + ecma: 2020, + compress: { + passes: 2, + }, + safari10: false, + }, + lib: { + entry: join('src', 'index.ts'), + fileName: 'index', + name: 'LamwarePowertoolsMetrics', + formats: ['es', 'cjs'], + }, + rollupOptions: { + external: [ + ...builtinModules, + ...Object.keys(pkg.dependencies ?? {}), + ], + }, + emptyOutDir: true, + }, +}); diff --git a/packages/powertools-tracing/README.md b/packages/powertools-tracing/README.md new file mode 100644 index 0000000..f62074c --- /dev/null +++ b/packages/powertools-tracing/README.md @@ -0,0 +1,54 @@ +
+ + NPM + + + Discord + + Apache-2.0 +

Lamware - AWS Powertools Tracing

+
+ +This [Lamware](https://github.com/tnotifier/lamware) Middleware utilizes the official [Lambda TypeScript Powertools](https://awslabs.github.io/aws-lambda-powertools-typescript/latest/core/tracer/) provided by AWS to: + +- Set-up and memoize a root `Tracer` instance +- Automatically set-up a root Tracer Segment to: + - Annotate the cold-start time + - Set the service name + - If the response is an error, capture that too + - Clean-up and close segments created by the package + +## Installation + +This package is available via NPM: + +```bash +yarn add @tnotifier/lamware-powertools-tracing + +# or + +npm install @tnotifier/lamware-powertools-tracing +``` + +## Usage + +```typescript +import { powertoolsTracing } from '@tnotifier/lamware-powertools-tracing'; +import type { APIGatewayProxyHandlerV2 } from 'aws-lambda'; +import { lamware } from '@tnotifier/lamware'; + +const { handler } = lamware>() + .use(powertoolsTracing({ + // Options are pass-through to the Tracing instance. + serviceName: 'my-api', + })) + .execute(async ({ state }) => { + // state.tracer + // state.segment + // state.subsegment + + return { statusCode: 200 }; + }); + +export { handler }; +``` diff --git a/packages/powertools-tracing/package.json b/packages/powertools-tracing/package.json index 5f5f1c1..bab0f07 100644 --- a/packages/powertools-tracing/package.json +++ b/packages/powertools-tracing/package.json @@ -12,7 +12,7 @@ "license": "GPL-3.0-only", "author": { "name": "Evil Kiwi Limited", - "url": "https://tnotifier.app", + "url": "https://evil.kiwi", "email": "support@tnotifier.app" }, "homepage": "https://github.com/tnotifier/lamware", diff --git a/packages/sentry/README.md b/packages/sentry/README.md new file mode 100644 index 0000000..d59c9be --- /dev/null +++ b/packages/sentry/README.md @@ -0,0 +1,49 @@ +
+ + NPM + + + Discord + + Apache-2.0 +

Lamware - Sentry

+
+ +This [Lamware](https://github.com/tnotifier/lamware) Middleware utilizes the [Sentry Serverless SDK](https://docs.sentry.io/platforms/node/guides/aws-lambda/) to automatically initialize and wrap your Lambda Function handler to capture errors and report them to Sentry. + +## Installation + +This package is available via NPM: + +```bash +yarn add @tnotifier/lamware-sentry + +# or + +npm install @tnotifier/lamware-sentry +``` + +## Usage + +```typescript +import type { APIGatewayProxyHandlerV2 } from 'aws-lambda'; +import { sentry } from '@tnotifier/lamware-sentry'; +import { lamware } from '@tnotifier/lamware'; + +const { handler } = lamware>() + .use(sentry({ + // You can provide config directly to the SDK `init()`. + config: { + dsn: 'your-sentry-dsn-here', + }, + // You can also optionally provide options to the wrapper. + wrapper: { + callbackWaitsForEmptyEventLoop: false, + }, + })) + .execute(async () => { + return { statusCode: 200 }; + }); + +export { handler }; +``` diff --git a/packages/sentry/package.json b/packages/sentry/package.json index 5388cec..e028385 100644 --- a/packages/sentry/package.json +++ b/packages/sentry/package.json @@ -12,7 +12,7 @@ "license": "GPL-3.0-only", "author": { "name": "Evil Kiwi Limited", - "url": "https://tnotifier.app", + "url": "https://evil.kiwi", "email": "support@tnotifier.app" }, "homepage": "https://github.com/tnotifier/lamware", diff --git a/packages/warmer/README.md b/packages/warmer/README.md new file mode 100644 index 0000000..3e38227 --- /dev/null +++ b/packages/warmer/README.md @@ -0,0 +1,43 @@ +
+ + NPM + + + Discord + + Apache-2.0 +

Lamware - Warmer

+
+ +This [Lamware](https://github.com/tnotifier/lamware) Middleware utilizes the [`lambda-warmer`](https://github.com/jeremydaly/lambda-warmer) package, automating usage of it by providing: + +- Automatically registering the warming listener +- Early-exit out of your Function if it detects a warming event + +## Installation + +This package is available via NPM: + +```bash +yarn add @tnotifier/lamware-warmer + +# or + +npm install @tnotifier/lamware-warmer +``` + +## Usage + +```typescript +import type { APIGatewayProxyHandlerV2 } from 'aws-lambda'; +import { warmer } from '@tnotifier/lamware-warmer'; +import { lamware } from '@tnotifier/lamware'; + +const { handler } = lamware>() + .use(warmer()) + .execute(async () => { + return { statusCode: 200 }; + }); + +export { handler }; +``` diff --git a/packages/warmer/package.json b/packages/warmer/package.json index f339211..752bcd4 100644 --- a/packages/warmer/package.json +++ b/packages/warmer/package.json @@ -12,7 +12,7 @@ "license": "GPL-3.0-only", "author": { "name": "Evil Kiwi Limited", - "url": "https://tnotifier.app", + "url": "https://evil.kiwi", "email": "support@tnotifier.app" }, "homepage": "https://github.com/tnotifier/lamware", diff --git a/yarn.lock b/yarn.lock index 0370e83..2382012 100644 --- a/yarn.lock +++ b/yarn.lock @@ -54,6 +54,15 @@ __metadata: languageName: node linkType: hard +"@aws-lambda-powertools/metrics@npm:^0.7.1": + version: 0.7.1 + resolution: "@aws-lambda-powertools/metrics@npm:0.7.1" + dependencies: + "@aws-lambda-powertools/commons": ^0.7.1 + checksum: bca8e3f8728f5785e44868bcc789aaac0faf64d7625fcf4ea45f8749531afbbbe20710c1ba2af0b2860dc8ec30efe628230a1140fab249c8f77c911931716d95 + languageName: node + linkType: hard + "@aws-lambda-powertools/tracer@npm:^0.7.1": version: 0.7.1 resolution: "@aws-lambda-powertools/tracer@npm:0.7.1" @@ -2107,6 +2116,22 @@ __metadata: languageName: unknown linkType: soft +"@tnotifier/lamware-powertools-metrics@workspace:packages/powertools-metrics": + version: 0.0.0-use.local + resolution: "@tnotifier/lamware-powertools-metrics@workspace:packages/powertools-metrics" + dependencies: + "@aws-lambda-powertools/metrics": ^0.7.1 + "@tnotifier/lamware": ^1.1.5 + "@types/node": ^14.18.12 + eslint: ^8.11.0 + rimraf: ^3.0.2 + tslib: ^2.3.1 + typescript: ^4.6.2 + vite: ^2.8.6 + vite-plugin-dts: ^0.9.10 + languageName: unknown + linkType: soft + "@tnotifier/lamware-powertools-tracing@workspace:packages/powertools-tracing": version: 0.0.0-use.local resolution: "@tnotifier/lamware-powertools-tracing@workspace:packages/powertools-tracing"