Skip to content

docs: refresh SAM examples #1180

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Dec 19, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions examples/lambda-functions/common/dynamodb-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { DynamoDBClient, ScanCommand, GetItemCommand, PutItemCommand } from '@aws-sdk/client-dynamodb';

const dynamodbClientV3 = new DynamoDBClient({});

export {
dynamodbClientV3,
ScanCommand,
GetItemCommand,
PutItemCommand
};
37 changes: 37 additions & 0 deletions examples/lambda-functions/common/powertools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Logger, injectLambdaContext } from '@aws-lambda-powertools/logger';
import { Metrics, logMetrics } from '@aws-lambda-powertools/metrics';
import { Tracer, captureLambdaHandler } from '@aws-lambda-powertools/tracer';
import { LambdaInterface } from '@aws-lambda-powertools/commons';

const awsLambdaPowertoolsVersion = '1.4.1';

const defaultValues = {
region: process.env.AWS_REGION || 'N/A',
executionEnv: process.env.AWS_EXECUTION_ENV || 'N/A'
};

const logger = new Logger({
persistentLogAttributes: {
...defaultValues,
logger: {
name: '@aws-lambda-powertools/logger',
version: awsLambdaPowertoolsVersion,
}
},
});

const metrics = new Metrics({
defaultDimensions: defaultValues
});

const tracer = new Tracer();

export {
logger,
metrics,
tracer,
injectLambdaContext,
logMetrics,
captureLambdaHandler,
LambdaInterface
};
67 changes: 48 additions & 19 deletions examples/lambda-functions/get-all-items.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
import { Metrics } from '@aws-lambda-powertools/metrics';
import { Logger } from '@aws-lambda-powertools/logger';
import { Tracer } from '@aws-lambda-powertools/tracer';
import { DocumentClient } from 'aws-sdk/clients/dynamodb';
import middy from '@middy/core';
import { logger, tracer, metrics, injectLambdaContext, logMetrics, captureLambdaHandler, } from './common/powertools';
import { dynamodbClientV3, ScanCommand } from './common/dynamodb-client';
import got from 'got';

// Create the PowerTools clients
const metrics = new Metrics();
const logger = new Logger();
const tracer = new Tracer();
/*
*
* This example uses the Middy middleware instrumentation.
* It is the best choice if your existing code base relies on the Middy middleware engine.
* Powertools offers compatible Middy middleware to make this integration seamless.
* Find more Information in the docs: https://awslabs.github.io/aws-lambda-powertools-typescript/
*
*/

// Create DynamoDB DocumentClient and patch it for tracing
const docClient = tracer.captureAWSClient(new DocumentClient());
// Patch DynamoDB client for tracing
const docClient = tracer.captureAWSv3Client(dynamodbClientV3);

// Get the DynamoDB table name from environment variables
const tableName = process.env.SAMPLE_TABLE;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this in a constants.ts file and import it throughout?


