From 186ed7ba26803e65bc3522c0f3dae07c35e4454f Mon Sep 17 00:00:00 2001 From: Simon Buchan Date: Sat, 15 Feb 2020 06:18:39 +1300 Subject: [PATCH] [aws-lambda] Some docs, split up types and tests (#42080) * [aws-lambda] Some docs, split up index.d.ts into multiple files. Documents handler type and conventions for adding triggers more explicitly. The same index.d.ts docs should explain the thinking for the new layout convention. No changes to the type tests in this first commit, to show that the tests still passs, and because I think they deserve more attention (and I need lunch right now). Most event structures have little to no changes other than re-ordering or removing inaccurately copy/pasted comments in a case or two. Loses history, but github doesn't support rename history anyway, and it's even more ugly to get git to track file splits even for local usage. Apologies for the huge diff. * [aws-lambda] Split tests to per-service files, add contributor README.md Updates most of the files to a consistent style. --- types/aws-lambda/README.md | 189 +++ types/aws-lambda/aws-lambda-tests.ts | 1414 ++--------------- types/aws-lambda/common/api-gateway.d.ts | 46 + types/aws-lambda/common/cloudfront.d.ts | 79 + types/aws-lambda/hander.d.ts | 171 ++ types/aws-lambda/index.d.ts | 1204 +------------- types/aws-lambda/test/alb-tests.ts | 30 + types/aws-lambda/test/api-gateway-tests.ts | 186 +++ types/aws-lambda/test/cloudformation-tests.ts | 74 + types/aws-lambda/test/cloudfront-tests.ts | 279 ++++ types/aws-lambda/test/cloudwatch-tests.ts | 45 + types/aws-lambda/test/codepipeline-tests.ts | 77 + types/aws-lambda/test/cognito-tests.ts | 114 ++ types/aws-lambda/test/dynamodb-tests.ts | 98 ++ types/aws-lambda/test/kinesis-tests.ts | 50 + types/aws-lambda/test/lex-tests.ts | 122 ++ types/aws-lambda/test/s3-tests.ts | 102 ++ types/aws-lambda/test/sns-tests.ts | 29 + types/aws-lambda/test/sqs-tests.ts | 44 + types/aws-lambda/trigger/alb.d.ts | 32 + .../trigger/api-gateway-authorizer.d.ts | 88 + .../aws-lambda/trigger/api-gateway-proxy.d.ts | 38 + .../cloudformation-custom-resource.d.ts | 68 + .../trigger/cloudfront-request.d.ts | 20 + .../trigger/cloudfront-response.d.ts | 21 + .../aws-lambda/trigger/cloudwatch-events.d.ts | 19 + types/aws-lambda/trigger/cloudwatch-logs.d.ts | 33 + .../codepipeline-cloudwatch-action.d.ts | 31 + .../codepipeline-cloudwatch-pipeline.d.ts | 32 + .../codepipeline-cloudwatch-stage.d.ts | 23 + .../trigger/codepipeline-cloudwatch.d.ts | 11 + types/aws-lambda/trigger/codepipeline.d.ts | 55 + .../trigger/cognito-user-pool-trigger.d.ts | 99 ++ types/aws-lambda/trigger/dynamodb-stream.d.ts | 45 + .../kinesis-firehose-transformation.d.ts | 45 + types/aws-lambda/trigger/kinesis-stream.d.ts | 28 + types/aws-lambda/trigger/lex.d.ts | 103 ++ types/aws-lambda/trigger/s3-batch.d.ts | 41 + types/aws-lambda/trigger/s3.d.ts | 60 + types/aws-lambda/trigger/sns.d.ts | 38 + types/aws-lambda/trigger/sqs.d.ts | 42 + types/aws-lambda/tsconfig.json | 17 +- types/aws-lambda/tslint.json | 6 +- 43 files changed, 2863 insertions(+), 2485 deletions(-) create mode 100644 types/aws-lambda/README.md create mode 100644 types/aws-lambda/common/api-gateway.d.ts create mode 100644 types/aws-lambda/common/cloudfront.d.ts create mode 100644 types/aws-lambda/hander.d.ts create mode 100644 types/aws-lambda/test/alb-tests.ts create mode 100644 types/aws-lambda/test/api-gateway-tests.ts create mode 100644 types/aws-lambda/test/cloudformation-tests.ts create mode 100644 types/aws-lambda/test/cloudfront-tests.ts create mode 100644 types/aws-lambda/test/cloudwatch-tests.ts create mode 100644 types/aws-lambda/test/codepipeline-tests.ts create mode 100644 types/aws-lambda/test/cognito-tests.ts create mode 100644 types/aws-lambda/test/dynamodb-tests.ts create mode 100644 types/aws-lambda/test/kinesis-tests.ts create mode 100644 types/aws-lambda/test/lex-tests.ts create mode 100644 types/aws-lambda/test/s3-tests.ts create mode 100644 types/aws-lambda/test/sns-tests.ts create mode 100644 types/aws-lambda/test/sqs-tests.ts create mode 100644 types/aws-lambda/trigger/alb.d.ts create mode 100644 types/aws-lambda/trigger/api-gateway-authorizer.d.ts create mode 100644 types/aws-lambda/trigger/api-gateway-proxy.d.ts create mode 100644 types/aws-lambda/trigger/cloudformation-custom-resource.d.ts create mode 100644 types/aws-lambda/trigger/cloudfront-request.d.ts create mode 100644 types/aws-lambda/trigger/cloudfront-response.d.ts create mode 100644 types/aws-lambda/trigger/cloudwatch-events.d.ts create mode 100644 types/aws-lambda/trigger/cloudwatch-logs.d.ts create mode 100644 types/aws-lambda/trigger/codepipeline-cloudwatch-action.d.ts create mode 100644 types/aws-lambda/trigger/codepipeline-cloudwatch-pipeline.d.ts create mode 100644 types/aws-lambda/trigger/codepipeline-cloudwatch-stage.d.ts create mode 100644 types/aws-lambda/trigger/codepipeline-cloudwatch.d.ts create mode 100644 types/aws-lambda/trigger/codepipeline.d.ts create mode 100644 types/aws-lambda/trigger/cognito-user-pool-trigger.d.ts create mode 100644 types/aws-lambda/trigger/dynamodb-stream.d.ts create mode 100644 types/aws-lambda/trigger/kinesis-firehose-transformation.d.ts create mode 100644 types/aws-lambda/trigger/kinesis-stream.d.ts create mode 100644 types/aws-lambda/trigger/lex.d.ts create mode 100644 types/aws-lambda/trigger/s3-batch.d.ts create mode 100644 types/aws-lambda/trigger/s3.d.ts create mode 100644 types/aws-lambda/trigger/sns.d.ts create mode 100644 types/aws-lambda/trigger/sqs.d.ts diff --git a/types/aws-lambda/README.md b/types/aws-lambda/README.md new file mode 100644 index 0000000000..135d7a7a57 --- /dev/null +++ b/types/aws-lambda/README.md @@ -0,0 +1,189 @@ +Types helpful for implementing handlers in the AWS Lambda NodeJS runtimes, +the handler interface and types for AWS-defined trigger sources. + +Unrelated to the npm package `aws-lambda`, a CLI tool. + +## Contributing + +Follow all the rules for [modifying a type package](../../README.md#edit-an-existing-package), +of course, but as this package has gotten quite large, please +also follow the established conventions to keep things simple +for future contributors: + +### Common types + +The main entry point, [index.d.ts]() simply does `export * from "..."` +for all type declaration files in the package. Make sure if you are +adding any files for new trigger sources that you add the +re-export here; the user should only need to import from `"aws-lambda"`. + +Similarly, [aws-lambda-tests.ts]() has declarations for the common type and tests +for [handler.d.ts](), while the actual service specific tests are all in [test/]() + +The mentioned [handler.d.ts]() contains definitions for `Handler` +and its associated types like `Context` and `Callback`, +which describe the Lambda NodeJS runtime API. + +### Triggers + +Each trigger-specific event structure should have a separate file in [trigger/]() based +on the service name and optionally the trigger type, if it's not a clear "default". + +If multiple triggers for a service share types, the common types +should be in a file in [common/]() named for the containing service. + +For a service `foo` and trigger `bar`, there should ideally be +a file `trigger/foo-bar.d.ts` containing something like: + +```ts +import { Callback, Handler } from "../handler"; + +import { FooCommonType } from "../common/foo"; + +export type FooBarHandler = Handler; +export type FooBarCallback = Callback; +// or, if there is no FooBarResult: +export type FooBarHandler = Handler; + +export interface FooBarEvent { + // ... +} + +export interface FooBarEventSpecificType { + // ... +} + +export interface FooBarResult { + // ... +} + +export interface FooBarResultSpecificType { + // ... +} + +export interface FooBarCommonType { + // ... +} + +// ... +``` + +As implied, all names are exported flat, try to ensure that it's clear what service +or trigger a type is for. (Note that especially the earlier types are not always +consistent with this) + +### Tests + +Each trigger should have a type test that exercises the type roughly as the user +would, in particular reading properties from the event, and creating each +valid result structure in general. When making any change: adding a property, +to a new trigger, also add the change to the tests. + +Tests are grouped by each service in [test/](), and must also be explicitly +added to `tsconfig.json`'s `files` list (use of `include` is banned by +DefinitelyTyped at the moment). + +The test file for a service `foo` with two triggers `bar` and `baz` may look like: + +```ts +import { + FooBarHandler, + FooBarResult, + // ... + } from "aws-lambda"; + +const barHandler: FooBarHandler = async (event, context, callback) => { + // Check event type + // Declarations for e.g. strOrNull, bool are in aws-lambda-tests.ts + strOrNull = event.body; + str = event.headers[str]; + str = event.multiValueHeaders[str][num]; + str = event.httpMethod; + bool = event.isBase64Encoded; + // this property is of a named type, validate that: + let requestContext: FooBarEventRequestContext; + requestContext = event.requestContext; + // for each accessible property declared in FooBarEvent... + + // recurse into each declared type + str = requestContext.accountId; + str = requestContext.apiId; + const authContext: AuthResponseContext | null | undefined = requestContext.authorizer; + numOrUndefined = requestContext.connectedAt; + strOrUndefined = requestContext.connectionId; + // ... + + + // Check result type + let result: FooBarResult; + // check minimally assignable case + result = { + statusCode: num, + body: str, + }; + // check maximally assignable case + result = { + statusCode: num, + headers: { + [str]: str, + [str]: bool, + [str]: num, + }, + multiValueHeaders: { + [str]: [str, bool, num], + }, + isBase64Encoded: true, + body: str, + }; + + // check reasonable result-returning styles + callback(new Error()); + callback(null, result); + return result; +}; + +const bazHandler: FooBazHandler = async (event, context, callback) => { + // ... as above, for each untested type + + // for handlers without a result + callback(); + callback(new Error()); +}; + +... +``` + +Not that currently many tests have not been updated to this style. +As always, PRs welcome, but simply adding your change in the new style +is a start. + +Ideally the existing test should still pass with your new change: if it doesn't that +implies that your change is incompatible with existing user code (if the +test is done correctly) This isn't always the wrong thing to do, but be +prepared to make a decently strong case for why this is an exception. + +## Future ideas + +* Adding JSDocs for types and properties (copied from AWS docs?). Many + triggers have unintuitive formats and requirements. + +* Splitting `import { Context, FooBarHandler, FooCommonType } from "aws-lambda"` into + + * `import { Context } from "@aws-lambda/runtime"` and + * `import { BarHandler, CommonType } from "@aws-lambda/foo"` + + With forwarding from the current package to preserve back-compat. + This would allow not only more fine-grained dependencies, but also + support backward-incompatible changes to triggers as they could be + independently major versioned. + + It's a lot of busywork to do all that renaming though, and ensuring + compatibility is kept. + +* Possibly related to the above, migrating this to, or somehow in support of, + an npm package with runtime support for implementing handlers correctly. + For example, catching and formatting `ClientError` into an API Gateway + 400 error, or parsing CloudWatch log payloads. + + This one is much more fraught, as it by default loses all the existing + DefinitelyTyped tooling and community support (e.g. cross package typing). diff --git a/types/aws-lambda/aws-lambda-tests.ts b/types/aws-lambda/aws-lambda-tests.ts index 2403f1c63d..d055f28ed7 100644 --- a/types/aws-lambda/aws-lambda-tests.ts +++ b/types/aws-lambda/aws-lambda-tests.ts @@ -1,3 +1,9 @@ +// Declare a bunch of basic types up front. Since this file has no imports +// or exports, typescript treats these as global declarations that can be +// used in tests/* files. +// Don't add declarations for service-specific types here; i.e. not from +// common/ or trigger/, but handler.d.ts is fine. Those can be in the +// service-specific tests in tests/{service}-tests.ts declare let str: string; declare let strOrNull: string | null; declare let strOrUndefined: string | undefined; @@ -10,533 +16,15 @@ declare let bool: boolean; declare let boolOrUndefined: boolean | undefined; declare let boolOrNumOrStr: boolean | number | string; declare let numOrUndefined: number | undefined; -declare let apiGwEvtReqCtx: AWSLambda.APIGatewayEventRequestContext; -declare let apiGwEvtReqCtxOpt: AWSLambda.APIGatewayEventRequestContext | null | undefined; -declare let apiGwEvt: AWSLambda.APIGatewayEvent; -declare let albEvtReqCtx: AWSLambda.ALBEventRequestContext; -declare let albEvt: AWSLambda.ALBEvent; -declare let albRes: AWSLambda.ALBResult; -declare let customAuthorizerEvt: AWSLambda.CustomAuthorizerEvent; + +// handler.d.ts types +declare let context: AWSLambda.Context; declare let clientCtx: AWSLambda.ClientContext; declare let clientCtxOrUndefined: AWSLambda.ClientContext | undefined; declare let clientContextEnv: AWSLambda.ClientContextEnv; declare let clientContextClient: AWSLambda.ClientContextClient; -declare let context: AWSLambda.Context; declare let identity: AWSLambda.CognitoIdentity; declare let identityOrUndefined: AWSLambda.CognitoIdentity | undefined; -declare let proxyResult: AWSLambda.ProxyResult; -declare let authResponse: AWSLambda.AuthResponse; -declare let policyDocument: AWSLambda.PolicyDocument; -declare let statement: AWSLambda.Statement; -declare let authResponseContext: AWSLambda.AuthResponseContext; -declare let authResponseContextOpt: AWSLambda.AuthResponseContext | null | undefined; -declare let snsEvt: AWSLambda.SNSEvent; -declare let snsEvtRecs: AWSLambda.SNSEventRecord[]; -declare let snsEvtRec: AWSLambda.SNSEventRecord; -declare let snsMsg: AWSLambda.SNSMessage; -declare let snsMsgAttr: AWSLambda.SNSMessageAttribute; -declare let snsMsgAttrs: AWSLambda.SNSMessageAttributes; -const S3EvtRec: AWSLambda.S3EventRecord = { - eventVersion: '2.1', - eventSource: 'aws:s3', - awsRegion: 'us-east-1', - eventTime: '1970-01-01T00:00:00.000Z', - eventName: 'ObjectRestore:Completed', - userIdentity: { - principalId: 'AIDAJDPLRKLG7UEXAMPLE', - }, - requestParameters: { - sourceIPAddress: '127.0.0.1', - }, - responseElements: { - 'x-amz-request-id': 'C3D13FE58DE4C810', - 'x-amz-id-2': 'FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD', - }, - s3: { - s3SchemaVersion: '1.0', - configurationId: 'testConfigRule', - bucket: { - name: 'mybucket', - ownerIdentity: { - principalId: 'A3NL1KOZZKExample', - }, - arn: 'arn:aws:s3:::mybucket', - }, - object: { - key: 'HappyFace.jpg', - size: 1024, - eTag: 'd41d8cd98f00b204e9800998ecf8427e', - versionId: '096fKKXTRTtl3on89fVO.nfljtsv6qko', - sequencer: '0055AED6DCD90281E5', - }, - }, - glacierEventData: { - restoreEventData: { - lifecycleRestorationExpiryTime: '1970-01-01T00:00:00.000Z', - lifecycleRestoreStorageClass: 'glacier-deep-archive', - }, - }, -}; - -const S3CreateEvent: AWSLambda.S3CreateEvent = { - Records: [S3EvtRec], -}; - -declare const cognitoUserPoolEvent: AWSLambda.CognitoUserPoolEvent; -declare const cloudformationCustomResourceEvent: AWSLambda.CloudFormationCustomResourceEvent; -declare const cloudformationCustomResourceResponse: AWSLambda.CloudFormationCustomResourceResponse; -declare const cloudwatchLogsEvent: AWSLambda.CloudWatchLogsEvent; -declare const cloudwatchLogsDecodedData: AWSLambda.CloudWatchLogsDecodedData; -declare const scheduledEvent: AWSLambda.ScheduledEvent; - -/* API Gateway Event request context */ -str = apiGwEvtReqCtx.accountId; -str = apiGwEvtReqCtx.apiId; -authResponseContextOpt = apiGwEvtReqCtx.authorizer; -numOrUndefined = apiGwEvtReqCtx.connectedAt; -strOrUndefined = apiGwEvtReqCtx.connectionId; -strOrUndefined = apiGwEvtReqCtx.domainName; -strOrUndefined = apiGwEvtReqCtx.eventType; -strOrUndefined = apiGwEvtReqCtx.extendedRequestId; -str = apiGwEvtReqCtx.httpMethod; -strOrNull = apiGwEvtReqCtx.identity.accessKey; -strOrNull = apiGwEvtReqCtx.identity.accountId; -strOrNull = apiGwEvtReqCtx.identity.apiKey; -strOrNull = apiGwEvtReqCtx.identity.apiKeyId; -strOrNull = apiGwEvtReqCtx.identity.caller; -strOrNull = apiGwEvtReqCtx.identity.cognitoAuthenticationProvider; -strOrNull = apiGwEvtReqCtx.identity.cognitoAuthenticationType; -strOrNull = apiGwEvtReqCtx.identity.cognitoIdentityId; -strOrNull = apiGwEvtReqCtx.identity.cognitoIdentityPoolId; -strOrNull = apiGwEvtReqCtx.identity.principalOrgId; -str = apiGwEvtReqCtx.identity.sourceIp; -strOrNull = apiGwEvtReqCtx.identity.user; -strOrNull = apiGwEvtReqCtx.identity.userAgent; -strOrNull = apiGwEvtReqCtx.identity.userArn; -strOrUndefined = apiGwEvtReqCtx.messageDirection; -strOrUndefinedOrNull = apiGwEvtReqCtx.messageId; -str = apiGwEvtReqCtx.path; -str = apiGwEvtReqCtx.stage; -str = apiGwEvtReqCtx.requestId; -strOrUndefined = apiGwEvtReqCtx.requestTime; -str = apiGwEvtReqCtx.resourceId; -str = apiGwEvtReqCtx.resourcePath; -strOrUndefined = apiGwEvtReqCtx.routeKey; - -/* API Gateway Event */ -strOrNull = apiGwEvt.body; -str = apiGwEvt.headers['example']; -str = apiGwEvt.multiValueHeaders['example'][0]; -str = apiGwEvt.httpMethod; -bool = apiGwEvt.isBase64Encoded; -str = apiGwEvt.path; -str = apiGwEvt.pathParameters!['example']; -str = apiGwEvt.queryStringParameters!['example']; -str = apiGwEvt.multiValueQueryStringParameters!['example'][0]; -str = apiGwEvt.stageVariables!['example']; -apiGwEvtReqCtx = apiGwEvt.requestContext; -str = apiGwEvt.resource; - -/* Application Load Balancer Event Request Context */ -str = albEvtReqCtx.elb.targetGroupArn; - -/* Application Load Balancer Event */ -str = albEvt.httpMethod; -str = albEvt.path; -str = albEvt.queryStringParameters!['example']; -str = albEvt.headers!['example']; -str = albEvt.multiValueQueryStringParameters!['example'][0]; -str = albEvt.multiValueHeaders!['example'][0]; -strOrNull = albEvt.body; -bool = albEvt.isBase64Encoded; - -/* Application Load Balancer Result */ -num = albRes.statusCode; -str = albRes.statusDescription; -boolOrNumOrStr = albRes.headers!['example']; -boolOrNumOrStr = albRes.multiValueHeaders!['example'][0]; -strOrUndefined = albRes.body; -bool = albRes.isBase64Encoded; - -/* API Gateway CustomAuthorizer Event */ -str = customAuthorizerEvt.type; -str = customAuthorizerEvt.methodArn; -strOrUndefined = customAuthorizerEvt.authorizationToken; -strOrUndefined = customAuthorizerEvt.resource; -strOrUndefined = customAuthorizerEvt.path; -strOrUndefined = customAuthorizerEvt.httpMethod; -str = customAuthorizerEvt.headers!['example']; -str = customAuthorizerEvt.multiValueHeaders!['example'][0]; -str = customAuthorizerEvt.pathParameters!['example']; -str = customAuthorizerEvt.queryStringParameters!['example']; -str = customAuthorizerEvt.multiValueQueryStringParameters!['example'][0]; -str = customAuthorizerEvt.stageVariables!['example']; -apiGwEvtReqCtxOpt = customAuthorizerEvt.requestContext; -strOrUndefined = customAuthorizerEvt.domainName; -strOrUndefined = customAuthorizerEvt.apiId; - -/* DynamoDB Stream Event */ -const dynamoDBStreamEvent: AWSLambda.DynamoDBStreamEvent = { - Records: [ - { - eventID: '1', - eventVersion: '1.0', - dynamodb: { - Keys: { - Id: { - N: '101', - }, - }, - NewImage: { - Message: { - S: 'New item!', - }, - Id: { - N: '101', - }, - }, - StreamViewType: 'NEW_AND_OLD_IMAGES', - SequenceNumber: '111', - SizeBytes: 26, - }, - awsRegion: 'us-west-2', - eventName: 'INSERT', - eventSourceARN: - 'arn:aws:dynamodb:us-west-2:account-id:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899', - eventSource: 'aws:dynamodb', - }, - { - eventID: '2', - eventVersion: '1.0', - dynamodb: { - OldImage: { - Message: { - S: 'New item!', - }, - Id: { - N: '101', - }, - }, - SequenceNumber: '222', - Keys: { - Id: { - N: '101', - }, - }, - SizeBytes: 59, - NewImage: { - Message: { - S: 'This item has changed', - }, - Id: { - N: '101', - }, - }, - StreamViewType: 'NEW_AND_OLD_IMAGES', - }, - awsRegion: 'us-west-2', - eventName: 'MODIFY', - eventSourceARN: - 'arn:aws:dynamodb:us-west-2:account-id:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899', - eventSource: 'aws:dynamodb', - }, - { - eventID: '3', - eventVersion: '1.0', - dynamodb: { - Keys: { - Id: { - N: '101', - }, - }, - SizeBytes: 38, - SequenceNumber: '333', - OldImage: { - Message: { - S: 'This item has changed', - }, - Id: { - N: '101', - }, - }, - StreamViewType: 'NEW_AND_OLD_IMAGES', - }, - awsRegion: 'us-west-2', - eventName: 'REMOVE', - eventSourceARN: - 'arn:aws:dynamodb:us-west-2:account-id:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899', - eventSource: 'aws:dynamodb', - }, - ], -}; - -/* SNS Event */ -snsEvtRecs = snsEvt.Records; - -str = snsEvtRec.EventSource; -str = snsEvtRec.EventSubscriptionArn; -str = snsEvtRec.EventVersion; -snsMsg = snsEvtRec.Sns; - -str = snsMsg.SignatureVersion; -str = snsMsg.Timestamp; -str = snsMsg.Signature; -str = snsMsg.SigningCertUrl; -str = snsMsg.MessageId; -str = snsMsg.Message; -snsMsgAttrs = snsMsg.MessageAttributes; -str = snsMsg.Type; -str = snsMsg.UnsubscribeUrl; -str = snsMsg.TopicArn; -str = snsMsg.Subject; - -snsMsgAttrs['example'] = snsMsgAttr; - -str = snsMsgAttr.Type; -str = snsMsgAttr.Value; - -/* Lambda Proxy Result */ -num = proxyResult.statusCode; -proxyResult.headers!['example'] = str; -proxyResult.headers!['example'] = bool; -proxyResult.headers!['example'] = num; -proxyResult.multiValueHeaders!['example'][0] = str; -proxyResult.multiValueHeaders!['example'][0] = bool; -proxyResult.multiValueHeaders!['example'][0] = num; -boolOrUndefined = proxyResult.isBase64Encoded; -str = proxyResult.body; - -/* API Gateway CustomAuthorizer AuthResponse */ -authResponseContext = { - stringKey: str, - numberKey: num, - booleanKey: bool, -}; - -statement = { - Action: str, - Effect: str, - Resource: str, -}; - -// $ExpectError -statement = { Effect: str, Action: str, Principal: 123, }; - -// Bad Resource -// $ExpectError -statement = { Effect: str, Action: str, Resource: 123, }; - -// Bad Resource with valid Principal -// $ExpectError -statement = { Effect: str, Action: str, Principal: { Service: str }, Resource: 123, }; - -// Bad principal with valid Resource -// $ExpectError -statement = { Effect: str, Action: str, Principal: 123, Resource: str, }; - -// No Effect -// $ExpectError -statement = { - Action: str, - Principal: str, -}; - -statement = { - Sid: str, - Action: [str, str], - Effect: str, - Resource: [str, str], - Condition: { - condition1: { key: 'value' }, - condition2: [ - { - key1: 'value', - key2: 'value', - }, - { - key3: 'value', - }, - ], - }, - Principal: [str, str], - NotPrincipal: [str, str], -}; - -statement = { - Action: str, - Principal: str, - Effect: str, -}; - -statement = { - Action: str, - NotPrincipal: { - Service: str, - }, - Effect: str, -}; - -statement = { - Effect: str, - NotAction: str, - NotResource: str, -}; - -policyDocument = { - Version: str, - Statement: [statement], -}; - -policyDocument = { - Version: str, - Statement: [statement, statement], -}; - -authResponse = { - principalId: str, - policyDocument, - context: authResponseContext, -}; - -authResponse = { - principalId: str, - policyDocument, -}; - -// CognitoUserPoolEvent -num = cognitoUserPoolEvent.version; -cognitoUserPoolEvent.triggerSource === 'PreSignUp_SignUp'; -cognitoUserPoolEvent.triggerSource === 'PreSignUp_ExternalProvider'; -cognitoUserPoolEvent.triggerSource === 'PostConfirmation_ConfirmSignUp'; -cognitoUserPoolEvent.triggerSource === 'PreAuthentication_Authentication'; -cognitoUserPoolEvent.triggerSource === 'PostAuthentication_Authentication'; -cognitoUserPoolEvent.triggerSource === 'CustomMessage_SignUp'; -cognitoUserPoolEvent.triggerSource === 'CustomMessage_AdminCreateUser'; -cognitoUserPoolEvent.triggerSource === 'CustomMessage_ResendCode'; -cognitoUserPoolEvent.triggerSource === 'CustomMessage_ForgotPassword'; -cognitoUserPoolEvent.triggerSource === 'CustomMessage_UpdateUserAttribute'; -cognitoUserPoolEvent.triggerSource === 'CustomMessage_VerifyUserAttribute'; -cognitoUserPoolEvent.triggerSource === 'CustomMessage_Authentication'; -cognitoUserPoolEvent.triggerSource === 'DefineAuthChallenge_Authentication'; -cognitoUserPoolEvent.triggerSource === 'CreateAuthChallenge_Authentication'; -cognitoUserPoolEvent.triggerSource === 'VerifyAuthChallengeResponse_Authentication'; -cognitoUserPoolEvent.triggerSource === 'PreSignUp_AdminCreateUser'; -cognitoUserPoolEvent.triggerSource === 'PostConfirmation_ConfirmForgotPassword'; -cognitoUserPoolEvent.triggerSource === 'TokenGeneration_HostedAuth'; -cognitoUserPoolEvent.triggerSource === 'TokenGeneration_Authentication'; -cognitoUserPoolEvent.triggerSource === 'TokenGeneration_NewPasswordChallenge'; -cognitoUserPoolEvent.triggerSource === 'TokenGeneration_AuthenticateDevice'; -cognitoUserPoolEvent.triggerSource === 'TokenGeneration_RefreshTokens'; -cognitoUserPoolEvent.triggerSource === 'UserMigration_Authentication'; -cognitoUserPoolEvent.triggerSource === 'UserMigration_ForgotPassword'; -str = cognitoUserPoolEvent.region; -str = cognitoUserPoolEvent.userPoolId; -strOrUndefined = cognitoUserPoolEvent.userName; -str = cognitoUserPoolEvent.callerContext.awsSdkVersion; -str = cognitoUserPoolEvent.callerContext.clientId; -str = cognitoUserPoolEvent.request.userAttributes['email']; -str = cognitoUserPoolEvent.request.validationData!['k1']; -strOrUndefined = cognitoUserPoolEvent.request.codeParameter; -strOrUndefined = cognitoUserPoolEvent.request.linkParameter; -strOrUndefined = cognitoUserPoolEvent.request.usernameParameter; -boolOrUndefined = cognitoUserPoolEvent.request.newDeviceUsed; -cognitoUserPoolEvent.request.session![0].challengeName === 'CUSTOM_CHALLENGE'; -cognitoUserPoolEvent.request.session![0].challengeName === 'PASSWORD_VERIFIER'; -cognitoUserPoolEvent.request.session![0].challengeName === 'SMS_MFA'; -cognitoUserPoolEvent.request.session![0].challengeName === 'DEVICE_SRP_AUTH'; -cognitoUserPoolEvent.request.session![0].challengeName === 'DEVICE_PASSWORD_VERIFIER'; -cognitoUserPoolEvent.request.session![0].challengeName === 'ADMIN_NO_SRP_AUTH'; -bool = cognitoUserPoolEvent.request.session![0].challengeResult; -strOrUndefined = cognitoUserPoolEvent.request.session![0].challengeMetadata; -strOrUndefined = cognitoUserPoolEvent.request.challengeName; -str = cognitoUserPoolEvent.request.privateChallengeParameters!['answer']; -str = cognitoUserPoolEvent.request.challengeAnswer!; -strOrUndefined = cognitoUserPoolEvent.request.password; -boolOrUndefined = cognitoUserPoolEvent.response.answerCorrect; -strOrUndefined = cognitoUserPoolEvent.response.smsMessage; -strOrUndefined = cognitoUserPoolEvent.response.emailMessage; -strOrUndefined = cognitoUserPoolEvent.response.emailSubject; -strOrUndefined = cognitoUserPoolEvent.response.challengeName; -boolOrUndefined = cognitoUserPoolEvent.response.issueTokens; -boolOrUndefined = cognitoUserPoolEvent.response.failAuthentication; -str = cognitoUserPoolEvent.response.publicChallengeParameters!['captchaUrl']; -str = cognitoUserPoolEvent.response.privateChallengeParameters!['answer']; -strOrUndefined = cognitoUserPoolEvent.response.challengeMetadata; -boolOrUndefined = cognitoUserPoolEvent.response.answerCorrect; -str = cognitoUserPoolEvent.response.userAttributes!['username']; -cognitoUserPoolEvent.response.finalUserStatus === 'CONFIRMED'; -cognitoUserPoolEvent.response.finalUserStatus === 'RESET_REQUIRED'; -cognitoUserPoolEvent.response.messageAction === 'SUPPRESS'; -cognitoUserPoolEvent.response.desiredDeliveryMediums === ['EMAIL']; -cognitoUserPoolEvent.response.desiredDeliveryMediums === ['SMS']; -cognitoUserPoolEvent.response.desiredDeliveryMediums === ['SMS', 'EMAIL']; -boolOrUndefined = cognitoUserPoolEvent.response.forceAliasCreation; -// From AWS examples -cognitoUserPoolEvent.response = { - claimsOverrideDetails: { - claimsToAddOrOverride: { - attribute_key2: 'attribute_value2', - attribute_key: 'attribute_value', - }, - claimsToSuppress: ['email'], - }, -}; -cognitoUserPoolEvent.response = { - claimsOverrideDetails: { - claimsToAddOrOverride: { - attribute_key2: 'attribute_value2', - attribute_key: 'attribute_value', - }, - claimsToSuppress: ['email'], - groupOverrideDetails: { - groupsToOverride: ['group-A', 'group-B', 'group-C'], - iamRolesToOverride: [ - 'arn:aws:iam::XXXXXXXXXXXX:role/sns_callerA', - 'arn:aws:iam::XXXXXXXXX:role/sns_callerB', - 'arn:aws:iam::XXXXXXXXXX:role/sns_callerC', - ], - preferredRole: 'arn:aws:iam::XXXXXXXXXXX:role/sns_caller', - }, - }, -}; -cognitoUserPoolEvent.response.claimsOverrideDetails!.groupOverrideDetails = null; - -// CloudFormation Custom Resource -switch (cloudformationCustomResourceEvent.RequestType) { - case 'Create': - str = cloudformationCustomResourceEvent.LogicalResourceId; - str = cloudformationCustomResourceEvent.RequestId; - anyObj = cloudformationCustomResourceEvent.ResourceProperties; - str = cloudformationCustomResourceEvent.ResourceProperties.ServiceToken; - str = cloudformationCustomResourceEvent.ResourceType; - str = cloudformationCustomResourceEvent.ResponseURL; - str = cloudformationCustomResourceEvent.ServiceToken; - str = cloudformationCustomResourceEvent.StackId; - break; - case 'Update': - anyObj = cloudformationCustomResourceEvent.OldResourceProperties; - break; - case 'Delete': - str = cloudformationCustomResourceEvent.PhysicalResourceId; - break; -} -anyObj = cloudformationCustomResourceResponse.Data; -str = cloudformationCustomResourceResponse.LogicalResourceId; -str = cloudformationCustomResourceResponse.PhysicalResourceId; -strOrUndefined = cloudformationCustomResourceResponse.Reason; -str = cloudformationCustomResourceResponse.RequestId; -str = cloudformationCustomResourceResponse.StackId; -str = cloudformationCustomResourceResponse.Status; -boolOrUndefined = cloudformationCustomResourceResponse.NoEcho; - -/* ScheduledEvent */ -str = scheduledEvent.account; -anyObj = scheduledEvent.detail; -str = scheduledEvent.id; -str = scheduledEvent.region; -str = scheduledEvent.resources[0]; -str = scheduledEvent.source; -str = scheduledEvent.time; /* Context */ bool = context.callbackWaitsForEmptyEventLoop; @@ -550,23 +38,9 @@ str = context.logStreamName; identityOrUndefined = context.identity; clientCtxOrUndefined = context.clientContext; -/* CognitoIdentity */ str = identity.cognitoIdentityId; str = identity.cognitoIdentityPoolId; -/* CloudWatch Logs */ -str = cloudwatchLogsEvent.awslogs.data; - -str = cloudwatchLogsDecodedData.owner; -str = cloudwatchLogsDecodedData.logGroup; -str = cloudwatchLogsDecodedData.logStream; -str = cloudwatchLogsDecodedData.subscriptionFilters[0]; -str = cloudwatchLogsDecodedData.messageType; -str = cloudwatchLogsDecodedData.logEvents[0].id; -num = cloudwatchLogsDecodedData.logEvents[0].timestamp; -str = cloudwatchLogsDecodedData.logEvents[0].message; -str = cloudwatchLogsDecodedData.logEvents[0].extractedFields!['example']; - /* ClientContext */ clientContextClient = clientCtx.client; anyObj = clientCtx.Custom; @@ -586,304 +60,35 @@ str = clientContextClient.appVersionCode; str = clientContextClient.appVersionName; str = clientContextClient.installationId; -/* Callback */ -function callback(cb: AWSLambda.Callback) { - cb(); - cb(null); - cb(error); - cb(str); // https://docs.aws.amazon.com/apigateway/latest/developerguide/handle-errors-in-lambda-integration.html - cb(null, anyObj); - cb(null, bool); - cb(null, str); - cb(null, num); -} +declare const untypedCallback: AWSLambda.Callback; +untypedCallback(); +untypedCallback(undefined); +untypedCallback(null); +untypedCallback(error); +untypedCallback(str); // https://docs.aws.amazon.com/apigateway/latest/developerguide/handle-errors-in-lambda-integration.html +untypedCallback(null, anyObj); +untypedCallback(undefined, bool); +untypedCallback(null, bool); +untypedCallback(null, str); +untypedCallback(null, num); +untypedCallback(null, { foo: 123 }); +untypedCallback(null, { bar: 123 }); +// $ExpectError +untypedCallback(null, anyObj, anyObj); -/* Proxy Callback */ -function proxyCallback(cb: AWSLambda.ProxyCallback) { - cb(); - cb(null); - cb(error); - cb(null, proxyResult); -} - -/* CustomAuthorizerCallback */ -function customAuthorizerCallback(cb: AWSLambda.CustomAuthorizerCallback) { - cb(); - cb(null); - cb(error); - cb(null, authResponse); -} - -/* CodePipeline events https://docs.aws.amazon.com/codepipeline/latest/userguide/actions-invoke-lambda-function.html#actions-invoke-lambda-function-json-event-example */ -const CodePipelineEvent: AWSLambda.CodePipelineEvent = { - 'CodePipeline.job': { - id: '11111111-abcd-1111-abcd-111111abcdef', - accountId: '111111111111', - data: { - actionConfiguration: { - configuration: { - FunctionName: 'MyLambdaFunctionForAWSCodePipeline', - UserParameters: 'some-input-such-as-a-URL', - }, - }, - inputArtifacts: [ - { - location: { - s3Location: { - bucketName: - 'the name of the bucket configured as the pipeline artifact store in Amazon S3, for example codepipeline-us-east-2-1234567890', - objectKey: 'the name of the application, for example CodePipelineDemoApplication.zip', - }, - type: 'S3', - }, - revision: null, - name: 'ArtifactName', - }, - ], - outputArtifacts: [], - artifactCredentials: { - secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', - sessionToken: `MIICiTCCAfICCQD6m7oRw0uXOjANBgkqhkiG9w - 0BAQUFADCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZ - WF0dGxlMQ8wDQYDVQQKEwZBbWF6b24xFDASBgNVBAsTC0lBTSBDb25zb2xlMRIw - EAYDVQQDEwlUZXN0Q2lsYWMxHzAdBgkqhkiG9w0BCQEWEG5vb25lQGFtYXpvbi5 - jb20wHhcNMTEwNDI1MjA0NTIxWhcNMTIwNDI0MjA0NTIxWjCBiDELMAkGA1UEBh - MCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZBb - WF6b24xFDASBgNVBAsTC0lBTSBDb25zb2xlMRIwEAYDVQQDEwlUZXN0Q2lsYWMx - HzAdBgkqhkiG9w0BCQEWEG5vb25lQGFtYXpvbi5jb20wgZ8wDQYJKoZIhvcNAQE - BBQADgY0AMIGJAoGBAMaK0dn+a4GmWIWJ21uUSfwfEvySWtC2XADZ4nB+BLYgVI - k60CpiwsZ3G93vUEIO3IyNoH/f0wYK8m9TrDHudUZg3qX4waLG5M43q7Wgc/MbQ - ITxOUSQv7c7ugFFDzQGBzZswY6786m86gpEIbb3OhjZnzcvQAaRHhdlQWIMm2nr - AgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAtCu4nUhVVxYUntneD9+h8Mg9q6q+auN - KyExzyLwaxlAoo7TJHidbtS4J5iNmZgXL0FkbFFBjvSfpJIlJ00zbhNYS5f6Guo - EDmFJl0ZxBHjJnyp378OD8uTs7fLvjx79LjSTbNYiytVbZPQUQ5Yaxu2jXnimvw - 3rrszlaEXAMPLE=`, - accessKeyId: 'AKIAIOSFODNN7EXAMPLE', - }, - continuationToken: 'A continuation token if continuing job', - }, - }, -}; - -CodePipelineEvent['CodePipeline.job'].data.encryptionKey = { type: 'KMS', id: 'key' }; - -/* CodePipeline CloudWatch Events - * see https://docs.aws.amazon.com/codepipeline/latest/userguide/detect-state-changes-cloudwatch-events.html - * Their documentation says that detail.version is a string, but it is actually an integer - */ -const CodePipelineCloudWatchEvent: AWSLambda.CodePipelineCloudWatchEvent = { - version: '0', - id: 'event_Id', - 'detail-type': 'CodePipeline Pipeline Execution State Change', - source: 'aws.codepipeline', - account: 'Pipeline_Account', - time: 'TimeStamp', - region: 'us-east-1', - resources: ['arn:aws:codepipeline:us-east-1:account_ID:myPipeline'], - detail: { - pipeline: 'myPipeline', - version: 1, - state: 'STARTED', - 'execution-id': 'execution_Id', - }, -}; - -/* CloudFront events, see http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html */ -const CloudFrontRequestWithCustomOriginEvent: AWSLambda.CloudFrontRequestEvent = { - Records: [ - { - cf: { - config: { - distributionDomainName: 'd123.cloudfront.net', - distributionId: 'EDFDVBD6EXAMPLE', - eventType: 'viewer-request', - requestId: 'MRVMF7KydIvxMWfJIglgwHQwZsbG2IhRJ07sn9AkKUFSHS9EXAMPLE==', - }, - request: { - body: { - action: 'read-only', - data: 'eyJ1c2VybmFtZSI6IkxhbWJkYUBFZGdlIiwiY29tbWVudCI6IlRoaXMgaXMgcmVxdWVzdCBib2R5In0=', - encoding: 'base64', - inputTruncated: false, - }, - clientIp: '2001:0db8:85a3:0:0:8a2e:0370:7334', - querystring: 'size=large', - uri: '/picture.jpg', - method: 'GET', - headers: { - host: [ - { - key: 'Host', - value: 'd111111abcdef8.cloudfront.net', - }, - ], - 'user-agent': [ - { - key: 'User-Agent', - value: 'curl/7.51.0', - }, - ], - }, - origin: { - custom: { - customHeaders: { - 'my-origin-custom-header': [ - { - key: 'My-Origin-Custom-Header', - value: 'Test', - }, - ], - }, - domainName: 'example.com', - keepaliveTimeout: 5, - path: '/custom_path', - port: 443, - protocol: 'https', - readTimeout: 5, - sslProtocols: ['TLSv1', 'TLSv1.1'], - }, - }, - }, - }, - }, - ], -}; - -const CloudFrontRequestWithS3OriginEvent: AWSLambda.CloudFrontRequestEvent = { - Records: [ - { - cf: { - config: { - distributionDomainName: 'd123.cloudfront.net', - distributionId: 'EDFDVBD6EXAMPLE', - eventType: 'viewer-request', - requestId: 'MRVMF7KydIvxMWfJIglgwHQwZsbG2IhRJ07sn9AkKUFSHS9EXAMPLE==', - }, - request: { - body: { - action: 'read-only', - data: 'eyJ1c2VybmFtZSI6IkxhbWJkYUBFZGdlIiwiY29tbWVudCI6IlRoaXMgaXMgcmVxdWVzdCBib2R5In0=', - encoding: 'base64', - inputTruncated: false, - }, - clientIp: '2001:0db8:85a3:0:0:8a2e:0370:7334', - querystring: 'size=large', - uri: '/picture.jpg', - method: 'GET', - headers: { - host: [ - { - key: 'Host', - value: 'd111111abcdef8.cloudfront.net', - }, - ], - 'user-agent': [ - { - key: 'User-Agent', - value: 'curl/7.51.0', - }, - ], - }, - origin: { - s3: { - authMethod: 'origin-access-identity', - customHeaders: { - 'my-origin-custom-header': [ - { - key: 'My-Origin-Custom-Header', - value: 'Test', - }, - ], - }, - domainName: 'my-bucket.s3.amazonaws.com', - path: '/s3_path', - region: 'us-east-1', - }, - }, - }, - }, - }, - ], -}; - -const CloudFrontResponseEvent: AWSLambda.CloudFrontResponseEvent = { - Records: [ - { - cf: { - config: { - distributionDomainName: 'd123.cloudfront.net', - distributionId: 'EDFDVBD6EXAMPLE', - eventType: 'viewer-response', - requestId: 'xGN7KWpVEmB9Dp7ctcVFQC4E-nrcOcEKS3QyAez--06dV7TEXAMPLE==', - }, - request: { - clientIp: '2001:0db8:85a3:0:0:8a2e:0370:7334', - method: 'GET', - uri: '/picture.jpg', - querystring: 'size=large', - headers: { - host: [ - { - key: 'Host', - value: 'd111111abcdef8.cloudfront.net', - }, - ], - 'user-agent': [ - { - key: 'User-Agent', - value: 'curl/7.18.1', - }, - ], - }, - }, - response: { - status: '200', - statusDescription: 'OK', - headers: { - server: [ - { - key: 'Server', - value: 'MyCustomOrigin', - }, - ], - 'set-cookie': [ - { - key: 'Set-Cookie', - value: 'theme=light', - }, - { - key: 'Set-Cookie', - value: 'sessionToken=abc123; Expires=Wed, 09 Jun 2021 10:18:14 GMT', - }, - ], - }, - }, - }, - }, - ], -}; - -/* Kinesis Data Stream Events */ -declare let kinesisStreamEvent: AWSLambda.KinesisStreamEvent; -declare let kinesisStreamRecord: AWSLambda.KinesisStreamRecord; -declare let kinesisStreamRecordPayload: AWSLambda.KinesisStreamRecordPayload; - -kinesisStreamRecord = kinesisStreamEvent.Records[0]; - -str = kinesisStreamRecord.awsRegion; -str = kinesisStreamRecord.eventID; -str = kinesisStreamRecord.eventName; -str = kinesisStreamRecord.eventSource; -str = kinesisStreamRecord.eventSourceARN; -str = kinesisStreamRecord.eventVersion; -str = kinesisStreamRecord.invokeIdentityArn; -kinesisStreamRecordPayload = kinesisStreamRecord.kinesis; - -num = kinesisStreamRecordPayload.approximateArrivalTimestamp; -str = kinesisStreamRecordPayload.data; -str = kinesisStreamRecordPayload.kinesisSchemaVersion; -str = kinesisStreamRecordPayload.partitionKey; -str = kinesisStreamRecordPayload.sequenceNumber; +interface TestResult { foo: number; } +declare const typedCallback: AWSLambda.Callback; +typedCallback(); +typedCallback(undefined); +typedCallback(null); +typedCallback(error); +typedCallback(str); // https://docs.aws.amazon.com/apigateway/latest/developerguide/handle-errors-in-lambda-integration.html +typedCallback(null, anyObj); +typedCallback(null, { foo: 123 }); +// $ExpectError +typedCallback(null, str); +// $ExpectError +typedCallback(null, { bar: 123 }); /* Compatibility functions */ context.done(); @@ -895,231 +100,6 @@ context.succeed(str, anyObj); context.fail(error); context.fail(str); -/* Handler */ -const handler: AWSLambda.Handler = (event: any, context: AWSLambda.Context, cb: AWSLambda.Callback) => {}; - -/* In node8.10 runtime, handlers may return a promise for the result value, so existing async - * handlers that return Promise before calling the callback will now have a `null` result. - * Be safe and make that badly typed with a major verson bump to 8.10 so users expect the breaking change, - * since the upgrade effort should be pretty low in most cases, and it points them at a nicer solution. - */ -// $ExpectError -const legacyAsyncHandler: AWSLambda.APIGatewayProxyHandler = async ( - event: AWSLambda.APIGatewayProxyEvent, - context: AWSLambda.Context, - cb: AWSLambda.Callback, -) => { - cb(null, { statusCode: 200, body: 'No longer valid' }); -}; - -const node8AsyncHandler: AWSLambda.APIGatewayProxyHandler = async ( - event: AWSLambda.APIGatewayProxyEvent, - context: AWSLambda.Context, - cb: AWSLambda.Callback, -) => { - return { statusCode: 200, body: 'Is now valid!' }; -}; - -const inferredHandler: AWSLambda.S3Handler = (event, context, cb) => { - // $ExpectType S3Event - event; - str = event.Records[0].eventName; - // $ExpectType Context - context; - str = context.functionName; - // $ExpectType Callback - cb; - cb(); - cb(null); - cb(new Error()); - // $ExpectError - cb(null, {}); -}; - -// Test using default Callback type still works. -const defaultCallbackHandler: AWSLambda.APIGatewayProxyHandler = ( - event: AWSLambda.APIGatewayEvent, - context: AWSLambda.Context, - cb: AWSLambda.Callback, -) => {}; - -const albSyncHandler: AWSLambda.ALBHandler = ( - event: AWSLambda.ALBEvent, - context: AWSLambda.Context, - cb: AWSLambda.ALBCallback, -) => { - cb(null, { - statusCode: 200, - statusDescription: '200 OK', - headers: {}, - body: '', - isBase64Encoded: false, - }); -}; -const albAsyncHandler: AWSLambda.ALBHandler = async ( - event: AWSLambda.ALBEvent, - context: AWSLambda.Context, - cb: AWSLambda.ALBCallback, -) => { - return { - statusCode: 200, - statusDescription: '200 OK', - headers: {}, - body: '', - isBase64Encoded: false, - }; -}; - -// Specific types -let s3Handler: AWSLambda.S3Handler = ( - event: AWSLambda.S3Event, - context: AWSLambda.Context, - cb: AWSLambda.Callback, -) => {}; -// Test old name -const s3CreateHandler: AWSLambda.S3Handler = ( - event: AWSLambda.S3CreateEvent, - context: AWSLambda.Context, - cb: AWSLambda.Callback, -) => {}; -s3Handler = s3CreateHandler; - -const dynamoDBStreamHandler: AWSLambda.DynamoDBStreamHandler = ( - event: AWSLambda.DynamoDBStreamEvent, - context: AWSLambda.Context, - cb: AWSLambda.Callback, -) => {}; - -const snsHandler: AWSLambda.SNSHandler = ( - event: AWSLambda.SNSEvent, - context: AWSLambda.Context, - cb: AWSLambda.Callback, -) => {}; - -const cognitoUserPoolHandler: AWSLambda.CognitoUserPoolTriggerHandler = ( - event: AWSLambda.CognitoUserPoolEvent, - context: AWSLambda.Context, - cb: AWSLambda.Callback, -) => {}; - -const cloudFormationCustomResourceHandler: AWSLambda.CloudFormationCustomResourceHandler = ( - event: AWSLambda.CloudFormationCustomResourceEvent, - context: AWSLambda.Context, - cb: AWSLambda.Callback, -) => {}; - -const cloudWatchLogsHandler: AWSLambda.CloudWatchLogsHandler = ( - event: AWSLambda.CloudWatchLogsEvent, - context: AWSLambda.Context, - cb: AWSLambda.Callback, -) => {}; - -const scheduledHandler: AWSLambda.ScheduledHandler = ( - event: AWSLambda.ScheduledEvent, - context: AWSLambda.Context, - cb: AWSLambda.Callback, -) => {}; - -let apiGtwProxyHandler: AWSLambda.APIGatewayProxyHandler = ( - event: AWSLambda.APIGatewayProxyEvent, - context: AWSLambda.Context, - cb: AWSLambda.APIGatewayProxyCallback, -) => {}; -// Test old names -const proxyHandler: AWSLambda.ProxyHandler = ( - event: AWSLambda.APIGatewayEvent, - context: AWSLambda.Context, - cb: AWSLambda.ProxyCallback, -) => {}; -apiGtwProxyHandler = proxyHandler; - -const codePipelineHandler: AWSLambda.CodePipelineHandler = ( - event: AWSLambda.CodePipelineEvent, - context: AWSLambda.Context, - cb: AWSLambda.Callback, -) => {}; - -const cloudFrontRequestHandler: AWSLambda.CloudFrontRequestHandler = ( - event: AWSLambda.CloudFrontRequestEvent, - context: AWSLambda.Context, - cb: AWSLambda.CloudFrontRequestCallback, -) => { - event = CloudFrontRequestWithCustomOriginEvent; - // $ExpectType CloudFrontRequestEvent - event; - let request = event.Records[0].cf.request; - - let s3Origin: AWSLambda.CloudFrontS3Origin = { - authMethod: 'none', - customHeaders: {}, - domainName: 'example.com', - path: '/', - region: 'us-east-1', - }; - - if (request.origin && request.origin.custom) { - request.origin.custom.domainName; - request.origin.custom.domainName = 'example2.com'; - - // $ExpectError - s3Origin = request.origin.s3; - - // $ExpectError - request.origin.s3.path = '/'; - } - - let customOrigin: AWSLambda.CloudFrontCustomOrigin = { - customHeaders: {}, - domainName: 'example.com', - keepaliveTimeout: 60, - path: '/', - port: 80, - protocol: 'http', - readTimeout: 30, - sslProtocols: [], - }; - - event = CloudFrontRequestWithS3OriginEvent; - // $ExpectType CloudFrontRequestEvent - event; - request = event.Records[0].cf.request; - if (request.origin && request.origin.s3) { - request.origin.s3.path; - request.origin.s3.path = '/new_path'; - - // $ExpectError - customOrigin = request.origin.custom; - - // $ExpectError - request.origin.custom.path = '/'; - } - - cb(); - cb(null); - cb(new Error('')); - cb(null, { clientIp: str, method: str, uri: str, querystring: str, headers: {} }); - cb(null, { status: str }); - // $ExpectError - cb(null, {}); -}; - -const cloudFrontResponseHandler: AWSLambda.CloudFrontResponseHandler = ( - event: AWSLambda.CloudFrontResponseEvent, - context: AWSLambda.Context, - cb: AWSLambda.CloudFrontResponseCallback, -) => {}; - -const cloudFrontHeaders: AWSLambda.CloudFrontHeaders = { - 'content-type': [{ value: 'text/plain' }], - 'x-foo-bar': [{ key: 'X-Foo-Bar', value: 'example' }], -}; - -const customAuthorizerHandler: AWSLambda.CustomAuthorizerHandler = ( - event: AWSLambda.CustomAuthorizerEvent, - context: AWSLambda.Context, - cb: AWSLambda.CustomAuthorizerCallback, -) => {}; - interface CustomEvent { eventString: string; eventBool: boolean; @@ -1128,281 +108,89 @@ interface CustomResult { resultString: string; resultBool?: boolean; } +type CustomHandler = AWSLambda.Handler; type CustomCallback = AWSLambda.Callback; -const customHandler: AWSLambda.Handler = (event, context, cb) => { - // $ExpectType CustomEvent + +// Untyped handlers should work +const untypedAsyncHandler: AWSLambda.Handler = async (event, context, cb) => { + // $ExpectType any event; - str = event.eventString; - bool = event.eventBool; // $ExpectType Context context; - // $ExpectType Callback + // $ExpectType Callback cb; - cb(null, { resultString: str, resultBool: bool }); - // $ExpectError + // Can still use callback + cb(null, { resultString: str }); + if (bool) { + // Uncaught error + return { resultString: bool }; + } + return { resultString: str }; +}; + +const untypedCallbackHandler: AWSLambda.Handler = (event, context, cb) => { + // $ExpectType any + event; + // $ExpectType Context + context; + // $ExpectType Callback + cb; + cb(null, { resultString: str }); + // Uncaught error cb(null, { resultString: bool }); }; -const kinesisStreamHandler: AWSLambda.KinesisStreamHandler = ( - event: AWSLambda.KinesisStreamEvent, - context: AWSLambda.Context, - cb: AWSLambda.Callback, -) => {}; +/* In node8.10 runtime, handlers may return a promise for the result value, so existing async + * handlers that return Promise before calling the callback will now have a `null` result. + * Be safe and make that badly typed with a major verson bump to 8.10 so users expect the breaking change, + * since the upgrade effort should be pretty low in most cases, and it points them at a nicer solution. + */ -const SQSMessageHandler: AWSLambda.SQSHandler = ( - event: AWSLambda.SQSEvent, - context: AWSLambda.Context, - cb: AWSLambda.Callback, -) => {}; - -// See https://docs.aws.amazon.com/lambda/latest/dg/eventsources.html#eventsources-sqs -const SQSEvent: AWSLambda.SQSEvent = { - Records: [ - { - messageId: 'c80e8021-a70a-42c7-a470-796e1186f753', - receiptHandle: 'AQEBJQ+/u6NsnT5t8Q/VbVxgdUl4TMKZ5FqhksRdIQvLBhwNvADoBxYSOVeCBXdnS9P+', - body: '{"foo":"bar"}', - attributes: { - ApproximateReceiveCount: '3', - SentTimestamp: '1529104986221', - SenderId: '594035263019', - ApproximateFirstReceiveTimestamp: '1529104986230', - }, - messageAttributes: { - testAttr: { - stringValue: '100', - binaryValue: 'base64Str', - stringListValues: [], - binaryListValues: [], - dataType: 'Number', - }, - }, - md5OfBody: '9bb58f26192e4ba00f01e2e7b136bbd8', - eventSource: 'aws:sqs', - eventSourceARN: 'arn:aws:sqs:us-west-2:594035263019:NOTFIFOQUEUE', - awsRegion: 'us-west-2', - }, - ], +// Test we get error for unsafe old style +// $ExpectError +const unsafeAsyncHandler: CustomHandler = async (event, context, cb) => { + cb(null, { resultString: 'No longer valid' }); }; -const SQSMessageLegacyAsyncHandler: AWSLambda.SQSHandler = async ( - event: AWSLambda.SQSEvent, - context: AWSLambda.Context, - cb: AWSLambda.Callback, -) => { - // $ExpectType SQSEvent +// Test safe old style still works +const typedCallbackHandler: CustomHandler = (event, context, cb) => { + // $ExpectType CustomEvent event; - str = event.Records[0].messageId; - anyObj = event.Records[0].body; + str = event.eventString; // $ExpectType Context context; str = context.functionName; - // $ExpectType Callback + // $ExpectType Callback cb; cb(); cb(null); cb(new Error()); + // $ExpectError + cb(null, {}); + cb(null, { resultString: str }); + // $ExpectError + cb(null, { resultString: bool }); }; -const SQSMessageNode8AsyncHandler: AWSLambda.SQSHandler = async ( - event: AWSLambda.SQSEvent, +// Test preferred new type +const typedAsyncHandler: CustomHandler = async (event, context, cb) => { + // $ExpectType CustomEvent + event; + // $ExpectType Context + context; + // $ExpectType Callback + cb; + // Can still use callback + cb(null, { resultString: str }); + return { resultString: 'Is now valid!' }; +}; + +// $ExpectError +const badTypedAsyncHandler: CustomHandler = async (event, context, cb) => ({ resultString: bool }); + +// Test using untyped Callback type still works. +const mixedUntypedCallbackTypedHandler: CustomHandler = ( + event: CustomEvent, context: AWSLambda.Context, -) => { - // $ExpectType SQSEvent - event; - str = event.Records[0].messageId; - anyObj = event.Records[0].body; - strOrUndefined = event.Records[0].messageAttributes.testAttr.stringValue; - strOrUndefined = event.Records[0].messageAttributes.testAttr.binaryValue; - str = event.Records[0].messageAttributes.testAttr.dataType; - - // $ExpectType Context - context; - str = context.functionName; - return; -}; - -const firehoseEventHandler: AWSLambda.FirehoseTransformationHandler = ( - event: AWSLambda.FirehoseTransformationEvent, - context: AWSLambda.Context, - callback: AWSLambda.FirehoseTransformationCallback, -) => { - // $ExpectType FirehoseTransformationEvent - event; - str = event.records[0].recordId; - - // $ExpectType Context - context; - str = context.functionName; - callback(null, { - records: [ - { - recordId: event.records[0].recordId, - result: 'Ok' as AWSLambda.FirehoseRecordTransformationStatus, - data: 'eyJmb28iOiJiYXIifQ==', - }, - ], - }); -}; - -declare let lexEvent: AWSLambda.LexEvent; -lexEvent = { - currentIntent: { - name: 'intent-name', - slots: { - slot1: null, - slot2: 'value2', - }, - slotDetails: { - slot1: { - resolutions: [{ value: 'value1' }], - originalValue: 'originalValue', - }, - }, - confirmationStatus: 'None', - }, - bot: { - name: 'bot name', - alias: 'bot alias', - version: 'bot version', - }, - userId: 'User ID specified in the POST request to Amazon Lex.', - inputTranscript: 'Text used to process the request', - invocationSource: 'FulfillmentCodeHook', - outputDialogMode: 'Text', - messageVersion: '1.0', - sessionAttributes: { - key1: 'value1', - key2: 'value2', - }, - requestAttributes: { - key1: 'value1', - key2: 'value2', - }, -}; - -declare let lexResult: AWSLambda.LexResult; -declare let lexDialogAction: AWSLambda.LexDialogAction; -declare let lexDialogActionBase: AWSLambda.LexDialogActionBase; -declare let lexDialogActionClose: AWSLambda.LexDialogActionClose; -declare let lexDialogActionConfirmIntent: AWSLambda.LexDialogActionConfirmIntent; -declare let lexDialogActionDelegate: AWSLambda.LexDialogActionDelegate; -declare let lexDialogActionElicitIntent: AWSLambda.LexDialogActionElicitIntent; -declare let lexDialogActionElicitSlot: AWSLambda.LexDialogActionElicitSlot; -declare let lexGenericAttachment: AWSLambda.LexGenericAttachment; - -lexResult = { - sessionAttributes: { - attrib1: 'Value One', - }, - dialogAction: { - type: 'Close', - fulfillmentState: 'Failed', - }, -}; - -str = lexGenericAttachment.title; -str = lexGenericAttachment.subTitle; -str = lexGenericAttachment.imageUrl; -str = lexGenericAttachment.attachmentLinkUrl; -str = lexGenericAttachment.buttons[0].text; -str = lexGenericAttachment.buttons[0].value; - -lexDialogAction.type === 'Close'; -lexDialogAction.type === 'ConfirmIntent'; -lexDialogAction.type === 'Delegate'; -lexDialogAction.type === 'ElicitIntent'; -lexDialogAction.type === 'ElicitSlot'; - -lexDialogActionBase.message!.contentType === 'CustomPayload'; -lexDialogActionBase.message!.contentType === 'PlainText'; -lexDialogActionBase.message!.contentType === 'SSML'; -str = lexDialogActionBase.message!.content; -num = lexDialogActionBase.responseCard!.version; -lexDialogActionBase.responseCard!.contentType === 'application/vnd.amazonaws.card.generic'; -// $ExpectType LexGenericAttachment -lexDialogActionBase.responseCard!.genericAttachments[0]; - -lexDialogActionClose.type === 'Close'; -lexDialogActionClose.fulfillmentState === 'Failed'; -lexDialogActionClose.fulfillmentState === 'Fulfilled'; - -lexDialogActionConfirmIntent.type === 'ConfirmIntent'; -str = lexDialogActionConfirmIntent.intentName; -strOrNull = lexDialogActionConfirmIntent.slots['example']; - -lexDialogActionDelegate.type === 'Delegate'; -strOrNull = lexDialogActionDelegate.slots['example']; - -lexDialogActionElicitIntent.type === 'ElicitIntent'; -lexDialogActionElicitSlot.type === 'ElicitSlot'; -strOrNull = lexDialogActionElicitSlot.slots['example']; -str = lexDialogActionElicitSlot.slotToElicit; -str = lexDialogActionElicitSlot.intentName; - -const lexEventHandler: AWSLambda.LexHandler = async (event: AWSLambda.LexEvent, context: AWSLambda.Context) => { - // $ExpectType LexEvent - event; - - // $ExpectType Context - context; - str = context.functionName; - return lexResult; -}; - -/** - * S3 Batch Operations event - * https://docs.aws.amazon.com/AmazonS3/latest/dev/batch-ops-invoke-lambda.html - */ -const S3BatchEvent: AWSLambda.S3BatchEvent = { - invocationSchemaVersion: '1.0', - invocationId: 'foo_invocation_id', - job: { id: 'foo_job_id' }, - tasks: [ - { - taskId: '11111', - s3Key: 'example.json', - s3BucketArn: 'arn:aws:s3:::foo-bucket', - s3VersionId: null, - }, - { - taskId: '11111', - s3Key: 'example.json', - s3BucketArn: 'arn:aws:s3:::foo-bucket', - s3VersionId: 'asdf', - }, - ], -}; - -const S3BatchResult: AWSLambda.S3BatchResult = { - invocationSchemaVersion: '1.0', - treatMissingKeysAs: 'PermanentFailure', - invocationId: 'foo_invocation_id', - results: [ - { - taskId: '11111', - resultCode: 'Succeeded', - resultString: 'foo', - }, - { - taskId: '22222', - resultCode: 'TemporaryFailure', - resultString: 'Error: failure', - }, - { - taskId: '33333', - resultCode: 'PermanentFailure', - resultString: 'Error: failure', - }, - ], -}; - -const S3BatchHandler: AWSLambda.S3BatchHandler = async (event: AWSLambda.S3BatchEvent, context: AWSLambda.Context) => { - // $ExpectType S3BatchEvent - event; - - // $ExpectType Context - context; - str = context.functionName; - return S3BatchResult; -}; + cb: AWSLambda.Callback, +) => {}; diff --git a/types/aws-lambda/common/api-gateway.d.ts b/types/aws-lambda/common/api-gateway.d.ts new file mode 100644 index 0000000000..f031f4c74a --- /dev/null +++ b/types/aws-lambda/common/api-gateway.d.ts @@ -0,0 +1,46 @@ +// Used by both APIGatewayPraxyEvent and APIGatewayAuthorizerEvent +export interface APIGatewayEventRequestContext { + accountId: string; + apiId: string; + authorizer?: AuthResponseContext | null; + connectedAt?: number; + connectionId?: string; + domainName?: string; + domainPrefix?: string; + eventType?: string; + extendedRequestId?: string; + httpMethod: string; + identity: { + accessKey: string | null; + accountId: string | null; + apiKey: string | null; + apiKeyId: string | null; + caller: string | null; + cognitoAuthenticationProvider: string | null; + cognitoAuthenticationType: string | null; + cognitoIdentityId: string | null; + cognitoIdentityPoolId: string | null; + principalOrgId: string | null; + sourceIp: string; + user: string | null; + userAgent: string | null; + userArn: string | null; + }; + messageDirection?: string; + messageId?: string | null; + path: string; + stage: string; + requestId: string; + requestTime?: string; + requestTimeEpoch: number; + resourceId: string; + resourcePath: string; + routeKey?: string; +} + +/** + * http://docs.aws.amazon.com/apigateway/latest/developerguide/use-custom-authorizer.html#api-gateway-custom-authorizer-output + */ +export interface AuthResponseContext { + [name: string]: any; +} diff --git a/types/aws-lambda/common/cloudfront.d.ts b/types/aws-lambda/common/cloudfront.d.ts new file mode 100644 index 0000000000..f0ebbae8f5 --- /dev/null +++ b/types/aws-lambda/common/cloudfront.d.ts @@ -0,0 +1,79 @@ +/** + * CloudFront events + * http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html + * Bear in mind that the "example" event structure in the page above includes + * both an S3 and a Custom origin, which is not strictly allowed. Only one + * of these per event may be present. + */ +export interface CloudFrontHeaders { + [name: string]: Array<{ + key?: string; + value: string; + }>; +} + +export type CloudFrontOrigin = + | { s3: CloudFrontS3Origin; custom?: never } + | { custom: CloudFrontCustomOrigin; s3?: never }; + +export interface CloudFrontCustomOrigin { + customHeaders: CloudFrontHeaders; + domainName: string; + keepaliveTimeout: number; + path: string; + port: number; + protocol: 'http' | 'https'; + readTimeout: number; + sslProtocols: string[]; +} + +export interface CloudFrontS3Origin { + authMethod: 'origin-access-identity' | 'none'; + customHeaders: CloudFrontHeaders; + domainName: string; + path: string; + region: string; +} + +export interface CloudFrontResponse { + status: string; + statusDescription: string; + headers: CloudFrontHeaders; +} + +export interface CloudFrontRequest { + body?: { + action: 'read-only' | 'replace'; + data: string; + encoding: 'base64' | 'text'; + readonly inputTruncated: boolean; + }; + readonly clientIp: string; + readonly method: string; + uri: string; + querystring: string; + headers: CloudFrontHeaders; + origin?: CloudFrontOrigin; +} + +export interface CloudFrontEvent { + config: { + readonly distributionDomainName: string; + readonly distributionId: string; + } & ( + | { readonly eventType: 'origin-request' | 'origin-response' } + | { readonly eventType: 'viewer-request' | 'viewer-response'; readonly requestId: string }); +} + +/** + * Generated HTTP response in viewer request event or origin request event + * + * https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-generating-http-responses-in-requests.html#lambda-generating-http-responses-object + */ +export interface CloudFrontResultResponse { + status: string; + statusDescription?: string; + headers?: CloudFrontHeaders; + bodyEncoding?: 'text' | 'base64'; + body?: string; +} diff --git a/types/aws-lambda/hander.d.ts b/types/aws-lambda/hander.d.ts new file mode 100644 index 0000000000..54c270a246 --- /dev/null +++ b/types/aws-lambda/hander.d.ts @@ -0,0 +1,171 @@ +/** + * The interface that AWS Lambda will invoke your handler with. + * There are more specialized types for many cases where AWS services + * invoke your lambda, but you can directly use this type for when you are invoking + * your lambda directly. + * + * See tme {@link http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html AWS documentation} + * for more information about the runtime behavior, and the + * {@link https://aws.amazon.com/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/ AWS Blog post} + * introducing the async handler behavior in the 8.10 runtime. + * + * @example Defining a custom handler type + * import { Handler } from 'aws-lambda' + * + * interface NameEvent { + * fullName: string + * } + * interface NameResult { + * firstName: string + * middleNames: string + * lastName: string + * } + * type PersonHandler = Handler + * + * export const handler: PersonHandler = async (event) => { + * const names = event.fullName.split(' ') + * const firstName = names.shift() + * const lastName = names.pop() + * return { firstName, middleNames: names, lastName } + * } + * + * @example Logs the contents of the event object and returns the location of the logs + * import { Handler } from 'aws-lambda' + * + * export const handler: Handler = async (event, context) => { + * console.log("EVENT: \n" + JSON.stringify(event, null, 2)) + * return context.logStreamName + * } + * + * @example AWS SDK with Async Function and Promises + * import { Handler } from 'aws-lambda' + * import AWS from 'aws-sdk' + * + * const s3 = new AWS.S3() + * + * export const handler: Handler = async (event) => { + * const response = await s3.listBuckets().promise() + * return response?.Buckets.map((bucket) => bucket.Name) + * } + * + * @example HTTP Request with Callback + * import { Handler } from 'aws-lambda' + * import https from 'https' + * + * let url = "https://docs.aws.amazon.com/lambda/latest/dg/welcome.html" + * + * export const handler: Handler = (event, context, callback) => { + * https.get(url, (res) => { + * callback(null, res.statusCode) + * }).on('error', (e) => { + * callback(Error(e)) + * }) + * } + * + * @param event + * Parsed JSON data in the lambda request payload. For an AWS service triggered + * lambda this should be in the format of a type ending in Event, for example the + * S3Handler receives an event of type S3Event. + * @param context + * Runtime contextual information of the current invocation, for example the caller + * identity, available memory and time remaining, legacy completion callbacks, and + * a mutable property controlling when the lambda execution completes. + * @param callback + * NodeJS-style completion callback that the AWS Lambda runtime will provide that can + * be used to provide the lambda result payload value, or any execution error. Can + * instead return a promise that resolves with the result payload value or rejects + * with the execution error. + * @return + * A promise that resolves with the lambda result payload value, or rejects with the + * execution error. Note that if you implement your handler as an async function, + * you will automatically return a promise that will resolve with a returned value, + * or reject with a thrown value. + */ +export type Handler = ( + event: TEvent, + context: Context, + callback: Callback, +) => void | Promise; + +/** + * {@link Handler} context parameter. + * See {@link https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html AWS documentation}. + */ +export interface Context { + callbackWaitsForEmptyEventLoop: boolean; + functionName: string; + functionVersion: string; + invokedFunctionArn: string; + memoryLimitInMB: string; + awsRequestId: string; + logGroupName: string; + logStreamName: string; + identity?: CognitoIdentity; + clientContext?: ClientContext; + + getRemainingTimeInMillis(): number; + + // Functions for compatibility with earlier Node.js Runtime v0.10.42 + // No longer documented, so they are deprecated, but they still work + // as of the 12.x runtime, so they are not removed from the types. + + /** @deprecated Use handler callback or promise result */ + done(error?: Error, result?: any): void; + /** @deprecated Use handler callback with first argument or reject a promise result */ + fail(error: Error | string): void; + /** @deprecated Use handler callback with second argument or resolve a promise result */ + succeed(messageOrObject: any): void; + // Unclear what behavior this is supposed to have, I couldn't find any still extant reference, + // and it behaves like the above, ignoring the object parameter. + /** @deprecated Use handler callback or promise result */ + succeed(message: string, object: any): void; +} + +export interface CognitoIdentity { + cognitoIdentityId: string; + cognitoIdentityPoolId: string; +} + +export interface ClientContext { + client: ClientContextClient; + Custom?: any; + env: ClientContextEnv; +} + +export interface ClientContextClient { + installationId: string; + appTitle: string; + appVersionName: string; + appVersionCode: string; + appPackageName: string; +} + +export interface ClientContextEnv { + platformVersion: string; + platform: string; + make: string; + model: string; + locale: string; +} + +/** + * NodeJS-style callback parameter for the {@link Handler} type. + * Can be used instead of returning a promise, see the + * {@link https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html AWS documentation} + * for the handler programming model. + * + * @param error + * Parameter to use to provide the error payload for a failed lambda execution. + * See {@link https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-mode-exceptions.html AWS documentation} + * for error handling. + * If an Error instance is passed, the error payload uses the `name` property as the `errorType`, + * the `message` property as the `errorMessage`, and parses the `stack` property string into + * the `trace` array. + * For other values, the `errorType` is `typeof value`, the `errorMessage` is `String(value)`, and + * `trace` is an empty array. + * + * @param result + * Parameter to use to provide the result payload for a successful lambda execution. + * Pass `null` or `undefined` for the `error` parameter to use this parameter. + */ +export type Callback = (error?: Error | string | null, result?: TResult) => void; diff --git a/types/aws-lambda/index.d.ts b/types/aws-lambda/index.d.ts index dc0262f046..58a9c7379d 100644 --- a/types/aws-lambda/index.d.ts +++ b/types/aws-lambda/index.d.ts @@ -1,8 +1,8 @@ // Type definitions for AWS Lambda 8.10 // Project: http://docs.aws.amazon.com/lambda -// Definitions by: James Darbyshire +// Definitions by: James Darbyshire // Michael Skarum -// Stef Heyenrath +// Stef Heyenrath // Toby Hede // Rich Buggy // Yoriki Yamaguchi @@ -36,1170 +36,40 @@ // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.8 -// API Gateway "event" request context -export interface APIGatewayEventRequestContext { - accountId: string; - apiId: string; - authorizer?: AuthResponseContext | null; - connectedAt?: number; - connectionId?: string; - domainName?: string; - domainPrefix?: string; - eventType?: string; - extendedRequestId?: string; - httpMethod: string; - identity: { - accessKey: string | null; - accountId: string | null; - apiKey: string | null; - apiKeyId: string | null; - caller: string | null; - cognitoAuthenticationProvider: string | null; - cognitoAuthenticationType: string | null; - cognitoIdentityId: string | null; - cognitoIdentityPoolId: string | null; - principalOrgId: string | null; - sourceIp: string; - user: string | null; - userAgent: string | null; - userArn: string | null; - }; - messageDirection?: string; - messageId?: string | null; - path: string; - stage: string; - requestId: string; - requestTime?: string; - requestTimeEpoch: number; - resourceId: string; - resourcePath: string; - routeKey?: string; -} - -// API Gateway "event" -export interface APIGatewayProxyEvent { - body: string | null; - headers: { [name: string]: string }; - multiValueHeaders: { [name: string]: string[] }; - httpMethod: string; - isBase64Encoded: boolean; - path: string; - pathParameters: { [name: string]: string } | null; - queryStringParameters: { [name: string]: string } | null; - multiValueQueryStringParameters: { [name: string]: string[] } | null; - stageVariables: { [name: string]: string } | null; - requestContext: APIGatewayEventRequestContext; - resource: string; -} -export type APIGatewayEvent = APIGatewayProxyEvent; // Old name - -// https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html -export interface ALBEventRequestContext { - elb: { - targetGroupArn: string; - }; -} -export interface ALBEvent { - requestContext: ALBEventRequestContext; - httpMethod: string; - path: string; - queryStringParameters?: { [parameter: string]: string }; // URL encoded - headers?: { [header: string]: string }; - multiValueQueryStringParameters?: { [parameter: string]: string[] }; // URL encoded - multiValueHeaders?: { [header: string]: string[] }; - body: string | null; - isBase64Encoded: boolean; -} - -// API Gateway CustomAuthorizer "event" -export interface CustomAuthorizerEvent { - type: string; - methodArn: string; - authorizationToken?: string; - resource?: string; - path?: string; - httpMethod?: string; - headers?: { [name: string]: string }; - multiValueHeaders?: { [name: string]: string[] }; - pathParameters?: { [name: string]: string } | null; - queryStringParameters?: { [name: string]: string } | null; - multiValueQueryStringParameters?: { [name: string]: string[] } | null; - stageVariables?: { [name: string]: string }; - requestContext?: APIGatewayEventRequestContext; - domainName?: string; - apiId?: string; -} - -// Context -// http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_streams_AttributeValue.html -export interface AttributeValue { - B?: string; - BS?: string[]; - BOOL?: boolean; - L?: AttributeValue[]; - M?: { [id: string]: AttributeValue }; - N?: string; - NS?: string[]; - NULL?: boolean; - S?: string; - SS?: string[]; -} - -// Context -// http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_streams_StreamRecord.html -export interface StreamRecord { - ApproximateCreationDateTime?: number; - Keys?: { [key: string]: AttributeValue }; - NewImage?: { [key: string]: AttributeValue }; - OldImage?: { [key: string]: AttributeValue }; - SequenceNumber?: string; - SizeBytes?: number; - StreamViewType?: 'KEYS_ONLY' | 'NEW_IMAGE' | 'OLD_IMAGE' | 'NEW_AND_OLD_IMAGES'; -} - -// Context -// http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_streams_Record.html -export interface DynamoDBRecord { - awsRegion?: string; - dynamodb?: StreamRecord; - eventID?: string; - eventName?: 'INSERT' | 'MODIFY' | 'REMOVE'; - eventSource?: string; - eventSourceARN?: string; - eventVersion?: string; - userIdentity?: any; -} - -// AWS Lambda Stream event -// Context -// http://docs.aws.amazon.com/lambda/latest/dg/eventsources.html#eventsources-ddb-update -export interface DynamoDBStreamEvent { - Records: DynamoDBRecord[]; -} - -// SNS "event" -export interface SNSMessageAttribute { - Type: string; - Value: string; -} - -export interface SNSMessageAttributes { - [name: string]: SNSMessageAttribute; -} - -export interface SNSMessage { - SignatureVersion: string; - Timestamp: string; - Signature: string; - SigningCertUrl: string; - MessageId: string; - Message: string; - MessageAttributes: SNSMessageAttributes; - Type: string; - UnsubscribeUrl: string; - TopicArn: string; - Subject: string; -} - -export interface SNSEventRecord { - EventVersion: string; - EventSubscriptionArn: string; - EventSource: string; - Sns: SNSMessage; -} - -export interface SNSEvent { - Records: SNSEventRecord[]; -} - -/** - * S3Create event - * https://docs.aws.amazon.com/AmazonS3/latest/dev/notification-content-structure.html - */ - -export interface S3EventRecordGlacierRestoreEventData { - lifecycleRestorationExpiryTime: string; - lifecycleRestoreStorageClass: string; -} - -export interface S3EventRecordGlacierEventData { - restoreEventData: S3EventRecordGlacierRestoreEventData; -} - -export interface S3EventRecord { - eventVersion: string; - eventSource: string; - awsRegion: string; - eventTime: string; - eventName: string; - userIdentity: { - principalId: string; - }; - requestParameters: { - sourceIPAddress: string; - }; - responseElements: { - 'x-amz-request-id': string; - 'x-amz-id-2': string; - }; - s3: { - s3SchemaVersion: string; - configurationId: string; - bucket: { - name: string; - ownerIdentity: { - principalId: string; - }; - arn: string; - }; - object: { - key: string; - size: number; - eTag: string; - versionId?: string; - sequencer: string; - }; - }; - glacierEventData?: S3EventRecordGlacierEventData; -} - -export interface S3Event { - Records: S3EventRecord[]; -} - -export type S3CreateEvent = S3Event; // old name - -/** - * S3 Batch Operations event - * https://docs.aws.amazon.com/AmazonS3/latest/dev/batch-ops-invoke-lambda.html - */ - -export interface S3BatchEvent { - invocationSchemaVersion: string; - invocationId: string; - job: S3BatchEventJob; - tasks: S3BatchEventTask[]; -} - -export interface S3BatchEventJob { - id: string; -} - -export interface S3BatchEventTask { - taskId: string; - s3Key: string; - s3VersionId: string | null; - s3BucketArn: string; -} - -export interface S3BatchResult { - invocationSchemaVersion: string; - treatMissingKeysAs: S3BatchResultResultCode; - invocationId: string; - results: S3BatchResultResult[]; -} - -export type S3BatchResultResultCode = 'Succeeded' | 'TemporaryFailure' | 'PermanentFailure'; - -export interface S3BatchResultResult { - taskId: string; - resultCode: S3BatchResultResultCode; - resultString: string; -} - -/** - * Cognito User Pool event - * http://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html - */ -export interface CognitoUserPoolTriggerEvent { - version: number; - triggerSource: - | 'PreSignUp_SignUp' - | 'PreSignUp_ExternalProvider' - | 'PostConfirmation_ConfirmSignUp' - | 'PreAuthentication_Authentication' - | 'PostAuthentication_Authentication' - | 'CustomMessage_SignUp' - | 'CustomMessage_AdminCreateUser' - | 'CustomMessage_ResendCode' - | 'CustomMessage_ForgotPassword' - | 'CustomMessage_UpdateUserAttribute' - | 'CustomMessage_VerifyUserAttribute' - | 'CustomMessage_Authentication' - | 'DefineAuthChallenge_Authentication' - | 'CreateAuthChallenge_Authentication' - | 'VerifyAuthChallengeResponse_Authentication' - | 'PreSignUp_AdminCreateUser' - | 'PostConfirmation_ConfirmForgotPassword' - | 'TokenGeneration_HostedAuth' - | 'TokenGeneration_Authentication' - | 'TokenGeneration_NewPasswordChallenge' - | 'TokenGeneration_AuthenticateDevice' - | 'TokenGeneration_RefreshTokens' - | 'UserMigration_Authentication' - | 'UserMigration_ForgotPassword'; - region: string; - userPoolId: string; - userName?: string; - callerContext: { - awsSdkVersion: string; - clientId: string; - }; - request: { - userAttributes: { [key: string]: string }; - validationData?: { [key: string]: string }; - codeParameter?: string; - linkParameter?: string; - usernameParameter?: string; - newDeviceUsed?: boolean; - session?: Array<{ - challengeName: - | 'CUSTOM_CHALLENGE' - | 'PASSWORD_VERIFIER' - | 'SMS_MFA' - | 'DEVICE_SRP_AUTH' - | 'DEVICE_PASSWORD_VERIFIER' - | 'ADMIN_NO_SRP_AUTH'; - challengeResult: boolean; - challengeMetadata?: string; - }>; - challengeName?: string; - privateChallengeParameters?: { [key: string]: string }; - challengeAnswer?: string; - password?: string; - }; - response: { - autoConfirmUser?: boolean; - autoVerifyPhone?: boolean; - autoVerifyEmail?: boolean; - smsMessage?: string; - emailMessage?: string; - emailSubject?: string; - challengeName?: string; - issueTokens?: boolean; - failAuthentication?: boolean; - publicChallengeParameters?: { [key: string]: string }; - privateChallengeParameters?: { [key: string]: string }; - challengeMetadata?: string; - answerCorrect?: boolean; - userAttributes?: { [key: string]: string }; - finalUserStatus?: 'CONFIRMED' | 'RESET_REQUIRED'; - messageAction?: 'SUPPRESS'; - desiredDeliveryMediums?: Array<'EMAIL' | 'SMS'>; - forceAliasCreation?: boolean; - claimsOverrideDetails?: { - claimsToAddOrOverride?: { [key: string]: string }; - claimsToSuppress?: string[]; - groupOverrideDetails?: null | { - groupsToOverride?: string[]; - iamRolesToOverride?: string[]; - preferredRole?: string; - }; - }; - }; -} -export type CognitoUserPoolEvent = CognitoUserPoolTriggerEvent; - -/** - * CloudFormation Custom Resource event and response - * http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref.html - */ -export interface CloudFormationCustomResourceEventCommon { - ServiceToken: string; - ResponseURL: string; - StackId: string; - RequestId: string; - LogicalResourceId: string; - ResourceType: string; - ResourceProperties: { - ServiceToken: string; - [Key: string]: any; - }; -} - -export interface CloudFormationCustomResourceCreateEvent extends CloudFormationCustomResourceEventCommon { - RequestType: 'Create'; -} - -export interface CloudFormationCustomResourceUpdateEvent extends CloudFormationCustomResourceEventCommon { - RequestType: 'Update'; - PhysicalResourceId: string; - OldResourceProperties: { - [Key: string]: any; - }; -} - -export interface CloudFormationCustomResourceDeleteEvent extends CloudFormationCustomResourceEventCommon { - RequestType: 'Delete'; - PhysicalResourceId: string; -} - -export type CloudFormationCustomResourceEvent = - | CloudFormationCustomResourceCreateEvent - | CloudFormationCustomResourceUpdateEvent - | CloudFormationCustomResourceDeleteEvent; - -export interface CloudFormationCustomResourceResponseCommon { - PhysicalResourceId: string; - StackId: string; - RequestId: string; - LogicalResourceId: string; - Data?: { - [Key: string]: any; - }; - NoEcho?: boolean; -} - -export interface CloudFormationCustomResourceSuccessResponse extends CloudFormationCustomResourceResponseCommon { - Status: 'SUCCESS'; - Reason?: string; -} - -export interface CloudFormationCustomResourceFailedResponse extends CloudFormationCustomResourceResponseCommon { - Status: 'FAILED'; - Reason: string; -} - -export type CloudFormationCustomResourceResponse = - | CloudFormationCustomResourceSuccessResponse - | CloudFormationCustomResourceFailedResponse; - -/** - * See https://docs.aws.amazon.com/lambda/latest/dg/eventsources.html#eventsources-scheduled-event - */ -export interface ScheduledEvent { - account: string; - region: string; - detail: any; - 'detail-type': string; - source: string; - time: string; - id: string; - resources: string[]; -} - -/** - * See http://docs.aws.amazon.com/lambda/latest/dg/eventsources.html#eventsources-cloudwatch-logs - */ -export interface CloudWatchLogsEvent { - awslogs: CloudWatchLogsEventData; -} - -export interface CloudWatchLogsEventData { - data: string; -} - -export interface CloudWatchLogsDecodedData { - owner: string; - logGroup: string; - logStream: string; - subscriptionFilters: string[]; - messageType: string; - logEvents: CloudWatchLogsLogEvent[]; -} - -/** - * See http://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/SubscriptionFilters.html#LambdaFunctionExample - */ -export interface CloudWatchLogsLogEvent { - id: string; - timestamp: number; - message: string; - extractedFields?: { [key: string]: string }; -} - -// Context -// http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html -export interface Context { - // Properties - callbackWaitsForEmptyEventLoop: boolean; - functionName: string; - functionVersion: string; - invokedFunctionArn: string; - memoryLimitInMB: string; - awsRequestId: string; - logGroupName: string; - logStreamName: string; - identity?: CognitoIdentity; - clientContext?: ClientContext; - - // Functions - getRemainingTimeInMillis(): number; - - // Functions for compatibility with earlier Node.js Runtime v0.10.42 - // For more details see http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-using-old-runtime.html#nodejs-prog-model-oldruntime-context-methods - done(error?: Error, result?: any): void; - fail(error: Error | string): void; - succeed(messageOrObject: any): void; - succeed(message: string, object: any): void; -} - -export interface CognitoIdentity { - cognitoIdentityId: string; - cognitoIdentityPoolId: string; -} - -export interface ClientContext { - client: ClientContextClient; - Custom?: any; - env: ClientContextEnv; -} - -export interface ClientContextClient { - installationId: string; - appTitle: string; - appVersionName: string; - appVersionCode: string; - appPackageName: string; -} - -export interface ClientContextEnv { - platformVersion: string; - platform: string; - make: string; - model: string; - locale: string; -} - -export interface APIGatewayProxyResult { - statusCode: number; - headers?: { - [header: string]: boolean | number | string; - }; - multiValueHeaders?: { - [header: string]: Array; - }; - body: string; - isBase64Encoded?: boolean; -} -export type ProxyResult = APIGatewayProxyResult; // Old name - -export interface ALBResult { - statusCode: number; - statusDescription: string; - headers?: { [header: string]: boolean | number | string }; - multiValueHeaders?: { [header: string]: Array }; - body?: string; - isBase64Encoded: boolean; -} - -/** - * API Gateway CustomAuthorizer AuthResponse. - * http://docs.aws.amazon.com/apigateway/latest/developerguide/use-custom-authorizer.html#api-gateway-custom-authorizer-output - */ -export interface CustomAuthorizerResult { - principalId: string; - policyDocument: PolicyDocument; - context?: AuthResponseContext; - usageIdentifierKey?: string; -} -export type AuthResponse = CustomAuthorizerResult; - -/** - * API Gateway CustomAuthorizer AuthResponse.PolicyDocument. - * https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-lambda-authorizer-output.html - * https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html#Condition - */ -export interface PolicyDocument { - Version: string; - Id?: string; - Statement: Statement[]; -} - -/** - * API Gateway CustomAuthorizer AuthResponse.PolicyDocument.Condition. - * https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-control-access-policy-language-overview.html - * https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html - */ -export interface ConditionBlock { - [condition: string]: Condition | Condition[]; -} - -export interface Condition { - [key: string]: string | string[]; -} - -/** - * API Gateway CustomAuthorizer AuthResponse.PolicyDocument.Statement. - * https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-control-access-policy-language-overview.html - * https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html - */ -export type Statement = BaseStatement & StatementAction & (StatementResource | StatementPrincipal); - -export interface BaseStatement { - Effect: string; - Sid?: string; - Condition?: ConditionBlock; -} - -export type PrincipalValue = { [key: string]: string | string[] } | string | string[]; -export interface MaybeStatementPrincipal { - Principal?: PrincipalValue; - NotPrincipal?: PrincipalValue; -} -export interface MaybeStatementResource { - Resource?: string | string[]; - NotResource?: string | string[]; -} -export type StatementAction = { Action: string | string[] } | { NotAction: string | string[] }; -export type StatementResource = MaybeStatementPrincipal & - ({ Resource: string | string[] } | { NotResource: string | string[] }); -export type StatementPrincipal = MaybeStatementResource & - ({ Principal: PrincipalValue } | { NotPrincipal: PrincipalValue }); -/** - * API Gateway CustomAuthorizer AuthResponse.PolicyDocument.Statement. - * http://docs.aws.amazon.com/apigateway/latest/developerguide/use-custom-authorizer.html#api-gateway-custom-authorizer-output - */ -export interface AuthResponseContext { - [name: string]: any; -} - -/** - * CodePipeline events - * https://docs.aws.amazon.com/codepipeline/latest/userguide/actions-invoke-lambda-function.html - */ -export interface S3ArtifactLocation { - bucketName: string; - objectKey: string; -} -export interface S3ArtifactStore { - type: 'S3'; - s3Location: S3ArtifactLocation; -} - -export type ArtifactLocation = S3ArtifactStore; - -export interface Artifact { - name: string; - revision: string | null; - location: ArtifactLocation; -} - -export interface Credentials { - accessKeyId: string; - secretAccessKey: string; - sessionToken?: string; -} - -export interface EncryptionKey { - type: string; - id: string; -} - -export interface CodePipelineEvent { - 'CodePipeline.job': { - id: string; - accountId: string; - data: { - actionConfiguration: { - configuration: { - FunctionName: string; - UserParameters: string; - }; - }; - inputArtifacts: Artifact[]; - outputArtifacts: Artifact[]; - artifactCredentials: Credentials; - encryptionKey?: EncryptionKey & { type: 'KMS' }; - continuationToken?: string; - }; - }; -} - -/** - * CodePipeline CloudWatch Events - * https://docs.aws.amazon.com/codepipeline/latest/userguide/detect-state-changes-cloudwatch-events.html - * - * The above CodePipelineEvent is when a lambda is invoked by a CodePipeline. - * These events are when you subscribe to CodePipeline events in CloudWatch. - * - * Their documentation says that detail.version is a string, but it is actually an integer - */ -export type CodePipelineState = 'STARTED' | 'SUCCEEDED' | 'RESUMED' | 'FAILED' | 'CANCELED' | 'SUPERSEDED'; - -export type CodePipelineStageState = 'STARTED' | 'SUCCEEDED' | 'RESUMED' | 'FAILED' | 'CANCELED'; - -export type CodePipelineActionState = 'STARTED' | 'SUCCEEDED' | 'FAILED' | 'CANCELED'; - -export interface CodePipelineCloudWatchPipelineEvent { - version: string; - id: string; - 'detail-type': 'CodePipeline Pipeline Execution State Change'; - source: 'aws.codepipeline'; - account: string; - time: string; - region: string; - resources: string[]; - detail: { - pipeline: string; - version: number; - state: CodePipelineState; - 'execution-id': string; - }; -} - -export interface CodePipelineCloudWatchStageEvent { - version: string; - id: string; - 'detail-type': 'CodePipeline Stage Execution State Change'; - source: 'aws.codepipeline'; - account: string; - time: string; - region: string; - resources: string[]; - detail: { - pipeline: string; - version: number; - 'execution-id': string; - stage: string; - state: CodePipelineStageState; - }; -} - -export type CodePipelineActionCategory = 'Approval' | 'Build' | 'Deploy' | 'Invoke' | 'Source' | 'Test'; - -export interface CodePipelineCloudWatchActionEvent { - version: string; - id: string; - 'detail-type': 'CodePipeline Action Execution State Change'; - source: 'aws.codepipeline'; - account: string; - time: string; - region: string; - resources: string[]; - detail: { - pipeline: string; - version: number; - 'execution-id': string; - stage: string; - action: string; - state: CodePipelineActionState; - type: { - owner: 'AWS' | 'Custom' | 'ThirdParty'; - category: CodePipelineActionCategory; - provider: string; - version: number; - }; - }; -} - -export type CodePipelineCloudWatchEvent = - | CodePipelineCloudWatchPipelineEvent - | CodePipelineCloudWatchStageEvent - | CodePipelineCloudWatchActionEvent; - -/** - * CloudFront events - * http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html - * Bear in mind that the "example" event structure in the page above includes - * both an S3 and a Custom origin, which is not strictly allowed. Only one - * of these per event may be present. - */ -export interface CloudFrontHeaders { - [name: string]: Array<{ - key?: string; - value: string; - }>; -} - -export type CloudFrontOrigin = - | { s3: CloudFrontS3Origin; custom?: never } - | { custom: CloudFrontCustomOrigin; s3?: never }; - -export interface CloudFrontCustomOrigin { - customHeaders: CloudFrontHeaders; - domainName: string; - keepaliveTimeout: number; - path: string; - port: number; - protocol: 'http' | 'https'; - readTimeout: number; - sslProtocols: string[]; -} - -export interface CloudFrontS3Origin { - authMethod: 'origin-access-identity' | 'none'; - customHeaders: CloudFrontHeaders; - domainName: string; - path: string; - region: string; -} - -export interface CloudFrontResponse { - status: string; - statusDescription: string; - headers: CloudFrontHeaders; -} - -export interface CloudFrontRequest { - body?: { - action: 'read-only' | 'replace'; - data: string; - encoding: 'base64' | 'text'; - readonly inputTruncated: boolean; - }; - readonly clientIp: string; - readonly method: string; - uri: string; - querystring: string; - headers: CloudFrontHeaders; - origin?: CloudFrontOrigin; -} - -export interface CloudFrontEvent { - config: { - readonly distributionDomainName: string; - readonly distributionId: string; - } & ( - | { readonly eventType: 'origin-request' | 'origin-response' } - | { readonly eventType: 'viewer-request' | 'viewer-response'; readonly requestId: string }); -} - -/** - * Generated HTTP response in viewer request event or origin request event - * - * https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-generating-http-responses-in-requests.html#lambda-generating-http-responses-object - */ -export interface CloudFrontResultResponse { - status: string; - statusDescription?: string; - headers?: CloudFrontHeaders; - bodyEncoding?: 'text' | 'base64'; - body?: string; -} - -/** - * CloudFront viewer response or origin response event - * - * https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html#lambda-event-structure-response - */ -export interface CloudFrontResponseEvent { - Records: Array<{ - cf: CloudFrontEvent & { - readonly request: Pick>; - response: CloudFrontResponse; - }; - }>; -} - -export type CloudFrontRequestResult = undefined | null | CloudFrontResultResponse | CloudFrontRequest; - -/** - * CloudFront viewer request or origin request event - * - * https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html#lambda-event-structure-request - */ -export interface CloudFrontRequestEvent { - Records: Array<{ - cf: CloudFrontEvent & { - request: CloudFrontRequest; - }; - }>; -} - -export type CloudFrontResponseResult = undefined | null | CloudFrontResultResponse; - -// Kinesis Streams -// https://docs.aws.amazon.com/lambda/latest/dg/eventsources.html#eventsources-kinesis-streams -export interface KinesisStreamRecordPayload { - approximateArrivalTimestamp: number; - data: string; - kinesisSchemaVersion: string; - partitionKey: string; - sequenceNumber: string; -} - -export interface KinesisStreamRecord { - awsRegion: string; - eventID: string; - eventName: string; - eventSource: string; - eventSourceARN: string; - eventVersion: string; - invokeIdentityArn: string; - kinesis: KinesisStreamRecordPayload; -} - -export interface KinesisStreamEvent { - Records: KinesisStreamRecord[]; -} - -// Kinesis Data Firehose Event -// https://docs.aws.amazon.com/lambda/latest/dg/eventsources.html#eventsources-kinesis-firehose -// https://docs.aws.amazon.com/firehose/latest/dev/data-transformation.html -// https://aws.amazon.com/blogs/compute/amazon-kinesis-firehose-data-transformation-with-aws-lambda/ -// Examples in the lambda blueprints -export interface FirehoseTransformationEvent { - invocationId: string; - deliveryStreamArn: string; - region: string; - records: FirehoseTransformationEventRecord[]; -} - -export interface FirehoseTransformationEventRecord { - recordId: string; - approximateArrivalTimestamp: number; - /** Base64 encoded */ - data: string; - kinesisRecordMetadata?: FirehoseRecordMetadata; -} - -export interface FirehoseRecordMetadata { - shardId: string; - partitionKey: string; - approximateArrivalTimestamp: string; - sequenceNumber: string; - subsequenceNumber: string; -} - -export type FirehoseRecordTransformationStatus = 'Ok' | 'Dropped' | 'ProcessingFailed'; - -export interface FirehoseTransformationResultRecord { - recordId: string; - result: FirehoseRecordTransformationStatus; - /** Encode in Base64 */ - data: string; -} - -export interface FirehoseTransformationResult { - records: FirehoseTransformationResultRecord[]; -} - -// SQS -// https://docs.aws.amazon.com/lambda/latest/dg/invoking-lambda-function.html#supported-event-source-sqs -export interface SQSRecord { - messageId: string; - receiptHandle: string; - body: string; - attributes: SQSRecordAttributes; - messageAttributes: SQSMessageAttributes; - md5OfBody: string; - eventSource: string; - eventSourceARN: string; - awsRegion: string; -} - -export interface SQSEvent { - Records: SQSRecord[]; -} - -export interface SQSRecordAttributes { - ApproximateReceiveCount: string; - SentTimestamp: string; - SenderId: string; - ApproximateFirstReceiveTimestamp: string; -} - -export type SQSMessageAttributeDataType = 'String' | 'Number' | 'Binary' | string; - -export interface SQSMessageAttribute { - stringValue?: string; - binaryValue?: string; - stringListValues: never[]; // Not implemented. Reserved for future use. - binaryListValues: never[]; // Not implemented. Reserved for future use. - dataType: SQSMessageAttributeDataType; -} - -export interface SQSMessageAttributes { - [name: string]: SQSMessageAttribute; -} - -// Lex -// https://docs.aws.amazon.com/lambda/latest/dg/invoking-lambda-function.html#supported-event-source-lex -export interface LexEvent { - currentIntent: { - name: string; - slots: { [name: string]: string | null }; - slotDetails: LexSlotDetails; - confirmationStatus: 'None' | 'Confirmed' | 'Denied'; - }; - bot: { - name: string; - alias: string; - version: string; - }; - userId: string; - inputTranscript: string; - invocationSource: 'DialogCodeHook' | 'FulfillmentCodeHook'; - outputDialogMode: 'Text' | 'Voice'; - messageVersion: '1.0'; - sessionAttributes: { [key: string]: string }; - requestAttributes: { [key: string]: string } | null; -} - -export interface LexSlotResolution { - value: string; -} - -export interface LexSlotDetails { - [name: string]: { - // The following line only works in TypeScript Version: 3.0, The array should have at least 1 and no more than 5 items - // resolutions: [LexSlotResolution, LexSlotResolution?, LexSlotResolution?, LexSlotResolution?, LexSlotResolution?]; - resolutions: LexSlotResolution[]; - originalValue: string; - }; -} - -export interface LexGenericAttachment { - title: string; - subTitle: string; - imageUrl: string; - attachmentLinkUrl: string; - buttons: Array<{ - text: string; - value: string; - }>; -} - -export interface LexDialogActionBase { - type: 'Close' | 'ElicitIntent' | 'ElicitSlot' | 'ConfirmIntent'; - message?: { - contentType: 'PlainText' | 'SSML' | 'CustomPayload'; - content: string; - }; - responseCard?: { - version: number; - contentType: 'application/vnd.amazonaws.card.generic'; - genericAttachments: LexGenericAttachment[]; - }; -} - -export interface LexDialogActionClose extends LexDialogActionBase { - type: 'Close'; - fulfillmentState: 'Fulfilled' | 'Failed'; -} - -export interface LexDialogActionElicitIntent extends LexDialogActionBase { - type: 'ElicitIntent'; -} - -export interface LexDialogActionElicitSlot extends LexDialogActionBase { - type: 'ElicitSlot'; - intentName: string; - slots: { [name: string]: string | null }; - slotToElicit: string; -} - -export interface LexDialogActionConfirmIntent extends LexDialogActionBase { - type: 'ConfirmIntent'; - intentName: string; - slots: { [name: string]: string | null }; -} - -export interface LexDialogActionDelegate { - type: 'Delegate'; - slots: { [name: string]: string | null }; -} - -export type LexDialogAction = - | LexDialogActionClose - | LexDialogActionElicitIntent - | LexDialogActionElicitSlot - | LexDialogActionConfirmIntent - | LexDialogActionDelegate; - -export interface LexResult { - sessionAttributes?: { [key: string]: string }; - dialogAction: LexDialogAction; -} - -/** - * AWS Lambda handler function. - * http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html - * - * @param event – event data. - * @param context – runtime information of the Lambda function that is executing. - * @param callback – optional callback to return information to the caller, otherwise return value is null. - * @return In the node8.10 runtime, a promise for the lambda result. - */ -export type Handler = ( - event: TEvent, - context: Context, - callback: Callback, -) => void | Promise; - -/** - * Optional callback parameter. - * http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html - * - * @param error – an optional parameter that you can use to provide results of the failed Lambda function execution. - * It can be a string for Lambda Proxy Integrations - * https://docs.aws.amazon.com/apigateway/latest/developerguide/handle-errors-in-lambda-integration.html - * @param result – an optional parameter that you can use to provide the result of a successful function execution. The result provided must be JSON.stringify compatible. - */ -export type Callback = (error?: Error | null | string, result?: TResult) => void; - -// Begin defining Handler and Callback types for each API trigger type. -// Ordered by https://docs.aws.amazon.com/lambda/latest/dg/invoking-lambda-function.html -// though that list is incomplete. - -export type S3Handler = Handler; - -export type DynamoDBStreamHandler = Handler; - -export type SNSHandler = Handler; - -// No SESHandler: SES event source is delivered as SNS notifications -// https://docs.aws.amazon.com/lambda/latest/dg/invoking-lambda-function.html#supported-event-source-ses - -// Result type is weird: docs and samples say to return the mutated event, but it only requires an object -// with a "response" field, the type of which is specific to the event.triggerType. Leave as any for now. -export type CognitoUserPoolTriggerHandler = Handler; -// TODO: Different event/handler types for each event trigger so we can type the result? - -export type SQSHandler = Handler; - -// TODO: CognitoSync - -export type CloudFormationCustomResourceHandler = Handler; - -// TODO: CloudWatchEvents - -export type CloudWatchLogsHandler = Handler; - -// TODO: CodeCommit - -export type ScheduledHandler = Handler; - -// TODO: AWS Config - -// TODO: Alexa - -export type LexHandler = Handler; -export type LexCallback = Callback; - -export type APIGatewayProxyHandler = Handler; -export type APIGatewayProxyCallback = Callback; -export type ProxyHandler = APIGatewayProxyHandler; // Old name -export type ProxyCallback = APIGatewayProxyCallback; // Old name - -export type ALBHandler = Handler; -export type ALBCallback = Callback; - -// TODO: IoT - -export type CodePipelineHandler = Handler; - -export type CodePipelineCloudWatchHandler = Handler; -export type CodePipelineCloudWatchPipelineHandler = Handler; -export type CodePipelineCloudWatchStageHandler = Handler; -export type CodePipelineCloudWatchActionHandler = Handler; - -export type CloudFrontRequestHandler = Handler; -export type CloudFrontRequestCallback = Callback; - -export type CloudFrontResponseHandler = Handler; -export type CloudFrontResponseCallback = Callback; - -export type KinesisStreamHandler = Handler; - -export type FirehoseTransformationCallback = Callback; -export type FirehoseTransformationHandler = Handler; - -export type CustomAuthorizerHandler = Handler; -export type CustomAuthorizerCallback = Callback; - -export type S3BatchHandler = Handler; -export type S3BatchCallback = Callback; +export * from "./hander"; +export * from "./common/api-gateway"; +export * from "./common/cloudfront"; +export * from "./trigger/alb"; +// TODO: export * from "./trigger/alexa"; +export * from "./trigger/api-gateway-authorizer"; +export * from "./trigger/api-gateway-proxy"; +// CloudTrail section just describes using S3 to trigger on cloudtrail changes. +export * from "./trigger/cloudformation-custom-resource"; +export * from "./trigger/cloudfront-request"; +export * from "./trigger/cloudfront-response"; +export * from "./trigger/cloudwatch-events"; +export * from "./trigger/cloudwatch-logs"; +// TODO: export * from "./trigger/codecommit"; +export * from "./trigger/codepipeline"; +export * from "./trigger/codepipeline-cloudwatch"; +export * from "./trigger/codepipeline-cloudwatch-action"; +export * from "./trigger/codepipeline-cloudwatch-pipeline"; +export * from "./trigger/codepipeline-cloudwatch-stage"; +// TODO: export * from "./trigger/cognito-sync"; +export * from "./trigger/cognito-user-pool-trigger"; +// TODO: export * from "./trigger/config"; +export * from "./trigger/dynamodb-stream"; +// ElastiCache section just describes using lambdas in an ElastiCache context (VPC issues, etc.) +// EC2 events are delivered using cloudwatch events... +// TODO: export * from "./trigger/iot"; for "IoT Events" +export * from "./trigger/kinesis-firehose-transformation"; +export * from "./trigger/kinesis-stream"; +export * from "./trigger/lex"; +// RDS events are delivered using SNS events... +export * from "./trigger/s3"; +export * from "./trigger/s3-batch"; +// SES events are delivered using SNS events... +export * from "./trigger/sns"; +export * from "./trigger/sqs"; export as namespace AWSLambda; diff --git a/types/aws-lambda/test/alb-tests.ts b/types/aws-lambda/test/alb-tests.ts new file mode 100644 index 0000000000..ceeea83fa6 --- /dev/null +++ b/types/aws-lambda/test/alb-tests.ts @@ -0,0 +1,30 @@ +import { ALBHandler, ALBResult } from "aws-lambda"; + +const handler: ALBHandler = async (event, context, callback) => { + str = event.httpMethod; + str = event.path; + str = event.queryStringParameters![str]; + str = event.headers![str]; + str = event.multiValueQueryStringParameters![str][num]; + str = event.multiValueHeaders![str][num]; + strOrNull = event.body; + bool = event.isBase64Encoded; + str = event.requestContext.elb.targetGroupArn; + + let result: ALBResult = { + statusCode: num, + statusDescription: str, + isBase64Encoded: bool, + }; + result = { + statusCode: num, + statusDescription: str, + headers: { [str]: boolOrNumOrStr }, + multiValueHeaders: { [str]: [boolOrNumOrStr] }, + body: str, + isBase64Encoded: bool, + }; + callback(new Error()); + callback(null, result); + return result; +}; diff --git a/types/aws-lambda/test/api-gateway-tests.ts b/types/aws-lambda/test/api-gateway-tests.ts new file mode 100644 index 0000000000..2ae90a6b4c --- /dev/null +++ b/types/aws-lambda/test/api-gateway-tests.ts @@ -0,0 +1,186 @@ +import { + APIGatewayEvent, + APIGatewayEventRequestContext, + APIGatewayProxyHandler, + APIGatewayProxyResult, + AuthResponseContext, + Context, + CustomAuthorizerHandler, + CustomAuthorizerResult, + PolicyDocument, + ProxyHandler, + ProxyCallback, + Statement, +} from "aws-lambda"; + +let proxyHandler: APIGatewayProxyHandler = async (event, context, callback) => { + strOrNull = event.body; + str = event.headers['example']; + str = event.multiValueHeaders['example'][0]; + str = event.httpMethod; + bool = event.isBase64Encoded; + str = event.path; + str = event.pathParameters!['example']; + str = event.queryStringParameters!['example']; + str = event.multiValueQueryStringParameters!['example'][0]; + str = event.stageVariables!['example']; + let requestContext: APIGatewayEventRequestContext; + requestContext = event.requestContext; + str = event.resource; + + str = requestContext.accountId; + str = requestContext.apiId; + const authContext: AuthResponseContext | null | undefined = requestContext.authorizer; + numOrUndefined = requestContext.connectedAt; + strOrUndefined = requestContext.connectionId; + strOrUndefined = requestContext.domainName; + strOrUndefined = requestContext.eventType; + strOrUndefined = requestContext.extendedRequestId; + str = requestContext.httpMethod; + strOrNull = requestContext.identity.accessKey; + strOrNull = requestContext.identity.accountId; + strOrNull = requestContext.identity.apiKey; + strOrNull = requestContext.identity.apiKeyId; + strOrNull = requestContext.identity.caller; + strOrNull = requestContext.identity.cognitoAuthenticationProvider; + strOrNull = requestContext.identity.cognitoAuthenticationType; + strOrNull = requestContext.identity.cognitoIdentityId; + strOrNull = requestContext.identity.cognitoIdentityPoolId; + strOrNull = requestContext.identity.principalOrgId; + str = requestContext.identity.sourceIp; + strOrNull = requestContext.identity.user; + strOrNull = requestContext.identity.userAgent; + strOrNull = requestContext.identity.userArn; + strOrUndefined = requestContext.messageDirection; + strOrUndefinedOrNull = requestContext.messageId; + str = requestContext.path; + str = requestContext.stage; + str = requestContext.requestId; + strOrUndefined = requestContext.requestTime; + str = requestContext.resourceId; + str = requestContext.resourcePath; + strOrUndefined = requestContext.routeKey; + + let result: APIGatewayProxyResult = { + statusCode: num, + body: str, + }; + result = { + statusCode: num, + headers: { + [str]: str, + [str]: bool, + [str]: num, + }, + multiValueHeaders: { + [str]: [str, bool, num], + }, + isBase64Encoded: true, + body: str, + }; + + callback(new Error()); + callback(null, result); + return result; +}; + +const authorizerHandler: CustomAuthorizerHandler = async (event, context, callback) => { + str = event.type; + str = event.methodArn; + strOrUndefined = event.authorizationToken; + strOrUndefined = event.resource; + strOrUndefined = event.path; + strOrUndefined = event.httpMethod; + str = event.headers![str]; + str = event.multiValueHeaders![str][num]; + str = event.pathParameters![str]; + str = event.queryStringParameters![str]; + str = event.multiValueQueryStringParameters![str][num]; + str = event.stageVariables![str]; + let requestContext: APIGatewayEventRequestContext; + requestContext = event.requestContext!; + strOrUndefined = event.domainName; + strOrUndefined = event.apiId; + + let statement: Statement = { + Action: str, + Effect: str, + Resource: str, + }; + + // $ExpectError + statement = { Effect: str, Action: str, Principal: 123, }; + + // Bad Resource + // $ExpectError + statement = { Effect: str, Action: str, Resource: 123, }; + + // Bad Resource with valid Principal + // $ExpectError + statement = { Effect: str, Action: str, Principal: { Service: str }, Resource: 123, }; + + // Bad principal with valid Resource + // $ExpectError + statement = { Effect: str, Action: str, Principal: 123, Resource: str, }; + + // No Effect + // $ExpectError + statement = { Action: str, Principal: str }; + + statement = { + Sid: str, + Action: [str, str], + Effect: str, + Resource: [str, str], + Condition: { + condition1: { key: 'value' }, + condition2: [ + { + key1: 'value', + key2: 'value', + }, + { + key3: 'value', + }, + ], + }, + Principal: [str, str], + NotPrincipal: [str, str], + }; + + statement = { Action: str, Principal: str, Effect: str }; + + statement = { Action: str, NotPrincipal: { Service: str }, Effect: str }; + + statement = { Effect: str, NotAction: str, NotResource: str }; + + let policyDocument: PolicyDocument = { Version: str, Statement: [statement] }; + + policyDocument = { Version: str, Statement: [statement, statement] }; + + const authResponseContext: AuthResponseContext = { + stringKey: str, + numberKey: num, + booleanKey: bool, + }; + + let result: CustomAuthorizerResult = { + principalId: str, + policyDocument, + context: authResponseContext, + }; + + result = { + principalId: str, + policyDocument, + }; + + callback(new Error()); + callback(null, result); + return result; +}; + +// Test old names +const oldNameProxyHandler: ProxyHandler = (event: APIGatewayEvent, context: Context, cb: ProxyCallback) => {}; + +proxyHandler = oldNameProxyHandler; diff --git a/types/aws-lambda/test/cloudformation-tests.ts b/types/aws-lambda/test/cloudformation-tests.ts new file mode 100644 index 0000000000..ffcceca2d0 --- /dev/null +++ b/types/aws-lambda/test/cloudformation-tests.ts @@ -0,0 +1,74 @@ +import { + CloudFormationCustomResourceFailedResponse, + CloudFormationCustomResourceHandler, + CloudFormationCustomResourceResponse, CloudFormationCustomResourceSuccessResponse, +} from "aws-lambda"; + +const handler: CloudFormationCustomResourceHandler = async (event, context, callback) => { + switch (event.RequestType) { + case 'Create': + str = event.LogicalResourceId; + str = event.RequestId; + anyObj = event.ResourceProperties; + str = event.ResourceProperties.ServiceToken; + str = event.ResourceType; + str = event.ResponseURL; + str = event.ServiceToken; + str = event.StackId; + break; + case 'Update': + anyObj = event.OldResourceProperties; + break; + case 'Delete': + str = event.PhysicalResourceId; + break; + } + + let response: CloudFormationCustomResourceResponse; + response = { + Data: { + [str]: anyObj, + }, + LogicalResourceId: str, + PhysicalResourceId: str, + Reason: strOrUndefined, + RequestId: str, + StackId: str, + Status: "SUCCESS", + NoEcho: boolOrUndefined, + }; + + let successResponse: CloudFormationCustomResourceSuccessResponse; + let failedResponse: CloudFormationCustomResourceFailedResponse; + successResponse = { + LogicalResourceId: str, + PhysicalResourceId: str, + RequestId: str, + StackId: str, + Status: "SUCCESS", + }; + successResponse = { + Data: anyObj, + LogicalResourceId: str, + PhysicalResourceId: str, + Reason: strOrUndefined, + RequestId: str, + StackId: str, + Status: "SUCCESS", + NoEcho: boolOrUndefined, + }; + failedResponse = { + Data: anyObj, + LogicalResourceId: str, + PhysicalResourceId: str, + Reason: str, + RequestId: str, + StackId: str, + Status: "FAILED", + }; + response = successResponse; + response = failedResponse; + + callback(); + callback(new Error()); +}; diff --git a/types/aws-lambda/test/cloudfront-tests.ts b/types/aws-lambda/test/cloudfront-tests.ts new file mode 100644 index 0000000000..5b141ae205 --- /dev/null +++ b/types/aws-lambda/test/cloudfront-tests.ts @@ -0,0 +1,279 @@ +import { + CloudFrontHeaders, + CloudFrontRequestEvent, + CloudFrontRequestHandler, + CloudFrontRequestResult, + CloudFrontResponseEvent, + CloudFrontResponseHandler, + CloudFrontResponseResult, + CloudFrontS3Origin, + CloudFrontCustomOrigin, +} from "aws-lambda"; + +// TODO: Update test to read all event properties, and write all result +// properties, like the user will. + +const cloudFrontHeaders: CloudFrontHeaders = { + 'content-type': [{ value: 'text/plain' }], + 'x-foo-bar': [{ key: 'X-Foo-Bar', value: 'example' }], +}; + +const requestHandler: CloudFrontRequestHandler = async (event, context, cb) => { + event = requestWithCustomOriginEvent; + // $ExpectType CloudFrontRequestEvent + event; + let request = event.Records[0].cf.request; + + let s3Origin: CloudFrontS3Origin = { + authMethod: 'none', + customHeaders: {}, + domainName: 'example.com', + path: '/', + region: 'us-east-1', + }; + + if (request.origin && request.origin.custom) { + request.origin.custom.domainName; + request.origin.custom.domainName = 'example2.com'; + + // $ExpectError + s3Origin = request.origin.s3; + + // $ExpectError + request.origin.s3.path = '/'; + } + + let customOrigin: CloudFrontCustomOrigin = { + customHeaders: {}, + domainName: 'example.com', + keepaliveTimeout: 60, + path: '/', + port: 80, + protocol: 'http', + readTimeout: 30, + sslProtocols: [], + }; + + event = requestWithS3OriginEvent; + // $ExpectType CloudFrontRequestEvent + event; + request = event.Records[0].cf.request; + if (request.origin && request.origin.s3) { + request.origin.s3.path; + request.origin.s3.path = '/new_path'; + + // $ExpectError + customOrigin = request.origin.custom; + + // $ExpectError + request.origin.custom.path = '/'; + } + + let result: CloudFrontRequestResult; + // Result can be either nullish (use current request), a replaced request, or an immediate response + result = undefined; + result = null; + result = { clientIp: str, method: str, uri: str, querystring: str, headers: cloudFrontHeaders }; + result = { status: str }; + result = { status: str, statusDescription: str, headers: cloudFrontHeaders, bodyEncoding: "text", body: str }; + // $ExpectError + result = {}; + + cb(new Error()); + cb(null, result); + return result; +}; + +const responseHandler: CloudFrontResponseHandler = async (event, context, callback) => { + let result: CloudFrontResponseResult; + // Result can be either nullish (use current response) or a replaced response, but not a request + result = undefined; + result = null; + result = { status: str }; + // $ExpectError + result = { clientIp: str, method: str, uri: str, querystring: str, headers: cloudFrontHeaders }; + result = { status: str, statusDescription: str, headers: cloudFrontHeaders, bodyEncoding: "text", body: str }; + result = { status: str, bodyEncoding: "base64", body: str }; + // $ExpectError + result = { status: str, bodyEncoding: "invalid-encoding", body: str }; + + callback(new Error()); + callback(null, result); + return result; +}; + +/* CloudFront events, see http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html */ + +const requestWithCustomOriginEvent: CloudFrontRequestEvent = { + Records: [ + { + cf: { + config: { + distributionDomainName: 'd123.cloudfront.net', + distributionId: 'EDFDVBD6EXAMPLE', + eventType: 'viewer-request', + requestId: 'MRVMF7KydIvxMWfJIglgwHQwZsbG2IhRJ07sn9AkKUFSHS9EXAMPLE==', + }, + request: { + body: { + action: 'read-only', + data: 'eyJ1c2VybmFtZSI6IkxhbWJkYUBFZGdlIiwiY29tbWVudCI6IlRoaXMgaXMgcmVxdWVzdCBib2R5In0=', + encoding: 'base64', + inputTruncated: false, + }, + clientIp: '2001:0db8:85a3:0:0:8a2e:0370:7334', + querystring: 'size=large', + uri: '/picture.jpg', + method: 'GET', + headers: { + host: [ + { + key: 'Host', + value: 'd111111abcdef8.cloudfront.net', + }, + ], + 'user-agent': [ + { + key: 'User-Agent', + value: 'curl/7.51.0', + }, + ], + }, + origin: { + custom: { + customHeaders: { + 'my-origin-custom-header': [ + { + key: 'My-Origin-Custom-Header', + value: 'Test', + }, + ], + }, + domainName: 'example.com', + keepaliveTimeout: 5, + path: '/custom_path', + port: 443, + protocol: 'https', + readTimeout: 5, + sslProtocols: ['TLSv1', 'TLSv1.1'], + }, + }, + }, + }, + }, + ], +}; + +const requestWithS3OriginEvent: CloudFrontRequestEvent = { + Records: [ + { + cf: { + config: { + distributionDomainName: 'd123.cloudfront.net', + distributionId: 'EDFDVBD6EXAMPLE', + eventType: 'viewer-request', + requestId: 'MRVMF7KydIvxMWfJIglgwHQwZsbG2IhRJ07sn9AkKUFSHS9EXAMPLE==', + }, + request: { + body: { + action: 'read-only', + data: 'eyJ1c2VybmFtZSI6IkxhbWJkYUBFZGdlIiwiY29tbWVudCI6IlRoaXMgaXMgcmVxdWVzdCBib2R5In0=', + encoding: 'base64', + inputTruncated: false, + }, + clientIp: '2001:0db8:85a3:0:0:8a2e:0370:7334', + querystring: 'size=large', + uri: '/picture.jpg', + method: 'GET', + headers: { + host: [ + { + key: 'Host', + value: 'd111111abcdef8.cloudfront.net', + }, + ], + 'user-agent': [ + { + key: 'User-Agent', + value: 'curl/7.51.0', + }, + ], + }, + origin: { + s3: { + authMethod: 'origin-access-identity', + customHeaders: { + 'my-origin-custom-header': [ + { + key: 'My-Origin-Custom-Header', + value: 'Test', + }, + ], + }, + domainName: 'my-bucket.s3.amazonaws.com', + path: '/s3_path', + region: 'us-east-1', + }, + }, + }, + }, + }, + ], +}; + +const responseEvent: CloudFrontResponseEvent = { + Records: [ + { + cf: { + config: { + distributionDomainName: 'd123.cloudfront.net', + distributionId: 'EDFDVBD6EXAMPLE', + eventType: 'viewer-response', + requestId: 'xGN7KWpVEmB9Dp7ctcVFQC4E-nrcOcEKS3QyAez--06dV7TEXAMPLE==', + }, + request: { + clientIp: '2001:0db8:85a3:0:0:8a2e:0370:7334', + method: 'GET', + uri: '/picture.jpg', + querystring: 'size=large', + headers: { + host: [ + { + key: 'Host', + value: 'd111111abcdef8.cloudfront.net', + }, + ], + 'user-agent': [ + { + key: 'User-Agent', + value: 'curl/7.18.1', + }, + ], + }, + }, + response: { + status: '200', + statusDescription: 'OK', + headers: { + server: [ + { + key: 'Server', + value: 'MyCustomOrigin', + }, + ], + 'set-cookie': [ + { + key: 'Set-Cookie', + value: 'theme=light', + }, + { + key: 'Set-Cookie', + value: 'sessionToken=abc123; Expires=Wed, 09 Jun 2021 10:18:14 GMT', + }, + ], + }, + }, + }, + }, + ], +}; diff --git a/types/aws-lambda/test/cloudwatch-tests.ts b/types/aws-lambda/test/cloudwatch-tests.ts new file mode 100644 index 0000000000..815d2bc61c --- /dev/null +++ b/types/aws-lambda/test/cloudwatch-tests.ts @@ -0,0 +1,45 @@ +/// + +import { + CloudWatchLogsDecodedData, + CloudWatchLogsHandler, + CloudWatchLogsLogEvent, + ScheduledHandler, +} from "aws-lambda"; + +import { gunzipSync } from "zlib"; + +const logsHandler: CloudWatchLogsHandler = async (event, context, callback) => { + const data: CloudWatchLogsDecodedData = JSON.parse( + gunzipSync(Buffer.from(event.awslogs.data, "base64")).toString() + ); + + str = data.owner; + str = data.logGroup; + str = data.logStream; + str = data.subscriptionFilters[0]; + str = data.messageType; + + const logEvent: CloudWatchLogsLogEvent = data.logEvents[0]; + str = logEvent.id; + num = logEvent.timestamp; + str = logEvent.message; + str = logEvent.extractedFields!['example']; + + callback(); + callback(new Error()); +}; + +// Technically part of cloudwatch +const scheduledHandler: ScheduledHandler = async (event, context, callback) => { + str = event.account; + anyObj = event.detail; + str = event.id; + str = event.region; + str = event.resources[0]; + str = event.source; + str = event.time; + + callback(); + callback(new Error()); +}; diff --git a/types/aws-lambda/test/codepipeline-tests.ts b/types/aws-lambda/test/codepipeline-tests.ts new file mode 100644 index 0000000000..7d481dd9a0 --- /dev/null +++ b/types/aws-lambda/test/codepipeline-tests.ts @@ -0,0 +1,77 @@ +import { CodePipelineCloudWatchEvent, CodePipelineEvent } from "aws-lambda"; + +// TODO + +/* CodePipeline events https://docs.aws.amazon.com/codepipeline/latest/userguide/actions-invoke-lambda-function.html#actions-invoke-lambda-function-json-event-example */ +const CodePipelineEvent: CodePipelineEvent = { + 'CodePipeline.job': { + id: '11111111-abcd-1111-abcd-111111abcdef', + accountId: '111111111111', + data: { + actionConfiguration: { + configuration: { + FunctionName: 'MyLambdaFunctionForAWSCodePipeline', + UserParameters: 'some-input-such-as-a-URL', + }, + }, + inputArtifacts: [ + { + location: { + s3Location: { + bucketName: + 'the name of the bucket configured as the pipeline artifact store in Amazon S3, for example codepipeline-us-east-2-1234567890', + objectKey: 'the name of the application, for example CodePipelineDemoApplication.zip', + }, + type: 'S3', + }, + revision: null, + name: 'ArtifactName', + }, + ], + outputArtifacts: [], + artifactCredentials: { + secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY', + sessionToken: `MIICiTCCAfICCQD6m7oRw0uXOjANBgkqhkiG9w + 0BAQUFADCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZ + WF0dGxlMQ8wDQYDVQQKEwZBbWF6b24xFDASBgNVBAsTC0lBTSBDb25zb2xlMRIw + EAYDVQQDEwlUZXN0Q2lsYWMxHzAdBgkqhkiG9w0BCQEWEG5vb25lQGFtYXpvbi5 + jb20wHhcNMTEwNDI1MjA0NTIxWhcNMTIwNDI0MjA0NTIxWjCBiDELMAkGA1UEBh + MCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdTZWF0dGxlMQ8wDQYDVQQKEwZBb + WF6b24xFDASBgNVBAsTC0lBTSBDb25zb2xlMRIwEAYDVQQDEwlUZXN0Q2lsYWMx + HzAdBgkqhkiG9w0BCQEWEG5vb25lQGFtYXpvbi5jb20wgZ8wDQYJKoZIhvcNAQE + BBQADgY0AMIGJAoGBAMaK0dn+a4GmWIWJ21uUSfwfEvySWtC2XADZ4nB+BLYgVI + k60CpiwsZ3G93vUEIO3IyNoH/f0wYK8m9TrDHudUZg3qX4waLG5M43q7Wgc/MbQ + ITxOUSQv7c7ugFFDzQGBzZswY6786m86gpEIbb3OhjZnzcvQAaRHhdlQWIMm2nr + AgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAtCu4nUhVVxYUntneD9+h8Mg9q6q+auN + KyExzyLwaxlAoo7TJHidbtS4J5iNmZgXL0FkbFFBjvSfpJIlJ00zbhNYS5f6Guo + EDmFJl0ZxBHjJnyp378OD8uTs7fLvjx79LjSTbNYiytVbZPQUQ5Yaxu2jXnimvw + 3rrszlaEXAMPLE=`, + accessKeyId: 'AKIAIOSFODNN7EXAMPLE', + }, + continuationToken: 'A continuation token if continuing job', + }, + }, +}; + +CodePipelineEvent['CodePipeline.job'].data.encryptionKey = { type: 'KMS', id: 'key' }; + +/* CodePipeline CloudWatch Events + * see https://docs.aws.amazon.com/codepipeline/latest/userguide/detect-state-changes-cloudwatch-events.html + * Their documentation says that detail.version is a string, but it is actually an integer + */ +const CodePipelineCloudWatchEvent: CodePipelineCloudWatchEvent = { + version: '0', + id: 'event_Id', + 'detail-type': 'CodePipeline Pipeline Execution State Change', + source: 'aws.codepipeline', + account: 'Pipeline_Account', + time: 'TimeStamp', + region: 'us-east-1', + resources: ['arn:aws:codepipeline:us-east-1:account_ID:myPipeline'], + detail: { + pipeline: 'myPipeline', + version: 1, + state: 'STARTED', + 'execution-id': 'execution_Id', + }, +}; diff --git a/types/aws-lambda/test/cognito-tests.ts b/types/aws-lambda/test/cognito-tests.ts new file mode 100644 index 0000000000..cab5581bc8 --- /dev/null +++ b/types/aws-lambda/test/cognito-tests.ts @@ -0,0 +1,114 @@ +// TODO: Update test to read all event properties, and write all result +// properties, like the user will. + +import { CognitoUserPoolTriggerHandler } from "aws-lambda"; + +const handler: CognitoUserPoolTriggerHandler = async (event, context, callback) => { + num = event.version; + event.triggerSource === 'PreSignUp_SignUp'; + event.triggerSource === 'PreSignUp_ExternalProvider'; + event.triggerSource === 'PostConfirmation_ConfirmSignUp'; + event.triggerSource === 'PreAuthentication_Authentication'; + event.triggerSource === 'PostAuthentication_Authentication'; + event.triggerSource === 'CustomMessage_SignUp'; + event.triggerSource === 'CustomMessage_AdminCreateUser'; + event.triggerSource === 'CustomMessage_ResendCode'; + event.triggerSource === 'CustomMessage_ForgotPassword'; + event.triggerSource === 'CustomMessage_UpdateUserAttribute'; + event.triggerSource === 'CustomMessage_VerifyUserAttribute'; + event.triggerSource === 'CustomMessage_Authentication'; + event.triggerSource === 'DefineAuthChallenge_Authentication'; + event.triggerSource === 'CreateAuthChallenge_Authentication'; + event.triggerSource === 'VerifyAuthChallengeResponse_Authentication'; + event.triggerSource === 'PreSignUp_AdminCreateUser'; + event.triggerSource === 'PostConfirmation_ConfirmForgotPassword'; + event.triggerSource === 'TokenGeneration_HostedAuth'; + event.triggerSource === 'TokenGeneration_Authentication'; + event.triggerSource === 'TokenGeneration_NewPasswordChallenge'; + event.triggerSource === 'TokenGeneration_AuthenticateDevice'; + event.triggerSource === 'TokenGeneration_RefreshTokens'; + event.triggerSource === 'UserMigration_Authentication'; + event.triggerSource === 'UserMigration_ForgotPassword'; + // $ExpectError + event.triggerSource === 'NoSuch_Trigger'; + + str = event.region; + str = event.userPoolId; + strOrUndefined = event.userName; + str = event.callerContext.awsSdkVersion; + str = event.callerContext.clientId; + str = event.request.userAttributes['email']; + str = event.request.validationData!['k1']; + strOrUndefined = event.request.codeParameter; + strOrUndefined = event.request.linkParameter; + strOrUndefined = event.request.usernameParameter; + boolOrUndefined = event.request.newDeviceUsed; + event.request.session![0].challengeName === 'CUSTOM_CHALLENGE'; + event.request.session![0].challengeName === 'PASSWORD_VERIFIER'; + event.request.session![0].challengeName === 'SMS_MFA'; + event.request.session![0].challengeName === 'DEVICE_SRP_AUTH'; + event.request.session![0].challengeName === 'DEVICE_PASSWORD_VERIFIER'; + event.request.session![0].challengeName === 'ADMIN_NO_SRP_AUTH'; + bool = event.request.session![0].challengeResult; + strOrUndefined = event.request.session![0].challengeMetadata; + strOrUndefined = event.request.challengeName; + str = event.request.privateChallengeParameters!['answer']; + str = event.request.challengeAnswer!; + strOrUndefined = event.request.password; + boolOrUndefined = event.response.answerCorrect; + strOrUndefined = event.response.smsMessage; + strOrUndefined = event.response.emailMessage; + strOrUndefined = event.response.emailSubject; + strOrUndefined = event.response.challengeName; + boolOrUndefined = event.response.issueTokens; + boolOrUndefined = event.response.failAuthentication; + str = event.response.publicChallengeParameters!['captchaUrl']; + str = event.response.privateChallengeParameters!['answer']; + strOrUndefined = event.response.challengeMetadata; + boolOrUndefined = event.response.answerCorrect; + str = event.response.userAttributes!['username']; + event.response.finalUserStatus === 'CONFIRMED'; + event.response.finalUserStatus === 'RESET_REQUIRED'; + event.response.messageAction === 'SUPPRESS'; + event.response.desiredDeliveryMediums === ['EMAIL']; + event.response.desiredDeliveryMediums === ['SMS']; + event.response.desiredDeliveryMediums === ['SMS', 'EMAIL']; + boolOrUndefined = event.response.forceAliasCreation; + + // From AWS examples + event.response = { + claimsOverrideDetails: { + claimsToAddOrOverride: { + attribute_key2: 'attribute_value2', + attribute_key: 'attribute_value', + }, + claimsToSuppress: ['email'], + }, + }; + event.response = { + claimsOverrideDetails: { + claimsToAddOrOverride: { + attribute_key2: 'attribute_value2', + attribute_key: 'attribute_value', + }, + claimsToSuppress: ['email'], + groupOverrideDetails: { + groupsToOverride: ['group-A', 'group-B', 'group-C'], + iamRolesToOverride: [ + 'arn:aws:iam::XXXXXXXXXXXX:role/sns_callerA', + 'arn:aws:iam::XXXXXXXXX:role/sns_callerB', + 'arn:aws:iam::XXXXXXXXXX:role/sns_callerC', + ], + preferredRole: 'arn:aws:iam::XXXXXXXXXXX:role/sns_caller', + }, + }, + }; + event.response.claimsOverrideDetails!.groupOverrideDetails = null; + + callback(new Error()); + callback(null, event); + callback(null, { + response: event.response, + }); + return event; +}; diff --git a/types/aws-lambda/test/dynamodb-tests.ts b/types/aws-lambda/test/dynamodb-tests.ts new file mode 100644 index 0000000000..9f043de9ce --- /dev/null +++ b/types/aws-lambda/test/dynamodb-tests.ts @@ -0,0 +1,98 @@ +import { DynamoDBStreamEvent } from "aws-lambda"; + +// TODO: Update test to read all event properties, and write all result +// properties, like the user will. + +const event: DynamoDBStreamEvent = { + Records: [ + { + eventID: '1', + eventVersion: '1.0', + dynamodb: { + Keys: { + Id: { + N: '101', + }, + }, + NewImage: { + Message: { + S: 'New item!', + }, + Id: { + N: '101', + }, + }, + StreamViewType: 'NEW_AND_OLD_IMAGES', + SequenceNumber: '111', + SizeBytes: 26, + }, + awsRegion: 'us-west-2', + eventName: 'INSERT', + eventSourceARN: + 'arn:aws:dynamodb:us-west-2:account-id:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899', + eventSource: 'aws:dynamodb', + }, + { + eventID: '2', + eventVersion: '1.0', + dynamodb: { + OldImage: { + Message: { + S: 'New item!', + }, + Id: { + N: '101', + }, + }, + SequenceNumber: '222', + Keys: { + Id: { + N: '101', + }, + }, + SizeBytes: 59, + NewImage: { + Message: { + S: 'This item has changed', + }, + Id: { + N: '101', + }, + }, + StreamViewType: 'NEW_AND_OLD_IMAGES', + }, + awsRegion: 'us-west-2', + eventName: 'MODIFY', + eventSourceARN: + 'arn:aws:dynamodb:us-west-2:account-id:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899', + eventSource: 'aws:dynamodb', + }, + { + eventID: '3', + eventVersion: '1.0', + dynamodb: { + Keys: { + Id: { + N: '101', + }, + }, + SizeBytes: 38, + SequenceNumber: '333', + OldImage: { + Message: { + S: 'This item has changed', + }, + Id: { + N: '101', + }, + }, + StreamViewType: 'NEW_AND_OLD_IMAGES', + }, + awsRegion: 'us-west-2', + eventName: 'REMOVE', + eventSourceARN: + 'arn:aws:dynamodb:us-west-2:account-id:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899', + eventSource: 'aws:dynamodb', + }, + ], +}; diff --git a/types/aws-lambda/test/kinesis-tests.ts b/types/aws-lambda/test/kinesis-tests.ts new file mode 100644 index 0000000000..f04dc17a6a --- /dev/null +++ b/types/aws-lambda/test/kinesis-tests.ts @@ -0,0 +1,50 @@ +import { + FirehoseRecordTransformationStatus, + FirehoseTransformationHandler, FirehoseTransformationResult, + KinesisStreamHandler, + KinesisStreamRecord, + KinesisStreamRecordPayload, +} from "aws-lambda"; + +const handler: KinesisStreamHandler = async (event, context, callback) => { + let kinesisStreamRecord: KinesisStreamRecord; + let kinesisStreamRecordPayload: KinesisStreamRecordPayload; + + kinesisStreamRecord = event.Records[num]; + + str = kinesisStreamRecord.awsRegion; + str = kinesisStreamRecord.eventID; + str = kinesisStreamRecord.eventName; + str = kinesisStreamRecord.eventSource; + str = kinesisStreamRecord.eventSourceARN; + str = kinesisStreamRecord.eventVersion; + str = kinesisStreamRecord.invokeIdentityArn; + kinesisStreamRecordPayload = kinesisStreamRecord.kinesis; + + num = kinesisStreamRecordPayload.approximateArrivalTimestamp; + str = kinesisStreamRecordPayload.data; + str = kinesisStreamRecordPayload.kinesisSchemaVersion; + str = kinesisStreamRecordPayload.partitionKey; + str = kinesisStreamRecordPayload.sequenceNumber; + + callback(); + callback(new Error()); +}; + +const firehoseHandler: FirehoseTransformationHandler = async (event, context, callback) => { + str = event.records[0].recordId; + + const result: FirehoseTransformationResult = { + records: [ + { + recordId: event.records[0].recordId, + result: 'Ok' as FirehoseRecordTransformationStatus, + data: 'eyJmb28iOiJiYXIifQ==', + }, + ], + }; + + callback(new Error()); + callback(null, result); + return result; +}; diff --git a/types/aws-lambda/test/lex-tests.ts b/types/aws-lambda/test/lex-tests.ts new file mode 100644 index 0000000000..408bf451a0 --- /dev/null +++ b/types/aws-lambda/test/lex-tests.ts @@ -0,0 +1,122 @@ +import { + LexDialogAction, + LexDialogActionBase, + LexDialogActionClose, + LexDialogActionConfirmIntent, + LexDialogActionDelegate, + LexDialogActionElicitIntent, + LexDialogActionElicitSlot, + LexEvent, + LexGenericAttachment, + LexHandler, + LexResult, +} from "aws-lambda"; + +// TODO: Update test to read all event properties, and write all result +// properties, like the user will. + +const handler: LexHandler = async (event, context, callback) => { + lexEvent = event; + + let result: LexResult; + result = { + dialogAction: lexDialogAction, + }; + result = { + sessionAttributes: { + attrib1: 'Value One', + }, + dialogAction: { + type: 'Close', + fulfillmentState: 'Failed', + }, + }; + + callback(new Error()); + callback(null, result); + return result; +}; + +declare let lexEvent: LexEvent; +lexEvent = { + currentIntent: { + name: 'intent-name', + slots: { + slot1: null, + slot2: 'value2', + }, + slotDetails: { + slot1: { + resolutions: [{ value: 'value1' }], + originalValue: 'originalValue', + }, + }, + confirmationStatus: 'None', + }, + bot: { + name: 'bot name', + alias: 'bot alias', + version: 'bot version', + }, + userId: 'User ID specified in the POST request to Amazon Lex.', + inputTranscript: 'Text used to process the request', + invocationSource: 'FulfillmentCodeHook', + outputDialogMode: 'Text', + messageVersion: '1.0', + sessionAttributes: { + key1: 'value1', + key2: 'value2', + }, + requestAttributes: { + key1: 'value1', + key2: 'value2', + }, +}; + +declare let lexDialogAction: LexDialogAction; +declare let lexDialogActionBase: LexDialogActionBase; +declare let lexDialogActionClose: LexDialogActionClose; +declare let lexDialogActionConfirmIntent: LexDialogActionConfirmIntent; +declare let lexDialogActionDelegate: LexDialogActionDelegate; +declare let lexDialogActionElicitIntent: LexDialogActionElicitIntent; +declare let lexDialogActionElicitSlot: LexDialogActionElicitSlot; +declare let lexGenericAttachment: LexGenericAttachment; + +str = lexGenericAttachment.title; +str = lexGenericAttachment.subTitle; +str = lexGenericAttachment.imageUrl; +str = lexGenericAttachment.attachmentLinkUrl; +str = lexGenericAttachment.buttons[0].text; +str = lexGenericAttachment.buttons[0].value; + +lexDialogAction.type === 'Close'; +lexDialogAction.type === 'ConfirmIntent'; +lexDialogAction.type === 'Delegate'; +lexDialogAction.type === 'ElicitIntent'; +lexDialogAction.type === 'ElicitSlot'; + +lexDialogActionBase.message!.contentType === 'CustomPayload'; +lexDialogActionBase.message!.contentType === 'PlainText'; +lexDialogActionBase.message!.contentType === 'SSML'; +str = lexDialogActionBase.message!.content; +num = lexDialogActionBase.responseCard!.version; +lexDialogActionBase.responseCard!.contentType === 'application/vnd.amazonaws.card.generic'; +// $ExpectType LexGenericAttachment +lexDialogActionBase.responseCard!.genericAttachments[0]; + +lexDialogActionClose.type === 'Close'; +lexDialogActionClose.fulfillmentState === 'Failed'; +lexDialogActionClose.fulfillmentState === 'Fulfilled'; + +lexDialogActionConfirmIntent.type === 'ConfirmIntent'; +str = lexDialogActionConfirmIntent.intentName; +strOrNull = lexDialogActionConfirmIntent.slots['example']; + +lexDialogActionDelegate.type === 'Delegate'; +strOrNull = lexDialogActionDelegate.slots['example']; + +lexDialogActionElicitIntent.type === 'ElicitIntent'; +lexDialogActionElicitSlot.type === 'ElicitSlot'; +strOrNull = lexDialogActionElicitSlot.slots['example']; +str = lexDialogActionElicitSlot.slotToElicit; +str = lexDialogActionElicitSlot.intentName; diff --git a/types/aws-lambda/test/s3-tests.ts b/types/aws-lambda/test/s3-tests.ts new file mode 100644 index 0000000000..65cfa9b78d --- /dev/null +++ b/types/aws-lambda/test/s3-tests.ts @@ -0,0 +1,102 @@ +import { Callback, Context, S3BatchEvent, S3BatchResult, S3CreateEvent, S3EventRecord, S3Handler } from "aws-lambda"; + +// TODO: Update test to read event properties, like the user will +let handler: S3Handler = async (event, context, callback) => { + callback(new Error()); +}; + +// Test old event name +handler = (event: S3CreateEvent, context: Context, cb: Callback) => {}; + +const record: S3EventRecord = { + eventVersion: '2.1', + eventSource: 'aws:s3', + awsRegion: 'us-east-1', + eventTime: '1970-01-01T00:00:00.000Z', + eventName: 'ObjectRestore:Completed', + userIdentity: { + principalId: 'AIDAJDPLRKLG7UEXAMPLE', + }, + requestParameters: { + sourceIPAddress: '127.0.0.1', + }, + responseElements: { + 'x-amz-request-id': 'C3D13FE58DE4C810', + 'x-amz-id-2': 'FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD', + }, + s3: { + s3SchemaVersion: '1.0', + configurationId: 'testConfigRule', + bucket: { + name: 'mybucket', + ownerIdentity: { + principalId: 'A3NL1KOZZKExample', + }, + arn: 'arn:aws:s3:::mybucket', + }, + object: { + key: 'HappyFace.jpg', + size: 1024, + eTag: 'd41d8cd98f00b204e9800998ecf8427e', + versionId: '096fKKXTRTtl3on89fVO.nfljtsv6qko', + sequencer: '0055AED6DCD90281E5', + }, + }, + glacierEventData: { + restoreEventData: { + lifecycleRestorationExpiryTime: '1970-01-01T00:00:00.000Z', + lifecycleRestoreStorageClass: 'glacier-deep-archive', + }, + }, +}; + +const event: S3CreateEvent = { + Records: [record], +}; + +/** + * S3 Batch Operations event + * https://docs.aws.amazon.com/AmazonS3/latest/dev/batch-ops-invoke-lambda.html + */ +const batchEvent: S3BatchEvent = { + invocationSchemaVersion: '1.0', + invocationId: 'foo_invocation_id', + job: { id: 'foo_job_id' }, + tasks: [ + { + taskId: '11111', + s3Key: 'example.json', + s3BucketArn: 'arn:aws:s3:::foo-bucket', + s3VersionId: null, + }, + { + taskId: '11111', + s3Key: 'example.json', + s3BucketArn: 'arn:aws:s3:::foo-bucket', + s3VersionId: 'asdf', + }, + ], +}; + +const batchResult: S3BatchResult = { + invocationSchemaVersion: '1.0', + treatMissingKeysAs: 'PermanentFailure', + invocationId: 'foo_invocation_id', + results: [ + { + taskId: '11111', + resultCode: 'Succeeded', + resultString: 'foo', + }, + { + taskId: '22222', + resultCode: 'TemporaryFailure', + resultString: 'Error: failure', + }, + { + taskId: '33333', + resultCode: 'PermanentFailure', + resultString: 'Error: failure', + }, + ], +}; diff --git a/types/aws-lambda/test/sns-tests.ts b/types/aws-lambda/test/sns-tests.ts new file mode 100644 index 0000000000..9235de7cff --- /dev/null +++ b/types/aws-lambda/test/sns-tests.ts @@ -0,0 +1,29 @@ +import { SNSEventRecord, SNSHandler, SNSMessage, SNSMessageAttribute, SNSMessageAttributes } from "aws-lambda"; + +const handler: SNSHandler = async (event, context, callback) => { + const record: SNSEventRecord = event.Records[num]; + + str = record.EventSource; + str = record.EventSubscriptionArn; + str = record.EventVersion; + const message: SNSMessage = record.Sns; + + str = message.SignatureVersion; + str = message.Timestamp; + str = message.Signature; + str = message.SigningCertUrl; + str = message.MessageId; + str = message.Message; + const attributes: SNSMessageAttributes = message.MessageAttributes; + str = message.Type; + str = message.UnsubscribeUrl; + str = message.TopicArn; + str = message.Subject; + + const attribute: SNSMessageAttribute = attributes[str]; + str = attribute.Type; + str = attribute.Value; + + callback(); + callback(new Error()); +}; diff --git a/types/aws-lambda/test/sqs-tests.ts b/types/aws-lambda/test/sqs-tests.ts new file mode 100644 index 0000000000..302ac03f9a --- /dev/null +++ b/types/aws-lambda/test/sqs-tests.ts @@ -0,0 +1,44 @@ +import { SQSEvent, SQSHandler } from "aws-lambda"; + +// TODO: Update test to read all event properties, like the user will. + +const handler: SQSHandler = async (event, context, callback) => { + str = event.Records[0].messageId; + anyObj = event.Records[0].body; + strOrUndefined = event.Records[0].messageAttributes.testAttr.stringValue; + strOrUndefined = event.Records[0].messageAttributes.testAttr.binaryValue; + str = event.Records[0].messageAttributes.testAttr.dataType; + + callback(); + callback(new Error()); +}; + +// See https://docs.aws.amazon.com/lambda/latest/dg/eventsources.html#eventsources-sqs +const event: SQSEvent = { + Records: [ + { + messageId: 'c80e8021-a70a-42c7-a470-796e1186f753', + receiptHandle: 'AQEBJQ+/u6NsnT5t8Q/VbVxgdUl4TMKZ5FqhksRdIQvLBhwNvADoBxYSOVeCBXdnS9P+', + body: '{"foo":"bar"}', + attributes: { + ApproximateReceiveCount: '3', + SentTimestamp: '1529104986221', + SenderId: '594035263019', + ApproximateFirstReceiveTimestamp: '1529104986230', + }, + messageAttributes: { + testAttr: { + stringValue: '100', + binaryValue: 'base64Str', + stringListValues: [], + binaryListValues: [], + dataType: 'Number', + }, + }, + md5OfBody: '9bb58f26192e4ba00f01e2e7b136bbd8', + eventSource: 'aws:sqs', + eventSourceARN: 'arn:aws:sqs:us-west-2:594035263019:NOTFIFOQUEUE', + awsRegion: 'us-west-2', + }, + ], +}; diff --git a/types/aws-lambda/trigger/alb.d.ts b/types/aws-lambda/trigger/alb.d.ts new file mode 100644 index 0000000000..b93c2d6646 --- /dev/null +++ b/types/aws-lambda/trigger/alb.d.ts @@ -0,0 +1,32 @@ +import { Handler, Callback } from "../hander"; + +export type ALBHandler = Handler; +export type ALBCallback = Callback; + +// https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html +export interface ALBEventRequestContext { + elb: { + targetGroupArn: string; + }; +} + +export interface ALBEvent { + requestContext: ALBEventRequestContext; + httpMethod: string; + path: string; + queryStringParameters?: { [parameter: string]: string }; // URL encoded + headers?: { [header: string]: string }; + multiValueQueryStringParameters?: { [parameter: string]: string[] }; // URL encoded + multiValueHeaders?: { [header: string]: string[] }; + body: string | null; + isBase64Encoded: boolean; +} + +export interface ALBResult { + statusCode: number; + statusDescription: string; + headers?: { [header: string]: boolean | number | string }; + multiValueHeaders?: { [header: string]: Array }; + body?: string; + isBase64Encoded: boolean; +} diff --git a/types/aws-lambda/trigger/api-gateway-authorizer.d.ts b/types/aws-lambda/trigger/api-gateway-authorizer.d.ts new file mode 100644 index 0000000000..761c4fb746 --- /dev/null +++ b/types/aws-lambda/trigger/api-gateway-authorizer.d.ts @@ -0,0 +1,88 @@ +import { APIGatewayEventRequestContext, AuthResponseContext } from "../common/api-gateway"; +import { Callback, Handler } from "../hander"; + +export type CustomAuthorizerHandler = Handler; +export type CustomAuthorizerCallback = Callback; + +// API Gateway CustomAuthorizer "event" +export interface CustomAuthorizerEvent { + type: string; + methodArn: string; + authorizationToken?: string; + resource?: string; + path?: string; + httpMethod?: string; + headers?: { [name: string]: string }; + multiValueHeaders?: { [name: string]: string[] }; + pathParameters?: { [name: string]: string } | null; + queryStringParameters?: { [name: string]: string } | null; + multiValueQueryStringParameters?: { [name: string]: string[] } | null; + stageVariables?: { [name: string]: string }; + requestContext?: APIGatewayEventRequestContext; + domainName?: string; + apiId?: string; +} + +/** + * API Gateway CustomAuthorizer AuthResponse. + * http://docs.aws.amazon.com/apigateway/latest/developerguide/use-custom-authorizer.html#api-gateway-custom-authorizer-output + */ +export interface CustomAuthorizerResult { + principalId: string; + policyDocument: PolicyDocument; + context?: AuthResponseContext; + usageIdentifierKey?: string; +} +export type AuthResponse = CustomAuthorizerResult; + +/** + * API Gateway CustomAuthorizer AuthResponse.PolicyDocument. + * https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-lambda-authorizer-output.html + * https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html#Condition + */ +export interface PolicyDocument { + Version: string; + Id?: string; + Statement: Statement[]; +} + +/** + * API Gateway CustomAuthorizer AuthResponse.PolicyDocument.Condition. + * https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-control-access-policy-language-overview.html + * https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html + */ +export interface ConditionBlock { + [condition: string]: Condition | Condition[]; +} + +export interface Condition { + [key: string]: string | string[]; +} + +/** + * API Gateway CustomAuthorizer AuthResponse.PolicyDocument.Statement. + * https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-control-access-policy-language-overview.html + * https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements.html + */ +export type Statement = BaseStatement & StatementAction & (StatementResource | StatementPrincipal); + +export interface BaseStatement { + Effect: string; + Sid?: string; + Condition?: ConditionBlock; +} + +export type PrincipalValue = { [key: string]: string | string[] } | string | string[]; +export interface MaybeStatementPrincipal { + Principal?: PrincipalValue; + NotPrincipal?: PrincipalValue; +} +export interface MaybeStatementResource { + Resource?: string | string[]; + NotResource?: string | string[]; +} +export type StatementAction = { Action: string | string[] } | { NotAction: string | string[] }; +export type StatementResource = MaybeStatementPrincipal & + ({ Resource: string | string[] } | { NotResource: string | string[] }); +export type StatementPrincipal = MaybeStatementResource & + ({ Principal: PrincipalValue } | { NotPrincipal: PrincipalValue }); diff --git a/types/aws-lambda/trigger/api-gateway-proxy.d.ts b/types/aws-lambda/trigger/api-gateway-proxy.d.ts new file mode 100644 index 0000000000..eb1890dc2a --- /dev/null +++ b/types/aws-lambda/trigger/api-gateway-proxy.d.ts @@ -0,0 +1,38 @@ +import { APIGatewayEventRequestContext } from "../common/api-gateway"; +import { Callback, Handler } from "../hander"; + +export type APIGatewayProxyHandler = Handler; +export type APIGatewayProxyCallback = Callback; + +export type ProxyHandler = APIGatewayProxyHandler; // Old name +export type ProxyCallback = APIGatewayProxyCallback; // Old name +export type APIGatewayEvent = APIGatewayProxyEvent; // Old name +export type ProxyResult = APIGatewayProxyResult; // Old name + +// API Gateway "event" +export interface APIGatewayProxyEvent { + body: string | null; + headers: { [name: string]: string }; + multiValueHeaders: { [name: string]: string[] }; + httpMethod: string; + isBase64Encoded: boolean; + path: string; + pathParameters: { [name: string]: string } | null; + queryStringParameters: { [name: string]: string } | null; + multiValueQueryStringParameters: { [name: string]: string[] } | null; + stageVariables: { [name: string]: string } | null; + requestContext: APIGatewayEventRequestContext; + resource: string; +} + +export interface APIGatewayProxyResult { + statusCode: number; + headers?: { + [header: string]: boolean | number | string; + }; + multiValueHeaders?: { + [header: string]: Array; + }; + body: string; + isBase64Encoded?: boolean; +} diff --git a/types/aws-lambda/trigger/cloudformation-custom-resource.d.ts b/types/aws-lambda/trigger/cloudformation-custom-resource.d.ts new file mode 100644 index 0000000000..a3ebd11f0b --- /dev/null +++ b/types/aws-lambda/trigger/cloudformation-custom-resource.d.ts @@ -0,0 +1,68 @@ +import { Handler } from "../hander"; + +// Note, responses are *not* lambda results, they are sent to the event ResponseURL. +export type CloudFormationCustomResourceHandler = Handler; + +export type CloudFormationCustomResourceEvent = + | CloudFormationCustomResourceCreateEvent + | CloudFormationCustomResourceUpdateEvent + | CloudFormationCustomResourceDeleteEvent; + +export type CloudFormationCustomResourceResponse = + | CloudFormationCustomResourceSuccessResponse + | CloudFormationCustomResourceFailedResponse; + +/** + * CloudFormation Custom Resource event and response + * http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/crpg-ref.html + */ +export interface CloudFormationCustomResourceEventCommon { + ServiceToken: string; + ResponseURL: string; + StackId: string; + RequestId: string; + LogicalResourceId: string; + ResourceType: string; + ResourceProperties: { + ServiceToken: string; + [Key: string]: any; + }; +} + +export interface CloudFormationCustomResourceCreateEvent extends CloudFormationCustomResourceEventCommon { + RequestType: 'Create'; +} + +export interface CloudFormationCustomResourceUpdateEvent extends CloudFormationCustomResourceEventCommon { + RequestType: 'Update'; + PhysicalResourceId: string; + OldResourceProperties: { + [Key: string]: any; + }; +} + +export interface CloudFormationCustomResourceDeleteEvent extends CloudFormationCustomResourceEventCommon { + RequestType: 'Delete'; + PhysicalResourceId: string; +} + +export interface CloudFormationCustomResourceResponseCommon { + PhysicalResourceId: string; + StackId: string; + RequestId: string; + LogicalResourceId: string; + Data?: { + [Key: string]: any; + }; + NoEcho?: boolean; +} + +export interface CloudFormationCustomResourceSuccessResponse extends CloudFormationCustomResourceResponseCommon { + Status: 'SUCCESS'; + Reason?: string; +} + +export interface CloudFormationCustomResourceFailedResponse extends CloudFormationCustomResourceResponseCommon { + Status: 'FAILED'; + Reason: string; +} diff --git a/types/aws-lambda/trigger/cloudfront-request.d.ts b/types/aws-lambda/trigger/cloudfront-request.d.ts new file mode 100644 index 0000000000..8a4d0e5269 --- /dev/null +++ b/types/aws-lambda/trigger/cloudfront-request.d.ts @@ -0,0 +1,20 @@ +import { CloudFrontEvent, CloudFrontRequest, CloudFrontResultResponse } from "../common/cloudfront"; +import { Handler, Callback } from "../hander"; + +export type CloudFrontRequestHandler = Handler; +export type CloudFrontRequestCallback = Callback; + +/** + * CloudFront viewer request or origin request event + * + * https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html#lambda-event-structure-request + */ +export interface CloudFrontRequestEvent { + Records: Array<{ + cf: CloudFrontEvent & { + request: CloudFrontRequest; + }; + }>; +} + +export type CloudFrontRequestResult = undefined | null | CloudFrontResultResponse | CloudFrontRequest; diff --git a/types/aws-lambda/trigger/cloudfront-response.d.ts b/types/aws-lambda/trigger/cloudfront-response.d.ts new file mode 100644 index 0000000000..d228d3d752 --- /dev/null +++ b/types/aws-lambda/trigger/cloudfront-response.d.ts @@ -0,0 +1,21 @@ +import { CloudFrontEvent, CloudFrontRequest, CloudFrontResponse, CloudFrontResultResponse } from "../common/cloudfront"; +import { Callback, Handler } from "../hander"; + +export type CloudFrontResponseHandler = Handler; +export type CloudFrontResponseCallback = Callback; + +/** + * CloudFront viewer response or origin response event + * + * https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html#lambda-event-structure-response + */ +export interface CloudFrontResponseEvent { + Records: Array<{ + cf: CloudFrontEvent & { + readonly request: Pick>; + response: CloudFrontResponse; + }; + }>; +} + +export type CloudFrontResponseResult = undefined | null | CloudFrontResultResponse; diff --git a/types/aws-lambda/trigger/cloudwatch-events.d.ts b/types/aws-lambda/trigger/cloudwatch-events.d.ts new file mode 100644 index 0000000000..4ddc681fb2 --- /dev/null +++ b/types/aws-lambda/trigger/cloudwatch-events.d.ts @@ -0,0 +1,19 @@ +import { Handler } from "../hander"; + +export type ScheduledHandler = Handler; + +// TODO: generic cloudwatch event types. + +/** + * https://docs.aws.amazon.com/lambda/latest/dg/with-scheduled-events.html + */ +export interface ScheduledEvent { + account: string; + region: string; + detail: any; + 'detail-type': string; + source: string; + time: string; + id: string; + resources: string[]; +} diff --git a/types/aws-lambda/trigger/cloudwatch-logs.d.ts b/types/aws-lambda/trigger/cloudwatch-logs.d.ts new file mode 100644 index 0000000000..1912b42ee3 --- /dev/null +++ b/types/aws-lambda/trigger/cloudwatch-logs.d.ts @@ -0,0 +1,33 @@ +import { Handler } from "../hander"; + +export type CloudWatchLogsHandler = Handler; + +/** + * See http://docs.aws.amazon.com/lambda/latest/dg/eventsources.html#eventsources-cloudwatch-logs + */ +export interface CloudWatchLogsEvent { + awslogs: CloudWatchLogsEventData; +} + +export interface CloudWatchLogsEventData { + data: string; +} + +export interface CloudWatchLogsDecodedData { + owner: string; + logGroup: string; + logStream: string; + subscriptionFilters: string[]; + messageType: string; + logEvents: CloudWatchLogsLogEvent[]; +} + +/** + * See http://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/SubscriptionFilters.html#LambdaFunctionExample + */ +export interface CloudWatchLogsLogEvent { + id: string; + timestamp: number; + message: string; + extractedFields?: { [key: string]: string }; +} diff --git a/types/aws-lambda/trigger/codepipeline-cloudwatch-action.d.ts b/types/aws-lambda/trigger/codepipeline-cloudwatch-action.d.ts new file mode 100644 index 0000000000..083da87bba --- /dev/null +++ b/types/aws-lambda/trigger/codepipeline-cloudwatch-action.d.ts @@ -0,0 +1,31 @@ +import { Handler } from "../hander"; + +export type CodePipelineCloudWatchActionHandler = Handler; + +export type CodePipelineActionCategory = 'Approval' | 'Build' | 'Deploy' | 'Invoke' | 'Source' | 'Test'; +export type CodePipelineActionState = 'STARTED' | 'SUCCEEDED' | 'FAILED' | 'CANCELED'; + +export interface CodePipelineCloudWatchActionEvent { + version: string; + id: string; + 'detail-type': 'CodePipeline Action Execution State Change'; + source: 'aws.codepipeline'; + account: string; + time: string; + region: string; + resources: string[]; + detail: { + pipeline: string; + version: number; + 'execution-id': string; + stage: string; + action: string; + state: CodePipelineActionState; + type: { + owner: 'AWS' | 'Custom' | 'ThirdParty'; + category: CodePipelineActionCategory; + provider: string; + version: number; + }; + }; +} diff --git a/types/aws-lambda/trigger/codepipeline-cloudwatch-pipeline.d.ts b/types/aws-lambda/trigger/codepipeline-cloudwatch-pipeline.d.ts new file mode 100644 index 0000000000..1d8da08cf7 --- /dev/null +++ b/types/aws-lambda/trigger/codepipeline-cloudwatch-pipeline.d.ts @@ -0,0 +1,32 @@ +import { Handler } from "../hander"; + +export type CodePipelineCloudWatchPipelineHandler = Handler; + +export type CodePipelineState = 'STARTED' | 'SUCCEEDED' | 'RESUMED' | 'FAILED' | 'CANCELED' | 'SUPERSEDED'; + +/** + * CodePipeline CloudWatch Events + * https://docs.aws.amazon.com/codepipeline/latest/userguide/detect-state-changes-cloudwatch-events.html + * + * The above CodePipelineEvent is when a lambda is invoked by a CodePipeline. + * These events are when you subscribe to CodePipeline events in CloudWatch. + * + * Their documentation says that detail.version is a string, but it is actually an integer + */ + +export interface CodePipelineCloudWatchPipelineEvent { + version: string; + id: string; + 'detail-type': 'CodePipeline Pipeline Execution State Change'; + source: 'aws.codepipeline'; + account: string; + time: string; + region: string; + resources: string[]; + detail: { + pipeline: string; + version: number; + state: CodePipelineState; + 'execution-id': string; + }; +} diff --git a/types/aws-lambda/trigger/codepipeline-cloudwatch-stage.d.ts b/types/aws-lambda/trigger/codepipeline-cloudwatch-stage.d.ts new file mode 100644 index 0000000000..9f0bdcf0d1 --- /dev/null +++ b/types/aws-lambda/trigger/codepipeline-cloudwatch-stage.d.ts @@ -0,0 +1,23 @@ +import { Handler } from "../hander"; + +export type CodePipelineCloudWatchStageHandler = Handler; + +export type CodePipelineStageState = 'STARTED' | 'SUCCEEDED' | 'RESUMED' | 'FAILED' | 'CANCELED'; + +export interface CodePipelineCloudWatchStageEvent { + version: string; + id: string; + 'detail-type': 'CodePipeline Stage Execution State Change'; + source: 'aws.codepipeline'; + account: string; + time: string; + region: string; + resources: string[]; + detail: { + pipeline: string; + version: number; + 'execution-id': string; + stage: string; + state: CodePipelineStageState; + }; +} diff --git a/types/aws-lambda/trigger/codepipeline-cloudwatch.d.ts b/types/aws-lambda/trigger/codepipeline-cloudwatch.d.ts new file mode 100644 index 0000000000..2235ddf4bb --- /dev/null +++ b/types/aws-lambda/trigger/codepipeline-cloudwatch.d.ts @@ -0,0 +1,11 @@ +import { Handler } from "../hander"; +import { CodePipelineCloudWatchActionEvent } from "./codepipeline-cloudwatch-action"; +import { CodePipelineCloudWatchPipelineEvent } from "./codepipeline-cloudwatch-pipeline"; +import { CodePipelineCloudWatchStageEvent } from "./codepipeline-cloudwatch-stage"; + +export type CodePipelineCloudWatchHandler = Handler; + +export type CodePipelineCloudWatchEvent = + | CodePipelineCloudWatchPipelineEvent + | CodePipelineCloudWatchStageEvent + | CodePipelineCloudWatchActionEvent; diff --git a/types/aws-lambda/trigger/codepipeline.d.ts b/types/aws-lambda/trigger/codepipeline.d.ts new file mode 100644 index 0000000000..9eb38a2db0 --- /dev/null +++ b/types/aws-lambda/trigger/codepipeline.d.ts @@ -0,0 +1,55 @@ +import { Handler } from "../hander"; + +export type CodePipelineHandler = Handler; + +/** + * CodePipeline events + * https://docs.aws.amazon.com/codepipeline/latest/userguide/actions-invoke-lambda-function.html + */ +export interface S3ArtifactLocation { + bucketName: string; + objectKey: string; +} +export interface S3ArtifactStore { + type: 'S3'; + s3Location: S3ArtifactLocation; +} + +export type ArtifactLocation = S3ArtifactStore; + +export interface Artifact { + name: string; + revision: string | null; + location: ArtifactLocation; +} + +export interface Credentials { + accessKeyId: string; + secretAccessKey: string; + sessionToken?: string; +} + +export interface EncryptionKey { + type: string; + id: string; +} + +export interface CodePipelineEvent { + 'CodePipeline.job': { + id: string; + accountId: string; + data: { + actionConfiguration: { + configuration: { + FunctionName: string; + UserParameters: string; + }; + }; + inputArtifacts: Artifact[]; + outputArtifacts: Artifact[]; + artifactCredentials: Credentials; + encryptionKey?: EncryptionKey & { type: 'KMS' }; + continuationToken?: string; + }; + }; +} diff --git a/types/aws-lambda/trigger/cognito-user-pool-trigger.d.ts b/types/aws-lambda/trigger/cognito-user-pool-trigger.d.ts new file mode 100644 index 0000000000..fe849fa514 --- /dev/null +++ b/types/aws-lambda/trigger/cognito-user-pool-trigger.d.ts @@ -0,0 +1,99 @@ +import { Handler } from "../hander"; + +// Result type is weird: docs and samples say to return the mutated event, but it only requires an object +// with a "response" field, the type of which is specific to the event.triggerType. Leave as any for now. +export type CognitoUserPoolTriggerHandler = Handler; +// TODO: Different event/handler types for each event trigger so we can type the result? + +/** + * Cognito User Pool event + * http://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html + */ +export interface CognitoUserPoolTriggerEvent { + version: number; + triggerSource: + | 'PreSignUp_SignUp' + | 'PreSignUp_ExternalProvider' + | 'PostConfirmation_ConfirmSignUp' + | 'PreAuthentication_Authentication' + | 'PostAuthentication_Authentication' + | 'CustomMessage_SignUp' + | 'CustomMessage_AdminCreateUser' + | 'CustomMessage_ResendCode' + | 'CustomMessage_ForgotPassword' + | 'CustomMessage_UpdateUserAttribute' + | 'CustomMessage_VerifyUserAttribute' + | 'CustomMessage_Authentication' + | 'DefineAuthChallenge_Authentication' + | 'CreateAuthChallenge_Authentication' + | 'VerifyAuthChallengeResponse_Authentication' + | 'PreSignUp_AdminCreateUser' + | 'PostConfirmation_ConfirmForgotPassword' + | 'TokenGeneration_HostedAuth' + | 'TokenGeneration_Authentication' + | 'TokenGeneration_NewPasswordChallenge' + | 'TokenGeneration_AuthenticateDevice' + | 'TokenGeneration_RefreshTokens' + | 'UserMigration_Authentication' + | 'UserMigration_ForgotPassword'; + region: string; + userPoolId: string; + userName?: string; + callerContext: { + awsSdkVersion: string; + clientId: string; + }; + request: { + userAttributes: { [key: string]: string }; + validationData?: { [key: string]: string }; + codeParameter?: string; + linkParameter?: string; + usernameParameter?: string; + newDeviceUsed?: boolean; + session?: Array<{ + challengeName: + | 'CUSTOM_CHALLENGE' + | 'PASSWORD_VERIFIER' + | 'SMS_MFA' + | 'DEVICE_SRP_AUTH' + | 'DEVICE_PASSWORD_VERIFIER' + | 'ADMIN_NO_SRP_AUTH'; + challengeResult: boolean; + challengeMetadata?: string; + }>; + challengeName?: string; + privateChallengeParameters?: { [key: string]: string }; + challengeAnswer?: string; + password?: string; + }; + response: { + autoConfirmUser?: boolean; + autoVerifyPhone?: boolean; + autoVerifyEmail?: boolean; + smsMessage?: string; + emailMessage?: string; + emailSubject?: string; + challengeName?: string; + issueTokens?: boolean; + failAuthentication?: boolean; + publicChallengeParameters?: { [key: string]: string }; + privateChallengeParameters?: { [key: string]: string }; + challengeMetadata?: string; + answerCorrect?: boolean; + userAttributes?: { [key: string]: string }; + finalUserStatus?: 'CONFIRMED' | 'RESET_REQUIRED'; + messageAction?: 'SUPPRESS'; + desiredDeliveryMediums?: Array<'EMAIL' | 'SMS'>; + forceAliasCreation?: boolean; + claimsOverrideDetails?: { + claimsToAddOrOverride?: { [key: string]: string }; + claimsToSuppress?: string[]; + groupOverrideDetails?: null | { + groupsToOverride?: string[]; + iamRolesToOverride?: string[]; + preferredRole?: string; + }; + }; + }; +} +export type CognitoUserPoolEvent = CognitoUserPoolTriggerEvent; diff --git a/types/aws-lambda/trigger/dynamodb-stream.d.ts b/types/aws-lambda/trigger/dynamodb-stream.d.ts new file mode 100644 index 0000000000..486523cb4b --- /dev/null +++ b/types/aws-lambda/trigger/dynamodb-stream.d.ts @@ -0,0 +1,45 @@ +import { Handler } from "../hander"; + +export type DynamoDBStreamHandler = Handler; + +// http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_streams_AttributeValue.html +export interface AttributeValue { + B?: string; + BS?: string[]; + BOOL?: boolean; + L?: AttributeValue[]; + M?: { [id: string]: AttributeValue }; + N?: string; + NS?: string[]; + NULL?: boolean; + S?: string; + SS?: string[]; +} + +// http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_streams_StreamRecord.html +export interface StreamRecord { + ApproximateCreationDateTime?: number; + Keys?: { [key: string]: AttributeValue }; + NewImage?: { [key: string]: AttributeValue }; + OldImage?: { [key: string]: AttributeValue }; + SequenceNumber?: string; + SizeBytes?: number; + StreamViewType?: 'KEYS_ONLY' | 'NEW_IMAGE' | 'OLD_IMAGE' | 'NEW_AND_OLD_IMAGES'; +} + +// http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_streams_Record.html +export interface DynamoDBRecord { + awsRegion?: string; + dynamodb?: StreamRecord; + eventID?: string; + eventName?: 'INSERT' | 'MODIFY' | 'REMOVE'; + eventSource?: string; + eventSourceARN?: string; + eventVersion?: string; + userIdentity?: any; +} + +// http://docs.aws.amazon.com/lambda/latest/dg/eventsources.html#eventsources-ddb-update +export interface DynamoDBStreamEvent { + Records: DynamoDBRecord[]; +} diff --git a/types/aws-lambda/trigger/kinesis-firehose-transformation.d.ts b/types/aws-lambda/trigger/kinesis-firehose-transformation.d.ts new file mode 100644 index 0000000000..020ae0d907 --- /dev/null +++ b/types/aws-lambda/trigger/kinesis-firehose-transformation.d.ts @@ -0,0 +1,45 @@ +import { Callback, Handler } from "../hander"; + +export type FirehoseTransformationHandler = Handler; +export type FirehoseTransformationCallback = Callback; + +// Kinesis Data Firehose Event +// https://docs.aws.amazon.com/lambda/latest/dg/eventsources.html#eventsources-kinesis-firehose +// https://docs.aws.amazon.com/firehose/latest/dev/data-transformation.html +// https://aws.amazon.com/blogs/compute/amazon-kinesis-firehose-data-transformation-with-aws-lambda/ +// Examples in the lambda blueprints +export interface FirehoseTransformationEvent { + invocationId: string; + deliveryStreamArn: string; + region: string; + records: FirehoseTransformationEventRecord[]; +} + +export interface FirehoseTransformationEventRecord { + recordId: string; + approximateArrivalTimestamp: number; + /** Base64 encoded */ + data: string; + kinesisRecordMetadata?: FirehoseRecordMetadata; +} + +export interface FirehoseRecordMetadata { + shardId: string; + partitionKey: string; + approximateArrivalTimestamp: string; + sequenceNumber: string; + subsequenceNumber: string; +} + +export type FirehoseRecordTransformationStatus = 'Ok' | 'Dropped' | 'ProcessingFailed'; + +export interface FirehoseTransformationResultRecord { + recordId: string; + result: FirehoseRecordTransformationStatus; + /** Encode in Base64 */ + data: string; +} + +export interface FirehoseTransformationResult { + records: FirehoseTransformationResultRecord[]; +} diff --git a/types/aws-lambda/trigger/kinesis-stream.d.ts b/types/aws-lambda/trigger/kinesis-stream.d.ts new file mode 100644 index 0000000000..54acd6ea49 --- /dev/null +++ b/types/aws-lambda/trigger/kinesis-stream.d.ts @@ -0,0 +1,28 @@ +import { Handler } from "../hander"; + +export type KinesisStreamHandler = Handler; + +// Kinesis Streams +// https://docs.aws.amazon.com/lambda/latest/dg/eventsources.html#eventsources-kinesis-streams +export interface KinesisStreamRecordPayload { + approximateArrivalTimestamp: number; + data: string; + kinesisSchemaVersion: string; + partitionKey: string; + sequenceNumber: string; +} + +export interface KinesisStreamRecord { + awsRegion: string; + eventID: string; + eventName: string; + eventSource: string; + eventSourceARN: string; + eventVersion: string; + invokeIdentityArn: string; + kinesis: KinesisStreamRecordPayload; +} + +export interface KinesisStreamEvent { + Records: KinesisStreamRecord[]; +} diff --git a/types/aws-lambda/trigger/lex.d.ts b/types/aws-lambda/trigger/lex.d.ts new file mode 100644 index 0000000000..d52eebdc98 --- /dev/null +++ b/types/aws-lambda/trigger/lex.d.ts @@ -0,0 +1,103 @@ +import { Callback, Handler } from "../hander"; + +export type LexHandler = Handler; +export type LexCallback = Callback; + +// Lex +// https://docs.aws.amazon.com/lambda/latest/dg/invoking-lambda-function.html#supported-event-source-lex +export interface LexEvent { + currentIntent: { + name: string; + slots: { [name: string]: string | null }; + slotDetails: LexSlotDetails; + confirmationStatus: 'None' | 'Confirmed' | 'Denied'; + }; + bot: { + name: string; + alias: string; + version: string; + }; + userId: string; + inputTranscript: string; + invocationSource: 'DialogCodeHook' | 'FulfillmentCodeHook'; + outputDialogMode: 'Text' | 'Voice'; + messageVersion: '1.0'; + sessionAttributes: { [key: string]: string }; + requestAttributes: { [key: string]: string } | null; +} + +export interface LexSlotResolution { + value: string; +} + +export interface LexSlotDetails { + [name: string]: { + // The following line only works in TypeScript Version: 3.0, The array should have at least 1 and no more than 5 items + // resolutions: [LexSlotResolution, LexSlotResolution?, LexSlotResolution?, LexSlotResolution?, LexSlotResolution?]; + resolutions: LexSlotResolution[]; + originalValue: string; + }; +} + +export interface LexGenericAttachment { + title: string; + subTitle: string; + imageUrl: string; + attachmentLinkUrl: string; + buttons: Array<{ + text: string; + value: string; + }>; +} + +export interface LexDialogActionBase { + type: 'Close' | 'ElicitIntent' | 'ElicitSlot' | 'ConfirmIntent'; + message?: { + contentType: 'PlainText' | 'SSML' | 'CustomPayload'; + content: string; + }; + responseCard?: { + version: number; + contentType: 'application/vnd.amazonaws.card.generic'; + genericAttachments: LexGenericAttachment[]; + }; +} + +export interface LexDialogActionClose extends LexDialogActionBase { + type: 'Close'; + fulfillmentState: 'Fulfilled' | 'Failed'; +} + +export interface LexDialogActionElicitIntent extends LexDialogActionBase { + type: 'ElicitIntent'; +} + +export interface LexDialogActionElicitSlot extends LexDialogActionBase { + type: 'ElicitSlot'; + intentName: string; + slots: { [name: string]: string | null }; + slotToElicit: string; +} + +export interface LexDialogActionConfirmIntent extends LexDialogActionBase { + type: 'ConfirmIntent'; + intentName: string; + slots: { [name: string]: string | null }; +} + +export interface LexDialogActionDelegate { + type: 'Delegate'; + slots: { [name: string]: string | null }; +} + +export type LexDialogAction = + | LexDialogActionClose + | LexDialogActionElicitIntent + | LexDialogActionElicitSlot + | LexDialogActionConfirmIntent + | LexDialogActionDelegate; + +export interface LexResult { + sessionAttributes?: { [key: string]: string }; + dialogAction: LexDialogAction; +} diff --git a/types/aws-lambda/trigger/s3-batch.d.ts b/types/aws-lambda/trigger/s3-batch.d.ts new file mode 100644 index 0000000000..2fd1d1e464 --- /dev/null +++ b/types/aws-lambda/trigger/s3-batch.d.ts @@ -0,0 +1,41 @@ +import { Handler, Callback } from "../hander"; + +/** + * S3 Batch Operations event + * https://docs.aws.amazon.com/AmazonS3/latest/dev/batch-ops-invoke-lambda.html + */ +export type S3BatchHandler = Handler; +export type S3BatchCallback = Callback; + +export interface S3BatchEvent { + invocationSchemaVersion: string; + invocationId: string; + job: S3BatchEventJob; + tasks: S3BatchEventTask[]; +} + +export interface S3BatchEventJob { + id: string; +} + +export interface S3BatchEventTask { + taskId: string; + s3Key: string; + s3VersionId: string | null; + s3BucketArn: string; +} + +export interface S3BatchResult { + invocationSchemaVersion: string; + treatMissingKeysAs: S3BatchResultResultCode; + invocationId: string; + results: S3BatchResultResult[]; +} + +export type S3BatchResultResultCode = 'Succeeded' | 'TemporaryFailure' | 'PermanentFailure'; + +export interface S3BatchResultResult { + taskId: string; + resultCode: S3BatchResultResultCode; + resultString: string; +} diff --git a/types/aws-lambda/trigger/s3.d.ts b/types/aws-lambda/trigger/s3.d.ts new file mode 100644 index 0000000000..4d48c13a6a --- /dev/null +++ b/types/aws-lambda/trigger/s3.d.ts @@ -0,0 +1,60 @@ +import { Handler } from "../hander"; + +export type S3Handler = Handler; + +/** + * S3Create event + * https://docs.aws.amazon.com/AmazonS3/latest/dev/notification-content-structure.html + */ + +export interface S3EventRecordGlacierRestoreEventData { + lifecycleRestorationExpiryTime: string; + lifecycleRestoreStorageClass: string; +} + +export interface S3EventRecordGlacierEventData { + restoreEventData: S3EventRecordGlacierRestoreEventData; +} + +export interface S3EventRecord { + eventVersion: string; + eventSource: string; + awsRegion: string; + eventTime: string; + eventName: string; + userIdentity: { + principalId: string; + }; + requestParameters: { + sourceIPAddress: string; + }; + responseElements: { + 'x-amz-request-id': string; + 'x-amz-id-2': string; + }; + s3: { + s3SchemaVersion: string; + configurationId: string; + bucket: { + name: string; + ownerIdentity: { + principalId: string; + }; + arn: string; + }; + object: { + key: string; + size: number; + eTag: string; + versionId?: string; + sequencer: string; + }; + }; + glacierEventData?: S3EventRecordGlacierEventData; +} + +export interface S3Event { + Records: S3EventRecord[]; +} + +export type S3CreateEvent = S3Event; // old name diff --git a/types/aws-lambda/trigger/sns.d.ts b/types/aws-lambda/trigger/sns.d.ts new file mode 100644 index 0000000000..6c6eac4a6b --- /dev/null +++ b/types/aws-lambda/trigger/sns.d.ts @@ -0,0 +1,38 @@ +import { Handler } from "../hander"; + +export type SNSHandler = Handler; + +// SNS "event" +export interface SNSMessageAttribute { + Type: string; + Value: string; +} + +export interface SNSMessageAttributes { + [name: string]: SNSMessageAttribute; +} + +export interface SNSMessage { + SignatureVersion: string; + Timestamp: string; + Signature: string; + SigningCertUrl: string; + MessageId: string; + Message: string; + MessageAttributes: SNSMessageAttributes; + Type: string; + UnsubscribeUrl: string; + TopicArn: string; + Subject: string; +} + +export interface SNSEventRecord { + EventVersion: string; + EventSubscriptionArn: string; + EventSource: string; + Sns: SNSMessage; +} + +export interface SNSEvent { + Records: SNSEventRecord[]; +} diff --git a/types/aws-lambda/trigger/sqs.d.ts b/types/aws-lambda/trigger/sqs.d.ts new file mode 100644 index 0000000000..9c23a9f280 --- /dev/null +++ b/types/aws-lambda/trigger/sqs.d.ts @@ -0,0 +1,42 @@ +import { Handler } from "../hander"; + +export type SQSHandler = Handler; + +// SQS +// https://docs.aws.amazon.com/lambda/latest/dg/invoking-lambda-function.html#supported-event-source-sqs +export interface SQSRecord { + messageId: string; + receiptHandle: string; + body: string; + attributes: SQSRecordAttributes; + messageAttributes: SQSMessageAttributes; + md5OfBody: string; + eventSource: string; + eventSourceARN: string; + awsRegion: string; +} + +export interface SQSEvent { + Records: SQSRecord[]; +} + +export interface SQSRecordAttributes { + ApproximateReceiveCount: string; + SentTimestamp: string; + SenderId: string; + ApproximateFirstReceiveTimestamp: string; +} + +export type SQSMessageAttributeDataType = 'String' | 'Number' | 'Binary' | string; + +export interface SQSMessageAttribute { + stringValue?: string; + binaryValue?: string; + stringListValues: never[]; // Not implemented. Reserved for future use. + binaryListValues: never[]; // Not implemented. Reserved for future use. + dataType: SQSMessageAttributeDataType; +} + +export interface SQSMessageAttributes { + [name: string]: SQSMessageAttribute; +} diff --git a/types/aws-lambda/tsconfig.json b/types/aws-lambda/tsconfig.json index 79e058b7ab..598e7b0fba 100644 --- a/types/aws-lambda/tsconfig.json +++ b/types/aws-lambda/tsconfig.json @@ -18,6 +18,19 @@ }, "files": [ "index.d.ts", - "aws-lambda-tests.ts" + "aws-lambda-tests.ts", + "test/alb-tests.ts", + "test/api-gateway-tests.ts", + "test/cloudformation-tests.ts", + "test/cloudfront-tests.ts", + "test/cloudwatch-tests.ts", + "test/codepipeline-tests.ts", + "test/cognito-tests.ts", + "test/dynamodb-tests.ts", + "test/kinesis-tests.ts", + "test/lex-tests.ts", + "test/s3-tests.ts", + "test/sns-tests.ts", + "test/sqs-tests.ts" ] -} \ No newline at end of file +} diff --git a/types/aws-lambda/tslint.json b/types/aws-lambda/tslint.json index f6fabe4180..c2d75e690a 100644 --- a/types/aws-lambda/tslint.json +++ b/types/aws-lambda/tslint.json @@ -1,8 +1,8 @@ { "extends": "dtslint/dt.json", "rules": { - // TODO - "npm-naming": false, - "dt-header": false + // This is needed because there's an unrelated aws-lambda package on NPM, + // and DTS is complaining that we're referring to a non-existing version. + "npm-naming": false } }