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 } }