/**
*
* Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
Expand All @@ -24,7 +28,7 @@ const tableName = process.env.SAMPLE_TABLE;
* @returns {Object} object - API Gateway Lambda Proxy Output Format
*
*/
export const getAllItemsHandler = async (event: APIGatewayProxyEvent, context: Context): Promise<APIGatewayProxyResult> => {
const getAllItemsHandler = async (event: APIGatewayProxyEvent, context: Context): Promise<APIGatewayProxyResult> => {
if (event.httpMethod !== 'GET') {
throw new Error(`getAllItems only accepts GET method, you tried: ${event.httpMethod}`);
}
Expand All @@ -40,29 +44,45 @@ export const getAllItemsHandler = async (event: APIGatewayProxyEvent, context: C
tracer.annotateColdStart();
tracer.addServiceNameAnnotation();

// Tracer: Add annotation for the awsRequestId
// Tracer: Add awsRequestId as annotation
tracer.putAnnotation('awsRequestId', context.awsRequestId);

// Metrics: Capture cold start metrics
metrics.captureColdStartMetric();

// Logger: Add persistent attributes to each log statement
logger.addPersistentLogAttributes({
// Logger: Append awsRequestId to each log statement
logger.appendKeys({
awsRequestId: context.awsRequestId,
});

// Request a sample random uuid from a webservice
const res = await got('https://httpbin.org/uuid');
const uuid = JSON.parse(res.body).uuid;

// Logger: Append uuid to each log statement
logger.appendKeys({ uuid });

// Tracer: Add uuid as annotation
tracer.putAnnotation('uuid', uuid);

// Metrics: Add uuid as metadata
metrics.addMetadata('uuid', uuid);

// Define response object
let response;

// get all items from the table (only first 1MB data, you can use `LastEvaluatedKey` to get the rest of data)
// https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#scan-property
// https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html
let response;
try {
if (!tableName) {
throw new Error('SAMPLE_TABLE environment variable is not set');
}

const data = await docClient.scan({
const data = await docClient.send(new ScanCommand({
TableName: tableName
}).promise();
}));

const items = data.Items;

// Logger: All log statements are written to CloudWatch
Expand Down Expand Up @@ -91,4 +111,13 @@ export const getAllItemsHandler = async (event: APIGatewayProxyEvent, context: C
logger.info(`response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}`);

return response;
};
};

// Wrap the handler with middy
export const handler = middy(getAllItemsHandler)
// Use the middleware by passing the Metrics instance as a parameter
.use(logMetrics(metrics))
// Use the middleware by passing the Logger instance as a parameter
.use(injectLambdaContext(logger, { logEvent: true }))
// Use the middleware by passing the Tracer instance as a parameter
.use(captureLambdaHandler(tracer));
181 changes: 110 additions & 71 deletions examples/lambda-functions/get-by-id.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
import { Metrics } from '@aws-lambda-powertools/metrics';
import { Logger } from '@aws-lambda-powertools/logger';
import { Tracer } from '@aws-lambda-powertools/tracer';
import { DocumentClient } from 'aws-sdk/clients/dynamodb';
import { logger, tracer, metrics, LambdaInterface } from './common/powertools';
import { dynamodbClientV3, GetItemCommand } from './common/dynamodb-client';
import got from 'got';

// Create the PowerTools clients
const metrics = new Metrics();
const logger = new Logger();
const tracer = new Tracer();
/*
*
* This example uses the Method decorator instrumentation.
* Use TypeScript method decorators if you prefer writing your business logic using TypeScript Classes.
* If you aren’t using Classes, this requires the most significant refactoring.
* Find more Information in the docs: https://awslabs.github.io/aws-lambda-powertools-typescript/
*
*/

// Create DynamoDB DocumentClient and patch it for tracing
const docClient = tracer.captureAWSClient(new DocumentClient());
// Patch DynamoDB client for tracing
const docClient = tracer.captureAWSv3Client(dynamodbClientV3);

// Get the DynamoDB table name from environment variables
const tableName = process.env.SAMPLE_TABLE;
Expand All @@ -25,72 +28,108 @@ const tableName = process.env.SAMPLE_TABLE;
*
*/

export const getByIdHandler = async (event: APIGatewayProxyEvent, context: Context): Promise<APIGatewayProxyResult> => {
if (event.httpMethod !== 'GET') {
throw new Error(`getById only accepts GET method, you tried: ${event.httpMethod}`);
class Lambda implements LambdaInterface {

@tracer.captureMethod()
public async getUuid(): Promise<string> {
// Request a sample random uuid from a webservice
const res = await got('https://httpbin.org/uuid');

return JSON.parse(res.body).uuid;
}
// Tracer: Get facade segment created by AWS Lambda
const segment = tracer.getSegment();

// Tracer: Create subsegment for the function & set it as active
const handlerSegment = segment.addNewSubsegment(`## ${process.env._HANDLER}`);
tracer.setSegment(handlerSegment);

// Tracer: Annotate the subsegment with the cold start & serviceName
tracer.annotateColdStart();
tracer.addServiceNameAnnotation();

// Tracer: Add annotation for the awsRequestId
tracer.putAnnotation('awsRequestId', context.awsRequestId);

// Metrics: Capture cold start metrics
metrics.captureColdStartMetric();

// Logger: Add persistent attributes to each log statement
logger.addPersistentLogAttributes({
awsRequestId: context.awsRequestId,
});

// Get the item from the table
// https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#get-property
let response;
try {
if (!tableName) {
throw new Error('SAMPLE_TABLE environment variable is not set');
}
if (!event.pathParameters) {
throw new Error('event does not contain pathParameters');

@tracer.captureLambdaHandler()
@logger.injectLambdaContext({ logEvent: true })
@metrics.logMetrics()
public async handler(event: APIGatewayProxyEvent, context: Context): Promise<APIGatewayProxyResult> {

if (event.httpMethod !== 'GET') {
throw new Error(`getById only accepts GET method, you tried: ${event.httpMethod}`);
}
if (!event.pathParameters.id) {
throw new Error('PathParameter id is missing');
// Tracer: Get facade segment created by AWS Lambda
const segment = tracer.getSegment();

// Tracer: Create subsegment for the function & set it as active
const handlerSegment = segment.addNewSubsegment(`## ${process.env._HANDLER}`);
tracer.setSegment(handlerSegment);

// Tracer: Annotate the subsegment with the cold start & serviceName
tracer.annotateColdStart();
tracer.addServiceNameAnnotation();

// Tracer: Add awsRequestId as annotation
tracer.putAnnotation('awsRequestId', context.awsRequestId);

// Metrics: Capture cold start metrics
metrics.captureColdStartMetric();

// Logger: Append awsRequestId to each log statement
logger.appendKeys({
awsRequestId: context.awsRequestId,
});

// Call the getUuid function
const uuid = await this.getUuid();

// Logger: Append uuid to each log statement
logger.appendKeys({ uuid });

// Tracer: Add uuid as annotation
tracer.putAnnotation('uuid', uuid);

// Metrics: Add uuid as metadata
metrics.addMetadata('uuid', uuid);

// Define response object
let response;

// Get the item from the table
// https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#get-property
try {
if (!tableName) {
throw new Error('SAMPLE_TABLE environment variable is not set');
}
if (!event.pathParameters) {
throw new Error('event does not contain pathParameters');
}
if (!event.pathParameters.id) {
throw new Error('PathParameter id is missing');
}
const data = await docClient.send(new GetItemCommand({
TableName: tableName,
Key: { id:
{
S: event.pathParameters.id
}
},
}));
const item = data.Item;
response = {
statusCode: 200,
body: JSON.stringify(item)
};
} catch (err) {
tracer.addErrorAsMetadata(err as Error);
logger.error('Error reading from table. ' + err);
response = {
statusCode: 500,
body: JSON.stringify({ 'error': 'Error reading from table.' })
};
}

const data = await docClient.get({
TableName: tableName,
Key: { id: event.pathParameters.id },
}).promise();
const item = data.Item;
response = {
statusCode: 200,
body: JSON.stringify(item)
};
} catch (err) {
tracer.addErrorAsMetadata(err as Error);
logger.error('Error reading from table. ' + err);
response = {
statusCode: 500,
body: JSON.stringify({ 'error': 'Error reading from table.' })
};
}
// Tracer: Close subsegment (the AWS Lambda one is closed automatically)
handlerSegment.close(); // (## index.handler)

// Tracer: Close subsegment (the AWS Lambda one is closed automatically)
handlerSegment.close(); // (## index.handler)
// Tracer: Set the facade segment as active again (the one created by AWS Lambda)
tracer.setSegment(segment);

// Tracer: Set the facade segment as active again (the one created by AWS Lambda)
tracer.setSegment(segment);
// All log statements are written to CloudWatch
logger.info(`response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}`);

return response;
}

// All log statements are written to CloudWatch
logger.info(`response from: ${event.path} statusCode: ${response.statusCode} body: ${response.body}`);
}

return response;
};
const handlerClass = new Lambda();
export const handler = handlerClass.handler.bind(handlerClass);
Loading