diff --git a/PUBLISHING.md b/PUBLISHING.md index 57d3f72d17..0fddd4dc21 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -65,7 +65,7 @@ Push your code to the remote repos and [create a pull request](https://docs.gith If this is the first push to the remote origin repo, you will be asked to Connect to GitHub to authorize the connection. Sometimes the pop-up window appears behind other windows. -1. Go to the [upstream repo](https://github.com/aws-samples/serverless-patterns) in Github and click "Compare & pull request". +1. Go to the [upstream repo](https://github.com/aws-samples/serverless-patterns) in GitHub and click "Compare & pull request". 1. Enter an appropriate title: Example title: `New serverless pattern - lambda-aurora-serverless` @@ -126,4 +126,4 @@ Delete any unnecessary local and origin branches. 1. API Gateway HTTP API to Lambda: [Website](https://serverlessland.com/patterns/apigw-lambda) | [GitHub](https://github.com/aws-samples/serverless-patterns/tree/main/apigw-http-api-lambda) 2. API Gateway REST API to DynamoDB: [Website](https://serverlessland.com/patterns/apigw-dynamodb) | [GitHub](https://github.com/aws-samples/serverless-patterns/tree/main/apigw-rest-api-dynamodb) 3. Lambda to SSM Parameter Store: [Website](https://serverlessland.com/patterns/lambda-ssm) | [GitHub](https://github.com/aws-samples/serverless-patterns/tree/main/lambda-ssm-parameter) -4. Lambda to S3 via a Custom Resource: [Website](https://serverlessland.com/patterns/lambda-s3-cfn) | [Github](https://github.com/aws-samples/serverless-patterns/tree/main/cfn-custom-resource-s3-create) +4. Lambda to S3 via a Custom Resource: [Website](https://serverlessland.com/patterns/lambda-s3-cfn) | [GitHub](https://github.com/aws-samples/serverless-patterns/tree/main/cfn-custom-resource-s3-create) diff --git a/_pattern-model/example-pattern.json b/_pattern-model/example-pattern.json index a8ee9f16b2..93092ecb0b 100644 --- a/_pattern-model/example-pattern.json +++ b/_pattern-model/example-pattern.json @@ -39,7 +39,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/alb-cognito-lambda/example-pattern.json b/alb-cognito-lambda/example-pattern.json index 1bd8a962ae..e991cd7e27 100644 --- a/alb-cognito-lambda/example-pattern.json +++ b/alb-cognito-lambda/example-pattern.json @@ -39,7 +39,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/alb-lambda-cdk/example-pattern.json b/alb-lambda-cdk/example-pattern.json index 6109f0ebdf..98b94d89ff 100644 --- a/alb-lambda-cdk/example-pattern.json +++ b/alb-lambda-cdk/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/alb-lambda-pulumi-cs/README.md b/alb-lambda-pulumi-cs/README.md index 1f62a1181a..a287b182e5 100644 --- a/alb-lambda-pulumi-cs/README.md +++ b/alb-lambda-pulumi-cs/README.md @@ -12,7 +12,7 @@ Important: this application uses various AWS services and there are costs associ * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) * [Pulumi](https://www.pulumi.com/docs/get-started/install/) installed -* [.NET Core](https://dotnet.microsoft.com/en-us/download) installed and configured +* [.NET 6](https://dotnet.microsoft.com/en-us/download) installed and configured ## Deployment Instructions diff --git a/alb-lambda-pulumi-cs/example-pattern.json b/alb-lambda-pulumi-cs/example-pattern.json index 3c414f507f..179a0de5e9 100644 --- a/alb-lambda-pulumi-cs/example-pattern.json +++ b/alb-lambda-pulumi-cs/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/alb-lambda-pulumi-go/example-pattern.json b/alb-lambda-pulumi-go/example-pattern.json index bd9be5c173..02a73c5ab5 100644 --- a/alb-lambda-pulumi-go/example-pattern.json +++ b/alb-lambda-pulumi-go/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/alb-lambda-pulumi-go/go.mod b/alb-lambda-pulumi-go/go.mod index f5f96a755b..67be1731ac 100644 --- a/alb-lambda-pulumi-go/go.mod +++ b/alb-lambda-pulumi-go/go.mod @@ -60,8 +60,8 @@ require ( golang.org/x/sys v0.5.0 // indirect golang.org/x/term v0.5.0 // indirect golang.org/x/text v0.7.0 // indirect - google.golang.org/genproto v0.0.0-20220802133213-ce4fa296bf78 // indirect - google.golang.org/grpc v1.51.0 // indirect + google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect + google.golang.org/grpc v1.53.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/alb-lambda-pulumi-go/go.sum b/alb-lambda-pulumi-go/go.sum index 925bdf3d12..1d113c23bf 100644 --- a/alb-lambda-pulumi-go/go.sum +++ b/alb-lambda-pulumi-go/go.sum @@ -1,5 +1,389 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= +cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= +cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= +cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= +cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= +cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= +cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= +cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= +cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= +cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= +cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= +cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= +cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= +cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= +cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= +cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= +cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= +cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= +cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= +cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= +cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= +cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= +cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= +cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= +cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= +cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= +cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= +cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= +cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= +cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= +cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= +cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= +cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= +cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= +cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= +cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= +cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= +cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= +cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= +cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= +cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= +cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= +cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= +cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= +cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= +cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= +cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= +cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= +cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= +cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= +cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= +cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= +cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= +cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= +cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= +cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= +cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= +cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= +cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= +cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= +cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= +cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= +cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= +cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= +cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= +cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= +cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= +cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= +cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= +cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= +cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= +cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= +cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= +cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= +cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= +cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= +cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= +cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= +cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= +cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= +cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= +cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= +cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= +cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= +cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= +cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= +cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= +cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= +cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= +cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= +cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= +cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= +cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= +cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= +cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= +cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= +cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= +cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= +cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= +cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= +cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= +cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= +cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= +cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= +cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= +cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= +cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= +cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= +cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= +cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= +cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= +cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= +cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= +cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= +cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= +cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= +cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= +cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= +cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= +cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= +cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= +cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= +cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= +cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= +cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= +cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= +cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= +cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= +cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= +cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= +cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= +cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= +cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= +cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= +cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= +cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= +cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= +cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= +cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= +cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= +cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= +cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= +cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= +cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= +cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= +cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= +cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= +cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= +cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= +cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -37,21 +421,33 @@ github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheggaaa/pb v1.0.18/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= github.com/cheggaaa/pb v1.0.29 h1:FckUN5ngEk2LpvuG0fw1GEFx6LtyY2pWI/Z2QgCnEYo= github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -77,9 +473,16 @@ github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FM github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= +github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= @@ -105,6 +508,8 @@ github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti github.com/go-git/go-git/v5 v5.6.0 h1:JvBdYfcttd+0kdpuWO7KTu0FYgCf5W0t5VwkWGobaa4= github.com/go-git/go-git/v5 v5.6.0/go.mod h1:6nmJ0tJ3N4noMV1Omv7rC5FG3/o8Cm51TB4CJp7mRmE= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= @@ -122,11 +527,24 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -136,8 +554,11 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -145,18 +566,61 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -167,7 +631,12 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= @@ -179,6 +648,8 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= @@ -190,6 +661,7 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -200,6 +672,8 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= @@ -242,6 +716,8 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk= github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -251,6 +727,7 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -272,6 +749,7 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= @@ -295,6 +773,9 @@ github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXi github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -345,11 +826,23 @@ github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= @@ -361,13 +854,16 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200317142112-1b76d66859c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= @@ -381,22 +877,44 @@ golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -405,33 +923,101 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -441,34 +1027,77 @@ golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -477,6 +1106,7 @@ golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -484,18 +1114,28 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -504,45 +1144,288 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200608174601-1b747fd94509/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= +google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= +google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= +google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200608115520-7c474a2e3482/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20220802133213-ce4fa296bf78 h1:QntLWYqZeuBtJkth3m/6DLznnI0AHJr+AgJXvVh/izw= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= google.golang.org/genproto v0.0.0-20220802133213-ce4fa296bf78/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= +google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -589,13 +1472,21 @@ gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= lukechampine.com/frand v1.4.2 h1:RzFIpOvkMXuPMBb9maa4ND4wjBn71E1Jpf8BzJHMaVw= lukechampine.com/frand v1.4.2/go.mod h1:4S/TM2ZgrKejMcKMbeLjISpJMO+/eZ1zu3vYX9dtj3s= pgregory.net/rapid v0.4.7/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU= pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA= pgregory.net/rapid v0.5.5/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 h1:hfyJ5ku9yFtLVOiSxa3IN+dx5eBQT9mPmKFypAmg8XM= sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/alb-lambda-pulumi-js/example-pattern.json b/alb-lambda-pulumi-js/example-pattern.json index e3a40a8bd1..feb119c81f 100644 --- a/alb-lambda-pulumi-js/example-pattern.json +++ b/alb-lambda-pulumi-js/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/alb-lambda-pulumi-ts/example-pattern.json b/alb-lambda-pulumi-ts/example-pattern.json index 2882c5755d..fe336229e7 100644 --- a/alb-lambda-pulumi-ts/example-pattern.json +++ b/alb-lambda-pulumi-ts/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/alb-lambda-pulumi-yaml/example-pattern.json b/alb-lambda-pulumi-yaml/example-pattern.json index d3988cb607..3ea15e72fb 100644 --- a/alb-lambda-pulumi-yaml/example-pattern.json +++ b/alb-lambda-pulumi-yaml/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/alb-lambda-rust/example-pattern.json b/alb-lambda-rust/example-pattern.json index fd1955d17f..29404669bb 100644 --- a/alb-lambda-rust/example-pattern.json +++ b/alb-lambda-rust/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/alb-lambda-serverless/README.md b/alb-lambda-serverless/README.md new file mode 100644 index 0000000000..4f1ab216eb --- /dev/null +++ b/alb-lambda-serverless/README.md @@ -0,0 +1,113 @@ +# Application Load Balancer with Lambda as a target + +This pattern registers the lambda function as the target for the Application Load Balancer. + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git CLI](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) installed +* [NodeJS](https://nodejs.org/en/download/) (LTS version) installed +* [Serverless Framework CLI](https://www.serverless.com/framework/docs/getting-started) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + + ``` sh + git clone https://github.com/aws-samples/serverless-patterns + ``` + +1. Change directory to the pattern directory: + + ``` sh + cd serverless-patterns/alb-lambda-serverless + ``` + +1. From the command line, use npm/yarn to install the development dependencies: + + ``` sh + npm install + ``` + -or- + + ``` sh + yarn install + ``` + +1. From the command line, use Serverless Framework to deploy the AWS resources for the pattern as specified in the serverless.yml file: + + ``` sh + serverless deploy --verbose + ``` + + The above command will deploy resources to `ap-south-1` region by default. You can override the target region with `--region ` CLI option, e.g. + + ``` sh + serverless deploy --verbose --region us-west-2 + ``` + +1. Note the `LoadBalancerDNSName` output from the Serverless Framework deployment process. You will use this value for testing. + +## How it works + +This pattern registers the lambda function as the target for the Application Load Balancer. When the load balancer endpoint is hit in the browser, if the specified URL (`/hello`) path matches, it triggers the lambda function & the file (hello) is downloaded in your local system with the output from lambda function. + + +## Testing + +### Hit the Load Balancer DNS endpoint. + +To test the endpoint, paste the output URL obtained from serverless framework in the browser of your choice. + +### Expected result + +As no specific URL path is provided, it returns the default output i.e. 404-Page Not Found. +```html +Page Not Found +``` + +Now edit the URL by appending the `/hello` path in it. This will download a file named `hello` in your local system. The file will have the following content. + +```json +

Lambda function integrated with ALB called

+``` + +### CloudWatch logs + +Open AWS CloudWatch Console and navigate to [/aws/lambda/alb-lambda-serverless-prod-functionOne](https://ap-south-1.console.aws.amazon.com/cloudwatch/home?region=ap-south-1#logsV2:log-groups/log-group/$252Faws$252Flambda$252Falb-lambda-serverless-prod-functionOne/) log group. +You should be able to see a new Event Stream with the Received Event information, and Event Message, logged into the stream. + + + + +## Cleanup + +1. Delete the stack + + ```sh + serverless remove --verbose + ``` + +1. Confirm the stack has been deleted + + ```sh + aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'alb-lambda-serverless-prod')].StackStatus" + ``` + + Expected output + + ```json + [ + "DELETE_COMPLETE" + ] + ``` + + NOTE: You might need to add `--region ` option to AWS CLI command if you AWS CLI default region does not match the one, that you used for the Serverless Framework deployment. + +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 \ No newline at end of file diff --git a/alb-lambda-serverless/example-pattern.json b/alb-lambda-serverless/example-pattern.json new file mode 100644 index 0000000000..0c22aa57b8 --- /dev/null +++ b/alb-lambda-serverless/example-pattern.json @@ -0,0 +1,66 @@ +{ + "title": "Application Load Balancer with Lambda as a target", + "description": "Create an Application Load Balancer with Lambda as target using Serverless Framework", + "language": "Node.js", + "architectureURL": "", + "videoId": "", + "level": "100", + "framework": "Serverless Framework", + "services": { + "from": "alb", + "to": "lambda" + }, + "introBox": { + "headline": "How it works", + "text": [ + "This pattern registers the lambda function as the target for the Application Load Balancer using the serverless framework.", + "", + "When the load balancer endpoint is hit in the browser, if the specified URL (`/hello`) path matches, it triggers the lambda function & the file (hello) is downloaded in your local system with the output from lambda function." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/alb-lambda-serverless", + "templateURL": "serverless-patterns/alb-lambda-serverless", + "projectFolder": "alb-lambda-serverless", + "templateFile": "serverless.yml" + } + }, + "resources": { + "headline": "Additional resources", + "bullets": [ + { + "text": "Using AWS Lambda with an Application Load Balancer", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/services-alb.html" + }, + { + "text": "Lambda functions as targets - Elastic Load Balancing", + "link": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html" + } + ] + }, + "deploy": { + "text": [ + "serverless deploy --verbose" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "serverless remove --verbose." + ] + }, + "authors": [ + { + "headline": "Presented by Anjali Modi, Software Engineer, Distinction-Dev", + "name": "Anjali Modi", + "bio": "Anjali Modi is a Software Engineer working for developing Serverless solutions at Distinction-Dev, India", + "linkedin":"https://www.linkedin.com/in/anjali-modi-068045119/", + "imageURL": "https://1.gravatar.com/avatar/51f84b006e95184c3943b4d5e17b1ac3" + } + ] +} \ No newline at end of file diff --git a/alb-lambda-serverless/handlers/alb-lambda-handler.js b/alb-lambda-serverless/handlers/alb-lambda-handler.js new file mode 100644 index 0000000000..d6b49d7fc4 --- /dev/null +++ b/alb-lambda-serverless/handlers/alb-lambda-handler.js @@ -0,0 +1,11 @@ +exports.handler=async(event, context) =>{ + const response = { + statusCode: 200, + headers:{ + contentType:'text/html' + }, + body:'

Lambda function integrated with ALB called

' + } + console.log('response obj---', response) + return response +} \ No newline at end of file diff --git a/alb-lambda-serverless/package.json b/alb-lambda-serverless/package.json new file mode 100644 index 0000000000..d22887d4ac --- /dev/null +++ b/alb-lambda-serverless/package.json @@ -0,0 +1,9 @@ +{ + "name": "alb-lambda-serverless", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "dependencies": { + "serverless": "^3.33.0" + } +} diff --git a/alb-lambda-serverless/serverless.yml b/alb-lambda-serverless/serverless.yml new file mode 100644 index 0000000000..35ea5f9696 --- /dev/null +++ b/alb-lambda-serverless/serverless.yml @@ -0,0 +1,138 @@ +service: alb-lambda-serverless +frameworkVersion: '^3.28.1' +useDotenv: true + +provider: + name: aws + runtime: nodejs14.x + memorySize: 256 + timeout: 30 + # override the default stage (dev) to be `prod`, or you can use the `--stage` CLI option + stage: ${opt:stage, "prod"} + # specify the region using --region flag while deploying the stack, else the default region ap-south-1 will be chosen + region: ${opt:region, "ap-south-1"} + iamRoleStatements: + - Effect: Allow + Action: s3:* + Resource: '*' + - Effect: Allow + Action: + - lambda:InvokeFunction + Resource: '*' + +functions: + # lambda function with the alb event configured in it. + functionOne: + handler: handlers/alb-lambda-handler.handler + events: + - alb: + listenerArn: { Ref: HTTPListener } + priority: 1 + conditions: + path: /hello + method: GET + +resources: + Resources: + # creates a VPC with 2 Public subnets within the VPC + VPC: + Type: AWS::EC2::VPC + Properties: + CidrBlock: 12.2.1.0/24 + EnableDnsHostnames: true + EnableDnsSupport: true + SubnetOne: + Type: AWS::EC2::Subnet + Properties: + VpcId: { Ref: VPC } + CidrBlock: 12.2.1.0/25 + MapPublicIpOnLaunch: true + AvailabilityZone: !Select + - 0 + - Fn::GetAZs: {Ref: 'AWS::Region'} + SubnetTwo: + Type: AWS::EC2::Subnet + Properties: + VpcId: { Ref: VPC } + MapPublicIpOnLaunch: true + CidrBlock: 12.2.1.128/25 + AvailabilityZone: !Select + - 1 + - Fn::GetAZs: { Ref: 'AWS::Region'} + # security group that allows all the http/s traffic to the load balancer + SecurityGroupALB: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: SG created via serverless framework + VpcId: { Ref: VPC } + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 80 + ToPort: 80 + CidrIp: 0.0.0.0/0 + - IpProtocol: tcp + FromPort: 443 + ToPort: 443 + CidrIp: 0.0.0.0/0 + # internet gateway that connects the vpc to the internet + InternetGateway: + Type: AWS::EC2::InternetGateway + AttachGateway: + Type: AWS::EC2::VPCGatewayAttachment + Properties: + VpcId: { Ref: VPC } + InternetGatewayId: { Ref: InternetGateway } + PublicSubnetRouteTable: + Type: AWS::EC2::RouteTable + Properties: + VpcId: { Ref: VPC } + # creates public routes for the subnets + PublicSubnetRoute: + Type: AWS::EC2::Route + DependsOn: AttachGateway + Properties: + RouteTableId: { Ref: PublicSubnetRouteTable } + DestinationCidrBlock: 0.0.0.0/0 + GatewayId: { Ref: InternetGateway } + PublicSubnetRouteTableAssociationOne: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + RouteTableId: { Ref: PublicSubnetRouteTable } + SubnetId: { Ref: SubnetOne } + PublicSubnetRouteTableAssociationTwo: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + RouteTableId: { Ref: PublicSubnetRouteTable } + SubnetId: { Ref: SubnetTwo } + # creates a internet-facing load balancer + LoadBalancer: + Type: AWS::ElasticLoadBalancingV2::LoadBalancer + Properties: + Name: alb-lambda + Type: 'application' + IpAddressType: 'ipv4' + Scheme: 'internet-facing' + Subnets: + - { Ref: SubnetOne } + - { Ref: SubnetTwo } + SecurityGroups: + - { Ref: SecurityGroupALB } + # creates a http listener for the alb + HTTPListener: + Type: AWS::ElasticLoadBalancingV2::Listener + Properties: + DefaultActions: + - Type: fixed-response + FixedResponseConfig: + ContentType: application/json + MessageBody: 'Page Not Found' + StatusCode: '404' + LoadBalancerArn: + Ref: LoadBalancer + Port: 80 + Protocol: HTTP + Outputs: + # DNS name for the load balancer that can be used to trigger the alb + LoadBalancerDNSName: + Value: !GetAtt LoadBalancer.DNSName + diff --git a/alb-lambda-terraform/.gitignore b/alb-lambda-terraform/.gitignore new file mode 100644 index 0000000000..b6af967a95 --- /dev/null +++ b/alb-lambda-terraform/.gitignore @@ -0,0 +1,5 @@ +*.js +!jest.config.js +*.d.ts +node_modules +!/lib \ No newline at end of file diff --git a/alb-lambda-terraform/.npmignore b/alb-lambda-terraform/.npmignore new file mode 100644 index 0000000000..959a611a85 --- /dev/null +++ b/alb-lambda-terraform/.npmignore @@ -0,0 +1,3 @@ +*.ts +!*.d.ts + diff --git a/alb-lambda-terraform/README.md b/alb-lambda-terraform/README.md new file mode 100644 index 0000000000..7ce8c10db4 --- /dev/null +++ b/alb-lambda-terraform/README.md @@ -0,0 +1,54 @@ +# Application Load balancer with AWS Lambda as target with Terraform + +This pattern demonstrates how to create an Application Load Balancer with AWS Lambda as target. Implemented in Terraform. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/alb-lambda-terraform + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [Terraform Installed](https://developer.hashicorp.com/terraform/downloads) + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +2. Change directory to the pattern directory: + ``` + cd alb-lambda-terraform + ``` +3. From the command line, run: + ``` + terraform init + ``` +4. From the command line, run: + ``` + terraform plan + ``` +5. From the command line, run: + ``` + terraform apply --auto-approve + ``` + +## Testing + +1. In the stack output, you can see `alb_url`. When you access the url, you should see the response "Hello World" from Lambda. + +** Please note: Application Load Balancer's default settings for health check are 5 consecutive health check successes with 35 seconds interval. So, it will take couple of minutes for the target to be healthy. + +## Cleanup + +1. To delete the stack, run: + ```bash + terraform destroy --auto-approve + ``` +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/alb-lambda-terraform/example-pattern.json b/alb-lambda-terraform/example-pattern.json new file mode 100644 index 0000000000..4a6a55fe34 --- /dev/null +++ b/alb-lambda-terraform/example-pattern.json @@ -0,0 +1,58 @@ +{ + "title": "Application Load Balancer with Lambda as target", + "description": "Create an Application Load Balancer with Lambda as target using Terraform", + "language": "TypeScript", + "level": "200", + "framework": "Terraform", + "introBox": { + "headline": "How it works", + "text": [ + "This sample project demonstrates how to create an Application Load Balancer with AWS Lambda as target.", + "Implemented in Terraform." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/alb-lambda-terraform", + "templateURL": "serverless-patterns/alb-lambda-terraform", + "projectFolder": "alb-lambda-terraform", + "templateFile": "alb-lambda-terraform/main.tf" + } + }, + "resources": { + "bullets": [ + { + "text": "Application Load Balancer", + "link": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html" + }, + { + "text": "ALB - Lambda target", + "link": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html" + } + ] + }, + "deploy": { + "text": [ + "terraform init", + "terraform apply" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "terraform destroy", + "terraform show" + ] + }, + "authors": [ + { + "name": "Sumit Bhati", + "image": "https://avatars.githubusercontent.com/u/139027745", + "bio": "I am a Customer Solutions Manager at AWS" + } + ] +} \ No newline at end of file diff --git a/alb-lambda-terraform/lambda.zip b/alb-lambda-terraform/lambda.zip new file mode 100644 index 0000000000..d97ac4fb0a Binary files /dev/null and b/alb-lambda-terraform/lambda.zip differ diff --git a/alb-lambda-terraform/main.tf b/alb-lambda-terraform/main.tf new file mode 100644 index 0000000000..2f56fde79a --- /dev/null +++ b/alb-lambda-terraform/main.tf @@ -0,0 +1,197 @@ +# Required providers configuration +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~>4.52.0" + } + } + + required_version = "~> 1.0" +} + +# AWS provider configuration +provider "aws" { + profile = "default" + region = "us-east-1" +} + +# Create AWS VPC +resource "aws_vpc" "vpc" { + cidr_block = var.vpc_cidr + +} + +# Create public subnet 1 +resource "aws_subnet" "public_subnet1" { + cidr_block = "10.0.1.0/24" + vpc_id = aws_vpc.vpc.id + availability_zone = "${var.region}a" + tags = { + Name = "Subnet for ${var.region}a" + } +} + +# Create public subnet 2 +resource "aws_subnet" "public_subnet2" { + cidr_block = "10.0.2.0/24" + vpc_id = aws_vpc.vpc.id + availability_zone = "${var.region}b" + tags = { + Name = "Subnet for ${var.region}b" + } +} + +# Create a route table +resource "aws_route_table" "public_rt" { + vpc_id = aws_vpc.vpc.id + + route { + cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.gw.id + } + + tags = { + Name = "public_rt" + } +} + +# Associate the route table with public subnet 1 +resource "aws_route_table_association" "public_rt_table_a" { + subnet_id = aws_subnet.public_subnet1.id + route_table_id = aws_route_table.public_rt.id +} + +# Associate the route table with public subnet 2 +resource "aws_route_table_association" "public_rt_table_b" { + subnet_id = aws_subnet.public_subnet2.id + route_table_id = aws_route_table.public_rt.id +} + +# Create an Internet Gateway +resource "aws_internet_gateway" "gw" { + vpc_id = aws_vpc.vpc.id +} + +# Create IAM Role for Lambda Function +resource "aws_iam_role" "lambda_role" { +name = "Lambda_Function_Role" +assume_role_policy = < Exe - netcoreapp3.1 + net6.0 Major diff --git a/apigw-http-api-eventbridge-java/example-pattern.json b/apigw-http-api-eventbridge-java/example-pattern.json index e336c1c03c..cf546e4481 100644 --- a/apigw-http-api-eventbridge-java/example-pattern.json +++ b/apigw-http-api-eventbridge-java/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-http-api-eventbridge-lambda-sls/example-pattern.json b/apigw-http-api-eventbridge-lambda-sls/example-pattern.json index ffcbbea17c..f20bb2ab92 100644 --- a/apigw-http-api-eventbridge-lambda-sls/example-pattern.json +++ b/apigw-http-api-eventbridge-lambda-sls/example-pattern.json @@ -47,7 +47,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-http-api-eventbridge-python/example-pattern.json b/apigw-http-api-eventbridge-python/example-pattern.json index daaca14984..28f15f00b7 100644 --- a/apigw-http-api-eventbridge-python/example-pattern.json +++ b/apigw-http-api-eventbridge-python/example-pattern.json @@ -59,7 +59,7 @@ ] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["cdk destroy"] diff --git a/apigw-http-api-fifo-sqs-lambda-sns-sam/README.md b/apigw-http-api-fifo-sqs-lambda-sns-sam/README.md new file mode 100644 index 0000000000..85215894f3 --- /dev/null +++ b/apigw-http-api-fifo-sqs-lambda-sns-sam/README.md @@ -0,0 +1,88 @@ +# Amazon API Gateway to Amazon Fifo SQS Queue to AWS Lambda to Amazon SNS + +This pattern explains how to deploy SAM application with Amazon API Gateway HTTP API, Amazon SQS Fifo Queue, AWS Lambda function and Amazon SNS topic. When a request is sent to API Gateway, the request will be forwarded to a FIFO SQS Queue, which is a backend to the API. A Lambda function is configured to poll messages from the queue. If messages fail to be proessed, they will be sent to a Dead-Letter queue. Upon successful processing by Lambda, a notification is sent to an SNS topipic, which will have an email subscription. + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd apigw-http-api-fifo-sqs-lambda-sns-sam + ``` +1. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: + ``` + sam deploy --guided + ``` +1. During the prompts: + * Enter a stack name + * Enter the desired AWS Region + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run `sam deploy --guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. + +## How it works + +architecture diagram + +- This pattern is designed to deploy an Amazon API Gateway HTTP API with a single route at / +- When the API Gateway receives a request from a client, it immediately sends the message to a FIFO (First-In-First-Out) queue. +- The FIFO queue ensures that messages are processed in the order they were received, maintaining strict ordering +- As the messages arrive in the FIFO queue, it automatically invokes a Lambda function responsible for message processing +- The Lambda function handles each message one by one in FIFO order. +- Depending on the use case, the Lambda may perform various actions, such as data processing, validation, or invoking other AWS services. +- Once the Lambda function has processed a message, it publishes the relevant information or processed data to an SNS (Simple Notification Service) standard topic. +- You can then Subscribe email addresses to the topic to be notified through the SNS topic, providing users with timely updates or notifications via email. + +## Testing + +Once the application is deployed: +- Retrieve the HttpApiEndpoint value from CloudFormation Outputs +- Retrieve the SNS Topic ARN and navigate to the topic and add an email subscription. i.e. https://docs.aws.amazon.com/sns/latest/dg/sns-create-subscribe-endpoint-to-topic.html +- Invoke the endpoint from Postman using some json payload and verify that you received the notification on the subscribed email. + +Request: +- Request URL: https://{RestApiEndpoint}.execute-api.{Region}.amazonaws.com/{gatewayStage}/ + - Region - Name of AWS Region, Example: us-east-1 + - gatewayStage - Name of the API Gateway Stage. A stage is a named reference to a deployment, which is a snapshot of the API. "prod" is the stage name used in the SAM template. +- Request Method: POST +- Request Header: "Content-Type: application/json" +- Request Body: {"messagegroup":"2", "message":"event message for testing"} (This could be any JSON payload) +- Ensure that the message json payload contains "messagegroup" field, this is required for fifo queues. + +Example URL: https://abc1234def.execute-api.us-east-1.amazonaws.com/prod/ + +Body/Payload: + +{ + "message":"event message for testing", + "messagegroup": "2" +} + +## Cleanup + +1. Delete the stack + ```bash + aws cloudformation delete-stack --stack-name STACK_NAME + ``` +1. Confirm the stack has been deleted + ```bash + aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'STACK_NAME')].StackStatus" + ``` +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 \ No newline at end of file diff --git a/apigw-http-api-fifo-sqs-lambda-sns-sam/api.yaml b/apigw-http-api-fifo-sqs-lambda-sns-sam/api.yaml new file mode 100644 index 0000000000..0b5efe2ee2 --- /dev/null +++ b/apigw-http-api-fifo-sqs-lambda-sns-sam/api.yaml @@ -0,0 +1,30 @@ +openapi: "3.0.1" +info: + title: "HttpApi" +paths: + /: + post: + responses: + default: + description: "Response for POST /" + x-amazon-apigateway-integration: + type: "aws_proxy" + integrationSubtype: "SQS-SendMessage" + credentials: + Fn::GetAtt: [MyAPiRole, Arn] + connectionType: "INTERNET" + payloadFormatVersion: "1.0" + requestParameters: + MessageBody: "$request.body" + MessageGroupId: "$request.body.messagegroup" + QueueUrl: + Ref: MySQSQueue + +x-amazon-apigateway-cors: + allowMethods: + - "*" + maxAge: 0 + allowCredentials: false + allowOrigins: + - "*" +x-amazon-apigateway-importexport-version: "1.0" \ No newline at end of file diff --git a/apigw-http-api-fifo-sqs-lambda-sns-sam/docs/apgw-sqs-lambda-sns.drawio b/apigw-http-api-fifo-sqs-lambda-sns-sam/docs/apgw-sqs-lambda-sns.drawio new file mode 100644 index 0000000000..1319ee4e7e --- /dev/null +++ b/apigw-http-api-fifo-sqs-lambda-sns-sam/docs/apgw-sqs-lambda-sns.drawio @@ -0,0 +1 @@ +7VrRcto4FP0aHpexLBvDYzCh7Wy2kw6d6WxfGAULo6lsubIIJF+/ki2DLTkNtC5koclMYl3JV9I55xr5JD0YJtt3HGWrf1iEac91om0PTnquC1zXkb9U5KmMBCO/DMScRHrQPjAjz1gH9X3xmkQ4bwwUjFFBsmZwwdIUL0Qjhjhnm+awJaPNWTMUYyswWyBqR7+QSKzK6NB39vH3mMSrambg6J4EVYN1IF+hiG1qIXjbgyFnTJRXyTbEVIFX4VLeN32hd7cwjlNxyA3fpv++D+PP939/5Z+/3n5cTT+B73+5ZZZHRNd6w3qx4qlCgK0FJSkOdwA7PTiOUL7CkW48Yi6IhOwOPWB6z3IiCEtl3wMTgiW1ATeUxKpDsExGkW4t5AYwl4GVSKhsA3kpwcrU/Mk2Vrrqo00O+wlKJdyJHD6XdOdMrXC8JJSGjDJerBZO3AmcQBmX90VEDq36Upaq4Xq/ckF4+yKQYEeP1DVmCRb8SQ7RN3iaUK1ot9LCZq+PASxDq5o0qhjSiox3ifekyQvN2xEcQovDkKqdW0zKLYsmzBYFJlMJiaICZo5z8oweilSK84yRVBQ78cc9f6JyrYWkvqhflXrJUjHTc4MuUB/0/QbuENq4V1TUca/qr3PcBz9ZOwqZmmLl91TNailW9k19L/BGtb4J4TJRWV4p42rfZgWEAYBgqmpIcPYN13qWxdcJqrfgXkvBNbSgpkR5VsKxJFu1jrZy9/pScmzNF/jDQq1HKbC8ao5CGZnHSOANeuqmvF3HqG/PrZRXU1owtJVWxTpXWnACpUHPG7rHKW0cAugPrkZp+fe8qw8QS2Hn1dfoBPoKhreOd5y+Jo4fguBq9EVR8hChbiQ2eHsPsepE/+cpdt6nWNrRU2z41p5iAPwR2FsQ2O/6mPQ8+8B/WoHZb1qWwHAa3SjbQRFBUZ6ThfFiW2igshLcHVI4snyIV3F65cWninFMkSCPzfRt4OgZ7tWrXe08PDLqfGikKEWh76obEEYi+FoigXiMhZWo4Gq37V+gz7tG+jwLdac6CRxLoP96qt9NoX+NFA66ozA4P4UH2CaXR6FnmIZw4PwcgWYiD4xOS98BXsTl0QdAZxUIhsF5PwWHFoE3CXqW5zjXubn/IH++026aSev/2j4Gg1GzAFvcY+Cd0j4GtuuyI2L2aXZZ8MOR14Dfb4F/dEr0XduNuB7028R/WvTtV/U9+h8vDP3gzaHv2uh/kaA7d6UDeVHom07o+dE/wEVoojSWSwkdhZYbqlbf9Y2A2Q6aAWC3VI5mwGwHzQAw0wNjfmAusBawWo30jjG/U1tgIZFW1+6FP/DXVLpZEYFnGVooVDccZQc4Ya3GV8zZOiumbDO8it65vJwvKFtHc0RFm9m3sxXr9qBe9I8dPYqXRUa5E5LGd0VrAn/kXDb8xJeO2792nmpWFRhCu6pAS1l51QvEEXUlm/t/kynPwft/NoK3/wE= \ No newline at end of file diff --git a/apigw-http-api-fifo-sqs-lambda-sns-sam/docs/apgw-sqs-lambda-sns.png b/apigw-http-api-fifo-sqs-lambda-sns-sam/docs/apgw-sqs-lambda-sns.png new file mode 100644 index 0000000000..a64eaa8f99 Binary files /dev/null and b/apigw-http-api-fifo-sqs-lambda-sns-sam/docs/apgw-sqs-lambda-sns.png differ diff --git a/apigw-http-api-fifo-sqs-lambda-sns-sam/docs/apgw-sqs-lambda-sns_services.drawio b/apigw-http-api-fifo-sqs-lambda-sns-sam/docs/apgw-sqs-lambda-sns_services.drawio new file mode 100644 index 0000000000..66872fe42d --- /dev/null +++ b/apigw-http-api-fifo-sqs-lambda-sns-sam/docs/apgw-sqs-lambda-sns_services.drawio @@ -0,0 +1 @@ +7VltU+IwEP41fDym6QuFj7SIOuc5Ojhzc35hQhvajGlT0yDor7+kTUvTosjIqaOnM9LdJJvNs0+6u9Kz/GRzymAW/6IhIj3TCDc9a9IzTWCahviQmsdS446cUhExHKpJW8UMPyGlVOuiFQ5Rrk3klBKOM10Z0DRFAdd0kDG61qctKdF3zWCEOopZAElX+xuHPC61Q8fY6s8QjuJqZ2CokQRWk5Uij2FI1w2VddKzfEYpL5+SjY+IBK/CpVw3fWa0doyhlL9mwd30z5kf3Vz9vGU3tyeX8fQa3P8YlFYeIFmpAytn+WOFAF1xglPk1wAbPctb0pT7lFBWzLHE71Tu6kUMhhhpY1PHdu1RY2yCmTCEaSrGU8okTN4SE9JY47vAAuIgXs4ZvUONkWXxI0ZCmMcoVO48IMaxCNoFXCByRXOszC8o5zRpTBgTHMkBTjOhhUoKhFdIbODFPCFCBuqEio7ArGSFitwS5lkJxxJvpB+eiG8mB5NNJK9CH65zu89QTlcsQOeB9McTYvmkz4IZnkeQozUUt8RTIREeo82zsQY1g8TVQzRBnD2KKWqBWbFQ3TrTNvvq3q23LHaHpSpuELjSQXVvotr2llriQbHrAKa578A0y7aH5mFM83xgOYNvw7T8Pj8Ow+wuwz6WX6N34Jc7PDHsw/g1MRwfuN+GXwQmixAeh2KDz/cSq6qS/2+xj32LpUd6iw0/21sMWPsJhtJwLCtbGQgC8xwHeiRKDlTVqlkjhcJOqbsXpwYOVcnbxKHSMUQgxw+6+V3gqB2uKBYbb8uVUSsMw5aJkhRqVbPGbRmy9hnikEWIdwwVsaqP/Ybw2d8xfHYHdaN6UR8aQGe/qX8dQuc7hnBwvBC6Hx/CYSeE4wQ+iTxiGuOrc/H3VDVb7cCKTML1UHZSXjszJjgM5XKZq/ATXNQJL5PHKw7seD1nIm2tuEi1ZYpsZUhwhGQGBiMNeMsyOskM2Ds4VP2H4ujZzOyWS3UgZtezrwW/NbL3wj96V/TBC+hffjH03U+HvtlF/7cA3bgoW6QvhX67Vft49F9RR+soecIV35Bomb6U+qbTUrRlV1eAriRt6Iq27OoK0DYPWvuDtoMNRUfSzBut/Y2GgwVFdvat7f40pSnSWbqOMUezDAYS1bXo0V7RC+5s/SJGV1mx5a6Wrxidi8d5QOgqnEPCd7W7dWPdbJCV0y/3tAQtC4viJDiNLgppYr3Uu2sd9XMl3xvbU+1WAbDjVoEd18oG7sH3Sojb7yLKSmz7jY518hc= \ No newline at end of file diff --git a/apigw-http-api-fifo-sqs-lambda-sns-sam/docs/apgw-sqs-lambda-sns_services.png b/apigw-http-api-fifo-sqs-lambda-sns-sam/docs/apgw-sqs-lambda-sns_services.png new file mode 100644 index 0000000000..da50add9e4 Binary files /dev/null and b/apigw-http-api-fifo-sqs-lambda-sns-sam/docs/apgw-sqs-lambda-sns_services.png differ diff --git a/apigw-http-api-fifo-sqs-lambda-sns-sam/example-pattern.json b/apigw-http-api-fifo-sqs-lambda-sns-sam/example-pattern.json new file mode 100644 index 0000000000..6907acfc01 --- /dev/null +++ b/apigw-http-api-fifo-sqs-lambda-sns-sam/example-pattern.json @@ -0,0 +1,69 @@ +{ + "title": "Amazon API Gateway to Amazon Fifo SQS Queue to AWS Lambda to Amazon SNS", + "description": "Process HTTP Post requests using Lambda Fubction while maintaining the order of the messages", + "language": "Python", + "level": "200", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern is designed to deploy an Amazon API Gateway HTTP API with a single route at \"/\"", + "When the API Gateway receives a request from a client, it immediately sends the message to a FIFO (First-In-First-Out) queue.", + "The FIFO queue ensures that messages are processed in the order they were received, maintaining strict ordering", + "As the messages arrive in the FIFO queue, it automatically invokes a Lambda function responsible for message processing", + "The Lambda function handles each message one by one in FIFO order.", + "Depending on the use case, the Lambda may perform various actions, such as data processing, validation, or invoking other AWS services.", + + "Once the Lambda function has processed a message, it publishes the relevant information or processed data to an SNS (Simple Notification Service) standard topic.", + "You can then Subscribe email addresses to the topic to be notified through the SNS topic, providing users with timely updates or notifications via email." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/apigw-http-api-fifo-sqs-lambda-sns-sam", + "templateURL": "serverless-patterns/apigw-http-api-fifo-sqs-lambda-sns-sam", + "projectFolder": "apigw-http-api-fifo-sqs-lambda-sns-sam", + "templateFile": "apigw-http-api-fifo-sqs-lambda-sns-sam/template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Fifo SQS Queues", + "link": "https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/FIFO-queues.html" + }, + { + "text": "Lambda sqs fifo trigger", + "link": "https://aws.amazon.com/blogs/compute/new-for-aws-lambda-sqs-fifo-as-an-event-source/" + } + { + "text": "Subscribe to an SNS topic", + "link": "https://docs.aws.amazon.com/sns/latest/dg/sns-create-subscribe-endpoint-to-topic.html" + } + + ] + }, + "deploy": { + "text": [ + "sam deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: cdk delete." + ] + }, + "authors": [ + { + "name": "Xivutiso Makhubela", + "image": "link-to-your-photo.jpg", + "bio": "I am a Startups Solutions Architect at Amazon Web Services", + "linkedin": "xivutiso" + } + ] +} diff --git a/apigw-http-api-fifo-sqs-lambda-sns-sam/src/app.py b/apigw-http-api-fifo-sqs-lambda-sns-sam/src/app.py new file mode 100644 index 0000000000..d940e92828 --- /dev/null +++ b/apigw-http-api-fifo-sqs-lambda-sns-sam/src/app.py @@ -0,0 +1,27 @@ +import os +import json +import boto3 + +def lambda_handler(event, context): + # Initialize SNS client + sns_client = boto3.client('sns') + + # Get the SNS topic ARN from the environment variable + sns_topic_arn = os.environ['TOPIC_ARN'] + + # Log the received message from SQS + for record in event['Records']: + body = json.loads(record['body']) + print(f"Received message: {json.dumps(body)}") + + # Send notification to SNS topic + sns_client.publish( + TopicArn=sns_topic_arn, + Message=json.dumps(body), + Subject='SQS Event Notification' + ) + + return { + 'statusCode': 200, + 'body': json.dumps('Lambda function executed successfully!') + } diff --git a/apigw-http-api-fifo-sqs-lambda-sns-sam/template.yaml b/apigw-http-api-fifo-sqs-lambda-sns-sam/template.yaml new file mode 100644 index 0000000000..76c3707050 --- /dev/null +++ b/apigw-http-api-fifo-sqs-lambda-sns-sam/template.yaml @@ -0,0 +1,132 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: ApigwSnsSqsLambda SAM Template + +# Define the parameters section +Parameters: + # Lambda Function Name parameter + FunctionName: + Type: String + Default: ServerlessLand_Function + Description: Name of the function that will process messages from sqs queue + + # API Stage Name parameter + StageName: + Type: String + Default: prod + Description: Name of API stage + + # Main SQS Name parameter + SQSQueueName: + Type: String + Default: ServerlessLandSQS.fifo + Description: Name of fifo sqs that will receive messages from Http API (add .fifo suffix) + + # DLQ SQS Name parameter + SQSDLQName: + Type: String + Default: ServerlessLandSQSDlq.fifo + Description: Name of the dead letter fifo queue that will be attached to the main queue (add .fifo suffix) + + # SNS Topic Name parameter + SNSTopicName: + Type: String + Default: ServerlessLandSNSTopic + Description: Name of sns standard topic that will receive messages from lambda function code (add .fifo suffix) + +# Define the resource section +Resources: + # Define the dead-letter queue for the main Queue (i.e. !Ref SQSQueueName) + MyDeadLetterQueueForSQS: + Type: AWS::SQS::Queue + Properties: + QueueName: !Ref SQSDLQName + FifoQueue: True # Set to true to create a FIFO queue, ensuring strict message ordering + ContentBasedDeduplication: True + VisibilityTimeout: 300 + + # Define the Amazon SNS topic resource + MySNSTopic: + Type: AWS::SNS::Topic + Properties: + TopicName: !Ref SNSTopicName + + # Define the AWS Lambda function resource. + MyLambdaFunction: + Type: AWS::Serverless::Function + Properties: + FunctionName: !Ref FunctionName + CodeUri: src/ + Handler: app.lambda_handler + Runtime: python3.9 + Environment: + Variables: + TOPIC_ARN: !Ref MySNSTopic + Policies: + - SQSPollerPolicy: + QueueName: + !GetAtt MySQSQueue.QueueName + - SNSPublishMessagePolicy: + TopicName: + !GetAtt MySNSTopic.TopicName + + Events: # Adding SQS event to the Lambda function + MySQSTrigger: + Type: SQS + Properties: + Queue: !GetAtt MySQSQueue.Arn + + + # Define the main Amazon SQS queue resource + MySQSQueue: + Type: AWS::SQS::Queue + Properties: + QueueName: !Ref SQSQueueName + FifoQueue: True + ContentBasedDeduplication: True + VisibilityTimeout: 300 + RedrivePolicy: # Define the dead-letter queue (DLQ) configuration + deadLetterTargetArn: !GetAtt MyDeadLetterQueueForSQS.Arn + maxReceiveCount: 5 + + # Define the IAM Role resource for the API Gateway + MyAPiRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: # Define the permissions policy for the IAM Role to allow API Gateway to assume the role + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: apigateway.amazonaws.com + Action: sts:AssumeRole + Policies: # Define the IAM Policy attached to the role to allow sending messages to the SQS queue + - PolicyName: SendMessagePolicy + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: sqs:SendMessage + Resource: !GetAtt MySQSQueue.Arn # Specify the ARN of the SQS queue + + # Define the AWS HTTP API resource + MyAPI: + Type: AWS::Serverless::HttpApi + Properties: + StageName: !Ref StageName # Specify the stage name for the API + DefinitionBody: # Define the API configuration using AWS::Include transformation + 'Fn::Transform': + Name: "AWS::Include" + Parameters: + Location: 'api.yaml' # Specify the location of the API configuration in 'api.yaml' + + +# Define the outputs section +Outputs: + # Output the API Gateway endpoint URL for the MyAPI in the Prod stage + MyAPI: + Description: "API Gateway endpoint URL for Prod stage for myfunction" + Value: !Sub "https://${MyAPI}.execute-api.${AWS::Region}.${AWS::URLSuffix}/${StageName}/" + MySNSTopic: + Description: "SNS topic arn. Use this topic to subscribe an email endpoint" + Value: !Ref MySNSTopic diff --git a/apigw-http-api-lambda-dotnet-sam/HttpApi/.gitignore b/apigw-http-api-lambda-dotnet-sam/HttpApi/.gitignore new file mode 100644 index 0000000000..7f62463b02 --- /dev/null +++ b/apigw-http-api-lambda-dotnet-sam/HttpApi/.gitignore @@ -0,0 +1,403 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# AWS SAM +.aws-sam + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +obj +bin +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml \ No newline at end of file diff --git a/apigw-http-api-lambda-dotnet-sam/HttpApi/Controllers/ValuesController.cs b/apigw-http-api-lambda-dotnet-sam/HttpApi/Controllers/ValuesController.cs new file mode 100644 index 0000000000..a2845c42ec --- /dev/null +++ b/apigw-http-api-lambda-dotnet-sam/HttpApi/Controllers/ValuesController.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Mvc; +namespace HttpApi.Controllers; + +[Route("/api")] +public class ValuesController : ControllerBase +{ + [HttpGet] + public IEnumerable Get() + { + return new string[] { "value1", "value2" }; + } + +} \ No newline at end of file diff --git a/apigw-http-api-lambda-dotnet-sam/HttpApi/HttpApi.csproj b/apigw-http-api-lambda-dotnet-sam/HttpApi/HttpApi.csproj new file mode 100644 index 0000000000..4d489a0ac8 --- /dev/null +++ b/apigw-http-api-lambda-dotnet-sam/HttpApi/HttpApi.csproj @@ -0,0 +1,17 @@ + + + Exe + net6.0 + enable + enable + true + Lambda + + true + + true + + + + + \ No newline at end of file diff --git a/apigw-http-api-lambda-dotnet-sam/HttpApi/LambdaEntryPoint.cs b/apigw-http-api-lambda-dotnet-sam/HttpApi/LambdaEntryPoint.cs new file mode 100644 index 0000000000..d0a8da2841 --- /dev/null +++ b/apigw-http-api-lambda-dotnet-sam/HttpApi/LambdaEntryPoint.cs @@ -0,0 +1,45 @@ +namespace HttpApi; + +/// +/// This class extends from APIGatewayProxyFunction which contains the method FunctionHandlerAsync which is the +/// actual Lambda function entry point. The Lambda handler field should be set to +/// +/// HttpApi::HttpApi.LambdaEntryPoint::FunctionHandlerAsync +/// +public class LambdaEntryPoint : + + // The base class must be set to match the AWS service invoking the Lambda function. If not Amazon.Lambda.AspNetCoreServer + // will fail to convert the incoming request correctly into a valid ASP.NET Core request. + // + // API Gateway REST API -> Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction + // API Gateway HTTP API payload version 1.0 -> Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction + // API Gateway HTTP API payload version 2.0 -> Amazon.Lambda.AspNetCoreServer.APIGatewayHttpApiV2ProxyFunction + // Application Load Balancer -> Amazon.Lambda.AspNetCoreServer.ApplicationLoadBalancerFunction + // + // Note: When using the AWS::Serverless::Function resource with an event type of "HttpApi" then payload version 2.0 + // will be the default and you must make Amazon.Lambda.AspNetCoreServer.APIGatewayHttpApiV2ProxyFunction the base class. + + Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction +{ + /// + /// The builder has configuration, logging and Amazon API Gateway already configured. The startup class + /// needs to be configured in this method using the UseStartup<>() method. + /// + /// + protected override void Init(IWebHostBuilder builder) + { + builder + .UseStartup(); + } + + /// + /// Use this override to customize the services registered with the IHostBuilder. + /// + /// It is recommended not to call ConfigureWebHostDefaults to configure the IWebHostBuilder inside this method. + /// Instead customize the IWebHostBuilder in the Init(IWebHostBuilder) overload. + /// + /// + protected override void Init(IHostBuilder builder) + { + } +} \ No newline at end of file diff --git a/apigw-http-api-lambda-dotnet-sam/HttpApi/LocalEntryPoint.cs b/apigw-http-api-lambda-dotnet-sam/HttpApi/LocalEntryPoint.cs new file mode 100644 index 0000000000..96bcaf22b9 --- /dev/null +++ b/apigw-http-api-lambda-dotnet-sam/HttpApi/LocalEntryPoint.cs @@ -0,0 +1,19 @@ +namespace HttpApi; + +/// +/// The Main function can be used to run the ASP.NET Core application locally using the Kestrel webserver. +/// +public class LocalEntryPoint +{ + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); +} \ No newline at end of file diff --git a/apigw-http-api-lambda-dotnet-sam/HttpApi/Startup.cs b/apigw-http-api-lambda-dotnet-sam/HttpApi/Startup.cs new file mode 100644 index 0000000000..453f5d5fb8 --- /dev/null +++ b/apigw-http-api-lambda-dotnet-sam/HttpApi/Startup.cs @@ -0,0 +1,41 @@ +namespace HttpApi; + +public class Startup +{ + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + endpoints.MapGet("/", async context => + { + await context.Response.WriteAsync("Welcome to running ASP.NET Core on AWS Lambda"); + }); + }); + } +} \ No newline at end of file diff --git a/apigw-http-api-lambda-dotnet-sam/HttpApi/aws-lambda-tools-defaults.json b/apigw-http-api-lambda-dotnet-sam/HttpApi/aws-lambda-tools-defaults.json new file mode 100644 index 0000000000..95cb961443 --- /dev/null +++ b/apigw-http-api-lambda-dotnet-sam/HttpApi/aws-lambda-tools-defaults.json @@ -0,0 +1,24 @@ +{ + "Information" : [ + "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", + "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", + "dotnet lambda help", + "All the command line options for the Lambda command can be specified in this file." + ], + "profile" : "default", + "region" : "ap-southeast-2", + "configuration" : "Release", + "function-runtime" : "dotnet6", + "function-memory-size" : 256, + "function-timeout" : 30, + "function-handler" : "HttpApi::HttpApi.Function::FunctionHandler", + "framework" : "net6.0", + "function-name" : "HttpApi", + "package-type" : "Zip", + "function-architecture" : "x86_64", + "function-subnets" : "", + "function-security-groups" : "", + "tracing-mode" : "PassThrough", + "environment-variables" : "", + "image-tag" : "" +} \ No newline at end of file diff --git a/apigw-http-api-lambda-dotnet-sam/README.md b/apigw-http-api-lambda-dotnet-sam/README.md new file mode 100644 index 0000000000..3231d28bb2 --- /dev/null +++ b/apigw-http-api-lambda-dotnet-sam/README.md @@ -0,0 +1,88 @@ +# ASP.NET Core Web API Serverless Application with AWS SAM + +This project shows how to use AWS SAM for an ASP.NET Core Web API project using a AWS Lambda function exposed through Amazon API Gateway. + + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed +* [.Net 6.0](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd apigw-http-api-lambda-dotnet-sam + ``` +1. From the command line, use AWS SAM to build and deploy the AWS resources for the pattern as specified in the template.yml file: + ``` + sam build + sam deploy --guided + ``` +1. During the prompts: + * Enter a stack name + * Enter the desired AWS Region + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run `sam deploy -guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. + +## How it works +- + +============================================== + +## Testing +The Cloudformation stack deployment will output your API endpoint URL +```bash +CloudFormation outputs from deployed stack +------------------------------------------------------------------------------------------------------------------------------------------------------------- +Outputs +------------------------------------------------------------------------------------------------------------------------------------------------------------- +Key ApiURL +Description API endpoint URL for Prod environment +Value https://hr029dslcf.execute-api.ap-southeast-2.amazonaws.com/Prod/ +------------------------------------------------------------------------------------------------------------------------------------------------------------- + + +Successfully created/updated stack - dotnet-http-api in ap-southeast-2 +``` + +You can use this endpoint to test the HTTP response + +```bash +export ApiUrl=https://hr029dslcf.execute-api.ap-southeast-2.amazonaws.com/Prod/ + +curl $ApiUrl +``` + +## Cleanup + +1. Delete the stack + ```bash + sam delete + ``` +1. Confirm the stack has been deleted + ```bash + aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'STACK_NAME')].StackStatus" + ``` + +## Resources + +For more .NET templates, view the .NET Lambda Tools Blueprints https://github.com/aws/aws-lambda-dotnet/tree/master/Blueprints +The NuGet package [Amazon.Lambda.AspNetCoreServer](https://www.nuget.org/packages/Amazon.Lambda.AspNetCoreServer) contains a Lambda function that is used to translate requests from API Gateway into the ASP.NET Core framework and then the responses from ASP.NET Core back to API Gateway. + +For more information about how the Amazon.Lambda.AspNetCoreServer package works and how to extend its behavior view its [README](https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.AspNetCoreServer/README.md) file in GitHub. + +---- +Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/apigw-http-api-lambda-dotnet-sam/example-pattern.json b/apigw-http-api-lambda-dotnet-sam/example-pattern.json new file mode 100644 index 0000000000..8854a4fe96 --- /dev/null +++ b/apigw-http-api-lambda-dotnet-sam/example-pattern.json @@ -0,0 +1,63 @@ +{ + "title": "AWS API Gateway HTTP API to AWS Lambda w/ ASP.NET 6.0", + "description": "Create an HTTP API to AWS Lambda w/ ASP.NET 6.0", + "language": ".NET", + "level": "200", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This project shows how to use AWS SAM for an ASP.NET Core Web API project using a AWS Lambda function exposed through Amazon API Gateway.", + "The function takes a request from an API Gateway Proxy or from an Application Load Balancer and converts that request into the classes the ASP.NET Core framework expects and then converts the response from the ASP.NET Core framework into the response body that API Gateway Proxy or Application Load Balancer understands." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/apigw-http-api-lambda-dotnet-sam", + "templateURL": "serverless-patterns/apigw-http-api-lambda-dotnet-sam", + "projectFolder": "apigw-http-api-lambda-dotnet-sam", + "templateFile": "apigw-http-api-lambda-dotnet-sam/template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Building Lambda functions with C#", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/lambda-csharp.html" + }, + { + "text": "ASP.NET Core Lambda NuGet Package", + "link": "https://www.nuget.org/packages/Amazon.Lambda.AspNetCoreServer" + }, + { + "text": "More blueprints for .NET Lambdas", + "link": "https://github.com/aws/aws-lambda-dotnet/tree/master/Blueprints" + } + ] + }, + "deploy": { + "text": [ + "sam build", + "sam deploy --guided" + ] + }, + "testing": { + "text": [ + "See the GitHub repo readme for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Empty the S3 bucket", + "sam delete --stack-name STACK_NAME" + ] + }, + "authors": [ + { + "name": "Kevin Chan", + "image": "https://github.com/Kevinwochan/kevinwochan.github.io/blob/main/src/images/kevin.jpeg?raw=true", + "bio": "ISV Solutions Architect @ Amazon Web Services", + "linkedin": "kevinwochan" + } + ] + } \ No newline at end of file diff --git a/apigw-http-api-lambda-dotnet-sam/template.yaml b/apigw-http-api-lambda-dotnet-sam/template.yaml new file mode 100644 index 0000000000..83c7511004 --- /dev/null +++ b/apigw-http-api-lambda-dotnet-sam/template.yaml @@ -0,0 +1,33 @@ +--- +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: An AWS Serverless Application that uses the ASP.NET Core framework running + in AWS Lambda. + +Resources: + AspNetCoreFunction: + Type: AWS::Serverless::Function + Properties: + Handler: HttpApi::HttpApi.LambdaEntryPoint::FunctionHandlerAsync + Runtime: dotnet6 + CodeUri: HttpApi/ + MemorySize: 256 + Timeout: 30 + Policies: + - AWSLambda_FullAccess + Events: + ProxyResource: + Type: Api + Properties: + Path: "/{proxy+}" + Method: ANY + RootResource: + Type: Api + Properties: + Path: "/" + Method: ANY +Outputs: + ApiURL: + Description: API endpoint URL for Prod environment + Value: + Fn::Sub: https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/ diff --git a/apigw-http-api-lambda-rds-proxy-java/apigw-http-api-lambda-rds-proxy-java-pattern.json b/apigw-http-api-lambda-rds-proxy-java/apigw-http-api-lambda-rds-proxy-java-pattern.json index 798d6811d2..465583c059 100644 --- a/apigw-http-api-lambda-rds-proxy-java/apigw-http-api-lambda-rds-proxy-java-pattern.json +++ b/apigw-http-api-lambda-rds-proxy-java/apigw-http-api-lambda-rds-proxy-java-pattern.json @@ -43,7 +43,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-http-api-lambda-rds-proxy-terraform/example-pattern.json b/apigw-http-api-lambda-rds-proxy-terraform/example-pattern.json index ce963ea094..9994200f02 100644 --- a/apigw-http-api-lambda-rds-proxy-terraform/example-pattern.json +++ b/apigw-http-api-lambda-rds-proxy-terraform/example-pattern.json @@ -34,7 +34,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-http-api-lambda-rust/example-pattern.json b/apigw-http-api-lambda-rust/example-pattern.json index a30eba7f9b..ef142ccac8 100644 --- a/apigw-http-api-lambda-rust/example-pattern.json +++ b/apigw-http-api-lambda-rust/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-http-api-lambda-terraform/example-pattern.json b/apigw-http-api-lambda-terraform/example-pattern.json index 02962d0ff9..8ed0649293 100644 --- a/apigw-http-api-lambda-terraform/example-pattern.json +++ b/apigw-http-api-lambda-terraform/example-pattern.json @@ -43,7 +43,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-http-api-sqs-lambda-cdk/_pattern-model-apigw-http-api-sqs-lambda-cdk b/apigw-http-api-sqs-lambda-cdk/_pattern-model-apigw-http-api-sqs-lambda-cdk index db62163e45..64c4e25c26 100644 --- a/apigw-http-api-sqs-lambda-cdk/_pattern-model-apigw-http-api-sqs-lambda-cdk +++ b/apigw-http-api-sqs-lambda-cdk/_pattern-model-apigw-http-api-sqs-lambda-cdk @@ -40,7 +40,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-http-api-sqs-lambda-sls/example-pattern.json b/apigw-http-api-sqs-lambda-sls/example-pattern.json index e23b5194b2..3c36d1aad3 100644 --- a/apigw-http-api-sqs-lambda-sls/example-pattern.json +++ b/apigw-http-api-sqs-lambda-sls/example-pattern.json @@ -40,7 +40,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-kinesis-lambda-ddb/README.md b/apigw-kinesis-lambda-ddb/README.md new file mode 100644 index 0000000000..10d5de491a --- /dev/null +++ b/apigw-kinesis-lambda-ddb/README.md @@ -0,0 +1,78 @@ +# Amazon API Gateway to Amazon Kinesis Data Stream to AWS Lambda to Amazon DynamoDB + +This pattern explains how to deploy a SAM application with Amazon API Gateway, Amazon Kinesis Data Stream, AWS Lambda, and Amazon DynamoDB. When an HTTP POST request is made to the Amazon API Gateway endpoint, Gateway authorizes the request by checking Basic auth credentials and on valid credentials, request payload is sent to Amazon Kinesis Data Stream. This pattern is especially useful in cases of large payloads since Kinesis Data Stream can support upto 1MB paylod size. AWS Lambda function consumes event from the Data Stream and inserts the event/payload into the Amazon DynamoDB table. Amazon Lambda is also configured with a Dead Letter Queue where events are sent when retries to process those messages are repeatedly failed. + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd apigw-kinesis-lambda-ddb + ``` +1. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: + ``` + sam deploy --guided + ``` +1. During the prompts: + * Enter a stack name + * Enter the desired AWS Region + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run `sam deploy -guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. + +## How it works + +architecture diagram + +- This pattern deploys an Amazon API Gateway HTTP API with route /submit/{streamName}/{eventId} configured with basic authentication. +- On receiving a request, API Gateway will invoke a Lambda authorizer which validates the request and returns a policy informing API Gateway to accept or deny the request. +- When request is accepted, API Gateway sends the message payload to Kinesis Data Stream. +- Messages from Kinesis Data Streams is posted to a lambda function to process them. Lambda uses a SQS queue as Dead Letter Queue to send the messages in case of continued failures to process the messages. +- Lambda saves the received messages into a DynamoDB table. + +## Testing + +Once the application is deployed: +- Retrieve the HttpApiEndpoint value from CloudFormation Outputs +- Retrieve the username and password from Secrets Manager in AWS Console. +- Invoke the endpoint from Postman using some json payload and verify the payload saved in DynamoDB. + +Request: +- Request URL: https://{RestApiEndpoint}.execute-api.{Region}.amazonaws.com/{gatewayStage}/submit/{streamName}/{eventId} + - Region - Name of AWS Resion, Example: us-east-1 + - gatewayStage - Name of the API Gateway Stage. A stage is a named reference to a deployment, which is a snapshot of the API. "prod" is the stage name used in the SAM template. + - streamName - This is the name of kinesis stream created i.e., GatewayEventsStream + - eventId - Value in this attribute is used to choose PartitionKey in Kinesis stream. This example uses a single shard but when multiple shards are used, this eventId should be unique to share the load with multiple shards. Example: 55ad376f-86bf-4b06-9d3a-23237464dbd4 +- Request Method: POST +- Request Header: "Content-Type: application/json" +- Request Header: "Authorization: Basic " (where credentials is the Base64 encoding of ID and password joined by a single colon :) +- Request Body: {"eventId":"value1", "message":"event message for testing"} (This could be any JSON payload) + +Example URL: https://abc1234def.execute-api.us-east-1.amazonaws.com/prod/submit/GatewayEventsStream/55ad376f-86bf-4b06-9d3a-23237464dbd4 + +## Cleanup + +1. Delete the stack + ```bash + sam delete + ``` +This pattern was contributed by Ravi Kiran Ganji. + +---- +Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/apigw-kinesis-lambda-ddb/api.yaml b/apigw-kinesis-lambda-ddb/api.yaml new file mode 100644 index 0000000000..818df3ba20 --- /dev/null +++ b/apigw-kinesis-lambda-ddb/api.yaml @@ -0,0 +1,58 @@ +openapi: "3.0.1" +info: + title: "Rest Api" + version: "2023-06-25 17:32:29UTC" +paths: + /submit/{streamName}/{eventId}: + post: + parameters: + - in: path + name: streamName + schema: + type: string + required: true + - in: path + name: eventId + schema: + type: string + required: true + responses: + 200: + description: "Response for POST /" + content: + application/json: + schema: + type: object + properties: + SequenceNumber: + type: string + ShardId: + type: string + x-amazon-apigateway-integration: + type: aws + credentials: + Fn::GetAtt: [ApiGatewayRole, Arn] + uri: arn:aws:apigateway:us-east-1:kinesis:action/PutRecord + responses: + default: + statusCode: "200" + requestParameters: + integration.request.header.Content-Type: "'application/x-amz-json-1.1'" + requestTemplates: + application/json: |- + { + "StreamName": "$input.params('streamName')", + "Data": "$util.base64Encode($input.json('$'))", + "PartitionKey": "$input.params('eventId')" + } + passthroughBehavior: NEVER + httpMethod: POST + +x-amazon-apigateway-cors: + allowMethods: + - "*" + maxAge: 0 + allowCredentials: false + allowOrigins: + - "*" +x-amazon-apigateway-importexport-version: "1.0" diff --git a/apigw-kinesis-lambda-ddb/docs/apigw-kinesis-lambda-ddb.drawio b/apigw-kinesis-lambda-ddb/docs/apigw-kinesis-lambda-ddb.drawio new file mode 100644 index 0000000000..22564d298e --- /dev/null +++ b/apigw-kinesis-lambda-ddb/docs/apigw-kinesis-lambda-ddb.drawio @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apigw-kinesis-lambda-ddb/docs/apigw-kinesis-lambda-ddb.drawio.png b/apigw-kinesis-lambda-ddb/docs/apigw-kinesis-lambda-ddb.drawio.png new file mode 100644 index 0000000000..4d0f4643a6 Binary files /dev/null and b/apigw-kinesis-lambda-ddb/docs/apigw-kinesis-lambda-ddb.drawio.png differ diff --git a/apigw-kinesis-lambda-ddb/example-pattern.json b/apigw-kinesis-lambda-ddb/example-pattern.json new file mode 100644 index 0000000000..cbdd626531 --- /dev/null +++ b/apigw-kinesis-lambda-ddb/example-pattern.json @@ -0,0 +1,63 @@ +{ + "title": "Amazon API Gateway to Amazon Kinesis Data Stream to AWS Lambda to Amazon DynamoDB", + "description": "This pattern explains how to deploy a SAM application with Amazon API Gateway, Amazon Kinesis Data Stream, AWS Lambda, and Amazon DynamoDB. When an HTTP POST request is made to the Amazon API Gateway endpoint, Gateway authorizes the request by checking Basic auth credentials and on valid credentials, request payload is sent to Amazon Kinesis Data Stream. This pattern is especially useful in cases of large payloads since Kinesis Data Stream can support upto 1MB paylod size. AWS Lambda function consumes event from the Data Stream and inserts the event/payload into the Amazon DynamoDB table. Amazon Lambda is also configured with a Dead Letter Queue where events are sent when retries to process those messages are repeatedly failed.", + "language": "Python", + "level": "300", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern deploys an Amazon API Gateway HTTP API with route /submit/{streamName}/{eventId} configured with basic authentication.", + "On receiving a request, API Gateway will invoke a Lambda authorizer which validates the request and returns a policy informing API Gateway to accept or deny the request.", + "When request is accepted, API Gateway sends the message payload to Kinesis Data Stream.", + "Messages from Kinesis Data Streams is posted to a lambda function to process them. Lambda uses a SQS queue as Dead Letter Queue to send the messages in case of continued failures to process the messages.", + "Lambda saves the received messages into a DynamoDB table." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/apigw-kinesis-lambda-ddb", + "templateURL": "serverless-patterns/apigw-kinesis-lambda-ddb", + "projectFolder": "apigw-kinesis-lambda-ddb", + "templateFile": "apigw-kinesis-lambda-ddb/template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Lambda Authorizers", + "link": "https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html" + }, + { + "text": "Dead Letter Queues", + "link": "https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-deadletterqueue.html" + }, + { + "text": "Amazon Kinesis Data Streams", + "link": "https://docs.aws.amazon.com/streams/latest/dev/introduction.html" + } + ] + }, + "deploy": { + "text": [ + "sam deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "name": "Ravi Kiran Ganji", + "bio": "I am a Senior Cloud Application Architect at AWS Professional Services, and Serverless Enthusiast.", + "linkedin": "ravi-kiran-ganji" + } + ] +} \ No newline at end of file diff --git a/apigw-kinesis-lambda-ddb/lambda/authorizer/index.py b/apigw-kinesis-lambda-ddb/lambda/authorizer/index.py new file mode 100644 index 0000000000..897aaf0633 --- /dev/null +++ b/apigw-kinesis-lambda-ddb/lambda/authorizer/index.py @@ -0,0 +1,55 @@ +from aws_lambda_powertools import Logger, Tracer +from aws_lambda_powertools.logging import correlation_paths +from aws_lambda_powertools.utilities.typing import LambdaContext +import boto3 +import json +import base64 + +logger = Logger() +tracer = Tracer() +client = boto3.client("secretsmanager") + + +@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST) +@tracer.capture_lambda_handler +def lambda_handler(event: dict, context: LambdaContext) -> dict: + username = get_secret("gateway/username", "UserName") + password = get_secret("gateway/password", "Password") + expected_auth_header = "Basic " + base64_encode(f"{username}:{password}") + method_arn = event["methodArn"] + + effect = "Deny" + if ( + "Authorization" in event["headers"] + and event["headers"]["Authorization"] == expected_auth_header + ): + effect = "Allow" + + statement = { + "principalId": "user", + "policyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "execute-api:Invoke", + "Resource": [method_arn], + "Effect": effect, + } + ], + }, + } + + logger.info(f"Response: {statement}") + return statement + + +# Method to read secret from secrets manager +def get_secret(secret_name: str, secret_key: str) -> str: + response = client.get_secret_value(SecretId=secret_name) + value = json.loads(response["SecretString"]) + return value[secret_key] + + +# Method to base64 encode string +def base64_encode(string: str) -> str: + return base64.b64encode(string.encode("utf-8")).decode("utf-8") diff --git a/apigw-kinesis-lambda-ddb/lambda/kinesis-handler/index.py b/apigw-kinesis-lambda-ddb/lambda/kinesis-handler/index.py new file mode 100644 index 0000000000..5ae0536b23 --- /dev/null +++ b/apigw-kinesis-lambda-ddb/lambda/kinesis-handler/index.py @@ -0,0 +1,27 @@ +from aws_lambda_powertools import Logger, Tracer +from aws_lambda_powertools.logging import correlation_paths +from aws_lambda_powertools.utilities.typing import LambdaContext +import json +import boto3 +import base64 + +logger = Logger() +tracer = Tracer() +table = boto3.resource("dynamodb").Table("EventTable") + + +@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST) +@tracer.capture_lambda_handler +def lambda_handler(event: dict, context: LambdaContext) -> dict: + logger.info(f"Received event: {event}") + + # process records from kinesis event + for record in event["Records"]: + payload = record["kinesis"]["data"] + # base64 decode payload + data = json.loads(base64.b64decode(payload)) + + logger.info(f"Received data: {data}") + table.put_item(Item=data) + + return {"statusCode": 200} diff --git a/apigw-kinesis-lambda-ddb/template.yaml b/apigw-kinesis-lambda-ddb/template.yaml new file mode 100644 index 0000000000..99068c66c7 --- /dev/null +++ b/apigw-kinesis-lambda-ddb/template.yaml @@ -0,0 +1,249 @@ +AWSTemplateFormatVersion: "2010-09-09" +Transform: AWS::Serverless-2016-10-31 +Description: A rest endpoint hosted by an API Gateway which invokes Kinesis data stream, consumed by lambda function and persisted in a dynamoDB table. + +Resources: + ########################################################################## + # REST API - Entrypoint # + ########################################################################## + RestApi: + Type: AWS::Serverless::Api + Properties: + AccessLogSetting: + DestinationArn: !GetAtt RestApiGatewayAccessLogs.Arn + Format: '{ "requestId":"$context.requestId", "ip": "$context.identity.sourceIp", "requestTime":"$context.requestTime", "httpMethod":"$context.httpMethod","routeKey":"$context.routeKey", "status":"$context.status","protocol":"$context.protocol", "responseLength":"$context.responseLength" }' + DefinitionBody: + "Fn::Transform": + Name: "AWS::Include" + Parameters: + Location: "api.yaml" + StageName: prod + Auth: + DefaultAuthorizer: GatewayAuthorizerFunction + Authorizers: + GatewayAuthorizerFunction: + FunctionArn: !GetAtt GatewayAuthorizerFunction.Arn + FunctionPayloadType: REQUEST + Identity: + Headers: + - Authorization + ReauthorizeEvery: 0 + + ########################################################################## + # Kinesis Data Streams # + ########################################################################## + KinesisStream: + Type: AWS::Kinesis::Stream + Properties: + Name: GatewayEventsStream + ShardCount: 1 + StreamEncryption: + EncryptionType: KMS + KeyId: alias/aws/kinesis + + ########################################################################## + # SQS Queues # + ########################################################################## + # SQS DLQ + DeadLetterQueue: + Type: AWS::SQS::Queue + Properties: + QueueName: LambdaDeadLetterQueue + SqsManagedSseEnabled: true + + ########################################################################## + # Lambda Functions # + ########################################################################## + # Api Gateway Authorizer + GatewayAuthorizerFunction: + Type: AWS::Serverless::Function + Properties: + FunctionName: GatewayAuthorizerFunction + Description: Lambda to be invoked by API Gateway for authorization + CodeUri: lambda/authorizer/ + Handler: index.lambda_handler + Runtime: python3.10 + Timeout: 3 + MemorySize: 128 + Role: !GetAtt AuthorizerLambdaExecutionRole.Arn + Layers: + [ + !Sub "arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:32", + ] + + # Kinesis Handler + KinesisHandlerFunction: + Type: AWS::Serverless::Function + Properties: + FunctionName: KinesisHandlerFunction + Description: Lambda to consumer from kinesis data stream + CodeUri: lambda/kinesis-handler/ + Runtime: python3.10 + Handler: index.lambda_handler + MemorySize: 128 + Layers: + [ + !Sub "arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:32", + ] + DeadLetterQueue: + TargetArn: !GetAtt DeadLetterQueue.Arn + Type: SQS + Policies: + - Statement: + - Sid: AllowDbConnect + Effect: Allow + Action: + - "dynamodb:PutItem" + Resource: + - !GetAtt EventTable.Arn + Events: + Stream: + Type: Kinesis + Properties: + Stream: !GetAtt KinesisStream.Arn + StartingPosition: LATEST + BatchSize: 10 + + ########################################################################## + # Roles # + ########################################################################## + ApiGatewayRole: + Type: "AWS::IAM::Role" + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: + - apigateway.amazonaws.com + Action: + - "sts:AssumeRole" + Policies: + - PolicyName: CustomPolicy + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - "kinesis:PutRecord" + - "kinesis:PutRecords" + - "kinesis:GetShardIterator" + - "kinesis:GetRecords" + Resource: !Sub + - "${varStreamArn}*" + - varStreamArn: !GetAtt KinesisStream.Arn + - Effect: Allow + Action: + - "logs:CreateLogGroup" + - "logs:CreateLogStream" + - "logs:DescribeLogGroups" + - "logs:DescribeLogStreams" + - "logs:PutLogEvents" + - "logs:GetLogEvents" + - "logs:FilterLogEvents" + Resource: !GetAtt RestApiGatewayAccessLogs.Arn + + AuthorizerLambdaExecutionRole: + Type: "AWS::IAM::Role" + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: + - lambda.amazonaws.com + Action: + - "sts:AssumeRole" + Policies: + - PolicyName: CustomPolicy + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - "logs:CreateLogGroup" + - "logs:CreateLogStream" + - "logs:PutLogEvents" + Resource: "*" + - Effect: Allow + Action: + - "secretsmanager:GetSecretValue" + Resource: [!Ref BasicAuthUsername, !Ref BasicAuthPassword] + + ########################################################################## + # DynamoDB Table # + ########################################################################## + EventTable: + Type: AWS::DynamoDB::Table + Properties: + AttributeDefinitions: + - AttributeName: eventId + AttributeType: S + BillingMode: PROVISIONED + ProvisionedThroughput: + ReadCapacityUnits: 5 + WriteCapacityUnits: 10 + KeySchema: + - AttributeName: eventId + KeyType: HASH + TableName: EventTable + SSESpecification: + SSEEnabled: true + + ########################################################################## + # Cloudwatch Logs # + ########################################################################## + + RestApiGatewayAccessLogs: + Type: AWS::Logs::LogGroup + Properties: + LogGroupName: Rest-ApiGw-Kinesis-Access-Logs + RetentionInDays: 1 + + ########################################################################## + # Secrets - Generated for Endpoint Auth # + ########################################################################## + + BasicAuthUsername: + Type: AWS::SecretsManager::Secret + Properties: + Description: Username to be used in basic auth + Name: gateway/username + GenerateSecretString: + ExcludeCharacters: "" + ExcludeLowercase: false + ExcludeNumbers: false + ExcludePunctuation: false + ExcludeUppercase: false + GenerateStringKey: UserName + IncludeSpace: false + PasswordLength: 32 + RequireEachIncludedType: true + SecretStringTemplate: !Sub '{"UserName": "REPLACED"}' + + BasicAuthPassword: + Type: AWS::SecretsManager::Secret + Properties: + Description: Password to be used in basic auth + Name: gateway/password + GenerateSecretString: + ExcludeCharacters: "" + ExcludeLowercase: false + ExcludeNumbers: false + ExcludePunctuation: false + ExcludeUppercase: false + GenerateStringKey: Password + IncludeSpace: false + PasswordLength: 32 + RequireEachIncludedType: true + SecretStringTemplate: !Sub '{"Password": "REPLACED"}' + +########################################################################## +# Outputs # +########################################################################## +Outputs: + RestApiEndpoint: + Description: "Rest API endpoint" + Value: !Sub "https://${RestApi}.execute-api.${AWS::Region}.amazonaws.com" diff --git a/apigw-kinesis-lambda/README.md b/apigw-kinesis-lambda/README.md new file mode 100644 index 0000000000..3e75268d61 --- /dev/null +++ b/apigw-kinesis-lambda/README.md @@ -0,0 +1,128 @@ +# Amazon API Gateway integration with Amazon Kinesis Stream with AWS Lambda function as consumer + +This pattern will help you to deploy Amazon API Gateway integration with Amazon Kinesis Stream and it will deploy a Lambda function as a consumer for deployed Amazon Kinesis Stream. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/apigw-kinesis-lambda +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed + +## Architecture +The following diagram illustrates the solution architecture: + +![Architecture Diagram](img/APIGW-Kinesis-Lambda.png) + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd apigw-kinesis-lambda + ``` +1. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: + ``` + sam deploy --guided + ``` +1. During the prompts: + * Enter a stack name + * Enter the desired AWS Region + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run `sam deploy --guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. + +## How it works + +You can use API Gateway endpoint appeared in Outputs of SAM deployment to make PutRecords action of Kinesis stream. + +1. Makes use of PUT method on the API's /streams/{stream-name}/record resource to push single data record to the stream. + +2. Makes use of PUT method on the API's /streams/{stream-name}/records resource to push list of data records to the stream. + +## Testing + +From the SAM deploy output in a previous step, you can get the URL of the API Gateway endpoint, Kinesis Stream name and Lambda ARN. + +Use your preferred client to send a HTTP request. Use API Gateway Id and Kinesis Stream name which you received in the Outputs of SAM deploy. + +1. Push single record to kinesis stream + +```bash +curl --location --request PUT 'https://.execute-api..amazonaws.com/dev/streams/{KinesisStream-name}/record' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "Data": "test data", + "PartitionKey": "key1" +}' ' +``` + +The response would be like: + +```bash +{ + "SequenceNumber": "49642636313397988766902760194803326071555094291815596034", + "ShardId": "shardId-000000000000" +} ' +``` +2. Push multiple records to kinesis stream + +```bash +curl --location --request PUT 'https://.execute-api..amazonaws.com/dev/streams/{Kinesisstream-name}/records' \ +--header 'Content-Type: application/json' \ +--data-raw ' +{ + "records": [ + { + "data": "test data2", + "partition-key": "key2" + }, + { + "data": "test data2", + "partition-key": "key3" + } + ] +} + ' +``` + +The response would be like: + +```bash +{ + "FailedRecordCount": 0, + "Records": [{ + "SequenceNumber": "49642636313397988766902760194800908219915836995919675394", + "ShardId": "shardId-000000000000" + }, { + "SequenceNumber": "49642636313397988766902760194802117145735451625094381570", + "ShardId": "shardId-000000000000" + }] +} +``` + +Lastly, you can review Lambda function's execution logs from the Cloudwatch, + +- Navigate to Lambda function from AWS Lambda console. +- Navigate to 'Monitor' tab. +- Click on 'View CloudWatch logs' button. +- Open latest log stream. +- Now, you should be able to see decoded message from the Kinesis Stream. + + +## Cleanup + +1. In your command line, from the sam application project directory, run the following: + ```bash + sam delete + + ``` +---- diff --git a/apigw-kinesis-lambda/example-pattern.json b/apigw-kinesis-lambda/example-pattern.json new file mode 100644 index 0000000000..893bbf1ea6 --- /dev/null +++ b/apigw-kinesis-lambda/example-pattern.json @@ -0,0 +1,67 @@ +{ + "title": "Amazon API Gateway to Amazon Kinesis with AWS Lambda as a consumer", + "description": "Create a REST API Gateway which performs PutRecords API call to Kinesis Data stream and the stream is consumed by a Lambda function", + "language": "Python", + "level": "300", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern will help you to deploy Amazon API Gateway API integration with Amazon Kinesis Stream and it will deploy Lambda function as a consumer for deployed Amazon Kinesis Stream.", + "In this pattern, Lambda function is logging the decoded message which was received from the Amazon Kinesis Stream." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/apigw-kinesis-lambda", + "templateURL": "serverless-patterns/aws-kinesis-lambda", + "projectFolder": "aws-kinesis-lambda", + "templateFile": "aws-kinesis-lambda/template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Tutorial: Create a REST API as an Amazon Kinesis proxy in API Gateway", + "link": "https://docs.aws.amazon.com/apigateway/latest/developerguide/integrating-api-with-aws-services-kinesis.html" + }, + { + "text": "Kinesis Data Stream PutRecord API", + "link": "https://docs.aws.amazon.com/kinesis/latest/APIReference/API_PutRecord.html" + }, + { + "text": "Using AWS Lambda with Amazon Kinesis", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/with-kinesis.html" + } + ] + }, + "deploy": { + "text": [ + "sam deploy --guided" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "name": "Vijay Konade", + "image": "img/vijay.png", + "bio": "Cloud Support Engineer @ AWS", + "linkedin": "https://www.linkedin.com/in/vijay-konade-14427479/" + }, + { + "name": "Thi Nguyen", + "image": "https://drive.google.com/file/d/188LpzUvUmHt1o7vzbwKw32S-fYabL-qY/view?usp=sharing", + "bio": "Solutions Architect @ AWS", + "linkedin": "https://www.linkedin.com/in/ndthi" + } + ] + } diff --git a/apigw-kinesis-lambda/img/APIGW-Kinesis-Lambda.png b/apigw-kinesis-lambda/img/APIGW-Kinesis-Lambda.png new file mode 100644 index 0000000000..b122002061 Binary files /dev/null and b/apigw-kinesis-lambda/img/APIGW-Kinesis-Lambda.png differ diff --git a/apigw-kinesis-lambda/img/vijay.png b/apigw-kinesis-lambda/img/vijay.png new file mode 100644 index 0000000000..8b9778cd9a Binary files /dev/null and b/apigw-kinesis-lambda/img/vijay.png differ diff --git a/apigw-kinesis-lambda/src/lambda_function.py b/apigw-kinesis-lambda/src/lambda_function.py new file mode 100644 index 0000000000..8dc5a42738 --- /dev/null +++ b/apigw-kinesis-lambda/src/lambda_function.py @@ -0,0 +1,8 @@ +import base64 +import json + +def lambda_handler(event, context): + for record in event['Records']: + # Kinesis data is base64 encoded so need to decode + data = base64.b64decode(record['kinesis']['data']).decode('utf-8') + print("Data After Decoding: " + data) diff --git a/apigw-kinesis-lambda/template.yaml b/apigw-kinesis-lambda/template.yaml new file mode 100644 index 0000000000..3904a62fa7 --- /dev/null +++ b/apigw-kinesis-lambda/template.yaml @@ -0,0 +1,169 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: An Amazon API Gateway REST API integration with a Kinesis Data Stream, which is consumed by Lambda function. + + +Resources: + KinesisStream: + Type: AWS::Kinesis::Stream + Properties: + ShardCount: 1 + + APIGatewayRole: + Type: 'AWS::IAM::Role' + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Action: + - 'sts:AssumeRole' + Effect: Allow + Principal: + Service: + - apigateway.amazonaws.com + Policies: + - PolicyName: APIGWKinesisPolicy + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - 'kinesis:PutRecord' + - 'kinesis:PutRecords' + - 'kinesis:GetShardIterator' + - 'kinesis:GetRecords' + Resource: !Sub + - '${varStreamArn}*' + - varStreamArn: !GetAtt KinesisStream.Arn + + Api: + Type: 'AWS::ApiGateway::RestApi' + Properties: + Name: apigw-kinesis-api + Description: REST API to push data to Kinesis Stream + streams: + Type: 'AWS::ApiGateway::Resource' + Properties: + RestApiId: !Ref Api + ParentId: !GetAtt Api.RootResourceId + PathPart: 'streams' + + streamName: + Type: 'AWS::ApiGateway::Resource' + Properties: + RestApiId: !Ref Api + ParentId: !Ref streams + PathPart: '{stream-name}' + + record: + Type: 'AWS::ApiGateway::Resource' + Properties: + RestApiId: !Ref Api + ParentId: !Ref streamName + PathPart: 'record' + + records: + Type: 'AWS::ApiGateway::Resource' + Properties: + RestApiId: !Ref Api + ParentId: !Ref streamName + PathPart: 'records' + + recordMethodPost: + Type: 'AWS::ApiGateway::Method' + Properties: + RestApiId: !Ref Api + ResourceId: !Ref record + HttpMethod: PUT + ApiKeyRequired: false + AuthorizationType: NONE + Integration: + Type: AWS + Credentials: !GetAtt APIGatewayRole.Arn + IntegrationHttpMethod: POST + Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:kinesis:action/PutRecord' + PassthroughBehavior: WHEN_NO_TEMPLATES + RequestTemplates: + application/json: !Sub | + + { + "StreamName": "$input.params('stream-name')", + "Data": "$util.base64Encode($input.json('$.Data'))", + "PartitionKey": "$input.path('$.PartitionKey')" + } + IntegrationResponses: + - StatusCode: '200' + MethodResponses: + - StatusCode: '200' + + recordsMethodPost: + Type: 'AWS::ApiGateway::Method' + Properties: + RestApiId: !Ref Api + ResourceId: !Ref records + HttpMethod: PUT + ApiKeyRequired: false + AuthorizationType: NONE + Integration: + Type: AWS + Credentials: !GetAtt APIGatewayRole.Arn + IntegrationHttpMethod: POST + Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:kinesis:action/PutRecords' + PassthroughBehavior: WHEN_NO_TEMPLATES + RequestTemplates: + application/json: !Sub | + { + "StreamName": "$input.params('stream-name')", + "Records": [ + #foreach($elem in $input.path('$.records')) + { + "Data": "$util.base64Encode($elem.data)", + "PartitionKey": "$elem.partition-key" + }#if($foreach.hasNext),#end + #end + ] + } + IntegrationResponses: + - StatusCode: '200' + MethodResponses: + - StatusCode: '200' + + ApiDeployment: + Type: 'AWS::ApiGateway::Deployment' + DependsOn: + - recordMethodPost + Properties: + RestApiId: !Ref Api + StageName: 'dev' + + LambdaConsumer: + Type: AWS::Serverless::Function + Properties: + FunctionName: LambdaConsumer + Handler: lambda_function.lambda_handler + Runtime: python3.9 + CodeUri: src/ + Policies: + - KinesisStreamReadPolicy: + StreamName: !Ref KinesisStream + Events: + Stream: + Type: Kinesis + Properties: + Stream: !GetAtt KinesisStream.Arn + StartingPosition: LATEST + BatchSize: 100 +Outputs: + ApiRootUrl: + Description: Root Url of the API + Value: !Sub + - 'https://${ApiId}.execute-api.${AWS::Region}.amazonaws.com/dev' + - ApiId: !Ref Api + + KinesisStream: + Description: Kinesis Data Stream name + Value: !Ref KinesisStream + + LambdaFunction: + Description: Lambda Function ARN + Value: !GetAtt LambdaConsumer.Arn diff --git a/apigw-kinesis/example-pattern.json b/apigw-kinesis/example-pattern.json index f6b0cf731f..cc5dd5ad47 100644 --- a/apigw-kinesis/example-pattern.json +++ b/apigw-kinesis/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-lambda-authorizer-custom-header/README.md b/apigw-lambda-authorizer-custom-header/README.md new file mode 100644 index 0000000000..1766f21e38 --- /dev/null +++ b/apigw-lambda-authorizer-custom-header/README.md @@ -0,0 +1,74 @@ +# Amazon API Gateway API using Lambda Token Authorizer and Mapping Template to add custom header + +The SAM template deploys an Amazon API Gateway API endpoint that uses a Lambda Token Authorizer for access control as well as added logic in the Authorizer function to enrich the request with additional data. + +This aproach can be used to inject data that downstream legacy systems expect and that is dependent on data that is available to authorizer. + +* If the request to the endpoint does not include a 'authorizationToken' header, the Lambda Authorizer will not be invoked and API Gateway will return a 401 Forbidden. +* If the request to the endpoint includes a 'authorizationToken' header, the Lambda Authorizer will be invoked and its response will depend on the value of the 'authorizationToken' header. +* If the value of 'authorizationToken' header is 'unauthorized', API Gateway will return a 401 Unauthorized error. +* If the value of 'authorizationToken' header is 'deny', API Gateway will return a 403 error. +* Only if the value of 'authorizationToken' header is 'allow', API Gateway will successfully call the HTTP backend and return a 200. +* For any other case, API Gateway will return a 403 error. + +Learn more about this pattern at Serverless Land Patterns: [https://serverlessland.com/patterns/apigw-lambda-authorizer-custom-header](https://serverlessland.com/patterns/apigw-lambda-authorizer-custom-header) + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +2. Change directory to the pattern directory: + ``` + TODO cd apigw-lambda-authorizer + ``` +3. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: + ``` + sam deploy -g + ``` +1. During the prompts: + * Enter a stack name + * Select the desired AWS Region + * Allow SAM to create roles with the required permissions if needed. + + Once you have run guided mode once, you can use `sam deploy` in future to use these defaults. + +1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. + +## Testing + +The stack will output the **api endpoint**. Use *curl* to make a HTTP request to the API Gateway that includes a header with the authorization token to test the Resource Lambda Token Authorizer. + +``` +curl -i https://12345abcde.execute-api.{region}.amazonaws.com/Prod -H "authorizationToken: allow" +``` + +will successfully return a 200 HTTP code and the request from API Gateway to the HTTP endpoint in the response body. You should see a header named `enrichmentHeader` with a string provided from the Lambda Token Authorizer `This data comes from Lambda Authorizer` + + +## Cleanup + +1. Delete the stack + ```bash + sam delete + ``` +1. Confirm the stack has been deleted + ```bash + aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'STACK_NAME')].StackStatus" + ``` +---- + +## Author bio +Shaun Guo +https://www.linkedin.com/in/shaun-guo/ +Senior Technical Account Manager diff --git a/apigw-lambda-authorizer-custom-header/example-pattern.json b/apigw-lambda-authorizer-custom-header/example-pattern.json new file mode 100644 index 0000000000..d4fb999af2 --- /dev/null +++ b/apigw-lambda-authorizer-custom-header/example-pattern.json @@ -0,0 +1,56 @@ +{ + "title": "Amazon API Gateway REST API with AWS Lambda Token Authorizer with custom headers", + "description": "Create an API with a mapping template that enriches the request with additional data from Lambda Token Authorizer.", + "language": "Node.js", + "level": "200", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This SAM template creates an API that uses additional logic from the Lambda Token Authorizer to add data to the request.", + "The data is passed to the HTTP endpoint in the form of a custom header.", + "This aproach can be used to inject data that downstream legacy systems expect and that is dependent on data that is available to authorizer.", + "The HTTP endpoint in the example returns a response showing the headers that are received." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/apigw-lambda-authorizer-custom-header", + "templateURL": "serverless-patterns/apigw-lambda-authorizer-custom-header", + "projectFolder": "apigw-lambda-authorizer-custom-header", + "templateFile": "apigw-lambda-authorizer-custom-header/template.yml" + } + }, + "resources": { + "bullets": [ + { + "text": "Working with API Gateway and mapping templates", + "link": "https://docs.aws.amazon.com/apigateway/latest/developerguide/models-mappings.html" + } + ] + }, + "deploy": { + "text": [ + "sam deploy" + ] + }, + "testing": { + "text": [ + "Once the application is deployed, retrieve the API URL and a request from Postman or from a terminal using the curl command with an AuthorizationToken header of 'allow'." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "headline": "Presented by Shaun Guo, Technical Account Manager", + "name": "Shaun Guo", + "image": "https://media.licdn.com/dms/image/C5103AQG3KMyMdEIKpA/profile-displayphoto-shrink_800_800/0/1517283953925?e=1692835200&v=beta&t=AxJ9ST_8K_bw8nqTPDaJB2F5dnQspES9FuJ64DBScC8", + "bio": "Shaun is a Senior Technical Account Manager at Amazon Web Services based in Australia", + "linkedin": "https://www.linkedin.com/in/shaun-guo/" + } + ] + } diff --git a/apigw-lambda-authorizer-custom-header/src/authorizer.js b/apigw-lambda-authorizer-custom-header/src/authorizer.js new file mode 100644 index 0000000000..6d2865ec59 --- /dev/null +++ b/apigw-lambda-authorizer-custom-header/src/authorizer.js @@ -0,0 +1,44 @@ +export const handler = function(event, context, callback) { + console.log(event); + var token = event.authorizationToken; + + switch (token) { + case 'allow': + // callback(null, generatePolicy('user', 'Allow', event.methodArn)); + callback(null, generatePolicy('user', 'Allow', event.methodArn)); + break; + case 'deny': + // callback(null, generatePolicy('user', 'Deny', event.methodArn)); + callback(null, generatePolicy('user', 'Deny', event.methodArn)); + break; + case 'unauthorized': + callback("Unauthorized"); // Return a 401 Unauthorized response + break; + default: + callback(null, generatePolicy('user', 'Deny', event.methodArn)); + } +}; + +// Help function to generate an IAM policy +var generatePolicy = function(principalId, effect, resource) { + var authResponse = {}; + + authResponse.principalId = principalId; + if (effect && resource) { + var policyDocument = {}; + policyDocument.Version = '2012-10-17'; + policyDocument.Statement = []; + var statementOne = {}; + statementOne.Action = 'execute-api:Invoke'; + statementOne.Effect = effect; + statementOne.Resource = resource; + policyDocument.Statement[0] = statementOne; + authResponse.policyDocument = policyDocument; + } + + // enrichment stub, add code here to add enrichment data + authResponse.context = { + "enrichment": "This data comes from Lambda Authorizer" + }; + return authResponse; +} \ No newline at end of file diff --git a/apigw-lambda-authorizer-custom-header/template.yaml b/apigw-lambda-authorizer-custom-header/template.yaml new file mode 100644 index 0000000000..79216c4c6a --- /dev/null +++ b/apigw-lambda-authorizer-custom-header/template.yaml @@ -0,0 +1,64 @@ +AWSTemplateFormatVersion: 2010-09-09 +Description: Serverless Pattern - Amazon API Gateway using Lambda Authorizer and Mapping Template to inject customer HTTP headers +Transform: AWS::Serverless-2016-10-31 +Globals: + Function: + Runtime: nodejs18.x +Resources: + # REST API + AppApi: + Type: AWS::Serverless::Api + Properties: + Name: apigw-lambda-authorizer-mapping-template + Description: API Gateway using Lambda Authorizer and Mapping Template to inject customer HTTP headers with additional data. + StageName: Prod + Auth: + DefaultAuthorizer: MyLambdaTokenAuthorizer + Authorizers: + MyLambdaTokenAuthorizer: + FunctionArn: !GetAtt AuthorizerFunction.Arn + Identity: + Header: AuthorizationToken + AuthorizerPayloadFormatVersion: 2.0 + EnableSimpleResponses: true + DefinitionBody: + swagger: "2.0" + paths: + /: + get: + x-amazon-apigateway-integration: + httpMethod: GET + passthroughBehavior: when_no_templates + type: http + uri: "https://httpbin.org/get" + responses: + "200": + statusCode: '200' + requestTemplates: + application/json: | + { + #set($enrichmentHeaderValue = "$context.authorizer.enrichment") + #set($context.requestOverride.header.enrichmentHeader = $enrichmentHeaderValue) + } + responses: + "200": + statusCode: '200' + + # Authorizer function + AuthorizerFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: src/ + Handler: authorizer.handler + Metadata: # Manage esbuild properties + BuildMethod: esbuild + BuildProperties: + Minify: false + Target: "es2020" + EntryPoints: + - authorizer.js +Outputs: + # API Gateway endpoint to be used during tests + AppApiEndpoint: + Description: API Endpoint + Value: !Sub "https://${AppApi}.execute-api.${AWS::Region}.amazonaws.com/Prod" \ No newline at end of file diff --git a/apigw-lambda-authorizer/example-pattern.json b/apigw-lambda-authorizer/example-pattern.json index aa7ed1d03c..fd6a16450a 100644 --- a/apigw-lambda-authorizer/example-pattern.json +++ b/apigw-lambda-authorizer/example-pattern.json @@ -39,7 +39,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-lambda-dynamodb-cdk-go/example-pattern.json b/apigw-lambda-dynamodb-cdk-go/example-pattern.json index b70783d5ac..224dbf8ec8 100644 --- a/apigw-lambda-dynamodb-cdk-go/example-pattern.json +++ b/apigw-lambda-dynamodb-cdk-go/example-pattern.json @@ -23,7 +23,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-lambda-dynamodb-cdk/README.md b/apigw-lambda-dynamodb-cdk/README.md index b2cd67961b..36e253442f 100644 --- a/apigw-lambda-dynamodb-cdk/README.md +++ b/apigw-lambda-dynamodb-cdk/README.md @@ -11,7 +11,7 @@ Important: this application uses various AWS services and there are costs associ * [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -* [.NET Core 3.1](https://dotnet.microsoft.com/en-us/download/dotnet/3.1) installed +* [.NET 6](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) installed * [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) (AWS CDK) installed ## Deployment Instructions diff --git a/apigw-lambda-dynamodb-cdk/cdk/cdk.json b/apigw-lambda-dynamodb-cdk/cdk/cdk.json index 231877de24..4b2fdd8555 100644 --- a/apigw-lambda-dynamodb-cdk/cdk/cdk.json +++ b/apigw-lambda-dynamodb-cdk/cdk/cdk.json @@ -1,5 +1,5 @@ { - "app": "dotnet run -p src/Cdk/Cdk.csproj", + "app": "dotnet run --project src/Cdk/Cdk.csproj", "watch": { "include": [ "**" diff --git a/apigw-lambda-dynamodb-cdk/cdk/code/src/DynamoDbLambda/DynamoDbLambda.csproj b/apigw-lambda-dynamodb-cdk/cdk/code/src/DynamoDbLambda/DynamoDbLambda.csproj index 66fba85990..621a8f0f36 100644 --- a/apigw-lambda-dynamodb-cdk/cdk/code/src/DynamoDbLambda/DynamoDbLambda.csproj +++ b/apigw-lambda-dynamodb-cdk/cdk/code/src/DynamoDbLambda/DynamoDbLambda.csproj @@ -1,19 +1,28 @@ - netcoreapp3.1 + net6.0 + enable + enable true Lambda true + + true - + - - + + + + + README.md + + \ No newline at end of file diff --git a/apigw-lambda-dynamodb-cdk/cdk/code/src/DynamoDbLambda/Function.cs b/apigw-lambda-dynamodb-cdk/cdk/code/src/DynamoDbLambda/Function.cs index 46150b9181..d8a7a28e7a 100644 --- a/apigw-lambda-dynamodb-cdk/cdk/code/src/DynamoDbLambda/Function.cs +++ b/apigw-lambda-dynamodb-cdk/cdk/code/src/DynamoDbLambda/Function.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.Model; diff --git a/apigw-lambda-dynamodb-cdk/cdk/code/src/DynamoDbLambda/aws-lambda-tools-defaults.json b/apigw-lambda-dynamodb-cdk/cdk/code/src/DynamoDbLambda/aws-lambda-tools-defaults.json deleted file mode 100644 index a77bda6837..0000000000 --- a/apigw-lambda-dynamodb-cdk/cdk/code/src/DynamoDbLambda/aws-lambda-tools-defaults.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "Information": [ - "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", - "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", - "dotnet lambda help", - "All the command line options for the Lambda command can be specified in this file." - ], - "profile": "", - "region": "", - "configuration": "Release", - "framework": "netcoreapp3.1", - "function-runtime": "dotnetcore3.1", - "function-memory-size": 256, - "function-timeout": 30, - "function-handler": "code::code.Function::FunctionHandler" -} \ No newline at end of file diff --git a/apigw-lambda-dynamodb-cdk/cdk/src/Cdk/Cdk.csproj b/apigw-lambda-dynamodb-cdk/cdk/src/Cdk/Cdk.csproj index 5837c5daee..7401ff3479 100644 --- a/apigw-lambda-dynamodb-cdk/cdk/src/Cdk/Cdk.csproj +++ b/apigw-lambda-dynamodb-cdk/cdk/src/Cdk/Cdk.csproj @@ -2,19 +2,25 @@ Exe - netcoreapp3.1 + net6.0 Major - - + + + + + cdk.json + + + diff --git a/apigw-lambda-dynamodb-cdk/cdk/src/Cdk/CdkStack.cs b/apigw-lambda-dynamodb-cdk/cdk/src/Cdk/CdkStack.cs index 5b2d353e97..bfc7fc23ff 100644 --- a/apigw-lambda-dynamodb-cdk/cdk/src/Cdk/CdkStack.cs +++ b/apigw-lambda-dynamodb-cdk/cdk/src/Cdk/CdkStack.cs @@ -16,14 +16,18 @@ internal CdkStack(Construct scope, string id, IStackProps props = null) : base(s { var tableName = "MyCdkTable"; - var buildCommands = new[] + var buildOption = new BundlingOptions() { - "cd /asset-input", - "export DOTNET_CLI_HOME=\"/tmp/DOTNET_CLI_HOME\"", - "export PATH=\"$PATH:/tmp/DOTNET_CLI_HOME/.dotnet/tools\"", - "dotnet tool install -g Amazon.Lambda.Tools", - "dotnet lambda package -o output.zip", - "unzip -o -d /asset-output output.zip" + Image = Runtime.DOTNET_6.BundlingImage, + User = "root", + OutputType = BundlingOutput.ARCHIVED, + Command = new string[]{ + "/bin/sh", + "-c", + " dotnet tool install -g Amazon.Lambda.Tools"+ + " && dotnet build"+ + " && dotnet lambda package --output-package /asset-output/function.zip" + } }; var dynamoDbTable = new Table(this, "MyCdkTable", new TableProps() @@ -55,7 +59,7 @@ internal CdkStack(Construct scope, string id, IStackProps props = null) : base(s var handler = new Function(this, "DynamoDbHandler", new FunctionProps() { - Runtime = Runtime.DOTNET_CORE_3_1, + Runtime = Runtime.DOTNET_6, Timeout = Duration.Seconds(30), Environment = new Dictionary(1) { @@ -63,14 +67,7 @@ internal CdkStack(Construct scope, string id, IStackProps props = null) : base(s }, Code = Code.FromAsset("code/src/DynamoDbLambda", new AssetOptions() { - Bundling = new BundlingOptions - { - Image = Runtime.DOTNET_CORE_3_1.BundlingImage, - Command = new [] - { - "bash", "-c", string.Join(" && ", buildCommands) - } - } + Bundling = buildOption }), Handler = "DynamoDbLambda::DynamoDbLambda.Function::FunctionHandler", Role = lambdaHandlerRole diff --git a/apigw-lambda-dynamodb-kinesis-lambda/README.md b/apigw-lambda-dynamodb-kinesis-lambda/README.md new file mode 100644 index 0000000000..55e1f22028 --- /dev/null +++ b/apigw-lambda-dynamodb-kinesis-lambda/README.md @@ -0,0 +1,63 @@ +# API Gateway, Lambda, DynamoDB, Kinesis, Lambda + +This pattern explains how to deploy a SAM application with Amazon API Gateway, AWS Lambda, and Amazon DynamoDB and Stream to Kinesis and finaly trigger a Lambda. When an HTTP POST request is made to the Amazon API Gateway endpoint, the AWS Lambda function is invoked and inserts an item into the Amazon DynamoDB table, then an event will be pushed into Kinesis and trigger a Lambda function + +![Concept](img/concept.png) + + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/apigw-lambda-dynamodb-kinesis-lambda-sam + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd apigw-lambda-dynamodb-kinesis-lambda + ``` +1. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: + ``` + sam deploy --guided + ``` +1. During the prompts: + * Enter a stack name + * Enter the desired AWS Region + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run `sam deploy --guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. + +## How it works + +Explain how the service interaction works. + +## Testing + +Provide steps to trigger the integration and show what should be observed if successful. + +## Cleanup + +1. Delete the stack + ```bash + aws cloudformation delete-stack --stack-name STACK_NAME + ``` +1. Confirm the stack has been deleted + ```bash + aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'STACK_NAME')].StackStatus" + ``` +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/apigw-lambda-dynamodb-kinesis-lambda/img/concept.png b/apigw-lambda-dynamodb-kinesis-lambda/img/concept.png new file mode 100644 index 0000000000..063e700db2 Binary files /dev/null and b/apigw-lambda-dynamodb-kinesis-lambda/img/concept.png differ diff --git a/apigw-lambda-dynamodb-kinesis-lambda/src/apiHandler.py b/apigw-lambda-dynamodb-kinesis-lambda/src/apiHandler.py new file mode 100644 index 0000000000..cc34163c1e --- /dev/null +++ b/apigw-lambda-dynamodb-kinesis-lambda/src/apiHandler.py @@ -0,0 +1,24 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: MIT-0 + +import boto3 +import json +dynamodb_client = boto3.resource('dynamodb') + +def lambda_handler(event, context): + table = dynamodb_client.Table('Transaction') + print(event) + item = json.loads(event['body']) + print(item) + table.put_item( + Item={ + 'id': item['id'], + 'name': item['name'], + 'description': item['description'], + 'customer': item['customer'] + } + ) + return { + 'statusCode': 200, + 'body': 'Successfully inserted data!' + } diff --git a/apigw-lambda-dynamodb-kinesis-lambda/src/messageHandler.py b/apigw-lambda-dynamodb-kinesis-lambda/src/messageHandler.py new file mode 100644 index 0000000000..1b35d57bdb --- /dev/null +++ b/apigw-lambda-dynamodb-kinesis-lambda/src/messageHandler.py @@ -0,0 +1,10 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: MIT-0 +import os +import boto3 +from aws_lambda_powertools.utilities.data_classes import KinesisStreamEvent +from aws_lambda_powertools.utilities.typing import LambdaContext + +def lambda_handler(event: KinesisStreamEvent, context: LambdaContext): + records = event["Records"] + print(records) diff --git a/apigw-lambda-dynamodb-kinesis-lambda/src/requirements.txt b/apigw-lambda-dynamodb-kinesis-lambda/src/requirements.txt new file mode 100644 index 0000000000..8e0dc717e2 --- /dev/null +++ b/apigw-lambda-dynamodb-kinesis-lambda/src/requirements.txt @@ -0,0 +1,2 @@ +aws-xray-sdk +aws_lambda_powertools \ No newline at end of file diff --git a/apigw-lambda-dynamodb-kinesis-lambda/template.yaml b/apigw-lambda-dynamodb-kinesis-lambda/template.yaml new file mode 100644 index 0000000000..872dbbdb7a --- /dev/null +++ b/apigw-lambda-dynamodb-kinesis-lambda/template.yaml @@ -0,0 +1,70 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: Serverless patterns - Amazon API Gateway to AWS Lambda to Amazon DynamoDB +Parameters: + KinesisStreamName: + Type: String + Default: testDataStream + Description: Enter a name for the Kinesis Data Stream. +Resources: + KinesisStream: + Type: AWS::Kinesis::Stream + Properties: + Name: !Ref KinesisStreamName + ShardCount: 1 + LambdaProducerFunction: + Type: AWS::Serverless::Function + Properties: + FunctionName: LambdaProducerFunction + Handler: apiHandler.lambda_handler + Runtime: python3.9 + CodeUri: src/ + Policies: + DynamoDBCrudPolicy: + TableName: !Ref DynamoDBTable + Events: + ApiEvent: + Type: Api + Properties: + Path: / + Method: POST + DynamoDBTable: + Type: AWS::DynamoDB::Table + Properties: + AttributeDefinitions: + - AttributeName: id + AttributeType: S + KeySchema: + - AttributeName: id + KeyType: HASH + ProvisionedThroughput: + ReadCapacityUnits: 5 + WriteCapacityUnits: 5 + StreamSpecification: + StreamViewType: NEW_IMAGE + TableName: Transaction + KinesisStreamSpecification: + StreamArn: !GetAtt KinesisStream.Arn + + LambdaConsumerFunction: + Type: AWS::Serverless::Function + Properties: + FunctionName: LambdaConsumerFunction + Handler: messageHandler.lambda_handler + Runtime: python3.9 + CodeUri: src/ + Policies: + - KinesisStreamReadPolicy: + StreamName: !Ref KinesisStreamName + Events: + Stream: + Type: Kinesis + Properties: + Stream: !GetAtt KinesisStream.Arn + StartingPosition: LATEST + BatchSize: 100 + +Outputs: + EndpointUrl: + Description: 'HTTP REST endpoint URL' + Value: !Sub 'https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod' diff --git a/apigw-lambda-dynamodb-stream-lambda/README.md b/apigw-lambda-dynamodb-stream-lambda/README.md new file mode 100644 index 0000000000..b6e6b88076 --- /dev/null +++ b/apigw-lambda-dynamodb-stream-lambda/README.md @@ -0,0 +1,63 @@ +# API Gateway, Lambda, DynamoDB, Kinesis, Lambda + +This pattern explains how to deploy a SAM application with Amazon API Gateway, AWS Lambda, and Amazon DynamoDB and DynamoDB Stream to trigger a Lambda. When an HTTP POST request is made to the Amazon API Gateway endpoint, the AWS Lambda function is invoked and inserts an item into the Amazon DynamoDB table, then an event will be pushed into Kinesis and trigger a Lambda function + +![Concept](img/concept.png) + + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/apigw-lambda-dynamodb-stream-lambda + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd apigw-lambda-dynamodb-stream-lambda + ``` +1. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: + ``` + sam deploy --guided + ``` +1. During the prompts: + * Enter a stack name + * Enter the desired AWS Region + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run `sam deploy --guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. + +## How it works + +Explain how the service interaction works. + +## Testing + +Provide steps to trigger the integration and show what should be observed if successful. + +## Cleanup + +1. Delete the stack + ```bash + sam delete + ``` +1. Confirm the stack has been deleted + ```bash + aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'STACK_NAME')].StackStatus" + ``` +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/apigw-lambda-dynamodb-stream-lambda/example-pattern.json b/apigw-lambda-dynamodb-stream-lambda/example-pattern.json new file mode 100644 index 0000000000..5753d3a8a1 --- /dev/null +++ b/apigw-lambda-dynamodb-stream-lambda/example-pattern.json @@ -0,0 +1,66 @@ +{ + "title": "API Gateway to Lambda, DynamoDB, Kinesis, Lambda integration", + "description": "API to trigger a Lambda function which save to DynamoDB, stream messages (with DynamoDB Stream) and trigger a Lambda function", + "language": "Python", + "level": "200", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This sample project demonstrates how to use Amazon API Gateway, AWS Lambda, Amazon DynamoDB and Kinesis together. When an HTTP POST request is made to the Amazon API Gateway endpoint, the AWS Lambda function is invoked and inserts an item into the Amazon DynamoDB table, then an event will be pushed into Kinesis and trigger a Lambda function." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/apigw-lambda-dynamodb-stream-lambda", + "templateURL": "serverless-patterns/apigw-lambda-dynamodb-stream-lambda", + "projectFolder": "apigw-lambda-dynamodb-stream-lambda", + "templateFile": "apigw-lambda-dynamodb-stream-lambda/sfn_athena_cdk_python_stack.py" + } + }, + "resources": { + "bullets": [ + { + "text": "API Gateway to trigger Lambda and save to DynamoDB", + "link": "https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-dynamo-db.html" + }, + { + "text": "DynamoDB to Kinesis", + "link": "https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/kds.html" + }, + { + "text": "Kinesis to Lambda", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/with-kinesis.html" + } + ] + }, + "deploy": { + "text": [ + "sam deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete" + ] + }, + "authors": [ + { + "name": "Thi Nguyen", + "image": "https://drive.google.com/file/d/188LpzUvUmHt1o7vzbwKw32S-fYabL-qY/view?usp=sharing", + "bio": "Solutions Architect @ AWS", + "linkedin": "https://www.linkedin.com/in/ndthi" + }, + { + "name": "Vijay Konade", + "image": "vijay.png", + "bio": "Cloud Support Eng @ AWS", + "linkedin": "https://www.linkedin.com/in/vijay-konade-14427479/" + } + ] +} diff --git a/apigw-lambda-dynamodb-stream-lambda/img/concept.png b/apigw-lambda-dynamodb-stream-lambda/img/concept.png new file mode 100644 index 0000000000..7a99cf673a Binary files /dev/null and b/apigw-lambda-dynamodb-stream-lambda/img/concept.png differ diff --git a/apigw-lambda-dynamodb-stream-lambda/src/apiHandler.py b/apigw-lambda-dynamodb-stream-lambda/src/apiHandler.py new file mode 100644 index 0000000000..0a9915b4cb --- /dev/null +++ b/apigw-lambda-dynamodb-stream-lambda/src/apiHandler.py @@ -0,0 +1,24 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: MIT-0 + +import boto3 +import json +dynamodb_client = boto3.resource('dynamodb') + +def lambda_handler(event, context): + table = dynamodb_client.Table('DemoStreamDynamoDBToLambda') + print(event) + item = json.loads(event['body']) + print(item) + table.put_item( + Item={ + 'id': item['id'], + 'name': item['name'], + 'description': item['description'], + 'customer': item['customer'] + } + ) + return { + 'statusCode': 200, + 'body': 'Successfully inserted data!' + } diff --git a/apigw-lambda-dynamodb-stream-lambda/src/messageHandler.py b/apigw-lambda-dynamodb-stream-lambda/src/messageHandler.py new file mode 100644 index 0000000000..d070fbda75 --- /dev/null +++ b/apigw-lambda-dynamodb-stream-lambda/src/messageHandler.py @@ -0,0 +1,10 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: MIT-0 +import os +import boto3 +from aws_lambda_powertools.utilities.data_classes import DynamoDBStreamEvent +from aws_lambda_powertools.utilities.typing import LambdaContext + +def lambda_handler(event: DynamoDBStreamEvent, context: LambdaContext): + records = event["Records"] + print(records) diff --git a/apigw-lambda-dynamodb-stream-lambda/src/requirements.txt b/apigw-lambda-dynamodb-stream-lambda/src/requirements.txt new file mode 100644 index 0000000000..8e0dc717e2 --- /dev/null +++ b/apigw-lambda-dynamodb-stream-lambda/src/requirements.txt @@ -0,0 +1,2 @@ +aws-xray-sdk +aws_lambda_powertools \ No newline at end of file diff --git a/apigw-lambda-dynamodb-stream-lambda/template.yaml b/apigw-lambda-dynamodb-stream-lambda/template.yaml new file mode 100644 index 0000000000..75560bacf6 --- /dev/null +++ b/apigw-lambda-dynamodb-stream-lambda/template.yaml @@ -0,0 +1,65 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: Serverless patterns - Amazon API Gateway to AWS Lambda to Amazon DynamoDB +Parameters: + DynamoDBTableName: + Type: String + Default: DemoStreamDynamoDBToLambda + Description: Enter a name for the Kinesis Data Stream. +Resources: + ProducerFunction: + Type: AWS::Serverless::Function + Properties: + FunctionName: ProducerFunction + Handler: apiHandler.lambda_handler + Runtime: python3.9 + CodeUri: src/ + Policies: + DynamoDBCrudPolicy: + TableName: !Ref DynamoDBTable + Events: + ApiEvent: + Type: Api + Properties: + Path: / + Method: POST + DynamoDBTable: + Type: AWS::DynamoDB::Table + Properties: + AttributeDefinitions: + - AttributeName: id + AttributeType: S + KeySchema: + - AttributeName: id + KeyType: HASH + ProvisionedThroughput: + ReadCapacityUnits: 5 + WriteCapacityUnits: 5 + StreamSpecification: + StreamViewType: NEW_IMAGE + TableName: !Ref DynamoDBTableName + + + ConsumerFunction: + Type: AWS::Serverless::Function + Properties: + FunctionName: ConsumerFunction + Handler: messageHandler.lambda_handler + Runtime: python3.9 + CodeUri: src/ + Policies: + - DynamoDBStreamReadPolicy: + TableName: !Ref DynamoDBTableName + StreamName: !Select [3, !Split ["/", !GetAtt DynamoDBTable.StreamArn]] + Events: + Stream: + Type: DynamoDB + Properties: + Stream: !GetAtt DynamoDBTable.StreamArn + StartingPosition: LATEST + BatchSize: 100 + +Outputs: + EndpointUrl: + Description: 'HTTP REST endpoint URL' + Value: !Sub 'https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod' diff --git a/apigw-lambda-dynamodb-terraform/example-pattern.json b/apigw-lambda-dynamodb-terraform/example-pattern.json index 6c320529c5..e06b2db484 100644 --- a/apigw-lambda-dynamodb-terraform/example-pattern.json +++ b/apigw-lambda-dynamodb-terraform/example-pattern.json @@ -37,7 +37,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-lambda-iot-cdk/example-pattern.json b/apigw-lambda-iot-cdk/example-pattern.json index c125b96f49..7f44f5813c 100644 --- a/apigw-lambda-iot-cdk/example-pattern.json +++ b/apigw-lambda-iot-cdk/example-pattern.json @@ -35,7 +35,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy."] diff --git a/apigw-lambda-json-xml-vtl-transformation/example-pattern.json b/apigw-lambda-json-xml-vtl-transformation/example-pattern.json index 31050ccbde..5d3f46c8b0 100644 --- a/apigw-lambda-json-xml-vtl-transformation/example-pattern.json +++ b/apigw-lambda-json-xml-vtl-transformation/example-pattern.json @@ -41,7 +41,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-lambda-memorydb/example-pattern.json b/apigw-lambda-memorydb/example-pattern.json index cffb2fd2d8..5305e52805 100644 --- a/apigw-lambda-memorydb/example-pattern.json +++ b/apigw-lambda-memorydb/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-lambda-qldb-cdk-dotnet/example-pattern.json b/apigw-lambda-qldb-cdk-dotnet/example-pattern.json index a6245c8bfd..d1860bafec 100644 --- a/apigw-lambda-qldb-cdk-dotnet/example-pattern.json +++ b/apigw-lambda-qldb-cdk-dotnet/example-pattern.json @@ -29,7 +29,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-lambda-qldb-terraform/example-pattern.json b/apigw-lambda-qldb-terraform/example-pattern.json index 9fe1f0b0c6..902989c8c2 100644 --- a/apigw-lambda-qldb-terraform/example-pattern.json +++ b/apigw-lambda-qldb-terraform/example-pattern.json @@ -50,7 +50,7 @@ }, "testing": { "text": [ - "See the README in the Github repo for detailed testing instructions." + "See the README in the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-lambda-rds-snapstart/example-pattern.json b/apigw-lambda-rds-snapstart/example-pattern.json index 5ce63452de..a25ca53aa2 100644 --- a/apigw-lambda-rds-snapstart/example-pattern.json +++ b/apigw-lambda-rds-snapstart/example-pattern.json @@ -47,7 +47,7 @@ }, "testing": { "text": [ - "See the README in the Github repo for detailed testing instructions." + "See the README in the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-lambda-rust/example-pattern.json b/apigw-lambda-rust/example-pattern.json index 20096b76ad..0069fd0f5f 100644 --- a/apigw-lambda-rust/example-pattern.json +++ b/apigw-lambda-rust/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-lambda-s3-cdk-dotnet/README.md b/apigw-lambda-s3-cdk-dotnet/README.md index e00047c007..a3810921e7 100644 --- a/apigw-lambda-s3-cdk-dotnet/README.md +++ b/apigw-lambda-s3-cdk-dotnet/README.md @@ -11,8 +11,8 @@ Important: this application uses various AWS services and there are costs associ * [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -* [.Net Core](https://dotnet.microsoft.com/en-us/download/dotnet) - - 3.1 for the CDK Stack - https://dotnet.microsoft.com/en-us/download/dotnet/3.1 +* [.NET 6](https://dotnet.microsoft.com/en-us/download/dotnet) + - 3.1 for the CDK Stack - https://dotnet.microsoft.com/en-us/download/dotnet/6.0 - 6.0 for the Lambda Function - https://dotnet.microsoft.com/en-us/download/dotnet/6.0 * [Docker](https://docs.docker.com/get-docker/) installed and running * [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) (AWS CDK) installed diff --git a/apigw-lambda-s3-cdk-dotnet/cdk/src/Cdk/Cdk.csproj b/apigw-lambda-s3-cdk-dotnet/cdk/src/Cdk/Cdk.csproj index 0e6bd64662..8d9ba63400 100644 --- a/apigw-lambda-s3-cdk-dotnet/cdk/src/Cdk/Cdk.csproj +++ b/apigw-lambda-s3-cdk-dotnet/cdk/src/Cdk/Cdk.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net6.0 Major diff --git a/apigw-lambda-s3-cdk-dotnet/example-pattern.json b/apigw-lambda-s3-cdk-dotnet/example-pattern.json index 43c2d54ab4..7e831ec9d6 100644 --- a/apigw-lambda-s3-cdk-dotnet/example-pattern.json +++ b/apigw-lambda-s3-cdk-dotnet/example-pattern.json @@ -31,17 +31,17 @@ }, "deploy": { "text": [ - "See the Github repo for detailed deployment instructions." + "See the GitHub repo for detailed deployment instructions." ] }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { "text": [ - "See the Github repo for detailed cleanup instructions." + "See the GitHub repo for detailed cleanup instructions." ] }, "authors": [ diff --git a/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/.gitignore b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/.gitignore new file mode 100644 index 0000000000..37833f8beb --- /dev/null +++ b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/.gitignore @@ -0,0 +1,10 @@ +*.swp +package-lock.json +__pycache__ +.pytest_cache +.venv +*.egg-info + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/README.md b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/README.md new file mode 100644 index 0000000000..3d9f141f4e --- /dev/null +++ b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/README.md @@ -0,0 +1,105 @@ +# Accessing Amazon SageMaker Endpoint via API Gateway and Lambda + +This pattern deploys a SageMaker Jumpstart model (Flan T5 XL) endpoint. It also adds a Lambda function and API Gateway integration to query the endpoint + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [Python, pip, virtualenv](https://docs.aws.amazon.com/cdk/latest/guide/work-with-cdk-python.html) installed +* [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html) (AWS CDK >= 2.2.0) Installed + +## Deployment Instructions + +1. Clone the project to your local working directory + + ```sh + git clone https://github.com/aws-samples/serverless-patterns + ``` + +2. Change to the pattern directory: + ```sh + cd serverless-patterns/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python + ``` +3. Create and activate the project's virtual environment. This allows the project's dependencies to be installed locally in the project folder, instead of globally. Note that if you have multiple versions of Python installed, where the `python` command references Python 2.x, then you can reference Python 3.x by using the `python3` command. You can check which version of Python is being referenced by running the command `python --version` or `python3 --version` + + ```sh + python3 -m venv .venv + source .venv/bin/activate + ``` + +4. Install the project dependencies + + ```sh + python -m pip install -r requirements.txt + ``` + +5. Deploy the stack to your default AWS account and region. + + ```sh + cdk deploy + ``` + +6. The default instance count for inference is set to 1. The instance count can be changed by passing the instance_count_param. + + ```sh + cdk deploy --context instance_count_param=2 + ``` + + +## How it works + +1. This pattern deploys a SageMaker Jumpstart model (Flan T5 XL from HuggingFace) endpoint using Amazon SageMaker. The model can be changed by modifying the ```MODEL_ID``` attribute in app.py file. + +2. The pattern also adds a lambda and an API Gateway query the endpoint. + +3. The API Gateway is protected using an API Key. To query the Api Gateway, ```x-api-key``` header needs to be added to the HTTP request. + + +## Testing + +1. Retrieve the Host URL of the API Gateway from AWS Console. + +2. Retrieve the API key from AWS Console. + +3. Send a sample HTTP request to the API Gateway: + ``` + POST /prod/generateimage HTTP/1.1 + Host: + x-api-key: + Content-Type: application/json + Cache-Control: no-cache + { + "query": { + "text_inputs": "A step by step recipe to make butter chicken:", + "max_length": 5000 + } + } + + ``` + +4. The API Gateway should respond with a JSON formatted response from the SageMaker endpoint + + +## Cleanup + +Run the given command to delete the resources that were created. It might take some time for the CloudFormation stack to get deleted. + +```sh +cdk destroy +``` + +## Author bio +Kaustav Dey, +https://www.linkedin.com/in/kaustavbecs/ +Solution Architect + +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/app.py b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/app.py new file mode 100644 index 0000000000..97fb66ec94 --- /dev/null +++ b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/app.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +import aws_cdk as cdk +import boto3 +from stack.ApigwLambdaSagemakerJumpstartendpointStack import ApigwLambdaSagemakerJumpstartendpointStack +from util.sagemaker_util import * + +region_name = boto3.Session().region_name +env = {"region": region_name} + + +# Obtain the model ID from: https://sagemaker.readthedocs.io/en/v2.173.0/doc_utils/pretrainedmodels.html +# Here we are using Flan T5 XL Model +MODEL_ID = "huggingface-text2text-flan-t5-xl" + +# Change the instance type to match your model. +# For GPU Service Quota related issues, please raise a quota request from AWS console +INFERENCE_INSTANCE_TYPE = "ml.g5.2xlarge" + +# Name of the stack: +STACK_NAME = "apigw-lambda-sagemaker-jumpstartendpoint-stack" + + + +MODEL_INFO = get_sagemaker_uris(model_id=MODEL_ID, + instance_type=INFERENCE_INSTANCE_TYPE, + region_name=region_name) + +app = cdk.App() + +stack = ApigwLambdaSagemakerJumpstartendpointStack( + app, + STACK_NAME, + model_info=MODEL_INFO, + env=env +) + +app.synth() diff --git a/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/cdk.json b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/cdk.json new file mode 100644 index 0000000000..c9710ade42 --- /dev/null +++ b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/cdk.json @@ -0,0 +1,52 @@ +{ + "app": "python3 app.py", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "requirements*.txt", + "source.bat", + "**/__init__.py", + "python/__pycache__", + "tests" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-redshift:columnId": true, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, + "@aws-cdk/aws-kms:aliasNameRef": true + } +} diff --git a/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/example-pattern.json b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/example-pattern.json new file mode 100644 index 0000000000..0b1dafefb8 --- /dev/null +++ b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/example-pattern.json @@ -0,0 +1,58 @@ +{ + "title": "Accessing Amazon SageMaker Endpoint via API Gateway and Lambda", + "description": "This pattern deploys a SageMaker Jumpstart model (Flan T5 XL) endpoint. It also uses a Lambda function and API Gateway to integrate the endpoint", + "language": "Python", + "level": "300", + "framework": "CDK", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern deploys a Amazon SageMaker Jumpstart model (Flan T5 XL from HuggingFace) endpoint using Amazon SageMaker. The model can be changed by modifying the MODEL_ID attribute in app.py file.", + "The pattern also adds a Lambda function with API Gateway integration to query the endpoint.", + "The API Gateway is protected using an API Key. To query the API Gateway endpoint, x-api-key header needs to be added to the HTTP request." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python", + "templateURL": "serverless-patterns/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python", + "projectFolder": "apigw-lambda-sagemaker-jumpstartendpoint-cdk-python", + "templateFile": "stack/ApigwLambdaSagemakerJumpstartendpointStack.py" + } + }, + "resources": { + "bullets": [ + { + "text": "Zero-shot prompting for the Flan-T5 foundation model in Amazon SageMaker JumpStart", + "link": "https://aws.amazon.com/blogs/machine-learning/zero-shot-prompting-for-the-flan-t5-foundation-model-in-amazon-sagemaker-jumpstart/" + }, + { + "text": "Instruction fine-tuning for FLAN T5 XL with Amazon SageMaker Jumpstart", + "link": "https://aws.amazon.com/blogs/machine-learning/instruction-fine-tuning-for-flan-t5-xl-with-amazon-sagemaker-jumpstart/" + } + ] + }, + "deploy": { + "text": [ + "cdk deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: cdk destroy." + ] + }, + "authors": [ + { + "name": "Kaustav Dey", + "image": "https://avatars.githubusercontent.com/u/13236519", + "bio": "Solution Architect at AWS", + "linkedin": "https://www.linkedin.com/in/kaustavbecs/" + } + ] +} diff --git a/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/lambda/InvokeSagemakerEndpointLambda.py b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/lambda/InvokeSagemakerEndpointLambda.py new file mode 100644 index 0000000000..703f2f3dbb --- /dev/null +++ b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/lambda/InvokeSagemakerEndpointLambda.py @@ -0,0 +1,35 @@ +import json +import os +import boto3 + + +def lambda_handler(event, context): + + # Extract the input data from the request body + input_payload = json.loads(event['body'])['query'] + + # Fetch the endpoint name from the environment variable + endpoint_name = os.environ['SAGEMAKER_ENDPOINT_NAME'] + + # Create a SageMaker runtime client + runtime = boto3.client('sagemaker-runtime') + + try: + """Query the SageMaker endpoint.""" + payload = json.dumps(input_payload) + response = runtime.invoke_endpoint(EndpointName=endpoint_name, + ContentType='application/json', + Body=payload) + query_response = response['Body'].read().decode('utf-8') + + # Return the result as the Lambda function response + return { + 'statusCode': 200, + 'body': query_response + } + except Exception as e: + # Handle any errors that occur during the invocation + return { + 'statusCode': 500, + 'body': str(e) + } diff --git a/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/requirements.txt b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/requirements.txt new file mode 100644 index 0000000000..b8fa8c302e --- /dev/null +++ b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/requirements.txt @@ -0,0 +1,5 @@ +aws-cdk-lib==2.83.1 +constructs>=10.0.0,<11.0.0 +requests +boto3 +sagemaker \ No newline at end of file diff --git a/aurora-serverless-s3-ingestion/cdk/source.bat b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/source.bat similarity index 100% rename from aurora-serverless-s3-ingestion/cdk/source.bat rename to apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/source.bat diff --git a/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/stack/ApigwLambdaSagemakerJumpstartendpointStack.py b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/stack/ApigwLambdaSagemakerJumpstartendpointStack.py new file mode 100644 index 0000000000..fc38ced41c --- /dev/null +++ b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/stack/ApigwLambdaSagemakerJumpstartendpointStack.py @@ -0,0 +1,181 @@ +from aws_cdk import ( + Stack, Duration, + aws_iam as iam, + aws_ssm as ssm, + aws_lambda as _lambda, + aws_apigateway as apigateway, +) + +from constructs import Construct +from util.sagemaker_endpoint_construct import SageMakerEndpointConstruct +from datetime import datetime + +class ApigwLambdaSagemakerJumpstartendpointStack(Stack): + + def __init__(self, scope: Construct, construct_id: str, model_info, **kwargs) -> None: + super().__init__(scope, construct_id, **kwargs) + + # Get the instance count parameter from the context or use a default value + instance_count_param = self.node.try_get_context("instance_count_param") + instance_count = int(instance_count_param) if instance_count_param else 1 + + role = iam.Role(self, "SageMaker-Policy", assumed_by=iam.ServicePrincipal("sagemaker.amazonaws.com")) + role.add_managed_policy(iam.ManagedPolicy.from_aws_managed_policy_name("AmazonS3FullAccess")) + + sts_policy = iam.Policy(self, "sm-deploy-policy-sts", + statements=[iam.PolicyStatement( + effect=iam.Effect.ALLOW, + actions=[ + "sts:AssumeRole" + ], + resources=["*"] + )] + ) + + logs_policy = iam.Policy(self, "sm-deploy-policy-logs", + statements=[iam.PolicyStatement( + effect=iam.Effect.ALLOW, + actions=[ + "cloudwatch:PutMetricData", + "logs:CreateLogStream", + "logs:PutLogEvents", + "logs:CreateLogGroup", + "logs:DescribeLogStreams", + "ecr:GetAuthorizationToken" + ], + resources=["*"] + )] + ) + + ecr_policy = iam.Policy(self, "sm-deploy-policy-ecr", + statements=[iam.PolicyStatement( + effect=iam.Effect.ALLOW, + actions=[ + "ecr:*", + ], + resources=["*"] + )] + ) + + role.attach_inline_policy(sts_policy) + role.attach_inline_policy(logs_policy) + role.attach_inline_policy(ecr_policy) + + # Generate a unique model name + model_name = f"JumpstartModel-{datetime.now().strftime('%Y%m%d%H%M%S')}" + + # Create a SageMaker endpoint that can be used to generate images from text + endpoint = SageMakerEndpointConstruct(self, "Jumpstart", + project_prefix=f"Jumpstart-{instance_count}", + role_arn=role.role_arn, + model_name=model_name, + model_bucket_name=model_info["model_bucket_name"], + model_bucket_key=model_info["model_bucket_key"], + model_docker_image=model_info["model_docker_image"], + variant_name="AllTraffic", + variant_weight=1, + instance_count=instance_count, + instance_type=model_info["instance_type"], + environment={ + "MMS_MAX_RESPONSE_SIZE": "20000000", + "SAGEMAKER_CONTAINER_LOG_LEVEL": "20", + "SAGEMAKER_PROGRAM": "inference.py", + "SAGEMAKER_REGION": model_info["region_name"], + "SAGEMAKER_SUBMIT_DIRECTORY": "/opt/ml/model/code", + }, + + deploy_enable=True + ) + + endpoint.node.add_dependency(sts_policy) + endpoint.node.add_dependency(logs_policy) + endpoint.node.add_dependency(ecr_policy) + + ssm.StringParameter(self, "Jumpstart_endpoint", parameter_name="Jumpstart_endpoint", + string_value=endpoint.endpoint_name) + + # Create a Lambda function to invoke the SageMaker endpoint + lambda_function = _lambda.Function( + self, + "InvokeSagemakerEndpointLambda", + function_name="InvokeSagemakerEndpointLambda", + runtime=_lambda.Runtime.PYTHON_3_9, + handler="InvokeSagemakerEndpointLambda.lambda_handler", + code=_lambda.Code.from_asset("lambda"), + environment={ + "SAGEMAKER_ENDPOINT_NAME": endpoint.endpoint_name, + }, + timeout=Duration.seconds(500) + ) + + # Add the necessary IAM permissions to the Lambda function to invoke the SageMaker endpoint + lambda_function.add_to_role_policy(iam.PolicyStatement( + effect=iam.Effect.ALLOW, + actions=["sagemaker:InvokeEndpoint"], + resources=[endpoint.endpoint_arn] + )) + + # Add the SageMaker endpoint as a dependency of the Lambda function + lambda_function.node.add_dependency(endpoint) + + # Create the REST API Gateway + api = apigateway.RestApi( + self, + "APIForSagemakerEndpoint", + rest_api_name="APIForSagemakerEndpoint" + ) + + # Store the API Gateway URL in an SSM Parameter + ssm.StringParameter( + self, + "api_gateway_url", + parameter_name="/SagemakerAPI/URL", + string_value=api.url, + ) + + # Add the usage plan + usage_plan = api.add_usage_plan( + "APIForSagemakerUsagePlan", + name="APIForSagemakerUsagePlan", + throttle=apigateway.ThrottleSettings( + rate_limit=1000, + burst_limit=2000 + ) + ) + # Create the API key + api_key = api.add_api_key("SagemakerAPIKey") + + # Associate the API key with the usage plan + usage_plan.add_api_key(api_key) + + # Store the API key in an SSM Parameter + ssm.StringParameter( + self, + "api_gateway_key", + parameter_name="/SagemakerAPI/APIKey", + string_value=api_key.key_id, # Store the API key value + ) + + # Create the API Gateway integration with the Lambda function + integration = apigateway.LambdaIntegration( + lambda_function, + proxy=True, + ) + + # Create a resource and attach the integration + resource = api.root.add_resource("generateimage") + method = resource.add_method( + http_method="POST", + integration=integration, + api_key_required=True + ) + # Add the API stage and associate it with the usage plan + stage = api.deployment_stage + usage_plan.add_api_stage( + api=api, + stage=api.deployment_stage + ) + + # Add dependency between Lambda function and API Gateway + api.node.add_dependency(lambda_function) + diff --git a/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/template.yaml b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/template.yaml new file mode 100644 index 0000000000..269f82e412 --- /dev/null +++ b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/template.yaml @@ -0,0 +1,16 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: Serverless patterns - Service to Service description + +# Comment on each global +Globals: + + +# Comment each resource section to explain usage +Resources: + + +# List all common outputs for usage +Outputs: + + diff --git a/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/util/sagemaker_endpoint_construct.py b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/util/sagemaker_endpoint_construct.py new file mode 100644 index 0000000000..5743c53037 --- /dev/null +++ b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/util/sagemaker_endpoint_construct.py @@ -0,0 +1,66 @@ +from aws_cdk import ( + aws_sagemaker as sagemaker, + aws_ssm as ssm, + CfnOutput +) +from constructs import Construct + + +class SageMakerEndpointConstruct(Construct): + + def __init__(self, scope: Construct, construct_id: str, + project_prefix: str, + role_arn: str, + model_name: str, + model_bucket_name: str, + model_bucket_key: str, + model_docker_image: str, + variant_name: str, + variant_weight: int, + instance_count: int, + instance_type: str, + environment: dict, + deploy_enable: bool) -> None: + super().__init__(scope, construct_id) + + model = sagemaker.CfnModel(self, f"{model_name}-Model", + execution_role_arn=role_arn, + containers=[ + sagemaker.CfnModel.ContainerDefinitionProperty( + image=model_docker_image, + model_data_url=f"s3://{model_bucket_name}/{model_bucket_key}", + environment=environment + ) + ], + model_name=f"{project_prefix}-{model_name}-Model" + ) + + config = sagemaker.CfnEndpointConfig(self, f"{model_name}-Config", + endpoint_config_name=f"{project_prefix}-{model_name}-Config", + production_variants=[ + sagemaker.CfnEndpointConfig.ProductionVariantProperty( + model_name=model.attr_model_name, + variant_name=variant_name, + initial_variant_weight=variant_weight, + initial_instance_count=instance_count, + instance_type=instance_type + ) + ] + ) + + self.deploy_enable = deploy_enable + if deploy_enable: + self.endpoint = sagemaker.CfnEndpoint(self, f"{model_name}-Endpoint", + endpoint_name=f"{project_prefix}-{model_name}-Endpoint", + endpoint_config_name=config.attr_endpoint_config_name + ) + + CfnOutput(scope=self, id=f"{model_name}EndpointName", value=self.endpoint.endpoint_name) + + @property + def endpoint_name(self) -> str: + return self.endpoint.attr_endpoint_name if self.deploy_enable else "not_yet_deployed" + + @property + def endpoint_arn(self) -> str: + return self.endpoint.ref if self.deploy_enable else "not_yet_deployed" diff --git a/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/util/sagemaker_util.py b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/util/sagemaker_util.py new file mode 100644 index 0000000000..dd336ef597 --- /dev/null +++ b/apigw-lambda-sagemaker-jumpstartendpoint-cdk-python/util/sagemaker_util.py @@ -0,0 +1,35 @@ +import sagemaker +from sagemaker import script_uris +from sagemaker import image_uris +from sagemaker import model_uris + +session = sagemaker.Session() + + +def get_sagemaker_uris(model_id, instance_type, region_name): + + MODEL_VERSION = "*" # latest + SCOPE = "inference" + + inference_image_uri = image_uris.retrieve(region=region_name, + framework=None, + model_id=model_id, + model_version=MODEL_VERSION, + image_scope=SCOPE, + instance_type=instance_type) + + inference_model_uri = model_uris.retrieve(model_id=model_id, + model_version=MODEL_VERSION, + model_scope=SCOPE) + + inference_source_uri = script_uris.retrieve(model_id=model_id, + model_version=MODEL_VERSION, + script_scope=SCOPE) + + model_bucket_name = inference_model_uri.split("/")[2] + model_bucket_key = "/".join(inference_model_uri.split("/")[3:]) + model_docker_image = inference_image_uri + + return {"model_bucket_name": model_bucket_name, "model_bucket_key": model_bucket_key, + "model_docker_image": model_docker_image, "instance_type": instance_type, + "inference_source_uri": inference_source_uri, "region_name": region_name} diff --git a/apigw-lambda-sfn-transcribe-translate-polly-sam/example-pattern.json b/apigw-lambda-sfn-transcribe-translate-polly-sam/example-pattern.json index 5d9230d634..dc6675016e 100644 --- a/apigw-lambda-sfn-transcribe-translate-polly-sam/example-pattern.json +++ b/apigw-lambda-sfn-transcribe-translate-polly-sam/example-pattern.json @@ -28,7 +28,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-lambda-sls/example-pattern.json b/apigw-lambda-sls/example-pattern.json index 534dcca120..32ff6ad390 100644 --- a/apigw-lambda-sls/example-pattern.json +++ b/apigw-lambda-sls/example-pattern.json @@ -45,7 +45,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-lambda-snapstart-terraform/example-pattern.json b/apigw-lambda-snapstart-terraform/example-pattern.json index b40fd620dd..5aaa78f919 100644 --- a/apigw-lambda-snapstart-terraform/example-pattern.json +++ b/apigw-lambda-snapstart-terraform/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-lambda-snapstart/README.md b/apigw-lambda-snapstart/README.md index 659a87771f..60c492160f 100644 --- a/apigw-lambda-snapstart/README.md +++ b/apigw-lambda-snapstart/README.md @@ -1,13 +1,23 @@ # Amazon API Gateway, AWS Lambda and Amazon DynamoDB with Lambda SnapStart for Java +This pattern demonstrates how to create a synchronous REST API using API Gateway, AWS Lambda and DynamoDB. +This pattern is built using [Spring Boot 3](https://spring.io/projects/spring-boot) and leverages the +[AWS Serverless Java Container](https://github.com/awslabs/aws-serverless-java-container) for seamless integration with +AWS Lambda. This pattern also demonstrates the usage of [SnapStart](https://aws.amazon.com/blogs/compute/reducing-java-cold-starts-on-aws-lambda-functions-with-snapstart/) +to improve startup performance. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/apigw-lambda-snapstart. + ## Requirements * [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) * [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed +* [Java 17](https://aws.amazon.com/corretto/) * [Maven](https://maven.apache.org/) * [CURL](https://curl.se/) +* [jq](https://jqlang.github.io/jq/) ## Deployment Instructions diff --git a/apigw-lambda-snapstart/UnicornFunction/pom.xml b/apigw-lambda-snapstart/UnicornFunction/pom.xml index 8853bc1c4f..6828bcf206 100644 --- a/apigw-lambda-snapstart/UnicornFunction/pom.xml +++ b/apigw-lambda-snapstart/UnicornFunction/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.8 + 3.1.1 com.unicorn @@ -15,31 +15,83 @@ Unicorn storage service - 11 + 17 + 1.16.0 UTF-8 UTF-8 - 2.20.0 + 2.20.99 + + + + software.amazon.awssdk + bom + ${aws.sdk.version} + pom + import + + + + + io.github.crac org-crac 0.1.3 + com.amazonaws.serverless - aws-serverless-java-container-springboot2 - 1.9.1 + aws-serverless-java-container-springboot3 + 2.0.0-M1 + org.springframework.boot spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + + + + software.amazon.lambda + powertools-logging + ${powertools.version} + + software.amazon.lambda + powertools-tracing + ${powertools.version} + + + software.amazon.lambda + powertools-metrics + ${powertools.version} + + software.amazon.awssdk dynamodb - ${aws.sdk.version} + + + software.amazon.awssdk + netty-nio-client + + + software.amazon.awssdk + apache-client + + + + + software.amazon.awssdk + dynamodb-enhanced software.amazon.awssdk @@ -54,7 +106,24 @@ software.amazon.awssdk aws-crt-client - ${aws.sdk.version} + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.testcontainers + testcontainers + 1.18.3 + test + + + org.testcontainers + junit-jupiter + 1.18.3 + test @@ -83,6 +152,48 @@ + + + org.apache.maven.plugins + maven-surefire-plugin + + + handler + + + + + + dev.aspectj + aspectj-maven-plugin + 1.13.1 + + 17 + 17 + 17 + + + software.amazon.lambda + powertools-logging + + + software.amazon.lambda + powertools-metrics + + + software.amazon.lambda + powertools-tracing + + + + + + + compile + + + + diff --git a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/StoreApplication.java b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/StoreApplication.java index a4cae79061..89b05d5abc 100644 --- a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/StoreApplication.java +++ b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/StoreApplication.java @@ -1,23 +1,13 @@ package com.unicorn.store; import org.springframework.boot.SpringApplication; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; @SpringBootApplication public class StoreApplication { - public static void main(String[] args) { - SpringApplication.run(StoreApplication.class, args); - } - - @Bean - public ObjectMapper getObjectMapper() { - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - return objectMapper; - } + public static void main(String[] args) { + SpringApplication.run(StoreApplication.class, args); + } } diff --git a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/StreamLambdaHandler.java b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/StreamLambdaHandler.java index 4d637c0c0e..3493295a5a 100644 --- a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/StreamLambdaHandler.java +++ b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/StreamLambdaHandler.java @@ -7,6 +7,11 @@ import com.amazonaws.serverless.proxy.spring.SpringBootProxyHandlerBuilder; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import software.amazon.lambda.powertools.logging.Logging; +import software.amazon.lambda.powertools.metrics.Metrics; +import software.amazon.lambda.powertools.tracing.Tracing; import java.io.IOException; import java.io.InputStream; @@ -14,6 +19,8 @@ public class StreamLambdaHandler implements RequestStreamHandler { + private static final Logger logger = LogManager.getLogger(); + private static final SpringBootLambdaContainerHandler handler; static { @@ -24,12 +31,15 @@ public class StreamLambdaHandler implements RequestStreamHandler { .springBootApplication(StoreApplication.class) .buildAndInitialize(); } catch (ContainerInitializationException e) { - e.printStackTrace(); + logger.error("Something went wrong while initializing Spring Boot Application", e); throw new RuntimeException("Could not initialize Spring Boot application", e); } } @Override + @Tracing + @Logging + @Metrics public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException { handler.proxyStream(inputStream, outputStream, context); diff --git a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/UnicornPrimingResource.java b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/UnicornPrimingResource.java index 5b24205cfe..b790cb3c66 100644 --- a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/UnicornPrimingResource.java +++ b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/UnicornPrimingResource.java @@ -4,6 +4,8 @@ package com.unicorn.store; import com.unicorn.store.controller.UnicornController; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.crac.Context; import org.crac.Core; import org.crac.Resource; @@ -12,6 +14,8 @@ @Configuration public class UnicornPrimingResource implements Resource { + private static final Logger logger = LogManager.getLogger(); + private final UnicornController unicornController; public UnicornPrimingResource(UnicornController unicornController) { @@ -21,9 +25,9 @@ public UnicornPrimingResource(UnicornController unicornController) { @Override public void beforeCheckpoint(Context context) { - System.out.println("beforeCheckpoint hook"); + logger.info("beforeCheckpoint hook"); try { - unicornController.getUnicorn("123"); + unicornController.retrieveUnicorn("123"); } catch (RuntimeException e) { // expected exception when unicorn doesn't exist. } @@ -31,6 +35,6 @@ public void beforeCheckpoint(Context context) { @Override public void afterRestore(Context context) { - System.out.println("afterRestore hook"); + logger.debug("afterRestore hook"); } } diff --git a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/config/DynamoDbConfig.java b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/config/DynamoDbConfig.java new file mode 100644 index 0000000000..d2bf575423 --- /dev/null +++ b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/config/DynamoDbConfig.java @@ -0,0 +1,39 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT-0 + +package com.unicorn.store.config; + +import com.unicorn.store.model.Unicorn; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import software.amazon.awssdk.core.SdkSystemSetting; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbAsyncTable; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedAsyncClient; +import software.amazon.awssdk.enhanced.dynamodb.TableSchema; +import software.amazon.awssdk.http.crt.AwsCrtAsyncHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient; + +@Configuration +public class DynamoDbConfig { + + @Bean + public DynamoDbAsyncClient dynamoDbAsyncClient() { + return DynamoDbAsyncClient.builder() + .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) + .httpClientBuilder(AwsCrtAsyncHttpClient.builder()) + .build(); + } + + @Bean + public DynamoDbEnhancedAsyncClient dynamoDbEnhancedClient(DynamoDbAsyncClient dynamoDbAsyncClient) { + return DynamoDbEnhancedAsyncClient.builder() + .dynamoDbClient(dynamoDbAsyncClient) + .build(); + } + + @Bean + public DynamoDbAsyncTable unicornDynamoDbAsyncTable(DynamoDbEnhancedAsyncClient dynamoDbEnhancedClient) { + return dynamoDbEnhancedClient.table(System.getenv("UNICORN_TABLE_NAME"), TableSchema.fromClass(Unicorn.class)); + } +} diff --git a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/controller/UnicornController.java b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/controller/UnicornController.java index 6c68358b31..c36788de7f 100644 --- a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/controller/UnicornController.java +++ b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/controller/UnicornController.java @@ -1,72 +1,57 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: MIT-0 + package com.unicorn.store.controller; -import com.unicorn.store.exceptions.ResourceNotFoundException; import com.unicorn.store.model.Unicorn; import com.unicorn.store.service.UnicornService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.server.ResponseStatusException; - -import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; -import static org.springframework.http.HttpStatus.NOT_FOUND; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; @RestController +@RequestMapping("/unicorns") public class UnicornController { + private static final Logger logger = LogManager.getLogger(); + private final UnicornService unicornService; - private static final Logger logger = LoggerFactory.getLogger(UnicornController.class); public UnicornController(UnicornService unicornService) { this.unicornService = unicornService; } - @PostMapping("/unicorns") - public ResponseEntity createUnicorn(@RequestBody Unicorn unicorn) { - try { - var savedUnicorn = unicornService.createUnicorn(unicorn); - return ResponseEntity.ok(savedUnicorn); - } catch (Exception e) { - String errorMsg = "Error creating unicorn"; - logger.error(errorMsg, e); - throw new ResponseStatusException(INTERNAL_SERVER_ERROR, errorMsg, e); - } + @GetMapping("/{unicornId}") + public Unicorn retrieveUnicorn(@PathVariable String unicornId) { + logger.debug("Received request to get unicorn with id {}", unicornId); + return unicornService.retrieveUnicorn(unicornId); } - @PutMapping("/unicorns/{unicornId}") - public ResponseEntity updateUnicorn(@RequestBody Unicorn unicorn, @PathVariable String unicornId) { - try { - var savedUnicorn = unicornService.updateUnicorn(unicorn, unicornId); - return ResponseEntity.ok(savedUnicorn); - } catch (Exception e) { - String errorMsg = "Error updating unicorn"; - logger.error(errorMsg, e); - throw new ResponseStatusException(INTERNAL_SERVER_ERROR, errorMsg, e); - } + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public Unicorn createUnicorn(@RequestBody Unicorn unicorn) { + logger.debug("Received request to create a unicorn {}", unicorn); + return unicornService.createUnicorn(unicorn); } - @GetMapping("/unicorns/{unicornId}") - public ResponseEntity getUnicorn(@PathVariable String unicornId) { - try { - var unicorn = unicornService.getUnicorn(unicornId); - return ResponseEntity.ok(unicorn); - } catch (ResourceNotFoundException e) { - String errorMsg = "Unicorn not found"; - logger.error(errorMsg, e); - throw new ResponseStatusException(NOT_FOUND, errorMsg, e); - } + @PutMapping("/{unicornId}") + public Unicorn updateUnicorn(@PathVariable String unicornId, @RequestBody Unicorn unicorn) { + logger.debug("Received request to update unicorn with id {} to {}", unicorn, unicornId); + return unicornService.updateUnicorn(unicornId, unicorn); } - @DeleteMapping("/unicorns/{unicornId}") - public ResponseEntity deleteUnicorn(@PathVariable String unicornId) { - try { - unicornService.deleteUnicorn(unicornId); - return ResponseEntity.ok().build(); - } catch (ResourceNotFoundException e) { - String errorMsg = "Unicorn not found"; - logger.error(errorMsg, e); - throw new ResponseStatusException(NOT_FOUND, errorMsg, e); - } + @DeleteMapping("/{unicornId}") + public void deleteUnicorn(@PathVariable String unicornId) { + logger.debug("Received request to delete unicorn with id {}", unicornId); + unicornService.deleteUnicorn(unicornId); } } diff --git a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/data/DynamoDBUnicornRepository.java b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/data/DynamoDBUnicornRepository.java index 95fb4b1115..81a95a9cf6 100644 --- a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/data/DynamoDBUnicornRepository.java +++ b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/data/DynamoDBUnicornRepository.java @@ -3,78 +3,108 @@ package com.unicorn.store.data; +import com.unicorn.store.exceptions.ResourceDeletionException; +import com.unicorn.store.exceptions.ResourceNotFoundException; +import com.unicorn.store.exceptions.ResourceRetrievalException; +import com.unicorn.store.exceptions.ResourceSaveException; +import com.unicorn.store.exceptions.ResourceUpdateException; import com.unicorn.store.model.Unicorn; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; -import software.amazon.awssdk.core.SdkSystemSetting; -import software.amazon.awssdk.http.crt.AwsCrtAsyncHttpClient; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; -import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.stereotype.Repository; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbAsyncTable; +import software.amazon.awssdk.enhanced.dynamodb.Expression; +import software.amazon.awssdk.enhanced.dynamodb.Key; +import software.amazon.awssdk.enhanced.dynamodb.model.UpdateItemEnhancedRequest; +import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException; -import java.util.Map; import java.util.Optional; -import java.util.UUID; import java.util.concurrent.ExecutionException; -@Service +@Repository public class DynamoDBUnicornRepository implements UnicornRepository { - private static final Logger logger = LoggerFactory.getLogger(DynamoDBUnicornRepository.class); - private static final String PRODUCT_TABLE_NAME = System.getenv("PRODUCT_TABLE_NAME"); + private static final Logger logger = LogManager.getLogger(); - private final DynamoDbAsyncClient dynamoDbClient = DynamoDbAsyncClient.builder() - .region(Region.of(System.getenv(SdkSystemSetting.AWS_REGION.environmentVariable()))) - .httpClientBuilder(AwsCrtAsyncHttpClient.builder()) - .build(); + private final DynamoDbAsyncTable unicornDynamoDbAsyncTable; + + public DynamoDBUnicornRepository(DynamoDbAsyncTable unicornDynamoDbAsyncTable) { + this.unicornDynamoDbAsyncTable = unicornDynamoDbAsyncTable; + } @Override public Unicorn save(Unicorn unicorn) { - String unicornId = UUID.randomUUID().toString(); - unicorn.setId(unicornId); - + logger.info("Saving unicorn with id {} to DynamoDB", unicorn.getId()); try { - dynamoDbClient.putItem(PutItemRequest.builder() - .tableName(PRODUCT_TABLE_NAME) - .item(UnicornMapper.unicornToDynamoDB(unicorn)) - .build()) - .get(); - } catch (InterruptedException | ExecutionException e) { - logger.error("putItem failed with message {}", e.getMessage()); + unicornDynamoDbAsyncTable.putItem(unicorn).get(); + logger.info("Unicorn with id {} saved successfully", unicorn.getId()); + return unicorn; + } catch (ExecutionException e) { + logger.error("Unicorn with id {} could not be saved to DynamoDB", unicorn.getId(), e); + throw new ResourceSaveException(e.getMessage()); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + logger.error("Unicorn with id {} could not be saved to DynamoDB", unicorn.getId(), e); + throw new ResourceSaveException(e.getMessage()); } - - return unicorn; } @Override public Optional findById(String unicornId) { try { - GetItemResponse getItemResponse = dynamoDbClient.getItem(GetItemRequest.builder() - .key(Map.of("PK", AttributeValue.builder().s(unicornId).build())) - .tableName(PRODUCT_TABLE_NAME) - .build()) - .get(); - if (getItemResponse.hasItem()) { - return Optional.of(UnicornMapper.unicornFromDynamoDB(getItemResponse.item())); - } else { + Unicorn unicorn = unicornDynamoDbAsyncTable.getItem(Key.builder().partitionValue(unicornId).build()).get(); + if (unicorn == null) { + logger.info("Unicorn with id {} not found in DynamoDB", unicornId); return Optional.empty(); } - } catch (InterruptedException | ExecutionException e) { - logger.error("getItem failed with message {}", e.getMessage()); - return Optional.empty(); + logger.info("Unicorn with id {} found in DynamoDB", unicornId); + return Optional.of(unicorn); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + logger.error("Unicorn with id {} could not be retrieved from DynamoDB", unicornId, e); + throw new ResourceRetrievalException(e.getMessage()); + } catch (ExecutionException e) { + logger.error("Unicorn with id {} could not be retrieved from DynamoDB", unicornId, e); + throw new ResourceRetrievalException(e.getMessage()); } } @Override - public void delete(Unicorn unicorn) { - dynamoDbClient.deleteItem(DeleteItemRequest.builder() - .key(Map.of("PK", AttributeValue.builder().s(unicorn.getId()).build())) - .tableName(PRODUCT_TABLE_NAME) - .build()); + public void deleteById(String unicornId) { + try { + unicornDynamoDbAsyncTable.deleteItem(Key.builder().partitionValue(unicornId).build()).get(); + logger.info("Unicorn with id {} deleted successfully", unicornId); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + logger.error("Unicorn with id {} could not be deleted from DynamoDB", unicornId, e); + throw new ResourceDeletionException(e.getMessage()); + } catch (ExecutionException e) { + logger.error("Unicorn with id {} could not be deleted from DynamoDB", unicornId, e); + throw new ResourceDeletionException(e.getMessage()); + } } + + @Override + public Unicorn update(Unicorn unicorn) { + UpdateItemEnhancedRequest updateItemEnhancedRequest = UpdateItemEnhancedRequest.builder(Unicorn.class) + .item(unicorn) + .conditionExpression(Expression.builder().expression("attribute_exists(id)").build()) + .build(); + try { + unicornDynamoDbAsyncTable.updateItem(updateItemEnhancedRequest).get(); + logger.info("Unicorn with id {} updated successfully", unicorn.getId()); + return unicorn; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + logger.error("Unicorn with id {} could not be updated in DynamoDB", unicorn.getId(), e); + throw new ResourceUpdateException(e.getMessage()); + } catch (ExecutionException e) { + logger.error("Unicorn with id {} could not be updated in DynamoDB", unicorn.getId(), e); + if (e.getCause() instanceof ConditionalCheckFailedException) { + throw new ResourceNotFoundException(String.format("Unicorn with id %s not found", unicorn.getId())); + } + throw new ResourceUpdateException(e.getMessage()); + } + } + } diff --git a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/data/UnicornMapper.java b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/data/UnicornMapper.java deleted file mode 100644 index 775b49ed16..0000000000 --- a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/data/UnicornMapper.java +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: MIT-0 - -package com.unicorn.store.data; - -import com.unicorn.store.model.Unicorn; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -import java.util.HashMap; -import java.util.Map; - -public class UnicornMapper { - private static final String PK = "PK"; - private static final String NAME = "name"; - private static final String AGE = "age"; - private static final String SIZE = "size"; - private static final String TYPE = "type"; - - public static Unicorn unicornFromDynamoDB(Map item) { - Unicorn unicorn = new Unicorn(); - unicorn.setId(item.get(PK).s()); - unicorn.setType(item.get(TYPE).s()); - unicorn.setSize(item.get(SIZE).s()); - unicorn.setName(item.get(NAME).s()); - unicorn.setAge(item.get(AGE).s()); - - return unicorn; - } - - public static Map unicornToDynamoDB(Unicorn unicorn) { - Map item = new HashMap<>(); - item.put(PK, AttributeValue.builder().s(unicorn.getId()).build()); - item.put(NAME, AttributeValue.builder().s(unicorn.getName()).build()); - item.put(AGE, AttributeValue.builder().s(unicorn.getAge()).build()); - item.put(SIZE, AttributeValue.builder().s(unicorn.getSize()).build()); - item.put(TYPE, AttributeValue.builder().s(unicorn.getType()).build()); - - return item; - } -} diff --git a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/data/UnicornRepository.java b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/data/UnicornRepository.java index 7d18d8f596..fc9da7ee43 100644 --- a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/data/UnicornRepository.java +++ b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/data/UnicornRepository.java @@ -5,9 +5,13 @@ import java.util.Optional; public interface UnicornRepository { + Unicorn save(Unicorn unicorn); Optional findById(String unicornId); - void delete(Unicorn unicorn); + void deleteById(String unicornId); + + Unicorn update(Unicorn unicorn); + } diff --git a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/exceptions/GlobalExceptionHandler.java b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/exceptions/GlobalExceptionHandler.java new file mode 100644 index 0000000000..12df5d3d80 --- /dev/null +++ b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/exceptions/GlobalExceptionHandler.java @@ -0,0 +1,64 @@ +package com.unicorn.store.exceptions; + +import com.unicorn.store.model.ErrorResponse; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.Collections; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(ResourceSaveException.class) + public ResponseEntity resourceSaveException() { + ErrorResponse errorResponse = new ErrorResponse( + "Something went wrong while saving Unicorn.", + Collections.singletonList("Unicorn could not be saved.") + ); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(errorResponse); + } + + @ExceptionHandler(ResourceNotFoundException.class) + public ResponseEntity resourceNotFoundException() { + ErrorResponse errorResponse = new ErrorResponse( + "Unicorn was not found in DynamoDB.", + Collections.singletonList("Requested Unicorn was not found.") + ); + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .body(errorResponse); + } + + @ExceptionHandler(ResourceDeletionException.class) + public ResponseEntity resourceDeletionException() { + ErrorResponse errorResponse = new ErrorResponse( + "Something went wrong while deleting Unicorn.", + Collections.singletonList("Unicorn could not be deleted.") + ); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(errorResponse); + } + + @ExceptionHandler(ResourceRetrievalException.class) + public ResponseEntity resourceRetrievalException() { + ErrorResponse errorResponse = new ErrorResponse( + "Something went wrong while retrieving Unicorn.", + Collections.singletonList("Unicorn could not be retrieved.") + ); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(errorResponse); + } + + @ExceptionHandler(ResourceUpdateException.class) + public ResponseEntity resourceUpdateException() { + ErrorResponse errorResponse = new ErrorResponse( + "Something went wrong while updating Unicorn.", + Collections.singletonList("Unicorn could not be updated.") + ); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(errorResponse); + } + +} diff --git a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/exceptions/ResourceDeletionException.java b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/exceptions/ResourceDeletionException.java new file mode 100644 index 0000000000..2372e5f370 --- /dev/null +++ b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/exceptions/ResourceDeletionException.java @@ -0,0 +1,8 @@ +package com.unicorn.store.exceptions; + +public class ResourceDeletionException extends RuntimeException { + + public ResourceDeletionException(String message) { + super(message); + } +} diff --git a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/exceptions/ResourceNotFoundException.java b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/exceptions/ResourceNotFoundException.java index 1740dbbc2d..ec80214791 100644 --- a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/exceptions/ResourceNotFoundException.java +++ b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/exceptions/ResourceNotFoundException.java @@ -2,4 +2,8 @@ public class ResourceNotFoundException extends RuntimeException { + public ResourceNotFoundException(String message) { + super(message); + } + } diff --git a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/exceptions/ResourceRetrievalException.java b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/exceptions/ResourceRetrievalException.java new file mode 100644 index 0000000000..05ea4ec7cd --- /dev/null +++ b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/exceptions/ResourceRetrievalException.java @@ -0,0 +1,8 @@ +package com.unicorn.store.exceptions; + +public class ResourceRetrievalException extends RuntimeException { + + public ResourceRetrievalException(String message) { + super(message); + } +} diff --git a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/exceptions/ResourceSaveException.java b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/exceptions/ResourceSaveException.java new file mode 100644 index 0000000000..a3828cfadc --- /dev/null +++ b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/exceptions/ResourceSaveException.java @@ -0,0 +1,8 @@ +package com.unicorn.store.exceptions; + +public class ResourceSaveException extends RuntimeException { + + public ResourceSaveException(String message) { + super(message); + } +} diff --git a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/exceptions/ResourceUpdateException.java b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/exceptions/ResourceUpdateException.java new file mode 100644 index 0000000000..1cffde064a --- /dev/null +++ b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/exceptions/ResourceUpdateException.java @@ -0,0 +1,8 @@ +package com.unicorn.store.exceptions; + +public class ResourceUpdateException extends RuntimeException { + + public ResourceUpdateException(String message) { + super(message); + } +} diff --git a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/model/ErrorResponse.java b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/model/ErrorResponse.java new file mode 100644 index 0000000000..b0be42d4cf --- /dev/null +++ b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/model/ErrorResponse.java @@ -0,0 +1,9 @@ +package com.unicorn.store.model; + +import java.util.List; + +public record ErrorResponse( + String message, + List errors +) { +} diff --git a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/model/Unicorn.java b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/model/Unicorn.java index f52908b282..3a19b27daa 100644 --- a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/model/Unicorn.java +++ b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/model/Unicorn.java @@ -1,5 +1,9 @@ package com.unicorn.store.model; +import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean; +import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey; + +@DynamoDbBean public class Unicorn { private String id; @@ -8,6 +12,7 @@ public class Unicorn { private String size; private String type; + @DynamoDbPartitionKey public String getId() { return id; } diff --git a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/service/UnicornService.java b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/service/UnicornService.java index 98d3aac012..48bbad3fad 100644 --- a/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/service/UnicornService.java +++ b/apigw-lambda-snapstart/UnicornFunction/src/main/java/com/unicorn/store/service/UnicornService.java @@ -5,8 +5,11 @@ import com.unicorn.store.model.Unicorn; import org.springframework.stereotype.Service; +import java.util.UUID; + @Service public class UnicornService { + private final UnicornRepository unicornRepository; public UnicornService(UnicornRepository unicornRepository) { @@ -14,20 +17,22 @@ public UnicornService(UnicornRepository unicornRepository) { } public Unicorn createUnicorn(Unicorn unicorn) { + String unicornId = UUID.randomUUID().toString(); + unicorn.setId(unicornId); return unicornRepository.save(unicorn); } - public Unicorn updateUnicorn(Unicorn unicorn, String unicornId) { - unicorn.setId(unicornId); - return unicornRepository.save(unicorn); + public Unicorn retrieveUnicorn(String unicornId) { + return unicornRepository.findById(unicornId) + .orElseThrow(() -> new ResourceNotFoundException(String.format("Unicorn with id %s not found", unicornId))); } - public Unicorn getUnicorn(String unicornId) { - return unicornRepository.findById(unicornId).orElseThrow(ResourceNotFoundException::new); + public Unicorn updateUnicorn(String unicornId, Unicorn unicorn) { + unicorn.setId(unicornId); + return unicornRepository.update(unicorn); } public void deleteUnicorn(String unicornId) { - Unicorn unicorn = unicornRepository.findById(unicornId).orElseThrow(ResourceNotFoundException::new); - unicornRepository.delete(unicorn); + unicornRepository.deleteById(unicornId); } } diff --git a/apigw-lambda-snapstart/UnicornFunction/src/main/resources/log4j2.xml b/apigw-lambda-snapstart/UnicornFunction/src/main/resources/log4j2.xml new file mode 100644 index 0000000000..e1fd14ceaf --- /dev/null +++ b/apigw-lambda-snapstart/UnicornFunction/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apigw-lambda-snapstart/UnicornFunction/src/test/java/com/unicorn/store/StoreApplicationTest.java b/apigw-lambda-snapstart/UnicornFunction/src/test/java/com/unicorn/store/StoreApplicationTest.java new file mode 100644 index 0000000000..793bee9851 --- /dev/null +++ b/apigw-lambda-snapstart/UnicornFunction/src/test/java/com/unicorn/store/StoreApplicationTest.java @@ -0,0 +1,245 @@ +package com.unicorn.store; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.unicorn.store.model.Unicorn; +import org.json.JSONException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.skyscreamer.jsonassert.JSONAssert; +import org.skyscreamer.jsonassert.JSONCompareMode; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbAsyncTable; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedAsyncClient; +import software.amazon.awssdk.enhanced.dynamodb.Key; +import software.amazon.awssdk.enhanced.dynamodb.TableSchema; +import software.amazon.awssdk.http.crt.AwsCrtAsyncHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +@Testcontainers +@SpringBootTest( + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties = {"spring.main.allow-bean-definition-overriding=true"} +) +@DisplayName("LambdaApplication Integration Test") +class StoreApplicationTest { + + @Container + public static final GenericContainer DYNAMODB_CONTAINER = new GenericContainer(DockerImageName.parse("amazon/dynamodb-local:2.0.0")) + .withExposedPorts(8000); + private final HttpClient httpClient = HttpClient.newHttpClient(); + + @Autowired + private DynamoDbAsyncTable dynamoDbAsyncTable; + + @LocalServerPort + private Integer serverPort; + + private String serverUrl; + + @BeforeEach + void setup() { + serverUrl = String.format("http://localhost:%d", serverPort); + dynamoDbAsyncTable.createTable().join(); + Unicorn unicorn = new Unicorn(); + unicorn.setId("123"); + unicorn.setName("Something"); + unicorn.setAge("Older"); + unicorn.setSize("Very big"); + unicorn.setType("Animal"); + dynamoDbAsyncTable.putItem(unicorn).join(); + } + + @AfterEach + void teardown() { + dynamoDbAsyncTable.deleteTable().join(); + } + + @Test + @DisplayName("GET /unicorns/{unicornId} - 200 Success") + void testRetrieveUnicornSuccess() throws IOException, InterruptedException, JSONException { + HttpRequest request = HttpRequest.newBuilder() + .GET() + .uri(URI.create(serverUrl.concat("/unicorns/123"))) + .build(); + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + assertEquals(200, response.statusCode()); + JSONAssert.assertEquals(""" + { + "id": "123", + "name": "Something", + "age": "Older", + "size": "Very big", + "type": "Animal" + } + """, response.body(), JSONCompareMode.STRICT); + } + + @Test + @DisplayName("GET /unicorns/{unicornId} - 404 Not Found") + void testRetrieveUnicornNotFound() throws JSONException, IOException, InterruptedException { + HttpRequest request = HttpRequest.newBuilder() + .GET() + .uri(URI.create(serverUrl.concat("/unicorns/999"))) + .build(); + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + assertEquals(404, response.statusCode()); + JSONAssert.assertEquals(""" + { + "message": "Unicorn was not found in DynamoDB.", + "errors": [ + "Requested Unicorn was not found." + ] + } + """, response.body(), JSONCompareMode.STRICT); + } + + @Test + @DisplayName("POST /unicorns/ - 201 Created") + void testCreateUnicornCreated() throws IOException, InterruptedException, JSONException { + HttpRequest request = HttpRequest.newBuilder() + .POST(HttpRequest.BodyPublishers.ofString(""" + { + "name": "Test Name", + "age": "Test Age", + "size": "Test Size", + "type": "Test Type" + } + """)) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .uri(URI.create(serverUrl.concat("/unicorns"))) + .build(); + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + assertEquals(201, response.statusCode()); + Unicorn savedUnicorn = new ObjectMapper().readValue(response.body(), Unicorn.class); + JSONAssert.assertEquals(String.format(""" + { + "id": "%s", + "name": "Test Name", + "age": "Test Age", + "size": "Test Size", + "type": "Test Type" + } + """, savedUnicorn.getId()), response.body(), JSONCompareMode.STRICT); + assertNotNull(dynamoDbAsyncTable.getItem(Key.builder().partitionValue(savedUnicorn.getId()).build())); + } + + @Test + @DisplayName("PUT /unicorns/{unicornId} - 200 Success") + void testPutUnicornSuccess() throws IOException, InterruptedException, JSONException { + HttpRequest request = HttpRequest.newBuilder() + .PUT(HttpRequest.BodyPublishers.ofString(""" + { + "name": "Test Name", + "age": "Test Age", + "size": "Test Size", + "type": "Test Type" + } + """)) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .uri(URI.create(serverUrl.concat("/unicorns/123"))) + .build(); + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + assertEquals(200, response.statusCode()); + JSONAssert.assertEquals(""" + { + "id": "123", + "name": "Test Name", + "age": "Test Age", + "size": "Test Size", + "type": "Test Type" + } + """, response.body(), JSONCompareMode.STRICT); + Unicorn updatedUnicorn = dynamoDbAsyncTable.getItem(Key.builder().partitionValue("123").build()).join(); + assertNotNull(updatedUnicorn); + assertEquals("Test Name", updatedUnicorn.getName()); + assertEquals("Test Age", updatedUnicorn.getAge()); + assertEquals("Test Size", updatedUnicorn.getSize()); + assertEquals("Test Type", updatedUnicorn.getType()); + } + + @Test + @DisplayName("PUT /unicorns/{unicornId} - 404 Not Found") + void testPutUnicornNotFound() throws IOException, InterruptedException { + HttpRequest request = HttpRequest.newBuilder() + .PUT(HttpRequest.BodyPublishers.ofString(""" + { + "name": "Test Name", + "age": "Test Age", + "size": "Test Size", + "type": "Test Type" + } + """)) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .uri(URI.create(serverUrl.concat("/unicorns/999"))) + .build(); + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + assertEquals(404, response.statusCode()); + Unicorn updatedUnicorn = dynamoDbAsyncTable.getItem(Key.builder().partitionValue("123").build()).join(); + assertNotNull(updatedUnicorn); + assertEquals("Something", updatedUnicorn.getName()); + assertEquals("Older", updatedUnicorn.getAge()); + assertEquals("Very big", updatedUnicorn.getSize()); + assertEquals("Animal", updatedUnicorn.getType()); + } + + @Test + @DisplayName("DELETE /unicorns/{unicornId} - 200 Success") + void testDeleteUnicornSuccess() throws IOException, InterruptedException { + assertNotNull(dynamoDbAsyncTable.getItem(Key.builder().partitionValue("123").build()).join()); + HttpRequest request = HttpRequest.newBuilder() + .DELETE() + .uri(URI.create(serverUrl.concat("/unicorns/123"))) + .build(); + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + assertEquals(200, response.statusCode()); + assertNull(dynamoDbAsyncTable.getItem(Key.builder().partitionValue("123").build()).join()); + } + + @TestConfiguration + static class StoreApplicationTestConfig { + + @Bean + @Primary + public DynamoDbAsyncClient dynamoDbAsyncClient() { + return DynamoDbAsyncClient.builder() + .region(Region.EU_WEST_1) + .endpointOverride(URI.create(String.format("http://%s:%d", DYNAMODB_CONTAINER.getHost(), DYNAMODB_CONTAINER.getFirstMappedPort()))) + .httpClient(AwsCrtAsyncHttpClient.create()) + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("fakeMyKeyId", "fakeSecretAccessKey"))) + .build(); + } + + @Bean + @Primary + public DynamoDbAsyncTable unicornDynamoDbAsyncTable(DynamoDbEnhancedAsyncClient dynamoDbEnhancedClient) { + return dynamoDbEnhancedClient.table("Unicorn-Local", TableSchema.fromClass(Unicorn.class)); + } + } + +} \ No newline at end of file diff --git a/apigw-lambda-snapstart/UnicornFunction/src/test/java/com/unicorn/store/UnicornPrimingResourceTest.java b/apigw-lambda-snapstart/UnicornFunction/src/test/java/com/unicorn/store/UnicornPrimingResourceTest.java new file mode 100644 index 0000000000..ebfe06c45c --- /dev/null +++ b/apigw-lambda-snapstart/UnicornFunction/src/test/java/com/unicorn/store/UnicornPrimingResourceTest.java @@ -0,0 +1,54 @@ +package com.unicorn.store; + +import com.unicorn.store.controller.UnicornController; +import org.crac.Resource; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +@DisplayName("UnicornPrimingResource Unit Test") +class UnicornPrimingResourceTest { + + private UnicornPrimingResource unicornPrimingResource; + + @Mock + private UnicornController unicornController; + + @BeforeEach + void setup() { + this.unicornPrimingResource = new UnicornPrimingResource(unicornController); + } + + @Test + @DisplayName("UnicornPrimingResourceTest is a CRAC Resource") + void testIsACracResource() { + assertInstanceOf(Resource.class, unicornPrimingResource); + } + + @Test + @DisplayName("UnicornPrimingResourceTest JIT Compiles code paths before checkpoint") + void testJitCompilationPreCheckpoint() { + unicornPrimingResource.beforeCheckpoint(null); + verify(unicornController, times(1)).retrieveUnicorn(any()); + } + + @Test + @DisplayName("Before Checkpoint Hook does not throw exception") + void testBeforeCheckpointNoException() { + doThrow(new RuntimeException("Some exception")) + .when(unicornController).retrieveUnicorn(any()); + assertDoesNotThrow(() -> unicornPrimingResource.beforeCheckpoint(null)); + } + +} \ No newline at end of file diff --git a/apigw-lambda-snapstart/UnicornFunction/src/test/java/com/unicorn/store/controller/UnicornControllerTest.java b/apigw-lambda-snapstart/UnicornFunction/src/test/java/com/unicorn/store/controller/UnicornControllerTest.java new file mode 100644 index 0000000000..31e2bed479 --- /dev/null +++ b/apigw-lambda-snapstart/UnicornFunction/src/test/java/com/unicorn/store/controller/UnicornControllerTest.java @@ -0,0 +1,260 @@ +package com.unicorn.store.controller; + +import com.unicorn.store.exceptions.ResourceDeletionException; +import com.unicorn.store.exceptions.ResourceNotFoundException; +import com.unicorn.store.exceptions.ResourceRetrievalException; +import com.unicorn.store.exceptions.ResourceSaveException; +import com.unicorn.store.exceptions.ResourceUpdateException; +import com.unicorn.store.model.Unicorn; +import com.unicorn.store.service.UnicornService; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(UnicornController.class) +@DisplayName("Unicorn Controller Web Layer Test") +class UnicornControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private UnicornService unicornService; + + @Test + @DisplayName("GET /unicorns/{unicornId} - 200") + void testGetUnicornByIdSuccess() throws Exception { + when(unicornService.retrieveUnicorn("123")) + .thenReturn(createTestUnicorn()); + mockMvc.perform(get("/unicorns/123")) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { + "id": "123", + "name": "Something", + "age": "Older", + "size": "Very big", + "type": "Animal" + } + """, true)); + } + + @Test + @DisplayName("GET /unicorns/{unicornId} - 404") + void testGetUnicornByIdNotFound() throws Exception { + when(unicornService.retrieveUnicorn("999")) + .thenThrow(new ResourceNotFoundException("Unicorn with id 999 not found.")); + mockMvc.perform(get("/unicorns/999")) + .andDo(print()) + .andExpect(status().isNotFound()) + .andExpect(content().json(""" + { + "message": "Unicorn was not found in DynamoDB.", + "errors": [ + "Requested Unicorn was not found." + ] + } + """, true)); + } + + @Test + @DisplayName("GET /unicorns/{unicornId} - 500") + void testGetUnicornByIdSomethingWentWrong() throws Exception { + when(unicornService.retrieveUnicorn("999")) + .thenThrow(new ResourceRetrievalException("Could not retrieve unicorn with id 999 from DynamoDB.")); + mockMvc.perform(get("/unicorns/999")) + .andDo(print()) + .andExpect(status().isInternalServerError()) + .andExpect(content().json(""" + { + "message": "Something went wrong while retrieving Unicorn.", + "errors": [ + "Unicorn could not be retrieved." + ] + } + """, true)); + } + + @Test + @DisplayName("POST /unicorns/ - 201") + void testPostUnicornCreated() throws Exception { + when(unicornService.createUnicorn(any())) + .thenReturn(createTestUnicorn()); + mockMvc.perform(post("/unicorns").contentType(MediaType.APPLICATION_JSON).content(""" + { + "name": "Something", + "age": "Older", + "size": "Very big", + "type": "Animal" + } + """)) + .andDo(print()) + .andExpect(status().isCreated()) + .andExpect(content().json(""" + { + "id": "123", + "name": "Something", + "age": "Older", + "size": "Very big", + "type": "Animal" + } + """, true)); + } + + @Test + @DisplayName("POST /unicorns/ - 500") + void testPostUnicornSomethingWentWrong() throws Exception { + when(unicornService.createUnicorn(any())) + .thenThrow(new ResourceSaveException("Unicorn could not be saved to DynamoDB.")); + mockMvc.perform(post("/unicorns").contentType(MediaType.APPLICATION_JSON).content(""" + { + "name": "Something", + "age": "Older", + "size": "Very big", + "type": "Animal" + } + """)) + .andDo(print()) + .andExpect(status().isInternalServerError()) + .andExpect(content().json(""" + { + "message": "Something went wrong while saving Unicorn.", + "errors": [ + "Unicorn could not be saved." + ] + } + """, true)); + } + + @Test + @DisplayName("PUT /unicorns/{unicornId} - 200") + void testPutUnicornSuccess() throws Exception { + when(unicornService.updateUnicorn(eq("123"), any())) + .thenReturn(createTestUnicorn()); + when(unicornService.createUnicorn(any())) + .thenReturn(createTestUnicorn()); + mockMvc.perform(put("/unicorns/123").contentType(MediaType.APPLICATION_JSON).content(""" + { + "name": "Something", + "age": "Older", + "size": "Very big", + "type": "Animal" + } + """)) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(content().json(""" + { + "id": "123", + "name": "Something", + "age": "Older", + "size": "Very big", + "type": "Animal" + } + """, true)); + } + + @Test + @DisplayName("PUT /unicorns/{unicornId} - 404") + void testPutUnicornNotFound() throws Exception { + when(unicornService.updateUnicorn(eq("999"), any())) + .thenThrow(new ResourceNotFoundException("Unicorn with id 999 not found.")); + mockMvc.perform(put("/unicorns/999").contentType(MediaType.APPLICATION_JSON).content(""" + { + "name": "Something", + "age": "Older", + "size": "Very big", + "type": "Animal" + } + """)) + .andDo(print()) + .andExpect(status().isNotFound()) + .andExpect(content().json(""" + { + "message": "Unicorn was not found in DynamoDB.", + "errors": [ + "Requested Unicorn was not found." + ] + } + """, true)); + } + + @Test + @DisplayName("PUT /unicorns/{unicornId} - 500") + void testPutUnicornSomethingWentWrong() throws Exception { + when(unicornService.updateUnicorn(eq("999"), any())) + .thenThrow(new ResourceUpdateException("Unicorn could not be updated in DynamoDB.")); + mockMvc.perform(put("/unicorns/999").contentType(MediaType.APPLICATION_JSON).content(""" + { + "name": "Something", + "age": "Older", + "size": "Very big", + "type": "Animal" + } + """)) + .andDo(print()) + .andExpect(status().isInternalServerError()) + .andExpect(content().json(""" + { + "message": "Something went wrong while updating Unicorn.", + "errors": [ + "Unicorn could not be updated." + ] + } + """, true)); + } + + @Test + @DisplayName("DELETE /unicorns/{unicornId} - 200") + void testDeleteUnicornSuccess() throws Exception { + mockMvc.perform(delete("/unicorns/123")) + .andDo(print()) + .andExpect(status().isOk()); + } + + @Test + @DisplayName("DELETE /unicorns/{unicornId} - 500") + void testDeleteUnicornSomethingWentWrong() throws Exception { + doThrow(new ResourceDeletionException("Could not delete unicorn with id 999 from DynamoDB.")) + .when(unicornService).deleteUnicorn("999"); + mockMvc.perform(delete("/unicorns/999")) + .andDo(print()) + .andExpect(status().isInternalServerError()) + .andExpect(content().json(""" + { + "message": "Something went wrong while deleting Unicorn.", + "errors": [ + "Unicorn could not be deleted." + ] + } + """, true)); + } + + private Unicorn createTestUnicorn() { + Unicorn unicorn = new Unicorn(); + unicorn.setId("123"); + unicorn.setName("Something"); + unicorn.setAge("Older"); + unicorn.setSize("Very big"); + unicorn.setType("Animal"); + return unicorn; + } + +} \ No newline at end of file diff --git a/apigw-lambda-snapstart/UnicornFunction/src/test/java/com/unicorn/store/data/DynamoDBUnicornRepositoryTest.java b/apigw-lambda-snapstart/UnicornFunction/src/test/java/com/unicorn/store/data/DynamoDBUnicornRepositoryTest.java new file mode 100644 index 0000000000..6d40713a5e --- /dev/null +++ b/apigw-lambda-snapstart/UnicornFunction/src/test/java/com/unicorn/store/data/DynamoDBUnicornRepositoryTest.java @@ -0,0 +1,148 @@ +package com.unicorn.store.data; + +import com.unicorn.store.exceptions.ResourceNotFoundException; +import com.unicorn.store.model.Unicorn; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.testcontainers.utility.DockerImageName; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbAsyncTable; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedAsyncClient; +import software.amazon.awssdk.enhanced.dynamodb.Key; +import software.amazon.awssdk.enhanced.dynamodb.TableSchema; +import software.amazon.awssdk.http.crt.AwsCrtAsyncHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient; + +import java.net.URI; +import java.util.Optional; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Testcontainers +@DisplayName("DynamoDBUnicornRepository Unit Test") +class DynamoDBUnicornRepositoryTest { + + @Container + public static final GenericContainer DYNAMODB_CONTAINER = new GenericContainer(DockerImageName.parse("amazon/dynamodb-local:2.0.0")) + .withExposedPorts(8000); + + private DynamoDBUnicornRepository dynamoDBUnicornRepository; + + @BeforeAll + static void setupAllTests() { + unicornAsyncTable().createTable().join(); + } + + private static DynamoDbAsyncTable unicornAsyncTable() { + DynamoDbAsyncClient dynamoDbAsyncClient = DynamoDbAsyncClient.builder() + .region(Region.EU_WEST_1) + .endpointOverride(URI.create(String.format("http://%s:%d", DYNAMODB_CONTAINER.getHost(), DYNAMODB_CONTAINER.getFirstMappedPort()))) + .httpClient(AwsCrtAsyncHttpClient.create()) + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("fakeMyKeyId", "fakeSecretAccessKey"))) + .build(); + DynamoDbEnhancedAsyncClient dynamoDbEnhancedAsyncClient = DynamoDbEnhancedAsyncClient.builder() + .dynamoDbClient(dynamoDbAsyncClient) + .build(); + return dynamoDbEnhancedAsyncClient.table("Unicorn-Local", TableSchema.fromClass(Unicorn.class)); + } + + @BeforeEach + void setup() { + this.dynamoDBUnicornRepository = new DynamoDBUnicornRepository(unicornAsyncTable()); + } + + @Test + @DisplayName("Unicorn is saved to DynamoDB") + void testSave() { + Unicorn unicornToSave = createTestUnicorn(); + assertNull(unicornAsyncTable().getItem(Key.builder().partitionValue(unicornToSave.getId()).build()).join()); + Unicorn savedUnicorn = dynamoDBUnicornRepository.save(unicornToSave); + assertNotNull(unicornAsyncTable().getItem(Key.builder().partitionValue(unicornToSave.getId()).build()).join()); + assertEquals(unicornToSave.getId(), savedUnicorn.getId()); + assertEquals(unicornToSave.getName(), savedUnicorn.getName()); + assertEquals(unicornToSave.getAge(), savedUnicorn.getAge()); + assertEquals(unicornToSave.getType(), savedUnicorn.getType()); + assertEquals(unicornToSave.getSize(), savedUnicorn.getSize()); + } + + @Test + @DisplayName("Unicorn is found in DynamoDB") + void testFindByIdFound() { + Unicorn existingUnicorn = createTestUnicorn(); + unicornAsyncTable().putItem(existingUnicorn).join(); + + Optional unicornFound = dynamoDBUnicornRepository.findById(existingUnicorn.getId()); + assertTrue(unicornFound.isPresent()); + Unicorn unicornRetrieved = unicornFound.get(); + assertEquals(existingUnicorn.getId(), unicornRetrieved.getId()); + assertEquals(existingUnicorn.getName(), unicornRetrieved.getName()); + assertEquals(existingUnicorn.getAge(), unicornRetrieved.getAge()); + assertEquals(existingUnicorn.getType(), unicornRetrieved.getType()); + assertEquals(existingUnicorn.getSize(), unicornRetrieved.getSize()); + } + + @Test + @DisplayName("Unicorn is not found in DynamoDB") + void testFindByIdNotFound() { + Optional unicornFound = dynamoDBUnicornRepository.findById("zzz"); + assertTrue(unicornFound.isEmpty()); + } + + @Test + @DisplayName("Unicorn is not deleted in DynamoDB") + void testDelete() { + Unicorn existingUnicorn = createTestUnicorn(); + unicornAsyncTable().putItem(existingUnicorn).join(); + + assertNotNull(unicornAsyncTable().getItem(Key.builder().partitionValue(existingUnicorn.getId()).build()).join()); + dynamoDBUnicornRepository.deleteById(existingUnicorn.getId()); + assertNull(unicornAsyncTable().getItem(Key.builder().partitionValue(existingUnicorn.getId()).build()).join()); + } + + @Test + @DisplayName("Unicorn is updated if found in DynamoDB") + void testUpdateFound() { + Unicorn existingUnicorn = createTestUnicorn(); + unicornAsyncTable().putItem(existingUnicorn).join(); + + Unicorn fromDynamoBeforeUpdate = unicornAsyncTable().getItem(Key.builder().partitionValue(existingUnicorn.getId()).build()).join(); + assertEquals("Something", fromDynamoBeforeUpdate.getName()); + + existingUnicorn.setName("Changed Name"); + dynamoDBUnicornRepository.update(existingUnicorn); + + Unicorn fromDynamoAfterUpdate = unicornAsyncTable().getItem(Key.builder().partitionValue(existingUnicorn.getId()).build()).join(); + assertEquals("Changed Name", fromDynamoAfterUpdate.getName()); + } + + @Test + @DisplayName("ResourceNotFoundException if Unicorn not found in DynamoDB during Update") + void testUpdateNotFound() { + Unicorn unicorn = createTestUnicorn(); + assertNull(unicornAsyncTable().getItem(Key.builder().partitionValue(unicorn.getId()).build()).join()); + assertThrows(ResourceNotFoundException.class, () -> dynamoDBUnicornRepository.update(unicorn)); + } + + private Unicorn createTestUnicorn() { + Unicorn unicorn = new Unicorn(); + unicorn.setId(UUID.randomUUID().toString()); + unicorn.setName("Something"); + unicorn.setAge("Older"); + unicorn.setSize("Very big"); + unicorn.setType("Animal"); + return unicorn; + } + +} \ No newline at end of file diff --git a/apigw-lambda-snapstart/UnicornFunction/src/test/java/com/unicorn/store/exceptions/GlobalExceptionHandlerTest.java b/apigw-lambda-snapstart/UnicornFunction/src/test/java/com/unicorn/store/exceptions/GlobalExceptionHandlerTest.java new file mode 100644 index 0000000000..08e2a7cce9 --- /dev/null +++ b/apigw-lambda-snapstart/UnicornFunction/src/test/java/com/unicorn/store/exceptions/GlobalExceptionHandlerTest.java @@ -0,0 +1,62 @@ +package com.unicorn.store.exceptions; + +import com.unicorn.store.model.ErrorResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@DisplayName("GlobalExceptionHandler Unit Test") +class GlobalExceptionHandlerTest { + + private GlobalExceptionHandler globalExceptionHandler; + + @BeforeEach + void setup() { + this.globalExceptionHandler = new GlobalExceptionHandler(); + } + + @Test + @DisplayName("ResourceSaveException - 500 Response Code") + void testResourceSaveException() { + ResponseEntity errorResponseResponseEntity = globalExceptionHandler.resourceSaveException(); + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, errorResponseResponseEntity.getStatusCode()); + assertEquals("Something went wrong while saving Unicorn.", errorResponseResponseEntity.getBody().message()); + } + + @Test + @DisplayName("ResourceNotFoundException - 404 Response Code") + void testResourceNotFoundException() { + ResponseEntity errorResponseResponseEntity = globalExceptionHandler.resourceNotFoundException(); + assertEquals(HttpStatus.NOT_FOUND, errorResponseResponseEntity.getStatusCode()); + assertEquals("Unicorn was not found in DynamoDB.", errorResponseResponseEntity.getBody().message()); + } + + @Test + @DisplayName("ResourceDeletionException - 500 Response Code") + void testResourceDeletionException() { + ResponseEntity errorResponseResponseEntity = globalExceptionHandler.resourceDeletionException(); + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, errorResponseResponseEntity.getStatusCode()); + assertEquals("Something went wrong while deleting Unicorn.", errorResponseResponseEntity.getBody().message()); + } + + @Test + @DisplayName("ResourceRetrievalException - 500 Response Code") + void testResourceRetrievalException() { + ResponseEntity errorResponseResponseEntity = globalExceptionHandler.resourceRetrievalException(); + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, errorResponseResponseEntity.getStatusCode()); + assertEquals("Something went wrong while retrieving Unicorn.", errorResponseResponseEntity.getBody().message()); + } + + @Test + @DisplayName("ResourceUpdateException - 500 Response Code") + void testResourceUpdateException() { + ResponseEntity errorResponseResponseEntity = globalExceptionHandler.resourceUpdateException(); + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, errorResponseResponseEntity.getStatusCode()); + assertEquals("Something went wrong while updating Unicorn.", errorResponseResponseEntity.getBody().message()); + } + +} \ No newline at end of file diff --git a/apigw-lambda-snapstart/UnicornFunction/src/test/java/com/unicorn/store/service/UnicornServiceTest.java b/apigw-lambda-snapstart/UnicornFunction/src/test/java/com/unicorn/store/service/UnicornServiceTest.java new file mode 100644 index 0000000000..378efdb98e --- /dev/null +++ b/apigw-lambda-snapstart/UnicornFunction/src/test/java/com/unicorn/store/service/UnicornServiceTest.java @@ -0,0 +1,111 @@ +package com.unicorn.store.service; + +import com.unicorn.store.data.UnicornRepository; +import com.unicorn.store.exceptions.ResourceNotFoundException; +import com.unicorn.store.model.Unicorn; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class UnicornServiceTest { + + @Mock + private UnicornRepository unicornRepository; + + @Captor + private ArgumentCaptor unicornArgumentCaptor; + + private UnicornService unicornService; + + @BeforeEach + void setup() { + this.unicornService = new UnicornService(unicornRepository); + } + + @Test + @DisplayName("Create Unicorn saves Unicorn to Repository") + void testCreateUnicorn() { + when(unicornRepository.save(any())).thenReturn(createTestUnicornWithId()); + Unicorn savedUnicorn = unicornService.createUnicorn(createTestUnicorn()); + verify(unicornRepository, times(1)).save(unicornArgumentCaptor.capture()); + assertEquals(savedUnicorn.getName(), unicornArgumentCaptor.getValue().getName()); + assertEquals(savedUnicorn.getAge(), unicornArgumentCaptor.getValue().getAge()); + assertEquals(savedUnicorn.getSize(), unicornArgumentCaptor.getValue().getSize()); + assertEquals(savedUnicorn.getType(), unicornArgumentCaptor.getValue().getType()); + } + + @Test + @DisplayName("Retrieve Unicorn retrieves Unicorn from Repository if found") + void testRetrieveUnicornFound() { + when(unicornRepository.findById("123")).thenReturn(Optional.of(createTestUnicornWithId())); + Unicorn retrievedUnicorn = unicornService.retrieveUnicorn("123"); + verify(unicornRepository, times(1)).findById("123"); + assertEquals("123", retrievedUnicorn.getId()); + assertEquals("Something", retrievedUnicorn.getName()); + assertEquals("Older", retrievedUnicorn.getAge()); + assertEquals("Very big", retrievedUnicorn.getSize()); + assertEquals("Animal", retrievedUnicorn.getType()); + } + + @Test + @DisplayName("Retrieve Unicorn throws ResourceNotFoundException when Unicorn not found in Repository") + void testRetrieveUnicornNotFound() { + when(unicornRepository.findById("123")).thenReturn(Optional.empty()); + ResourceNotFoundException resourceNotFoundException = assertThrows(ResourceNotFoundException.class, + () -> unicornService.retrieveUnicorn("123")); + verify(unicornRepository, times(1)).findById("123"); + assertEquals("Unicorn with id 123 not found", resourceNotFoundException.getMessage()); + } + + @Test + @DisplayName("Update Unicorn updates Unicorn in Repository") + void testUpdateUnicorn() { + when(unicornRepository.update(any())).thenReturn(createTestUnicornWithId()); + Unicorn updatedUnicorn = unicornService.updateUnicorn("123", createTestUnicorn()); + verify(unicornRepository, times(1)).update(unicornArgumentCaptor.capture()); + assertEquals("123", updatedUnicorn.getId()); + assertEquals("Something", updatedUnicorn.getName()); + assertEquals("Older", updatedUnicorn.getAge()); + assertEquals("Very big", updatedUnicorn.getSize()); + assertEquals("Animal", updatedUnicorn.getType()); + assertEquals("123", unicornArgumentCaptor.getValue().getId()); + } + + @Test + @DisplayName("Delete Unicorn deletes Unicorn from Repository") + void testDeleteUnicorn() { + unicornService.deleteUnicorn("123"); + verify(unicornRepository, times(1)).deleteById("123"); + } + + private Unicorn createTestUnicorn() { + Unicorn unicorn = new Unicorn(); + unicorn.setName("Something"); + unicorn.setAge("Older"); + unicorn.setSize("Very big"); + unicorn.setType("Animal"); + return unicorn; + } + + private Unicorn createTestUnicornWithId() { + Unicorn unicorn = createTestUnicorn(); + unicorn.setId("123"); + return unicorn; + } + +} \ No newline at end of file diff --git a/apigw-lambda-snapstart/example-pattern.json b/apigw-lambda-snapstart/example-pattern.json index eafedfdefa..b8086e2b46 100644 --- a/apigw-lambda-snapstart/example-pattern.json +++ b/apigw-lambda-snapstart/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-lambda-snapstart/template.yaml b/apigw-lambda-snapstart/template.yaml index e0b6de1015..59f9b28c85 100644 --- a/apigw-lambda-snapstart/template.yaml +++ b/apigw-lambda-snapstart/template.yaml @@ -4,7 +4,7 @@ Description: AWS-SAM-APIGW-Lambda-SnapStart Globals: Function: - Runtime: java11 + Runtime: java17 MemorySize: 2048 Timeout: 29 @@ -13,23 +13,28 @@ Resources: Type: AWS::Serverless::Api Properties: StageName: prod + TracingEnabled: true UnicornStoreSpringFunction: Type: AWS::Serverless::Function Properties: Handler: com.unicorn.store.StreamLambdaHandler CodeUri: UnicornFunction + Tracing: Active Environment: Variables: - JAVA_TOOL_OPTIONS: -XX:+TieredCompilation -XX:TieredStopAtLevel=1 # https://aws.amazon.com/blogs/compute/optimizing-aws-lambda-function-performance-for-java/ AWS_SERVERLESS_JAVA_CONTAINER_INIT_GRACE_TIME: 500 - PRODUCT_TABLE_NAME: !Ref UnicornTable + UNICORN_TABLE_NAME: !Ref UnicornTable + POWERTOOLS_LOG_LEVEL: INFO + POWERTOOLS_SERVICE_NAME: unicorn + POWERTOOLS_METRICS_NAMESPACE: UnicornApplication AutoPublishAlias: live SnapStart: ApplyOn: PublishedVersions Policies: - DynamoDBCrudPolicy: TableName: !Ref UnicornTable + - arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess Events: ApiEvents: Type: Api @@ -42,7 +47,7 @@ Resources: Type: AWS::Serverless::SimpleTable Properties: PrimaryKey: - Name: PK + Name: id Type: String Outputs: diff --git a/apigw-lambda-sns/example-pattern.json b/apigw-lambda-sns/example-pattern.json index c28465d896..36a9c1b4a8 100644 --- a/apigw-lambda-sns/example-pattern.json +++ b/apigw-lambda-sns/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-lambda-translate/example-pattern.json b/apigw-lambda-translate/example-pattern.json index 31db46d691..a57aa88549 100644 --- a/apigw-lambda-translate/example-pattern.json +++ b/apigw-lambda-translate/example-pattern.json @@ -39,7 +39,7 @@ "sam deploy --guided" ] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": [ diff --git a/apigw-mappingtemplate-lambda/example-pattern.json b/apigw-mappingtemplate-lambda/example-pattern.json index ee166869c8..d179832783 100644 --- a/apigw-mappingtemplate-lambda/example-pattern.json +++ b/apigw-mappingtemplate-lambda/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-method-cache/example-pattern.json b/apigw-method-cache/example-pattern.json index 8d0cd1b745..647c0151c5 100644 --- a/apigw-method-cache/example-pattern.json +++ b/apigw-method-cache/example-pattern.json @@ -36,7 +36,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-resource-policy/example-pattern.json b/apigw-resource-policy/example-pattern.json index 2878e710a9..ac85008d54 100644 --- a/apigw-resource-policy/example-pattern.json +++ b/apigw-resource-policy/example-pattern.json @@ -35,7 +35,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-rest-api-dynamodb-sls/example-pattern.json b/apigw-rest-api-dynamodb-sls/example-pattern.json index 4d927a2de5..c9871aff9c 100644 --- a/apigw-rest-api-dynamodb-sls/example-pattern.json +++ b/apigw-rest-api-dynamodb-sls/example-pattern.json @@ -41,7 +41,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-rest-api-eventbridge-sqs-sam/example-pattern.json b/apigw-rest-api-eventbridge-sqs-sam/example-pattern.json index 551a8a79e6..982a60b8c3 100644 --- a/apigw-rest-api-eventbridge-sqs-sam/example-pattern.json +++ b/apigw-rest-api-eventbridge-sqs-sam/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-rest-api-eventbridge-terraform/example-pattern.json b/apigw-rest-api-eventbridge-terraform/example-pattern.json index 19b74519f5..6d474e9f68 100644 --- a/apigw-rest-api-eventbridge-terraform/example-pattern.json +++ b/apigw-rest-api-eventbridge-terraform/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-rest-api-http-integration/example-pattern.json b/apigw-rest-api-http-integration/example-pattern.json index 5c50e9f047..96e5f87df9 100644 --- a/apigw-rest-api-http-integration/example-pattern.json +++ b/apigw-rest-api-http-integration/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-rest-api-kinesis-cdk/example-pattern.json b/apigw-rest-api-kinesis-cdk/example-pattern.json index dbee9f5051..05d4e874d0 100644 --- a/apigw-rest-api-kinesis-cdk/example-pattern.json +++ b/apigw-rest-api-kinesis-cdk/example-pattern.json @@ -47,7 +47,7 @@ }, "testing": { "text": [ - "See the Github repo README for detailed testing instructions." + "See the GitHub repo README for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-rest-api-lambda-ecr/README.md b/apigw-rest-api-lambda-ecr/README.md index 1b22942426..68a25dbf3e 100644 --- a/apigw-rest-api-lambda-ecr/README.md +++ b/apigw-rest-api-lambda-ecr/README.md @@ -16,7 +16,7 @@ Learn more about this pattern at Serverless Land Patterns: https://serverlesslan * [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -* [.NET 6](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) installed +* [.NET 7](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) installed * [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) (AWS CDK) installed ## Deployment Instructions diff --git a/apigw-rest-api-lambda-ecr/example-pattern.json b/apigw-rest-api-lambda-ecr/example-pattern.json index 57303d1236..a4a7ed32bc 100644 --- a/apigw-rest-api-lambda-ecr/example-pattern.json +++ b/apigw-rest-api-lambda-ecr/example-pattern.json @@ -26,7 +26,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-rest-api-lambda-efs-dotnet/README.md b/apigw-rest-api-lambda-efs-dotnet/README.md index 8ecd5e3951..95aa0c6108 100644 --- a/apigw-rest-api-lambda-efs-dotnet/README.md +++ b/apigw-rest-api-lambda-efs-dotnet/README.md @@ -16,7 +16,7 @@ Learn more about this pattern at Serverless Land Patterns: https://serverlesslan * [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -* [.NET 6](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) installed +* [.NET 7](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) installed * [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) (AWS CDK) installed ## Deployment Instructions diff --git a/apigw-rest-api-lambda-efs-dotnet/example-pattern.json b/apigw-rest-api-lambda-efs-dotnet/example-pattern.json index 071cef538c..efbdce9738 100644 --- a/apigw-rest-api-lambda-efs-dotnet/example-pattern.json +++ b/apigw-rest-api-lambda-efs-dotnet/example-pattern.json @@ -26,7 +26,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-rest-api-lambda-vpc-sqs/example-pattern.json b/apigw-rest-api-lambda-vpc-sqs/example-pattern.json index d201991960..bb4ebd3db4 100644 --- a/apigw-rest-api-lambda-vpc-sqs/example-pattern.json +++ b/apigw-rest-api-lambda-vpc-sqs/example-pattern.json @@ -51,7 +51,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-rest-api-mock-integration/example-pattern.json b/apigw-rest-api-mock-integration/example-pattern.json index 1bbbb42eba..d96bee294b 100644 --- a/apigw-rest-api-mock-integration/example-pattern.json +++ b/apigw-rest-api-mock-integration/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-rest-api-vpclink/example-pattern.json b/apigw-rest-api-vpclink/example-pattern.json index 3d2b7cf54f..8588ccfe13 100644 --- a/apigw-rest-api-vpclink/example-pattern.json +++ b/apigw-rest-api-vpclink/example-pattern.json @@ -34,7 +34,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-route53-failover/example-pattern.json b/apigw-route53-failover/example-pattern.json index d017b3dea5..4e058a3e81 100644 --- a/apigw-route53-failover/example-pattern.json +++ b/apigw-route53-failover/example-pattern.json @@ -27,7 +27,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-s3-proxy/example-pattern.json b/apigw-s3-proxy/example-pattern.json index d2e7d5d249..4bd8c8d71c 100644 --- a/apigw-s3-proxy/example-pattern.json +++ b/apigw-s3-proxy/example-pattern.json @@ -39,7 +39,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-sfn-cdk/README.md b/apigw-sfn-cdk/README.md index 6ee9284043..7514356461 100644 --- a/apigw-sfn-cdk/README.md +++ b/apigw-sfn-cdk/README.md @@ -13,7 +13,7 @@ Important: this application uses various AWS services and there are costs associ * [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -* [.NET Core 3.1](https://dotnet.microsoft.com/en-us/download/dotnet/3.1) installed +* [.NET 6](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) installed * [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) (AWS CDK) installed ## Deployment Instructions diff --git a/apigw-sfn-cdk/cdk/src/Cdk/Cdk.csproj b/apigw-sfn-cdk/cdk/src/Cdk/Cdk.csproj index 5837c5daee..aa2742bc0a 100644 --- a/apigw-sfn-cdk/cdk/src/Cdk/Cdk.csproj +++ b/apigw-sfn-cdk/cdk/src/Cdk/Cdk.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net6.0 Major diff --git a/apigw-sfn-crud/example-pattern.json b/apigw-sfn-crud/example-pattern.json index 35de46c444..26b69915e9 100644 --- a/apigw-sfn-crud/example-pattern.json +++ b/apigw-sfn-crud/example-pattern.json @@ -25,7 +25,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-sns-sms/example-pattern.json b/apigw-sns-sms/example-pattern.json index 27fa002cf0..75d8a8be89 100644 --- a/apigw-sns-sms/example-pattern.json +++ b/apigw-sns-sms/example-pattern.json @@ -32,7 +32,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-sns-sqs-lambda-cdk-dotnet/README.md b/apigw-sns-sqs-lambda-cdk-dotnet/README.md index 31456a64f6..1577905c50 100644 --- a/apigw-sns-sqs-lambda-cdk-dotnet/README.md +++ b/apigw-sns-sqs-lambda-cdk-dotnet/README.md @@ -11,7 +11,7 @@ Important: This application uses various AWS services and there are costs associ * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) * [Node and NPM](https://nodejs.org/en/download/) installed * [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) (AWS CDK) installed -* [.NET](https://dotnet.microsoft.com/en-us/download/dotnet/3.1) (.NET Core 3.1) installed +* [.NET 6](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) installed ## Deployment Instructions diff --git a/apigw-sns-sqs-lambda-cdk-dotnet/src/ApigwSnsSqsLambdaCdkDotnet/ApigwSnsSqsLambdaCdkDotnet.csproj b/apigw-sns-sqs-lambda-cdk-dotnet/src/ApigwSnsSqsLambdaCdkDotnet/ApigwSnsSqsLambdaCdkDotnet.csproj index f75b87c92d..732dc94209 100644 --- a/apigw-sns-sqs-lambda-cdk-dotnet/src/ApigwSnsSqsLambdaCdkDotnet/ApigwSnsSqsLambdaCdkDotnet.csproj +++ b/apigw-sns-sqs-lambda-cdk-dotnet/src/ApigwSnsSqsLambdaCdkDotnet/ApigwSnsSqsLambdaCdkDotnet.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net6.0 Major diff --git a/apigw-sns-sqs-lambda-cdk-dotnet/src/ApigwSnsSqsLambdaCdkDotnet/ApigwSnsSqsLambdaCdkDotnetStack.cs b/apigw-sns-sqs-lambda-cdk-dotnet/src/ApigwSnsSqsLambdaCdkDotnet/ApigwSnsSqsLambdaCdkDotnetStack.cs index 26e5a690a0..5540e0864a 100644 --- a/apigw-sns-sqs-lambda-cdk-dotnet/src/ApigwSnsSqsLambdaCdkDotnet/ApigwSnsSqsLambdaCdkDotnetStack.cs +++ b/apigw-sns-sqs-lambda-cdk-dotnet/src/ApigwSnsSqsLambdaCdkDotnet/ApigwSnsSqsLambdaCdkDotnetStack.cs @@ -30,9 +30,9 @@ internal ApigwSnsSqsLambdaCdkDotnetStack(Construct scope, string id, IStackProps var lambdaCarChangeQueueNonPremium = new Function(this, "nonPremiumWorkerHandler", new FunctionProps { - Runtime = Runtime.DOTNET_CORE_3_1, + Runtime = Runtime.DOTNET_6, Handler = "ApiEventHandler::ApiEventHandler.Function::SQSHandler", - Code = Code.FromAsset("./src/lambdaHandler/ApiEventHandler/src/ApiEventHandler/bin/Debug/netcoreapp3.1"), + Code = Code.FromAsset("./src/lambdaHandler/ApiEventHandler/src/ApiEventHandler/bin/Debug/net6.0"), }); @@ -47,29 +47,27 @@ internal ApigwSnsSqsLambdaCdkDotnetStack(Construct scope, string id, IStackProps topicCarPriceChange.AddSubscription(new SqsSubscription(queueCarChangeQueuPremium)); - IEnumerable commands = new[] + var buildOption = new BundlingOptions() { - "cd /asset-input", - "export DOTNET_CLI_HOME=\"/tmp/DOTNET_CLI_HOME\"", - "export PATH=\"$PATH:/tmp/DOTNET_CLI_HOME/.dotnet/tools\"", - "dotnet tool install -g Amazon.Lambda.Tools", - "dotnet lambda package -o output.zip", - "unzip -o -d /asset-output output.zip" + Image = Runtime.DOTNET_6.BundlingImage, + User = "root", + OutputType = BundlingOutput.ARCHIVED, + Command = new string[]{ + "/bin/sh", + "-c", + " dotnet tool install -g Amazon.Lambda.Tools"+ + " && dotnet build"+ + " && dotnet lambda package --output-package /asset-output/function.zip" + } }; + var lambdaCarChangeQueuePremium = new Function(this, "premiumWorkerHandler", new FunctionProps { - Runtime = Runtime.DOTNET_CORE_3_1, + Runtime = Runtime.DOTNET_6, Handler = "Lambdas::Lambdas.Function::SQSHandler", Code = Code.FromAsset("./src/lambdaHandler/ApiEventHandler/src/ApiEventHandler", new AssetOptions { - Bundling = new BundlingOptions - { - Image = Runtime.DOTNET_CORE_3_1.BundlingImage, - Command = new[] - { - "bash", "-c", string.Join(" && ", commands) - } - } + Bundling = buildOption }) diff --git a/apigw-sns-sqs-lambda-cdk-dotnet/src/lambdaHandler/ApiEventHandler/src/ApiEventHandler/ApiEventHandler.csproj b/apigw-sns-sqs-lambda-cdk-dotnet/src/lambdaHandler/ApiEventHandler/src/ApiEventHandler/ApiEventHandler.csproj index 173e178f62..3219eca090 100644 --- a/apigw-sns-sqs-lambda-cdk-dotnet/src/lambdaHandler/ApiEventHandler/src/ApiEventHandler/ApiEventHandler.csproj +++ b/apigw-sns-sqs-lambda-cdk-dotnet/src/lambdaHandler/ApiEventHandler/src/ApiEventHandler/ApiEventHandler.csproj @@ -1,6 +1,6 @@ - netcoreapp3.1 + net6.0 true Lambda diff --git a/apigw-sns-sqs-lambda-cdk-dotnet/src/lambdaHandler/ApiEventHandler/src/ApiEventHandler/aws-lambda-tools-defaults.json b/apigw-sns-sqs-lambda-cdk-dotnet/src/lambdaHandler/ApiEventHandler/src/ApiEventHandler/aws-lambda-tools-defaults.json index 9493270263..2d93319e54 100644 --- a/apigw-sns-sqs-lambda-cdk-dotnet/src/lambdaHandler/ApiEventHandler/src/ApiEventHandler/aws-lambda-tools-defaults.json +++ b/apigw-sns-sqs-lambda-cdk-dotnet/src/lambdaHandler/ApiEventHandler/src/ApiEventHandler/aws-lambda-tools-defaults.json @@ -8,7 +8,7 @@ "profile": "", "region": "", "configuration": "Release", - "framework": "netcoreapp3.1", + "framework": "net6.0", "function-runtime": "dotnetcore3.1", "function-memory-size": 256, "function-timeout": 30, diff --git a/apigw-sns-sqs-lambda-cdk-dotnet/src/lambdaHandler/ApiEventHandler/test/ApiEventHandler.Tests/ApiEventHandler.Tests.csproj b/apigw-sns-sqs-lambda-cdk-dotnet/src/lambdaHandler/ApiEventHandler/test/ApiEventHandler.Tests/ApiEventHandler.Tests.csproj index fb9d59b956..4fef72c9a9 100644 --- a/apigw-sns-sqs-lambda-cdk-dotnet/src/lambdaHandler/ApiEventHandler/test/ApiEventHandler.Tests/ApiEventHandler.Tests.csproj +++ b/apigw-sns-sqs-lambda-cdk-dotnet/src/lambdaHandler/ApiEventHandler/test/ApiEventHandler.Tests/ApiEventHandler.Tests.csproj @@ -1,6 +1,6 @@ - netcoreapp3.1 + net6.0 diff --git a/apigw-sqs-cdk-typescript/example-pattern.json b/apigw-sqs-cdk-typescript/example-pattern.json index 5eb795aeca..22296ea774 100644 --- a/apigw-sqs-cdk-typescript/example-pattern.json +++ b/apigw-sqs-cdk-typescript/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-sqs-lambda-ddb/README.md b/apigw-sqs-lambda-ddb/README.md new file mode 100644 index 0000000000..072f4b4fed --- /dev/null +++ b/apigw-sqs-lambda-ddb/README.md @@ -0,0 +1,123 @@ +# Amazon API Gateway to Amazon SQS to AWS Lambda to Amazon DynamoDB + +This pattern explains how to deploy a SAM application with Amazon API Gateway, Amazon SQS, AWS Lambda, AWS Secrets Manager and Amazon DynamoDB. + +This pattern is useful to accept and respond to requests quickly but offloading the processing as asynchronous process e.g., webhook endpoints. Once the request is placed in SQS, API gateway responds back to the caller immediately without waiting for those messages to be processed. + +When an HTTP POST request is made to the Amazon API Gateway endpoint, Gateway authorizes the request by checking Basic auth credentials against values in AWS Secrets manager. When credentials are valid, request payload is sent to Amazon Simple Queue Service. AWS Lambda function consumes event from the Queue and inserts the event/payload into the Amazon DynamoDB table. Amazon Simple Queue Service is also configured with a Dead Letter Queue where events are sent when retries to process those messages are repeatedly failed. + +Key Benefits: +- Operations: AWS services used in this pattern can scale easily and quickly based on incoming traffic. +- Security: The APIs created with Amazon API Gateway expose HTTPS endpoints only. All user data stored in Amazon DynamoDB and Amazon SQS is fully encrypted at rest. Secrets are securely stored in AWS Secrets Manager. +- Reliability: A dead-letter queue ensures no messages are lost due to issues. Helps debugging failed messages and once underlying issue is resolved, messages can be redrived back to source queues for processing. +- Performance: AWS Lambda supports batching with Amazon SQS. Lambda reads messages in batches and invokes the function once for each batch. +- Cost: Pay only for what you use. No minimum fee. + + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd apigw-sqs-lambda-ddb + ``` +1. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: + ``` + sam deploy --guided + ``` +1. During the prompts: + * Enter a stack name + * Enter the desired AWS Region + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run `sam deploy -guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. + +## How it works + +architecture diagram + +- This pattern deploys an Amazon API Gateway HTTP API with route /submit configured with basic authentication. +- On receiving a request, API Gateway will invoke a Lambda authorizer which validates the request and returns a policy informing API Gateway to accept or deny the request. +- When request is accepted, API Gateway sends the message payload to a queue in SQS. SQS uses another queue as Dead Letter Queue to send the messages in case of continued failures to process the messages from downstream lambda. +- Messages from SQS is posted to a lambda function to process them. +- Lambda function receives the messages from SQS and saves them into a DynamoDB table. + +## Testing + +Once the application is deployed, follow the below steps to test this pattern: + +### Retrieve Information +- Retrieve the HttpApiEndpoint value from CloudFormation Outputs +- Retrieve the username and password from Secrets Manager in AWS Console. Base64 encoding of username and password joined by a single colon : will be used as {Credential} in Authorization header. + +### Construct Request +- Replace the placeholders in the Curl request given below with retrieved values. +- Region: AWS Region used for deployment e.g., us-east-1 + +Example POST Request (Curl): +``` +curl --location 'https://{HttpApiEndpoint}.execute-api.{Region}.amazonaws.com/submit' \ +--header 'Content-Type: application/json' \ +--header 'Authorization: Basic {Credentials}' \ +--data '{ + "eventId": "f5337ee6-e13e-4bcb-8082-872898b5d1a8", + "message": "Sample message for testing" +}' +``` + +### Verify +- Execute the fully formed Curl request and verify that you received a successful response with an XML message. Response is basically an XML representation of SQS SendMessage response. +Example: +``` + + + + 9c22e4eb-09f6-4374-95b8-f6030f33da33 + 93355f3c10001c53dcae387ea9d5b71e + + + 84728dba-affe-50d7-8e62-d08a5bb32a38 + + +``` + +- View the contents of DynamoDB Table "EventTable" from AWS Console and verify that JSON data used in your payload is saved as a record. + + +### Steps To Verify Dead-Letter Queue + +- Messages should be moved to Dead-Letter Queue when there's repeated failures in processing the message by Lambda function. +- Lambda which consumes the message from source queue (BufferingQueue) expects the message to be in JSON format. When the message is not in JSON format, it will fail to process it. To verify that messages are moved to Dead-Letter Queue, use a request which doesn't use a JSON payload. Example: +``` +curl --location 'https://{HttpApiEndpoint}.execute-api.{Region}.amazonaws.com/submit' \ +--header 'Content-Type: application/json' \ +--header 'Authorization: Basic {Credentials}' \ +--data 'Test Message' +``` +- After few minutes, login to AWS console and verify that the Dead-Letter queue (DeadLetterQueue) has available messages greater than 0. You can also try "Poll for messages" in this queue to verify the message body. + +## Cleanup + +1. Delete the stack + ```bash + sam delete + ``` + +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 \ No newline at end of file diff --git a/apigw-sqs-lambda-ddb/api.yaml b/apigw-sqs-lambda-ddb/api.yaml new file mode 100644 index 0000000000..291a6d382a --- /dev/null +++ b/apigw-sqs-lambda-ddb/api.yaml @@ -0,0 +1,30 @@ +openapi: "3.0.1" +info: + title: "Http Api" + version: "2023-06-25 17:32:29UTC" +paths: + /submit: + post: + responses: + default: + description: "Response for POST /" + x-amazon-apigateway-integration: + type: "aws_proxy" + integrationSubtype: "SQS-SendMessage" + credentials: + Fn::GetAtt: [ApiGatewayRole, Arn] + connectionType: "INTERNET" + payloadFormatVersion: "1.0" + requestParameters: + MessageBody: "$request.body" + QueueUrl: + Ref: BufferingQueue + +x-amazon-apigateway-cors: + allowMethods: + - "*" + maxAge: 0 + allowCredentials: false + allowOrigins: + - "*" +x-amazon-apigateway-importexport-version: "1.0" diff --git a/apigw-sqs-lambda-ddb/docs/apigw-sqs-lambda-ddb.drawio b/apigw-sqs-lambda-ddb/docs/apigw-sqs-lambda-ddb.drawio new file mode 100644 index 0000000000..02d7506cbb --- /dev/null +++ b/apigw-sqs-lambda-ddb/docs/apigw-sqs-lambda-ddb.drawio @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apigw-sqs-lambda-ddb/docs/apigw-sqs-lambda-ddb.drawio.png b/apigw-sqs-lambda-ddb/docs/apigw-sqs-lambda-ddb.drawio.png new file mode 100644 index 0000000000..2a9e8f88d0 Binary files /dev/null and b/apigw-sqs-lambda-ddb/docs/apigw-sqs-lambda-ddb.drawio.png differ diff --git a/apigw-sqs-lambda-ddb/example-pattern.json b/apigw-sqs-lambda-ddb/example-pattern.json new file mode 100644 index 0000000000..9ca9defd3c --- /dev/null +++ b/apigw-sqs-lambda-ddb/example-pattern.json @@ -0,0 +1,63 @@ +{ + "title": "Amazon API Gateway to Amazon SQS to AWS Lambda to Amazon DynamoDB", + "description": "This pattern explains how to deploy a SAM application with Amazon API Gateway, Amazon SQS, AWS Lambda, and Amazon DynamoDB. When an HTTP POST request is made to the Amazon API Gateway endpoint, Gateway authorizes the request by checking Basic auth credentials and on valid credentials, request payload is sent to Amazon Simple Queue Service. AWS Lambda function consumes event from the Queue and inserts the event/payload into the Amazon DynamoDB table. Amazon Simple Queue Service is also configured with a Dead Letter Queue where events are sent when retries to process those messages are repeatedly failed.", + "language": "Python", + "level": "300", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern deploys an Amazon API Gateway HTTP API with route /submit configured with basic authentication.", + "On receiving a request, API Gateway will invoke a Lambda authorizer which validates the request and returns a policy informing API Gateway to accept or deny the request.", + "When request is accepted, API Gateway sends the message payload to a queue in SQS. SQS uses another queue as Dead Letter Queue to send the messages in case of continued failures to process the messages from downstream lambda.", + "Messages from SQS is posted to a lambda function to process them.", + "Lambda function receives the messages from SQS and saves them into a DynamoDB table." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/apigw-sqs-lambda-ddb", + "templateURL": "serverless-patterns/apigw-sqs-lambda-ddb", + "projectFolder": "apigw-sqs-lambda-ddb", + "templateFile": "apigw-sqs-lambda-ddb/template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Lambda Authorizers", + "link": "https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html" + }, + { + "text": "Amazon SQS dead-letter queues", + "link": "https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html" + }, + { + "text": "Working with HTTP APIs", + "link": "https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api.html" + } + ] + }, + "deploy": { + "text": [ + "sam deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "name": "Ravi Kiran Ganji", + "bio": "I am a Senior Cloud Application Architect at AWS Professional Services, and Serverless Enthusiast.", + "linkedin": "ravi-kiran-ganji" + } + ] +} \ No newline at end of file diff --git a/apigw-sqs-lambda-ddb/lambda/authorizer/index.py b/apigw-sqs-lambda-ddb/lambda/authorizer/index.py new file mode 100644 index 0000000000..100353e6bc --- /dev/null +++ b/apigw-sqs-lambda-ddb/lambda/authorizer/index.py @@ -0,0 +1,39 @@ +from aws_lambda_powertools import Logger, Tracer +from aws_lambda_powertools.logging import correlation_paths +from aws_lambda_powertools.utilities.typing import LambdaContext +import boto3 +import json +import base64 + +logger = Logger() +tracer = Tracer() +client = boto3.client("secretsmanager") + + +@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST) +@tracer.capture_lambda_handler +def lambda_handler(event: dict, context: LambdaContext) -> dict: + username = get_secret("endpoint/username", "UserName") + password = get_secret("endpoint/password", "Password") + expected_auth_header = "Basic " + base64_encode(f"{username}:{password}") + + is_authorized = False + if ( + "authorization" in event["headers"] + and event["headers"]["authorization"] == expected_auth_header + ): + is_authorized = True + + return {"isAuthorized": is_authorized} + + +# Method to read secret from secrets manager +def get_secret(secret_name: str, secret_key: str) -> str: + response = client.get_secret_value(SecretId=secret_name) + value = json.loads(response["SecretString"]) + return value[secret_key] + + +# Method to base64 encode string +def base64_encode(string: str) -> str: + return base64.b64encode(string.encode("utf-8")).decode("utf-8") diff --git a/apigw-sqs-lambda-ddb/lambda/sqs-handler/index.py b/apigw-sqs-lambda-ddb/lambda/sqs-handler/index.py new file mode 100644 index 0000000000..c691e84169 --- /dev/null +++ b/apigw-sqs-lambda-ddb/lambda/sqs-handler/index.py @@ -0,0 +1,20 @@ +from aws_lambda_powertools import Logger, Tracer +from aws_lambda_powertools.logging import correlation_paths +from aws_lambda_powertools.utilities.typing import LambdaContext +import json +import boto3 + +logger = Logger() +tracer = Tracer() +table = boto3.resource("dynamodb").Table("EventTable") + + +@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST) +@tracer.capture_lambda_handler +def lambda_handler(event: dict, context: LambdaContext) -> dict: + # process records from sqs event + for record in event["Records"]: + payload = json.loads(record["body"]) + table.put_item(Item=payload) + + return {"statusCode": 200} diff --git a/apigw-sqs-lambda-ddb/template.yaml b/apigw-sqs-lambda-ddb/template.yaml new file mode 100644 index 0000000000..7c353edc7e --- /dev/null +++ b/apigw-sqs-lambda-ddb/template.yaml @@ -0,0 +1,274 @@ +AWSTemplateFormatVersion: "2010-09-09" +Transform: AWS::Serverless-2016-10-31 +Description: A endpoint hosted by an API Gateway which invokes SQS, consumed by lambda function and persisted in a dynamoDB table. + +Resources: + ########################################################################## + # HTTP API - Entrypoint # + ########################################################################## + HttpApi: + Type: AWS::Serverless::HttpApi + Properties: + AccessLogSettings: + DestinationArn: !GetAtt ApiGatewayAccessLogs.Arn + Format: '{ "requestId":"$context.requestId", "ip": "$context.identity.sourceIp", "requestTime":"$context.requestTime", "httpMethod":"$context.httpMethod","routeKey":"$context.routeKey", "status":"$context.status","protocol":"$context.protocol", "responseLength":"$context.responseLength" }' + DefinitionBody: + "Fn::Transform": + Name: "AWS::Include" + Parameters: + Location: "api.yaml" + Auth: + DefaultAuthorizer: AuthorizerFunction + Authorizers: + AuthorizerFunction: + FunctionArn: !GetAtt GatewayAuthorizerFunction.Arn + AuthorizerPayloadFormatVersion: 2.0 + EnableFunctionDefaultPermissions: true + EnableSimpleResponses: true + + ########################################################################## + # SQS Queues # + ########################################################################## + # SQS queue for request buffering + BufferingQueue: + Type: AWS::SQS::Queue + Properties: + QueueName: BufferingQueue + SqsManagedSseEnabled: true + RedrivePolicy: + deadLetterTargetArn: !GetAtt DeadLetterQueue.Arn + maxReceiveCount: 5 + + # SQS DLQ + DeadLetterQueue: + Type: AWS::SQS::Queue + Properties: + QueueName: DeadLetterQueue + SqsManagedSseEnabled: true + + ########################################################################## + # Lambda Function - Gateway Handler # + ########################################################################## + SqsHandlerFunction: + Type: AWS::Serverless::Function + DependsOn: SqsHandlerLambdaExecutionRole + Properties: + FunctionName: SQSHandlerFunction + Description: Lambda to be invoked by the SQS Queue + CodeUri: lambda/sqs-handler/ + Handler: index.lambda_handler + Runtime: python3.10 + Timeout: 3 + MemorySize: 128 + Role: !GetAtt SqsHandlerLambdaExecutionRole.Arn + Layers: + [ + !Sub "arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:32", + ] + Events: + SQSEvent: + Type: SQS + Properties: + Queue: !GetAtt BufferingQueue.Arn + BatchSize: 10 + + ########################################################################## + # Lambda Function - Gateway Authorizer # + ########################################################################## + GatewayAuthorizerFunction: + Type: AWS::Serverless::Function + Properties: + FunctionName: GatewayAuthorizerFunction + Description: Lambda to be invoked by API Gateway for authorization + CodeUri: lambda/authorizer/ + Handler: index.lambda_handler + Runtime: python3.10 + Timeout: 3 + MemorySize: 128 + Role: !GetAtt AuthorizerLambdaExecutionRole.Arn + Layers: + [ + !Sub "arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPythonV2:32", + ] + + ########################################################################## + # DynamoDB Table # + ########################################################################## + EventTable: + Type: AWS::DynamoDB::Table + Properties: + AttributeDefinitions: + - AttributeName: eventId + AttributeType: S + BillingMode: PAY_PER_REQUEST + KeySchema: + - AttributeName: eventId + KeyType: HASH + TableName: EventTable + SSESpecification: + SSEEnabled: true + + ########################################################################## + # Roles # + ########################################################################## + ApiGatewayRole: + Type: "AWS::IAM::Role" + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: + - apigateway.amazonaws.com + Action: + - "sts:AssumeRole" + Policies: + - PolicyName: CustomPolicy + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - "sqs:SendMessage" + - "sqs:GetQueueUrl" + - "sqs:SendMessageBatch" + Resource: !GetAtt BufferingQueue.Arn + - Effect: Allow + Action: + - "logs:CreateLogGroup" + - "logs:CreateLogStream" + - "logs:DescribeLogGroups" + - "logs:DescribeLogStreams" + - "logs:PutLogEvents" + - "logs:GetLogEvents" + - "logs:FilterLogEvents" + Resource: !GetAtt ApiGatewayAccessLogs.Arn + + SqsHandlerLambdaExecutionRole: + Type: "AWS::IAM::Role" + DependsOn: EventTable + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: + - lambda.amazonaws.com + Action: + - "sts:AssumeRole" + Policies: + - PolicyName: CustomPolicy + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - "dynamodb:List*" + - "dynamodb:DescribeReservedCapacity*" + - "dynamodb:DescribeLimits" + - "dynamodb:DescribeTimeToLive" + - "dynamodb:Get*" + - "dynamodb:PutItem" + Resource: !GetAtt EventTable.Arn + - Effect: Allow + Action: + - "logs:CreateLogGroup" + - "logs:CreateLogStream" + - "logs:PutLogEvents" + Resource: "*" + - Effect: Allow + Action: + - "sqs:ReceiveMessage" + - "sqs:DeleteMessage" + - "sqs:GetQueueAttributes" + Resource: !GetAtt BufferingQueue.Arn + - Effect: Allow + Action: + - "sqs:SendMessage" + Resource: !GetAtt DeadLetterQueue.Arn + + AuthorizerLambdaExecutionRole: + Type: "AWS::IAM::Role" + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: + - lambda.amazonaws.com + Action: + - "sts:AssumeRole" + Policies: + - PolicyName: CustomPolicy + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - "logs:CreateLogGroup" + - "logs:CreateLogStream" + - "logs:PutLogEvents" + Resource: "*" + - Effect: Allow + Action: + - "secretsmanager:GetSecretValue" + Resource: [!Ref BasicAuthUsername, !Ref BasicAuthPassword] + + ########################################################################## + # Cloudwatch Logs # + ########################################################################## + + ApiGatewayAccessLogs: + Type: AWS::Logs::LogGroup + Properties: + LogGroupName: Http-ApiGw-Access-Logs + RetentionInDays: 1 + + ########################################################################## + # Secrets - Generated for Endpoint Auth # + ########################################################################## + + BasicAuthUsername: + Type: AWS::SecretsManager::Secret + Properties: + Description: Username to be used in basic auth + Name: endpoint/username + GenerateSecretString: + ExcludeCharacters: "" + ExcludeLowercase: false + ExcludeNumbers: false + ExcludePunctuation: false + ExcludeUppercase: false + GenerateStringKey: UserName + IncludeSpace: false + PasswordLength: 32 + RequireEachIncludedType: true + SecretStringTemplate: !Sub '{"UserName": "REPLACED"}' + + BasicAuthPassword: + Type: AWS::SecretsManager::Secret + Properties: + Description: Password to be used in basic auth + Name: endpoint/password + GenerateSecretString: + ExcludeCharacters: "" + ExcludeLowercase: false + ExcludeNumbers: false + ExcludePunctuation: false + ExcludeUppercase: false + GenerateStringKey: Password + IncludeSpace: false + PasswordLength: 32 + RequireEachIncludedType: true + SecretStringTemplate: !Sub '{"Password": "REPLACED"}' + +########################################################################## +# Outputs # +########################################################################## +Outputs: + HttpApiEndpoint: + Description: "Http API endpoint" + Value: !Sub "https://${HttpApi}.execute-api.${AWS::Region}.amazonaws.com" diff --git a/apigw-sqs-lambda-java/example-pattern.json b/apigw-sqs-lambda-java/example-pattern.json index 9ad7fefbd9..3d1162fc6d 100644 --- a/apigw-sqs-lambda-java/example-pattern.json +++ b/apigw-sqs-lambda-java/example-pattern.json @@ -34,7 +34,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-throttle-sam/example-pattern.json b/apigw-throttle-sam/example-pattern.json index 6b7b31e058..2c7edc01f2 100644 --- a/apigw-throttle-sam/example-pattern.json +++ b/apigw-throttle-sam/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-usage-plans/example-pattern.json b/apigw-usage-plans/example-pattern.json index f964d7d173..36e3084189 100644 --- a/apigw-usage-plans/example-pattern.json +++ b/apigw-usage-plans/example-pattern.json @@ -36,7 +36,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-websocket-api-connection-dynamodb/example-pattern.json b/apigw-websocket-api-connection-dynamodb/example-pattern.json index 1655612a56..46060c9a6b 100644 --- a/apigw-websocket-api-connection-dynamodb/example-pattern.json +++ b/apigw-websocket-api-connection-dynamodb/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-websocket-api-sfn-async/example-pattern.json b/apigw-websocket-api-sfn-async/example-pattern.json index b38792006c..fe6b071a4f 100644 --- a/apigw-websocket-api-sfn-async/example-pattern.json +++ b/apigw-websocket-api-sfn-async/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-websocket-api-sfn-sync/example-pattern.json b/apigw-websocket-api-sfn-sync/example-pattern.json index cdef018656..be1f7b763a 100644 --- a/apigw-websocket-api-sfn-sync/example-pattern.json +++ b/apigw-websocket-api-sfn-sync/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-websocket-api-sns/example-pattern.json b/apigw-websocket-api-sns/example-pattern.json index b7df652a4f..9b55cb4a98 100644 --- a/apigw-websocket-api-sns/example-pattern.json +++ b/apigw-websocket-api-sns/example-pattern.json @@ -39,7 +39,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-websocket-api-sqs-lambda-java/example-pattern.json b/apigw-websocket-api-sqs-lambda-java/example-pattern.json index e5ceee7ac4..396d8bb276 100644 --- a/apigw-websocket-api-sqs-lambda-java/example-pattern.json +++ b/apigw-websocket-api-sqs-lambda-java/example-pattern.json @@ -39,7 +39,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/apigw-websocket-api-sqs-lambda-terraform/example-pattern.json b/apigw-websocket-api-sqs-lambda-terraform/example-pattern.json index 17c4264f4b..8afcb56967 100644 --- a/apigw-websocket-api-sqs-lambda-terraform/example-pattern.json +++ b/apigw-websocket-api-sqs-lambda-terraform/example-pattern.json @@ -46,7 +46,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/appconfig-feature-flag-cdk/example-pattern.json b/appconfig-feature-flag-cdk/example-pattern.json index e951b9a385..5fd374a6c5 100644 --- a/appconfig-feature-flag-cdk/example-pattern.json +++ b/appconfig-feature-flag-cdk/example-pattern.json @@ -46,7 +46,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/appconfig-feature-flag-sam/example.pattern.json b/appconfig-feature-flag-sam/example.pattern.json index 3c1301f07f..12f89a8ad3 100644 --- a/appconfig-feature-flag-sam/example.pattern.json +++ b/appconfig-feature-flag-sam/example.pattern.json @@ -45,7 +45,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/appconfig-lambda-rust/example-pattern.json b/appconfig-lambda-rust/example-pattern.json index 98f4b63d3d..77edf060fe 100644 --- a/appconfig-lambda-rust/example-pattern.json +++ b/appconfig-lambda-rust/example-pattern.json @@ -34,7 +34,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/appsync-cognito-lambda-cdk/example-pattern.json b/appsync-cognito-lambda-cdk/example-pattern.json index 0bb6aa2bd8..03d76f094d 100644 --- a/appsync-cognito-lambda-cdk/example-pattern.json +++ b/appsync-cognito-lambda-cdk/example-pattern.json @@ -42,7 +42,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/appsync-direct-lambda-resolver/pattern-cdk-appsync-lambda.json b/appsync-direct-lambda-resolver/pattern-cdk-appsync-lambda.json index b9b3f2bfaa..3149568de3 100644 --- a/appsync-direct-lambda-resolver/pattern-cdk-appsync-lambda.json +++ b/appsync-direct-lambda-resolver/pattern-cdk-appsync-lambda.json @@ -17,7 +17,7 @@ ] }, "deploy": { - "text": ["See the Github repo for detailed instructions"] + "text": ["See the GitHub repo for detailed instructions"] }, "gitHub": { "template": { diff --git a/appsync-dynamodb-singletable-cdk/example-pattern.json b/appsync-dynamodb-singletable-cdk/example-pattern.json index 9674b67241..896512d67d 100644 --- a/appsync-dynamodb-singletable-cdk/example-pattern.json +++ b/appsync-dynamodb-singletable-cdk/example-pattern.json @@ -39,7 +39,7 @@ "text": ["npx aws-cdk deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk delete."] diff --git a/appsync-dynamodb-singletable-js-resolver/README.md b/appsync-dynamodb-singletable-js-resolver/README.md new file mode 100644 index 0000000000..e3fcc2acca --- /dev/null +++ b/appsync-dynamodb-singletable-js-resolver/README.md @@ -0,0 +1,151 @@ +# AWS AppSync to Amazon DynamoDB - Single table design - JS Resolvers + +This pattern creates an AppSync API with a schema and a javascript pipeline resolver to query a DynamoDB table following the single table design model. The pattern uses the new [`GraphQLApi` resource for AWS SAM](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-graphqlapi.html). + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/appsync-dynamodb-singletable-js-resolver + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd appsync-dynamodb-singletable-js-resolver + ``` +1. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: + ``` + sam deploy --guided + ``` +1. During the prompts: + * Enter a stack name + * Enter the desired AWS Region + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run `sam deploy --guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. + +## How it works + +This template creates an AppSync api that uses a DynamoDB javascript pipeline resolver. The `GetParentAndChildResolver` demonstrate using a custom resolver to format results from a single table design for AppSync. + +## Testing + +The easiest way to test the AppSync API is with the AppSync console at https://console.aws.amazon.com/appsync/home#/apis (change to your appropriate region) + +Click on the API you created and visit the Queries tab + +### Mutations + +#### Parents +Paste the following command in the query editor to create a first parent: +```graphql + mutation MyMutation { + createParentItem(data: "This is a parent item", PK: "ParentPK", SK: "ParentSK") { + PK + SK + data + type + } + } +``` +Paste the following command in the query editor to create a second parent: +```graphql + mutation MyMutation { + createParentItem(data: "This is another parent item", PK: "ParentPK2", SK: "ParentSK2") { + PK + SK + data + type + } + } +``` +![mutation-parents](./images/mutation-create-parent-item.png) + + +#### Children +Paste the following command in the query editor to create a first child: +```graphql + mutation MyMutation { + createChildItem(data: "This is child 1", parentPK: "ParentPK", parentSK: "ParentSK", PK: "ChildPK1", SK: "ChildSK1") { + PK + SK + data + parentPK + parentSK + type + } + } +``` +Paste the following command in the query editor to create a second child: +```graphql + mutation MyMutation { + createChildItem(data: "This is child 2", parentPK: "ParentPK2", parentSK: "ParentSK2", PK: "ChildPK2", SK: "ChildSK2") { + PK + SK + data + parentPK + parentSK + type + } + } +``` +Paste the following command in the query editor to create a third child: +```graphql + mutation MyMutation { + createChildItem(data: "This is child 3", parentPK: "ParentPK", parentSK: "ParentSK", PK: "ChildPK3", SK: "ChildSK3") { + PK + SK + data + parentPK + parentSK + type + } + } +``` +![mutation-children](./images/mutation-create-child-item.png) + +### Queries +Now that we have all our records set, let's retrieve parent 1 and the related children. +```graphql + query MyQuery { + getParentWithChildren(PK: "ParentPK", SK: "ParentSK") { + PK + SK + data + type + children { + PK + SK + data + parentPK + parentSK + type + } + } + } +``` +The result of this query should show parent 1 along with child 1 and 3. +![query](./images/query-get-parent-with-children.png) + +## Cleanup + +1. Delete the stack, Enter `Y` to confirm deleting the stack and folder. + ``` + sam delete + ``` +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/appsync-dynamodb-singletable-js-resolver/example-pattern.json b/appsync-dynamodb-singletable-js-resolver/example-pattern.json new file mode 100644 index 0000000000..4ff4702a3a --- /dev/null +++ b/appsync-dynamodb-singletable-js-resolver/example-pattern.json @@ -0,0 +1,62 @@ +{ + "title": "AppSync to DynamoDB using JS Resolvers", + "description": "Use AppSync JS Resolvers to query a DynamoDB table using single table design.", + "language": "Node.js", + "level": "200", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This sample project creates an AppSync API with a schema and a javascript pipeline resolver to query a DynamoDB table following a single table design model.", + "Using JS pipeline resolvers can help you easily transform your query to retrieve nested records in your DynamoDB table in a single API call." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/appsync-dynamodb-singletable-js-resolver", + "templateURL": "serverless-patterns/appsync-dynamodb-singletable-js-resolver", + "projectFolder": "appsync-dynamodb-singletable-js-resolver", + "templateFile": "appsync-dynamodb-singletable-js-resolver/template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "AWS Appsync", + "link": "https://aws.amazon.com/appsync/" + }, + { + "text": "AWS AppSync - JS Resolvers", + "link": "https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-overview-js.html" + }, + { + "text": "AWS AppSync - SAM Appsync Abstraction", + "link": "https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-graphqlapi.html" + } + ] + }, + "deploy": { + "text": [ + "sam deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "name": "Alexis Philippart de Foy", + "image": "https://media.licdn.com/dms/image/C4D03AQFyN5GLrFk-0g/profile-displayphoto-shrink_800_800/0/1586853008031?e=1693440000&v=beta&t=0BtjX92oZxhHUrrIzh55wah8V1OiwOAOyigPtfAcZdo", + "bio": " I am a Startup Solutions Architect, Serverless Enthusiast and former startup founder.", + "linkedin": "apdf", + "twitter": "@aphilippartd" + } + ] +} \ No newline at end of file diff --git a/appsync-dynamodb-singletable-js-resolver/images/mutation-create-child-item.png b/appsync-dynamodb-singletable-js-resolver/images/mutation-create-child-item.png new file mode 100644 index 0000000000..8ae0f4c36a Binary files /dev/null and b/appsync-dynamodb-singletable-js-resolver/images/mutation-create-child-item.png differ diff --git a/appsync-dynamodb-singletable-js-resolver/images/mutation-create-parent-item.png b/appsync-dynamodb-singletable-js-resolver/images/mutation-create-parent-item.png new file mode 100644 index 0000000000..cedfd41bbb Binary files /dev/null and b/appsync-dynamodb-singletable-js-resolver/images/mutation-create-parent-item.png differ diff --git a/appsync-dynamodb-singletable-js-resolver/images/query-get-parent-with-children.png b/appsync-dynamodb-singletable-js-resolver/images/query-get-parent-with-children.png new file mode 100644 index 0000000000..fe3e73faff Binary files /dev/null and b/appsync-dynamodb-singletable-js-resolver/images/query-get-parent-with-children.png differ diff --git a/appsync-dynamodb-singletable-js-resolver/sam_graphql_api/resolvers/functions/createChild.js b/appsync-dynamodb-singletable-js-resolver/sam_graphql_api/resolvers/functions/createChild.js new file mode 100644 index 0000000000..2aadae8456 --- /dev/null +++ b/appsync-dynamodb-singletable-js-resolver/sam_graphql_api/resolvers/functions/createChild.js @@ -0,0 +1,18 @@ +import { util } from '@aws-appsync/utils'; + +export function request(ctx) { + const { PK, SK, ...values } = ctx.arguments; + return dynamodbPutRequest({ key: { PK, SK }, values: { ...values, type: "Child" } }); +} + +export function response(ctx) { + return ctx.result; +} + +function dynamodbPutRequest({ key, values }) { + return { + operation: 'PutItem', + key: util.dynamodb.toMapValues(key), + attributeValues: util.dynamodb.toMapValues(values) + }; +} \ No newline at end of file diff --git a/appsync-dynamodb-singletable-js-resolver/sam_graphql_api/resolvers/functions/createParent.js b/appsync-dynamodb-singletable-js-resolver/sam_graphql_api/resolvers/functions/createParent.js new file mode 100644 index 0000000000..e26943f4ff --- /dev/null +++ b/appsync-dynamodb-singletable-js-resolver/sam_graphql_api/resolvers/functions/createParent.js @@ -0,0 +1,18 @@ +import { util } from '@aws-appsync/utils'; + +export function request(ctx) { + const { PK, SK, ...values } = ctx.arguments; + return dynamodbPutRequest({ key: { PK, SK }, values: { ...values, type: "Parent" } }); +} + +export function response(ctx) { + return ctx.result; +} + +function dynamodbPutRequest({ key, values }) { + return { + operation: 'PutItem', + key: util.dynamodb.toMapValues(key), + attributeValues: util.dynamodb.toMapValues(values) + }; +} \ No newline at end of file diff --git a/appsync-dynamodb-singletable-js-resolver/sam_graphql_api/resolvers/functions/getParent.js b/appsync-dynamodb-singletable-js-resolver/sam_graphql_api/resolvers/functions/getParent.js new file mode 100644 index 0000000000..ca44937b12 --- /dev/null +++ b/appsync-dynamodb-singletable-js-resolver/sam_graphql_api/resolvers/functions/getParent.js @@ -0,0 +1,18 @@ +import { util } from '@aws-appsync/utils'; + +export function request(ctx) { + const { PK, SK } = ctx.args; + return dynamoDBGetItemRequest({ PK, SK }); +} + +export function response(ctx) { + ctx.stash.parent = ctx.result; + return ctx.result; +} + +function dynamoDBGetItemRequest(key) { + return { + operation: 'GetItem', + key: util.dynamodb.toMapValues(key), + }; +} \ No newline at end of file diff --git a/appsync-dynamodb-singletable-js-resolver/sam_graphql_api/resolvers/functions/getParentChildren.js b/appsync-dynamodb-singletable-js-resolver/sam_graphql_api/resolvers/functions/getParentChildren.js new file mode 100644 index 0000000000..733f9c0797 --- /dev/null +++ b/appsync-dynamodb-singletable-js-resolver/sam_graphql_api/resolvers/functions/getParentChildren.js @@ -0,0 +1,25 @@ +import { util } from '@aws-appsync/utils'; + +export function request(ctx) { + const { PK, SK } = ctx.args; + return dynamoDBQueryRequest({ parentPK: PK, parentSK: SK }); +} + +export function response(ctx) { + ctx.stash.children = ctx.result?.items; + return ctx.result?.items; +} + +function dynamoDBQueryRequest({ parentPK, parentSK }) { + return { + operation: 'Query', + index: 'ParentGSI', + query: { + "expression" : "parentPK = :parentPK and parentSK = :parentSK", + "expressionValues" : { + ":parentPK" : util.dynamodb.toDynamoDB(parentPK), + ":parentSK" : util.dynamodb.toDynamoDB(parentSK) + } + } + }; +} \ No newline at end of file diff --git a/appsync-dynamodb-singletable-js-resolver/sam_graphql_api/resolvers/getParentWithChildren.js b/appsync-dynamodb-singletable-js-resolver/sam_graphql_api/resolvers/getParentWithChildren.js new file mode 100644 index 0000000000..14580b158f --- /dev/null +++ b/appsync-dynamodb-singletable-js-resolver/sam_graphql_api/resolvers/getParentWithChildren.js @@ -0,0 +1,8 @@ +export function request(_) { + return {}; +} + +export function response(ctx) { + if (!ctx.stash.parent) return + return { ...ctx.stash.parent, children: ctx.stash.children }; +} \ No newline at end of file diff --git a/appsync-dynamodb-singletable-js-resolver/sam_graphql_api/schema.graphql b/appsync-dynamodb-singletable-js-resolver/sam_graphql_api/schema.graphql new file mode 100644 index 0000000000..f8c0e119ce --- /dev/null +++ b/appsync-dynamodb-singletable-js-resolver/sam_graphql_api/schema.graphql @@ -0,0 +1,36 @@ +type Parent{ + PK: String! + SK: String! + children: [Child] + data: String! + type: String! +} + +type Child { + PK: String! + SK: String! + data: String! + type: String! + parentPK: String! + parentSK: String! +} + +type Mutation { + createParentItem( + PK: ID!, + SK: String!, + data: String! + ): Parent + + createChildItem( + PK: ID!, + SK: String!, + data: String!, + parentPK: String! + parentSK: String! + ): Child +} + +type Query{ + getParentWithChildren(PK: ID!, SK: ID!): Parent +} \ No newline at end of file diff --git a/appsync-dynamodb-singletable-js-resolver/template.yaml b/appsync-dynamodb-singletable-js-resolver/template.yaml new file mode 100644 index 0000000000..e84a363b47 --- /dev/null +++ b/appsync-dynamodb-singletable-js-resolver/template.yaml @@ -0,0 +1,104 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: Serverless patterns - AppSync to DynamoDB single-table - JS Resolvers + +Resources: + ################################################### + ### DynamoDB Single Table ### + ################################################### + DDBTable: + Type: "AWS::DynamoDB::Table" + Properties: + BillingMode: PAY_PER_REQUEST + AttributeDefinitions: + - AttributeName: PK + AttributeType: S + - AttributeName: SK + AttributeType: S + - AttributeName: parentPK + AttributeType: S + - AttributeName: parentSK + AttributeType: S + KeySchema: + - AttributeName: PK + KeyType: HASH + - AttributeName: SK + KeyType: RANGE + GlobalSecondaryIndexes: + - IndexName: ParentGSI + KeySchema: + - AttributeName: parentPK + KeyType: HASH + - AttributeName: parentSK + KeyType: RANGE + Projection: + ProjectionType: ALL + + ################################################### + ### AppSync API ### + ################################################### + MyGraphQLAPI: + Type: AWS::Serverless::GraphQLApi + Properties: + SchemaUri: ./sam_graphql_api/schema.graphql + ApiKeys: + TestApiKey: + Description: Test Api Key + Auth: + Type: API_KEY + DataSources: + DynamoDb: + ItemsDataSource: + TableName: !Ref DDBTable + TableArn: !GetAtt DDBTable.Arn + Functions: + getParentFunction: + Runtime: + Name: APPSYNC_JS + Version: 1.0.0 + DataSource: ItemsDataSource + CodeUri: ./sam_graphql_api/resolvers/functions/getParent.js + + getParentChildrenFunction: + Runtime: + Name: APPSYNC_JS + Version: 1.0.0 + DataSource: ItemsDataSource + CodeUri: ./sam_graphql_api/resolvers/functions/getParentChildren.js + + createParentFunction: + Runtime: + Name: APPSYNC_JS + Version: 1.0.0 + DataSource: ItemsDataSource + CodeUri: ./sam_graphql_api/resolvers/functions/createParent.js + + createChildFunction: + Runtime: + Name: APPSYNC_JS + Version: 1.0.0 + DataSource: ItemsDataSource + CodeUri: ./sam_graphql_api/resolvers/functions/createChild.js + Resolvers: + Query: + getParentWithChildren: + Runtime: + Name: APPSYNC_JS + Version: "1.0.0" + CodeUri: ./sam_graphql_api/resolvers/getParentWithChildren.js + Pipeline: + - getParentFunction + - getParentChildrenFunction + Mutation: + createParentItem: + Runtime: + Name: APPSYNC_JS + Version: "1.0.0" + Pipeline: + - createParentFunction + createChildItem: + Runtime: + Name: APPSYNC_JS + Version: "1.0.0" + Pipeline: + - createChildFunction \ No newline at end of file diff --git a/appsync-eventbridge-datasource/example-pattern.json b/appsync-eventbridge-datasource/example-pattern.json index dc4e9627e1..0e8bad8855 100644 --- a/appsync-eventbridge-datasource/example-pattern.json +++ b/appsync-eventbridge-datasource/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/appsync-lambda-kendra/example-pattern.json b/appsync-lambda-kendra/example-pattern.json index 4ecd73a610..8ea98e6cbc 100644 --- a/appsync-lambda-kendra/example-pattern.json +++ b/appsync-lambda-kendra/example-pattern.json @@ -37,7 +37,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/appsync-lambda-sfn-cdk/example-pattern.json b/appsync-lambda-sfn-cdk/example-pattern.json index 5f77c36673..b7289d2f5f 100644 --- a/appsync-lambda-sfn-cdk/example-pattern.json +++ b/appsync-lambda-sfn-cdk/example-pattern.json @@ -47,7 +47,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/appsync-lambda-sfn-sam/example-pattern.json b/appsync-lambda-sfn-sam/example-pattern.json index aa72aa745a..0ec5d68916 100644 --- a/appsync-lambda-sfn-sam/example-pattern.json +++ b/appsync-lambda-sfn-sam/example-pattern.json @@ -42,7 +42,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/appsync-mongodb/example-pattern.json b/appsync-mongodb/example-pattern.json index 70588f93c3..f4bf4aabcb 100644 --- a/appsync-mongodb/example-pattern.json +++ b/appsync-mongodb/example-pattern.json @@ -40,7 +40,7 @@ "text": ["npx aws-cdk deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: npx aws-cdk destroy."] diff --git a/appsync-stepfunctions-express-sam/README.md b/appsync-stepfunctions-express-sam/README.md new file mode 100644 index 0000000000..4ea890ec5c --- /dev/null +++ b/appsync-stepfunctions-express-sam/README.md @@ -0,0 +1,55 @@ +# AWS AppSync to AWS Step Functions (sync express workflow) + +This project contains a sample AWS SAM template for running synchronous express workflows for Step Functions from an AppSync API. The State Machine will be added as an HTTP data source to AppSync API. + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +2. Change directory to the pattern directory: + ``` + cd appsync-stepfunctions-express-sam + ``` +3. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: + ``` + sam deploy --guided + ``` +4. During the prompts: + * Enter a stack name + * Enter the desired AWS Region + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run `sam deploy --guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +5. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. + +## How it works + +This project creates an AppSync API. From your AppSync API, you are able to run a mutation called `startWorkflow` to start your step function express workflow execution syncronously. + +## Testing + +You can test your AppSync API and Step Functions express workflow integration by running a query from AWS AppSync console. +![mutation](images/mutation.png) + +## Cleanup + +1. Delete the stack, Enter `Y` to confirm deleting the stack and folder. + ``` + sam delete + ``` +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 \ No newline at end of file diff --git a/appsync-stepfunctions-express-sam/example-pattern.json b/appsync-stepfunctions-express-sam/example-pattern.json new file mode 100644 index 0000000000..eab6ee78cd --- /dev/null +++ b/appsync-stepfunctions-express-sam/example-pattern.json @@ -0,0 +1,62 @@ +{ + "title": "AWS AppSync to AWS Step Functions (sync express workflow)", + "description": "Create AppSync API to trigger a syncrounuous SFN execution", + "language": "YAML", + "level": "200", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This project contains a sample AWS SAM template for running synchronous express workflows for Step Functions from an AppSync API.", + "The State Machine will be added as an HTTP data source to AppSync API." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/appsync-stepfunctions-express-sam", + "templateURL": "serverless-patterns/appsync-stepfunctions-express-sam", + "projectFolder": "appsync-stepfunctions-express-sam", + "templateFile": "appsync-stepfunctions-express-sam/template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Invoke AWS services directly from AWS AppSync", + "link": "https://aws.amazon.com/blogs/mobile/invoke-aws-services-directly-from-aws-appsync/" + }, + { + "text": "AWS Appsync", + "link": "https://aws.amazon.com/appsync/" + }, + { + "text": "AWS Step Functions", + "link": "https://aws.amazon.com/step-functions/" + } + ] + }, + "deploy": { + "text": [ + "sam deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "name": "Alexis Philippart de Foy", + "image": "https://media.licdn.com/dms/image/C4D03AQFyN5GLrFk-0g/profile-displayphoto-shrink_800_800/0/1586853008031?e=1693440000&v=beta&t=0BtjX92oZxhHUrrIzh55wah8V1OiwOAOyigPtfAcZdo", + "bio": " I am a Startup Solutions Architect, Serverless enthusiast and former Startup Founder.", + "linkedin": "apdf", + "twitter": "@aphilippartd" + } + ] +} diff --git a/appsync-stepfunctions-express-sam/images/mutation.png b/appsync-stepfunctions-express-sam/images/mutation.png new file mode 100644 index 0000000000..6803b241ef Binary files /dev/null and b/appsync-stepfunctions-express-sam/images/mutation.png differ diff --git a/appsync-stepfunctions-express-sam/schema.graphql b/appsync-stepfunctions-express-sam/schema.graphql new file mode 100644 index 0000000000..cd53078dc0 --- /dev/null +++ b/appsync-stepfunctions-express-sam/schema.graphql @@ -0,0 +1,19 @@ +type Execution { + name: String + status: String + input: String + executionArn: String + startDate: String + stopDate: String + output: String +} + +type Mutation { + startWorkflow ( + name: String! + ): Execution +} + +# Any Query you need for your application +type QueryOutput { output: String! } +type Query { myQuery(input: String!): QueryOutput } diff --git a/appsync-stepfunctions-express-sam/template.yaml b/appsync-stepfunctions-express-sam/template.yaml new file mode 100644 index 0000000000..137ac6283a --- /dev/null +++ b/appsync-stepfunctions-express-sam/template.yaml @@ -0,0 +1,126 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: AppSync to Step Functions Express (Sync) + +Resources: +########################################################################## +# AppSync API # +########################################################################## + AppSyncApi: + Type: AWS::Serverless::GraphQLApi + Properties: + SchemaUri: ./schema.graphql + ApiKeys: + TestApiKey: + Description: Test Api Key + Auth: + Type: API_KEY + Functions: + addWorkflowArnToStashFunction: + Runtime: + Name: APPSYNC_JS + Version: 1.0.0 + DataSource: None + InlineCode: !Sub | + export function request(ctx) { + ctx.stash.stateMachineArn = "${StateMachine}" + return {}; + } + export function response(ctx) { + return ctx.args; + } + startWorkflowFunction: + Runtime: + Name: APPSYNC_JS + Version: 1.0.0 + DataSource: !GetAtt StepFunctionDataSource.Name + InlineCode: | + import { util } from '@aws-appsync/utils' + export function request(ctx) { + return { + "version": "2018-05-29", + "method": "POST", + "resourcePath": "/", + "params": { + "headers": { + "content-type": "application/x-amz-json-1.0", + "x-amz-target":"AWSStepFunctions.StartSyncExecution" + }, + "body": { + "stateMachineArn": ctx.stash.stateMachineArn, + "input": JSON.stringify(ctx.args) + } + } + }; + } + export function response(ctx) { + if (ctx.error) util.error(ctx.error.message, ctx.error.type); + if (ctx.result.statusCode === 200) { + return JSON.parse(ctx.result.body); + } else { + util.appendError(ctx.result.body, ctx.result.statusCode); + } + } + Resolvers: + Mutation: + startWorkflow: + Runtime: + Name: APPSYNC_JS + Version: "1.0.0" + Pipeline: + - addWorkflowArnToStashFunction + - startWorkflowFunction + +########################################################################## +# AppSync Data Sources # +########################################################################## + StepFunctionDataSource: + Type: AWS::AppSync::DataSource + Properties: + ApiId: !GetAtt AppSyncApi.ApiId + Name: StepFunctionDataSource + Type: HTTP + ServiceRoleArn: !GetAtt StepFunctionDataSourceRole.Arn + HttpConfig: + Endpoint: !Sub "https://sync-states.${AWS::Region}.amazonaws.com/" + AuthorizationConfig: + AuthorizationType: AWS_IAM + AwsIamConfig: + SigningRegion: !Ref AWS::Region + SigningServiceName: states + + StepFunctionDataSourceRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: + Service: appsync.amazonaws.com + Action: sts:AssumeRole + Policies: + - PolicyName: WorkflowPolicy + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Action: states:StartSyncExecution + Resource: !Ref StateMachine + +########################################################################## +# SFN State Machine # +########################################################################## + StateMachine: + Type: AWS::Serverless::StateMachine + Properties: + Type: EXPRESS + Definition: + Comment: StateMachine + StartAt: Pass + States: + Pass: + Type: Pass + End: true + Parameters: + message.$: States.Format('Hey {}, this is a message from your state machine',$.name) \ No newline at end of file diff --git a/aurora-serverless-s3-ingestion/README.md b/aurora-serverless-s3-ingestion/README.md deleted file mode 100644 index 493405388a..0000000000 --- a/aurora-serverless-s3-ingestion/README.md +++ /dev/null @@ -1,86 +0,0 @@ -# Amazon Aurora Serverless data ingestion from Amazon S3 - -This pattern contains a sample AWS Cloud Development Kit (AWS CDK) template to deploying an Aurora Serverless Cluster Database, a AWS Secrets Manager entry, a S3 bucket and a lambda function. The Lambda function is triggered by a S3 put object and the handler ingest the .CSV file to AWS Aurora Serverless. With this pattern, an Aurora table called *movies* is created at the first Lambda call and the .CSV is designed according to the movies table. - -Learn more about this pattern at Serverless Land Patterns: -[Amazon Aurora Serverless data ingestion from S3](https://github.com/aws-samples/serverless-patterns/aurora-serverless-s3-ingestion) - -Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. - -## Requirements - -* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. -* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured -* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed -* [Supported features in Amazon Aurora by AWS Region](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Concepts.AuroraFeaturesRegionsDBEngines.grids.html#Concepts.Aurora_Fea_Regions_DB-eng.Feature.Serverless) - Ensure that you deploy this pattern on Region that supports Amazon Aurora Serverless - -## Deployment Instructions - -1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: - ``` - git clone https://github.com/aws-samples/serverless-patterns - ``` -1. Change directory to the pattern directory: - ``` - cd aurora-serverless-s3-ingestion/cdk - ``` -1. Create a virtual environment for python: - ``` - python3 -m venv .venv - ``` -1. Activate the virtual environment: - ``` - source .venv/bin/activate - ``` -2. Install python modules: - ``` - python3 -m pip install -r requirements.txt - ``` -2. From the command line, use CDK to synthesize the CloudFormation template and check for errors: - ``` - cdk synth - ``` -2. From the command line, use CDK to deploy the stack: - ``` - cdk deploy - ``` - -## How it works - -- The VPC and Subnets are created; -- A RDS security group is created to allow connections at 3306 port from all VPC CIDR range; -- The Amazon Aurora Serverless Cluster Database is created; -- An IAM Policy to be used by the Lambda function is created; -- An IAM Role is created; -- An S3 Bucket is created and is used as stage to raw data; -- A Lambda function is create using the same VPC as Amazon Aurora Serverless, with 10 minutes timeout and is triggered by S3 create put on the raw S3 bucket. - -## Testing - -After deploying this pattern CDK will output two lines: -1. Copy the raw data to Amazon S3 Bucket. -2. Query the database to confirm the data was consumed by Amazon Aurora Serverless. - -Example: - -``` -aurora-serverless-ingestion.S3UploadFileCommand = aws s3 cp ../movies.csv s3:// - -aurora-serverless-ingestion.QueryDatabaseCommand = aws rds-data execute-statement --resource-arn "arn:aws:rds:us-west-1:xxxx:cluster:" --database "mydatabase" --secret-arn "arn:aws:secretsmanager:us-west-1:xxxxx:secret:" --sql "select count(1) from movies" -``` - -## Cleanup - -1. Delete the stack - ```bash - cdk destroy - ``` -1. Confirm the stack has been deleted - ```bash - aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'STACK_NAME')].StackStatus" - ``` ----- -Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. - -SPDX-License-Identifier: MIT-0 diff --git a/aurora-serverless-s3-ingestion/cdk/.gitignore b/aurora-serverless-s3-ingestion/cdk/.gitignore deleted file mode 100644 index 95f954427c..0000000000 --- a/aurora-serverless-s3-ingestion/cdk/.gitignore +++ /dev/null @@ -1,22 +0,0 @@ -*.swp -package-lock.json -.pytest_cache -*.egg-info - -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# CDK Context & Staging files -.cdk.staging/ -cdk.out/ \ No newline at end of file diff --git a/aurora-serverless-s3-ingestion/cdk/README.md b/aurora-serverless-s3-ingestion/cdk/README.md deleted file mode 100644 index 0ce9226b0b..0000000000 --- a/aurora-serverless-s3-ingestion/cdk/README.md +++ /dev/null @@ -1,65 +0,0 @@ - -# Welcome to your CDK Python project! - -You should explore the contents of this project. It demonstrates a CDK app with an instance of a stack (`aurora_serverless_ingestion_stack`) -which contains an Amazon SQS queue that is subscribed to an Amazon SNS topic. - -The `cdk.json` file tells the CDK Toolkit how to execute your app. - -This project is set up like a standard Python project. The initialization process also creates -a virtualenv within this project, stored under the .venv directory. To create the virtualenv -it assumes that there is a `python3` executable in your path with access to the `venv` package. -If for any reason the automatic creation of the virtualenv fails, you can create the virtualenv -manually once the init process completes. - -To manually create a virtualenv on MacOS and Linux: - -``` -$ python3 -m venv .venv -``` - -After the init process completes and the virtualenv is created, you can use the following -step to activate your virtualenv. - -``` -$ source .venv/bin/activate -``` - -If you are a Windows platform, you would activate the virtualenv like this: - -``` -% .venv\Scripts\activate.bat -``` - -Once the virtualenv is activated, you can install the required dependencies. - -``` -$ pip install -r requirements.txt -``` - -At this point you can now synthesize the CloudFormation template for this code. - -``` -$ cdk synth -``` - -You can now begin exploring the source code, contained in the hello directory. -There is also a very trivial test included that can be run like this: - -``` -$ pytest -``` - -To add additional dependencies, for example other CDK libraries, just add to -your requirements.txt file and rerun the `pip install -r requirements.txt` -command. - -## Useful commands - - * `cdk ls` list all stacks in the app - * `cdk synth` emits the synthesized CloudFormation template - * `cdk deploy` deploy this stack to your default AWS account/region - * `cdk diff` compare deployed stack with current state - * `cdk docs` open CDK documentation - -Enjoy! diff --git a/aurora-serverless-s3-ingestion/cdk/app.py b/aurora-serverless-s3-ingestion/cdk/app.py deleted file mode 100644 index efde5c20c5..0000000000 --- a/aurora-serverless-s3-ingestion/cdk/app.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env python3 - -import aws_cdk as cdk - -# from aurora_serverless_ingestion.aurora_serverless_ingestion_stack import AuroraServerlessIngestionStack - -# from aws_cdk import core -from aurora_serverless_ingestion.aurora_serverless_ingestion_stack import AuroraServerlessIngestionStack - -app = cdk.App() -AuroraServerlessIngestionStack(app, "aurora-serverless-ingestion") - -app.synth() diff --git a/aurora-serverless-s3-ingestion/cdk/aurora_serverless_ingestion/aurora_serverless_ingestion_stack.py b/aurora-serverless-s3-ingestion/cdk/aurora_serverless_ingestion/aurora_serverless_ingestion_stack.py deleted file mode 100644 index 744097e706..0000000000 --- a/aurora-serverless-s3-ingestion/cdk/aurora_serverless_ingestion/aurora_serverless_ingestion_stack.py +++ /dev/null @@ -1,74 +0,0 @@ -from constructs import Construct -from aws_cdk import ( - CfnOutput, - Stack, - Duration, - aws_iam as iam, - aws_s3 as s3, - aws_ec2 as ec2, - aws_rds as rds, - aws_lambda as _lambda, - # core -) -from aws_cdk.aws_lambda_event_sources import S3EventSource - -class AuroraServerlessIngestionStack(Stack): - - def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: - super().__init__(scope, construct_id, **kwargs) - - # Creates a S3 Bucket - s3bucket = s3.Bucket(self, "AuroraIngestionBucket") - s3bucketarn = s3bucket.bucket_arn - - # Creates a new VPC - vpc = ec2.Vpc(self, "VPC") - vpc_cidr = vpc.vpc_cidr_block - - # Create a security group - - aurora_sg = ec2.SecurityGroup(self, "SecurityGroup", - vpc=vpc, - description="Allow ssh access to aurora cluster", - allow_all_outbound=True - ) - aurora_sg.add_ingress_rule( - ec2.Peer.ipv4(vpc_cidr), - ec2.Port.tcp(3306) - ) - - # Creates a new Aurora Serverless database - cluster = rds.ServerlessCluster(self, "MyAuroraCluster", - engine = rds.DatabaseClusterEngine.AURORA_MYSQL, - vpc = vpc, - enable_data_api = True, - default_database_name = "mydatabase", - security_groups = [aurora_sg] - ) - - # Creates a lambda function to ingest data from S3 to Aurora - fn = _lambda.Function(self, "Function", - code=_lambda.Code.from_asset('./lambda'), - runtime = _lambda.Runtime.PYTHON_3_9, - handler = 'DataIngest.lambda_handler', - vpc = vpc, - timeout = Duration.seconds(600), - environment = { - "BUCKET": s3bucketarn, - "CLUSTER_ARN": cluster.cluster_arn, - "SECRET_ARN": cluster.secret.secret_arn, - "DATABASE": "mydatabase" - } - ) - - s3bucket.grant_read(fn) - cluster.grant_data_api_access(fn); - - # Add a S3 event to lambda - fn.add_event_source( - S3EventSource(s3bucket, - events=[s3.EventType.OBJECT_CREATED] - )) - - CfnOutput(self, "S3UploadFileCommand", value="aws s3 cp ../movies.csv s3://" + s3bucket.bucket_name) - CfnOutput(self, "QueryDatabaseCommand", value='aws rds-data execute-statement --resource-arn "' + cluster.cluster_arn + '" --database "mydatabase" --secret-arn "' + cluster.secret.secret_arn + '" --sql "select count(1) from movies"') \ No newline at end of file diff --git a/aurora-serverless-s3-ingestion/cdk/cdk.json b/aurora-serverless-s3-ingestion/cdk/cdk.json deleted file mode 100644 index 7894c83284..0000000000 --- a/aurora-serverless-s3-ingestion/cdk/cdk.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "app": "python3 app.py", - "watch": { - "include": [ - "**" - ], - "exclude": [ - "README.md", - "cdk*.json", - "requirements*.txt", - "source.bat", - "**/__init__.py", - "python/__pycache__", - "tests" - ] - }, - "context": { - "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, - "@aws-cdk/core:stackRelativeExports": true, - "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, - "@aws-cdk/aws-lambda:recognizeVersionProps": true, - "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true - } -} diff --git a/aurora-serverless-s3-ingestion/cdk/lambda/DataIngest.py b/aurora-serverless-s3-ingestion/cdk/lambda/DataIngest.py deleted file mode 100644 index dd9dc60f95..0000000000 --- a/aurora-serverless-s3-ingestion/cdk/lambda/DataIngest.py +++ /dev/null @@ -1,89 +0,0 @@ -import csv -import os -import time -import tempfile -import boto3 -from csv import reader -from botocore.exceptions import ClientError - -rdsdata = boto3.client('rds-data') -s3 = boto3.client('s3') -my_resource_arn = os.environ.get('CLUSTER_ARN') -my_secret_arn = os.environ.get('SECRET_ARN') -my_database = 'mydatabase' - -def count_rows(): - sql_stm = f"""select count(1) from movies""" - return(sql_stm) - -def insert_rows(values): - sql_stm = f"""insert into movies values {values}""" - return(sql_stm) - -def create_table(): - response = rdsdata.execute_statement ( - resourceArn = my_resource_arn, - secretArn = my_secret_arn, - database = my_database, - sql = 'Create table IF NOT EXISTS movies (Year int(4),Length int(5),Title varchar(150),Subject varchar(250),Actor varchar(150),Actress varchar(150),Director varchar(150),Popularity int(5),Awards varchar(150),Image varchar(250))' - ) - -def wake_aurora(): - delay = 5 - max_attempts = 10 - - attempt = 0 - while attempt < max_attempts: - attempt += 1 - try: - wake = rdsdata.execute_statement ( - resourceArn = my_resource_arn, - secretArn = my_secret_arn, - database = my_database, - sql = 'select 1' - ) - return - except ClientError as ce: - error_code = ce.response.get("Error").get('Code') - error_msg = ce.response.get("Error").get('Message') - # Aurora serverless is waking up - if error_code == 'BadRequestException' and 'Communications link failure' in error_msg: - time.sleep(delay) - else: - raise ce - - raise Exception('Waited for Aurora Serveless but still got errors') - -def lambda_handler(event, context): - - wake_aurora() - - create_table() - - for record in event['Records']: - source_bucket = record['s3']['bucket']['name'] - key = record['s3']['object']['key'] - with tempfile.TemporaryDirectory() as tmpdir: - download_path = os.path.join(tmpdir, key) - s3.download_file(source_bucket, key, download_path) - # items = read_csv(download_path) - with open(download_path, 'r') as read_obj: - # pass the file object to reader() to get the reader object - csv_reader = reader(read_obj) - # Iterate over each row in the csv using reader object - next(csv_reader) - for row in csv_reader: - counter = 0 - for i in row: - if i == '': - # Transform null values into 0 - row[counter] = 0 - counter += 1 - values = tuple(row) - sql_stm = insert_rows(values) - response2 = rdsdata.execute_statement ( - resourceArn = my_resource_arn, - secretArn = my_secret_arn, - database = my_database, - sql = sql_stm - ) \ No newline at end of file diff --git a/aurora-serverless-s3-ingestion/cdk/lambda/DataIngest.py.zip b/aurora-serverless-s3-ingestion/cdk/lambda/DataIngest.py.zip deleted file mode 100644 index ae537e8643..0000000000 Binary files a/aurora-serverless-s3-ingestion/cdk/lambda/DataIngest.py.zip and /dev/null differ diff --git a/aurora-serverless-s3-ingestion/cdk/requirements-dev.txt b/aurora-serverless-s3-ingestion/cdk/requirements-dev.txt deleted file mode 100644 index 927094516e..0000000000 --- a/aurora-serverless-s3-ingestion/cdk/requirements-dev.txt +++ /dev/null @@ -1 +0,0 @@ -pytest==6.2.5 diff --git a/aurora-serverless-s3-ingestion/cdk/requirements.txt b/aurora-serverless-s3-ingestion/cdk/requirements.txt deleted file mode 100644 index ca6ffbed92..0000000000 --- a/aurora-serverless-s3-ingestion/cdk/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -aws-cdk-lib==2.1.0 -constructs>=10.0.0,<11.0.0 \ No newline at end of file diff --git a/aurora-serverless-s3-ingestion/cdk/tests/unit/test_aurora_serverless_ingestion_stack.py b/aurora-serverless-s3-ingestion/cdk/tests/unit/test_aurora_serverless_ingestion_stack.py deleted file mode 100644 index 82b8ef2b55..0000000000 --- a/aurora-serverless-s3-ingestion/cdk/tests/unit/test_aurora_serverless_ingestion_stack.py +++ /dev/null @@ -1,21 +0,0 @@ -import aws_cdk as core -import aws_cdk.assertions as assertions -from aurora_serverless_ingestion.aurora_serverless_ingestion_stack import AuroraServerlessIngestionStack - - -def test_sqs_queue_created(): - app = core.App() - stack = AuroraServerlessIngestionStack(app, "aurora-serverless-ingestion") - template = assertions.Template.from_stack(stack) - - template.has_resource_properties("AWS::SQS::Queue", { - "VisibilityTimeout": 300 - }) - - -def test_sns_topic_created(): - app = core.App() - stack = AuroraServerlessIngestionStack(app, "aurora-serverless-ingestion") - template = assertions.Template.from_stack(stack) - - template.resource_count_is("AWS::SNS::Topic", 1) diff --git a/aurora-serverless-s3-ingestion/movies.csv b/aurora-serverless-s3-ingestion/movies.csv deleted file mode 100644 index ece906388e..0000000000 --- a/aurora-serverless-s3-ingestion/movies.csv +++ /dev/null @@ -1,1660 +0,0 @@ -Year,Length,Title,Subject,Actor,Actress,Director,Popularity,Awards,Image -1990,111,Tie Me Up! Tie Me Down!,Comedy,"Banderas, Antonio","Abril, Victoria","Almodóvar, Pedro",68,No,NicholasCage.png -1991,113,High Heels,Comedy,"Bosé, Miguel","Abril, Victoria","Almodóvar, Pedro",68,No,NicholasCage.png -1983,104,"Dead Zone, The",Horror,"Walken, Christopher","Adams, Brooke","Cronenberg, David",79,No,NicholasCage.png -1979,122,Cuba,Action,"Connery, Sean","Adams, Brooke","Lester, Richard",6,No,seanConnery.png -1978,94,Days of Heaven,Drama,"Gere, Richard","Adams, Brooke","Malick, Terrence",14,No,NicholasCage.png -1983,140,Octopussy,Action,"Moore, Roger","Adams, Maud","Glen, John",68,No,NicholasCage.png -1984,101,Target Eagle,Action,"Connors, Chuck","Adams, Maud","Loma, José Antonio de la",14,No,NicholasCage.png -1989,99,"American Angels: Baptism of Blood, The",Drama,"Bergen, Robert D.","Adams, Trudy","Sebastian, Beverly",28,No,NicholasCage.png -1985,104,Subway,Drama,"Lambert, Christopher","Adjani, Isabelle","Besson, Luc",6,No,NicholasCage.png -1990,149,Camille Claudel,Drama,"Depardieu, Gérard","Adjani, Isabelle","Nuytten, Bruno",32,No,NicholasCage.png -1982,188,Fanny and Alexander,Drama,"Ahlstedt, Börje","Adolphson, Kristina","Bergman, Ingmar",81,Yes,Bergman.png -1982,117,Tragedy of a Ridiculous Man,Drama,"Tognazzi, Ugo","Aimee, Anouk","Bertolucci, Bernardo",17,No,NicholasCage.png -1966,103,A Man & a Woman,Drama,"Trintignant, Jean-Louis","Aimee, Anouk","Lelouch, Claude",46,Yes,NicholasCage.png -1986,112,A Man & a Woman: Twenty Years Later,Drama,"Trintignant, Jean-Louis","Aimee, Anouk","Lelouch, Claude",49,No,NicholasCage.png -1966,103,Un Hombre y una Mujer,Drama,"Trintignant, Jean-Louis","Aimee, Anouk","Lelouch, Claude",6,Yes,NicholasCage.png -1985,112,"Official Story, The",Drama,"Alterio, Hector","Aleandro, Norma","Puenzo, Luiz",39,Yes,NicholasCage.png -1976,150,"Lindbergh Kidnapping Case, The",Drama,"Hopkins, Anthony","Alexander, Denise","Kulik, Buzz",51,No,AnthonyHopkins.png -1929,84,Blackmail,Mystery,"Longden, John","Algood, Sara","Hitchcock, Alfred",2,No,alfredHitchcock.png -1963,109,Donovan's Reef,Comedy,"Wayne, John","Allen, Elizabeth","Ford, John",62,No,johnWayne.png -1988,110,Tucker: The Man & His Dream,Drama,"Bridges, Jeff","Allen, Joan","Coppola, Francis Ford",68,No,NicholasCage.png -1988,101,Scrooged,Comedy,"Murray, Bill","Allen, Karen","Donner, Richard",15,No,NicholasCage.png -1981,116,Raiders of the Lost Ark,Action,"Ford, Harrison","Allen, Karen","Spielberg, Steven",8,No,NicholasCage.png -1987,101,"Running Man, The",Science Fiction,"Schwarzenegger, Arnold","Alonso, Maria Conchita","Glaser, Paul Michael",31,No,NicholasCage.png -1991,105,Predator 2,Action,"Glover, Danny","Alonso, Maria Conchita","Hopkins, Stephen",79,No,NicholasCage.png -1988,127,Colors,Drama,"Penn, Sean","Alonso, Maria Conchita","Hopper, Dennis",23,No,NicholasCage.png -1990,97,Zandalee,Drama,"Cage, Nicolas","Anderson, Erika","Pillsbury, Sam",80,No,NicholasCage.png -1988,108,Miles from Home,Drama,"Anderson, Kevin","Anderson, Jo","Sinise, Gary",53,No,NicholasCage.png -1980,,Happy Birthday to Me,Horror,"Ford, Glenn","Anderson, Melissa Sue","Thompson, J. Lee",88,No,glennFord.png -1989,88,Final Notice,Mystery,"Gerard, Gil","Anderson, Melody","Stern, Steven Hilliard",88,No,NicholasCage.png -1979,110,Quintet,Drama,"Newman, Paul","Andersson, Bibi","Altman, Robert",19,No,paulNewman.png -1960,90,"Devil's Eye, The",Drama,"Kulle, Jarl","Andersson, Bibi","Bergman, Ingmar",20,No,Bergman.png -1957,91,Wild Strawberries,Drama,"Sjöström, Victor","Andersson, Bibi","Bergman, Ingmar",42,Yes,Bergman.png -1956,96,"Seventh Seal, The",Drama,"Sydow, Max von","Andersson, Bibi","Bergman, Ingmar",62,No,Bergman.png -1992,90,Germicide,Drama,"Taylor, Rod","Andersson, Bibi",,36,No,NicholasCage.png -1955,86,Dreams,Drama,"Björnstrand, Gunnar","Andersson, Harriet","Bergman, Ingmar",14,No,Bergman.png -1955,95,"Naked Night, The",Drama,"Björnstrand, Gunnar","Andersson, Harriet","Bergman, Ingmar",38,No,Bergman.png -1962,91,Through a Glass Darkly,Drama,"Björnstrand, Gunnar","Andersson, Harriet","Bergman, Ingmar",64,Yes,Bergman.png -1972,91,Cries & Whispers,Drama,"Josephson, Erland","Andersson, Harriet","Bergman, Ingmar",18,Yes,Bergman.png -1958,104,"Barbarian & the Geisha, The",Action,"Wayne, John","Ando, Eiko","Huston, John",52,No,johnWayne.png -1967,130,Casino Royale,Comedy,"Niven, David","Andress, Ursula","Hughes, Ken",11,No,NicholasCage.png -1962,,Dr. No,Action,"Connery, Sean","Andress, Ursula","Young, Terence",7,No,seanConnery.png -1954,103,Elephant Walk,Drama,"Finch, Peter","Andrews, Dana",,11,No,NicholasCage.png -1979,121,Ten,Comedy,"Moore, Dudley","Andrews, Julie","Edwards, Blake",60,No,NicholasCage.png -1983,118,"Man Who Loved Women, The",Comedy,"Reynolds, Burt","Andrews, Julie","Edwards, Blake",67,No,NicholasCage.png -1966,190,Hawaii,Drama,"Sydow, Max von","Andrews, Julie","Hill, George Roy",8,No,NicholasCage.png -1966,125,Torn Curtain,Mystery,"Newman, Paul","Andrews, Julie","Hitchcock, Alfred",35,No,paulNewman.png -1986,107,Duet for One,Drama,"Bates, Alan","Andrews, Julie","Konchalovsky, Andrei",82,No,NicholasCage.png -1965,172,"Sound of Music, The",Music,"Plummer, Christopher","Andrews, Julie","Wise, Robert",59,Yes,NicholasCage.png -1985,55,Gonzo Presents Muppet Weird Stuff,Comedy,"Cleese, John","Andrews, Julie",,88,No,NicholasCage.png -1984,140,Tartuffe,Comedy,"Depardieu, Gérard","Annen, Paule","Depardieu, Gérard",67,No,NicholasCage.png -1988,104,A New Life,Comedy,"Alda, Alan",Ann-Margret,"Alda, Alan",53,No,NicholasCage.png -1978,106,Magic,Mystery,"Hopkins, Anthony",Ann-Margret,"Attenborough, Richard",85,No,AnthonyHopkins.png -1992,286,Tommy,Music,"Daltry, Roger",Ann-Margret,"Russell, Ken",5,No,NicholasCage.png -1978,108,"Big Fix, The",Mystery,"Dreyfuss, Richard","Anspach, Susan","Kagan, Jeremy Paul",19,No,NicholasCage.png -1992,95,Alan & Naomi,Drama,"Haas, Lukas","Aquino, Vanessa","Vanwagenen, Sterling",3,No,NicholasCage.png -1987,120,Fatal Attraction,Mystery,"Douglas, Michael","Archer, Anne","Lyne, Adrian",61,No,NicholasCage.png -1992,117,Patriot Games,Action,"Ford, Harrison","Archer, Anne","Noyce, Phillip",28,No,NicholasCage.png -1981,106,"Woman Next Door, The",Drama,"Depardieu, Gérard","Ardant, Fanny","Truffaut, François",82,No,NicholasCage.png -1992,97,Hunting,Mystery,"Savage, John","Armstrong, Kerry","Howson, Frank",68,No,NicholasCage.png -1991,115,Bataan,War,"Taylor, Robert","Arnaz, Desi",,68,No,NicholasCage.png -1924,110,"Siegfried, The Nibelungenlied",Drama,"Richter, Paul","Arnold, Gertrud","Lang, Fritz",79,No,NicholasCage.png -1991,90,"Henry, Portrait of a Serial Killer",Horror,"Rooker, Michael","Arnold, Tracy",,69,No,NicholasCage.png -1988,118,"Big Blue, The",Drama,"Barr, Jean-Marc","Arquette, Rosanna","Besson, Luc",7,No,NicholasCage.png -1991,115,Flight of the Intruder,Drama,"Glover, Danny","Arquette, Rosanna","Milius, John",51,No,NicholasCage.png -1986,108,Nobody's Fool,Comedy,"Roberts, Eric","Arquette, Rosanna","Purcell, Evelyn",52,No,NicholasCage.png -1985,97,After Hours,Comedy,"Dunne, Griffin","Arquette, Rosanna","Scorsese, Martin",81,No,NicholasCage.png -1985,104,Desperately Seeking Susan,Comedy,"Quinn, Aidan","Arquette, Rosanna","Seidelman, Susan",41,No,NicholasCage.png -1971,102,A New Leaf,Comedy,"Matthau, Walter","Arrick, Rose","May, Elaine",83,No,NicholasCage.png -1959,91,Killers of Kilimanjaro,Action,"Taylor, Robert","Aslan, Gregoire","Thorpe, Richard",11,No,NicholasCage.png -1926,126,Don Juan,Action,"Barrymore, John","Astor, Mary","Crosland, Alan",55,No,NicholasCage.png -1987,102,Babette's Feast,Drama,"LaFont, Jean-Philippe","Audran, Stéphane","Axel, Gabriel",79,Yes,NicholasCage.png -1989,118,"Vincent, Francois, Paul & the Others",Drama,"Montand, Yves","Audran, Stéphane",,20,No,NicholasCage.png -1988,141,Thunderball,Action,"Connery, Sean","Auger, Claudine","Young, Terrence",8,No,seanConnery.png -1926,66,Lodger (Story of the London Fog),Mystery,"Chesney, Arthur","Ault, Marie","Hitchcock, Alfred",76,No,alfredHitchcock.png -1988,103,Appointment with Death,Mystery,"Ustinov, Peter","Bacall, Lauren","Donaggio, Michael Winner",75,No,NicholasCage.png -1974,128,Murder on the Orient Express,Mystery,"Balsam, Martin","Bacall, Lauren","Lumet, Sidney",8,Yes,NicholasCage.png -1955,115,Blood Alley,War,"Wayne, John","Bacall, Lauren","Wellman, William",15,No,johnWayne.png -1977,136,"Spy Who Loved Me, The",Action,"Moore, Roger","Bach, Barbara","Gilbert, Lewis",27,No,NicholasCage.png -1988,100,Storm,Action,"Palfy, David","Bahtia, Stacy Christensen","Winning, David",61,No,NicholasCage.png -1991,89,Bloodbath,Horror,"Hopper, Dennis","Baker, Carroll",,37,No,NicholasCage.png -1989,103,Miami Cops,Action,"Roundtree, Richard","Baker, Dawn","Bradley, Al",40,No,NicholasCage.png -1996,96,"Island of Dr. Moreau, The",Horror,"Thewlis, David","Balk, Fairuza","Frankenheimer, John",39,No,NicholasCage.png -1992,100,Eighty-Four Charing Cross Road,Drama,"Hopkins, Anthony","Bancroft, Anne","Jones, David",9,No,AnthonyHopkins.png -1980,124,"Elephant Man, The",Drama,"Hopkins, Anthony","Bancroft, Anne","Lynch, David",3,Yes,AnthonyHopkins.png -1988,90,Dr Alien,Science Fiction,"Jacoby, Billy","Barash, Olivia","DeCoteau, David",70,No,NicholasCage.png -1982,120,Creepshow,Horror,"Holbrook, Hal","Barbeau, Adrienne","Romero, George A.",70,No,NicholasCage.png -1987,100,Sammy & Rosie Get Laid,Drama,"Din, Ayub Khan","Barber, Frances","Frears, Stephen",6,No,NicholasCage.png -1971,101,"Goalie's Anxiety at the Penalty Kick, The",Drama,"Brauss, Arthur","Bardischewski, Maria","Wenders, Wim",62,No,NicholasCage.png -1957,99,Mademoiselle Striptease,Comedy,"Gelin, Daniel","Bardot, Brigitte","Allegret, Marc",25,No,brigitteBardot.png -1969,86,"Women, The",Drama,"Ronet, Maurice","Bardot, Brigitte","Aurel, Jean",66,No,brigitteBardot.png -1958,77,That Naughty Girl,Comedy,"Bretonniere, Jean","Bardot, Brigitte","Boisrond, Michel",37,No,brigitteBardot.png -1959,90,Voulez-Vous Danser Avec Moi?,Comedy,"Vidal, Henri","Bardot, Brigitte","Boisrond, Michel",16,No,brigitteBardot.png -1967,100,"A Coeur Joie, (Head Over Heels)",Action,"Terzieff, Laurent","Bardot, Brigitte","Bourguignon, Serge",54,No,brigitteBardot.png -1968,113,Shalako,Westerns,"Connery, Sean","Bardot, Brigitte","Dmytryk, Edward",0,No,brigitteBardot.png -1964,102,Contempt,Drama,"Palance, Jack","Bardot, Brigitte","Godard, Jean-Luc",81,No,brigitteBardot.png -1965,100,Dear Brigitte,Comedy,"Mumy, Billy","Bardot, Brigitte","Koster, Henry",71,No,brigitteBardot.png -1962,134,A Very Private Affair,Drama,"Mastroianni, Marcello","Bardot, Brigitte","Malle, Louis",30,No,brigitteBardot.png -1964,99,"Ravishing Idiot, The",Comedy,"Perkins, Anthony","Bardot, Brigitte","Molinaro, Edouard",34,No,brigitteBardot.png -1958,90,"Bride Is Much Too Beautiful, The",Comedy,"Jourdan, Louis","Bardot, Brigitte","Surin, Fred",70,No,brigitteBardot.png -1955,90,Doctor at Sea,Comedy,"Bogarde, Dirk","Bardot, Brigitte","Thomas, Ralph",83,No,brigitteBardot.png -1962,100,"Le Repos Du Guerrier, (Warrior's Rest)",War,"Hossein, Robert","Bardot, Brigitte","Vadim, Roger",8,No,brigitteBardot.png -1957,90,And God Created Woman,Drama,"Jurgens, Curt","Bardot, Brigitte","Vadim, Roger",29,No,brigitteBardot.png -1973,87,Ms. Don Juan,Drama,"Ronet, Maurice","Bardot, Brigitte","Vadim, Roger",39,No,brigitteBardot.png -1987,97,Siesta,Drama,"Byrne, Gabriel","Barkin, Ellen","Lambert, Mary",48,No,NicholasCage.png -1932,92,Rich & Strange,Drama,"Kendall, Henry","Barry, Joan","Hitchcock, Alfred",57,No,alfredHitchcock.png -1987,104,Lionheart,Action,"Stoltz, Eric","Barrymore, Deborah","Schaffner, Franklin J.",9,No,NicholasCage.png -1982,115,E. T. The Extra-Terrestrial,Science Fiction,"Wallace, Dee","Barrymore, Drew","Spielberg, Steven",8,Yes,NicholasCage.png -1992,101,Cool World,Drama,"Byrne, Gabriel","Basinger, Kim","Bakshi, Ralph",44,No,NicholasCage.png -1988,83,Nadine,Comedy,"Bridges, Jeff","Basinger, Kim","Benton, Robert",47,No,NicholasCage.png -1989,126,Batman,Action,"Nicholson, Jack","Basinger, Kim","Burton, Tim",14,No,JackNicholson.png -1987,95,Blind Date,Comedy,"Willis, Bruce","Basinger, Kim","Edwards, Blake",7,No,NicholasCage.png -1982,101,Mother Lode,Action,"Heston, Charlton","Basinger, Kim","Heston, Charlton",40,No,NicholasCage.png -1992,125,Final Analysis,Drama,"Gere, Richard","Basinger, Kim","Joanou, Phil",50,No,NicholasCage.png -1983,134,Never Say Never Again,Action,"Connery, Sean","Basinger, Kim","Kershner, Irvin",8,No,seanConnery.png -1986,117,Nine & a Half Weeks,Drama,"Rourke, Mickey","Basinger, Kim","Lyne, Adrian",7,No,NicholasCage.png -1989,,Killjoy,Mystery,"Culp, Robert","Basinger, Kim","Moxey, John Llewellyn",71,No,NicholasCage.png -1986,108,No Mercy,Drama,"Gere, Richard","Basinger, Kim","Pearce, Richard",11,No,NicholasCage.png -1991,116,"Marrying Man, The",Comedy,"Baldwin, Alec","Basinger, Kim","Rees, Jerry",84,No,NicholasCage.png -1990,123,Misery,Horror,"Caan, James","Bates, Kathy","Reiner, Rob",48,Yes,NicholasCage.png -1946,93,Crisis,Drama,"Andersson, Wiktor","Baude, Anna-Lisa","Bergman, Ingmar",66,No,Bergman.png -1984,95,Samson & Delilah,Drama,"Hamilton, Antony","Bauer, Belinda","Philips, Lee",36,No,NicholasCage.png -1990,101,Act of Piracy,Mystery,"Busey, Gary","Bauer, Belinda",,74,No,NicholasCage.png -1988,96,Split Decisions,Drama,"Hackman, Gene","Beals, Jennifer","Drury, David",52,No,NicholasCage.png -1989,103,Vampire's Kiss,Comedy,"Cage, Nicolas","Beals, Jennifer",,49,No,NicholasCage.png -1988,96,Nightmare at Noon,Action,"Hauser, Wings","Beck, Kimberly","Mastorakis, Nico",0,No,NicholasCage.png -1990,127,Presumed Innocent,Mystery,"Ford, Harrison","Bedelia, Bonnie","Pakula, Alan J.",69,No,NicholasCage.png -1942,123,Reap the Wild Wind,Drama,"Wayne, John","Beecher, Janet","DeMille, Cecil B.",59,No,johnWayne.png -1972,100,Pocket Money,Comedy,"Newman, Paul","Belford, Christine","Rosenberg, Stuart",55,No,paulNewman.png -1977,102,Mary White,Drama,"Flanders, Ed","Beller, Kathleen","Taylor, Jud",2,No,NicholasCage.png -1982,,"Catch a Rising Star, Tenth Anniversary",Comedy,"Belzer, Richard","Benatar, Pat",,18,No,NicholasCage.png -1990,105,Guilty by Suspicion,Drama,"De Niro, Robert","Bening, Annette","Winkler, Irwin",88,No,NicholasCage.png -1948,99,Secret Beyond the Door,Mystery,"Redgrave, Michael","Bennett, Joan","Lang, Fritz",31,No,NicholasCage.png -1945,103,Scarlet Street,Drama,"Robinson, Edward G.","Bennett, Joan","Lang, Fritz",80,No,NicholasCage.png -1988,76,Daffy Duck's Quackbusters,Action,"Blanc, Mel","Bennett, Julie","Ford, Greg",68,No,NicholasCage.png -1985,55,Rowlf's Rhapsodies with the Muppets,Comedy,"Burns, George","Berenson, Marisa",,79,No,NicholasCage.png -1982,188,Gandhi,Drama,"Kingsley, Ben","Bergen, Candice","Attenborough, Richard",7,Yes,NicholasCage.png -1975,120,"Wind & the Lion, The",Action,"Connery, Sean","Bergen, Candice","Milius, John",2,No,seanConnery.png -1971,96,Carnal Knowledge,Drama,"Nicholson, Jack","Bergen, Candice","Nichols, Mike",10,No,JackNicholson.png -1970,126,Getting Straight,Comedy,"Gould, Elliott","Bergen, Candice","Rush, Richard",83,No,NicholasCage.png -1972,90,"Scarlet Letter, The",Drama,"Albaicín, Rafael","Berger, Senta","Wenders, Wim",55,No,NicholasCage.png -1935,75,"Count of Old Town, The",Comedy,"Adolphson, Edvin","Bergman, Ingrid","Adolphson, Edvin",72,No,ingridBergman.png -1978,97,Autumn Sonata,Drama,"Björk, Halvar","Bergman, Ingrid","Bergman, Ingmar",49,Yes,ingridBergman.png -1944,114,Gaslight,Drama,"Boyer, Charles","Bergman, Ingrid","Cukor, George",25,Yes,ingridBergman.png -1958,100,Indiscreet,Drama,"Grant, Cary","Bergman, Ingrid","Donen, Stanley",1,No,ingridBergman.png -1941,75,Walpurgis Night,Drama,"Sjöström, Victor","Bergman, Ingrid","Edgren, Gustaf",32,No,ingridBergman.png -1948,100,Joan of Arc,Drama,"Ferrer, Jose","Bergman, Ingrid","Fleming, Victor",7,No,ingridBergman.png -1982,195,A Woman Called Golda,Drama,"Beatty, Ned","Bergman, Ingrid","Gibson, Alan",15,Yes,ingridBergman.png -1969,98,A Walk in the Spring Rain,Drama,"Quinn, Anthony","Bergman, Ingrid","Green, Guy",2,No,ingridBergman.png -1949,117,Under Capricorn,Drama,"Cotten, Joseph","Bergman, Ingrid","Hitchcock, Alfred",74,No,ingridBergman.png -1946,101,Notorious,Mystery,"Grant, Cary","Bergman, Ingrid","Hitchcock, Alfred",42,No,ingridBergman.png -1940,90,June Night,Drama,"Widgren, Olof","Bergman, Ingrid","Lindberg, Per",14,No,ingridBergman.png -1961,120,Goodbye Again,Drama,"Perkins, Anthony","Bergman, Ingrid","Litvak, Anatole",6,No,ingridBergman.png -1956,106,Anastasia,Drama,"Tamiroff, Akim","Bergman, Ingrid","Litvak, Anatole",24,Yes,ingridBergman.png -1945,126,"Bells of St. Mary's, The",Drama,"Crosby, Bing","Bergman, Ingrid","McCarey, Leo",31,No,ingridBergman.png -1937,91,Intermezzo,Drama,"Ekman, Gösta","Bergman, Ingrid","Molander, Gustaf",32,No,ingridBergman.png -1938,104,A Woman's Face,Drama,"Svennberg, Tore","Bergman, Ingrid","Molander, Gustaf",49,No,ingridBergman.png -1935,90,Swedenhielms,Drama,"Westergren, Håkan","Bergman, Ingrid","Molander, Gustaf",88,No,ingridBergman.png -1939,87,Only One Night,Drama,"Adolphson, Edvin","Bergman, Ingrid","Molander, Gustav",26,No,ingridBergman.png -1938,74,Dollar,Drama,"Rydeberg, Georg","Bergman, Ingrid","Molander, Gustav",19,No,ingridBergman.png -1956,98,Elena & Her Men,Drama,"Ferrer, Mel","Bergman, Ingrid","Renoir, Jean",33,No,ingridBergman.png -1952,110,Europa Fifty-One,Drama,"Knox, Alexander","Bergman, Ingrid","Rossellini, Roberto",34,No,ingridBergman.png -1953,83,Voyage in Italy,Drama,"Sanders, George","Bergman, Ingrid","Rossellini, Roberto",57,No,ingridBergman.png -1954,81,Fear,Drama,"Wieman, Mathias","Bergman, Ingrid","Rossellini, Roberto",69,No,ingridBergman.png -1950,107,Stromboli,Drama,"Vitale, Mario","Bergman, Ingrid","Rossellini, Roberto",69,No,ingridBergman.png -1969,103,Cactus Flower,Comedy,"Matthau, Walter","Bergman, Ingrid","Saks, Gene",67,Yes,ingridBergman.png -1989,105,Hideaways,Comedy,"Conover, Bruce","Bergman, Ingrid",,16,No,ingridBergman.png -1990,90,Twenty Four Hours in a Woman's Life,Drama,"Torn, Rip","Bergman, Ingrid",,16,No,ingridBergman.png -1987,91,Programmed to Kill,Action,"Ginty, Robert","Bergman, Sandahl","Holzman, Allan",71,No,NicholasCage.png -1982,128,Conan the Barbarian,Action,"Schwarzenegger, Arnold","Bergman, Sandahl","Milius, John",45,No,NicholasCage.png -1991,91,Raw Nerve,Mystery,"Ford, Glenn","Bergman, Sandahl","Prior, David A.",88,No,glennFord.png -1970,94,Think Dirty,Comedy,"Feldman, Marty","Berman, Shelley","Clark, Jim",31,No,NicholasCage.png -1982,108,King of Comedy,Drama,"De Niro, Robert","Bernhard, Sandra","Scorsese, Martin",84,No,NicholasCage.png -1983,60,"Best of the Big Laff Off, The",Comedy,"Murphy, Eddie","Bernhard, Sandra",,20,No,NicholasCage.png -1984,158,Amadeus,Drama,"Abraham, F. Murray","Berridge, Elizabeth","Forman, Milos",6,Yes,NicholasCage.png -1973,101,White Lightning,Action,"Reynolds, Burt","Billingsley, Jennifer","Sargent, Joseph",54,No,NicholasCage.png -1988,172,"Unbearable Lightness of Being, The",Drama,"Day-Lewis, Daniel","Binoche, Juliette","Kaufman, Philip",5,Yes,NicholasCage.png -1972,124,"Life & Times of Judge Roy Bean, The",Western,"Newman, Paul","Bisset, Jacqueline","Huston, John",65,No,paulNewman.png -1970,137,Airport,Drama,"Lancaster, Burt","Bisset, Jacqueline","Seaton, George",0,Yes,burtLancaster.png -1973,116,Day for Night,Drama,"Aumont, Jean-Pierre","Bisset, Jacqueline","Truffaut, François",10,Yes,NicholasCage.png -1952,107,Secrets of Women,Comedy,"Malmsten, Birger","Björk, Anita","Bergman, Ingmar",66,No,Bergman.png -1976,116,Burnt Offerings,Horror,"Reed, Oliver","Black, Karen","Curtis, Dan",35,No,NicholasCage.png -1969,94,Easy Rider,Drama,"Fonda, Peter","Black, Karen","Hopper, Dennis",36,No,NicholasCage.png -1991,98,Five Easy Pieces,Drama,"Nicholson, Jack","Black, Karen","Rafelson, Bob",2,No,JackNicholson.png -1974,144,"Day of the Locust, The",Drama,"Sutherland, Donald","Black, Karen","Schlesinger, John",81,No,NicholasCage.png -1964,112,Goldfinger,Action,"Connery, Sean","Blackman, Honor","Hamilton, Guy",77,No,seanConnery.png -1977,117,"Exorcist II, The Heretic",Horror,"Burton, Richard","Blair, Linda","Boorman, John",29,No,NicholasCage.png -1953,61,White Lightning,,"Clements, Stanley","Blondell, Gloria","Bernds, Edward",,No,NicholasCage.png -1942,88,Lady for a Night,Drama,"Wayne, John","Blondell, Joan","Leigh, Jason",12,No,johnWayne.png -1968,103,Charly,Drama,"Robertson, Cliff","Bloom, Claire","Nelson, Ralph",38,Yes,NicholasCage.png -1973,105,High Plains Drifter,Western,"Eastwood, Clint","Bloom, Verna","Eastwood, Clint",57,No,clintEastwood.png -1982,123,Honkytonk Man,Drama,"Eastwood, Clint","Bloom, Verna","Eastwood, Clint",69,No,clintEastwood.png -1990,102,Nightbreed,Horror,"Cronenberg, David","Bobby, Anne","Barker, Clive",72,No,NicholasCage.png -1987,98,Under the Sun of Satan,Drama,"Depardieu, Gérard","Bonnaire, Sandrine","Pialat, Maurice",45,No,NicholasCage.png -1985,105,Vagabond,Drama,"Meril, Macha","Bonnaire, Sandrine","Varda, Agnes",49,No,NicholasCage.png -1993,60,"Bill Cosby, Live at Harrah's",Comedy,"Cosby, Bill","Boosler, Elayne",,13,No,NicholasCage.png -1974,89,Monty Python & the Holy Grail,Comedy,"Chapman, Graham","Booth, Connie","Gilliam, Terry",83,No,NicholasCage.png -1993,65,John Cleese on How to Irritate People,Comedy,"Cleese, John","Booth, Connie",,62,No,NicholasCage.png -1958,101,"Matchmaker, The",Comedy,"Perkins, Anthony","Booth, Shirley","Anthony, Joseph",67,No,NicholasCage.png -1981,129,For Your Eyes Only,Action,"Moore, Roger","Bouquet, Carole","Glen, John",86,No,NicholasCage.png -1928,139,Wings,War,"Rogers, Buddy","Bow, Clara","Wellman, William",44,Yes,NicholasCage.png -1992,106,Medicine Man,Action,"Connery, Sean","Bracco, Lorraine","McTiernan, John",6,No,seanConnery.png -1989,,Good Fellas,Drama,"De Niro, Robert","Bracco, Lorraine","Scorsese, Martin",15,No,NicholasCage.png -1985,119,Kiss of the Spider Woman,Drama,"Hurt, William","Braga, Sonia","Babenco, Hector",10,Yes,NicholasCage.png -1990,121,"Rookie, THe",Action,"Eastwood, Clint","Braga, Sonia","Eastwood, Clint",48,No,clintEastwood.png -1973,129,"Sting, The",Drama,"Newman, Paul","Brennan, Eileen","Hill, George Roy",83,Yes,paulNewman.png -1958,96,Torpedo Run,War,"Ford, Glenn","Brewster, Diane","Pevney, Joseph",50,No,glennFord.png -1986,101,Instant Justice,Drama,"Paré, Michael","Bridges, Lynda","Rumar, Craig",45,No,NicholasCage.png -1990,135,Cyrano de Bergerac,Drama,"Depardieu, Gérard","Brochet, Anne","Rappeneau, Jean-Paul",76,No,NicholasCage.png -1948,110,Border Street,Drama,"Fijewski, Tadeusz","Broniewska, Maria","Ford, Aleksander",73,No,NicholasCage.png -1987,91,Firehouse,Comedy,"Hopkins, Barrett","Brown, Violet","Ingvordsen, J. Christian",66,No,NicholasCage.png -1965,123,Morituri,Drama,"Brando, Marlon","Brynner, Yul","Wicki, Bernhard",9,No,brando.png -1980,104,From the Life of the Marionettes,Drama,"Atzorn, Robert","Buchegger, Christine","Bergman, Ingmar",58,No,Bergman.png -1988,120,Frantic,Mystery,"Ford, Harrison","Buckley, Betty","Polanski, Roman",17,No,NicholasCage.png -1978,114,Coma,Science Fiction,"Douglas, Michael","Bujold, Geneviève","Crichton, Michael",64,No,NicholasCage.png -1988,117,Dead Ringers,Drama,"Irons, Jeremy","Bujold, Geneviève","Cronenberg, David",29,No,NicholasCage.png -1988,90,Golden Ninja Invasion,Action,"West, Leonard","Burd, Stephanie","Lambert, Bruce",13,No,NicholasCage.png -1973,122,"Exorcist, The",Horror,"Sydow, Max von","Burstyn, Ellen","Friedkin, William",28,Yes,NicholasCage.png -1975,112,Alice Doesn't Live Here Anymore,Comedy,"Kristofferson, Kris","Burstyn, Ellen",,82,Yes,NicholasCage.png -1982,94,"Eyes of the Amaryllis, The",Drama,"Bolt, Jonathan","Byrne, Martha","King Keller, Frederick",70,No,NicholasCage.png -1952,109,What Price Glory?,War,"Cagney, James","Calvet, Corinne","Ford, John",4,No,johnFord.png -1954,40,Inauguration of the Pleasure Dome,Short,"De Brier, Sampson","Cameron, Marjorie","Anger, Kenneth",62,No,NicholasCage.png -1989,114,School Daze,Comedy,"Fishburne, Larry","Campbell, Tisha","Lee, Spike",18,No,NicholasCage.png -1990,102,"End of Innocence, The",Drama,"Heard, John","Cannon, Dyan","Cannon, Dyan",6,No,NicholasCage.png -1971,98,"Anderson Tapes, The",Mystery,"Connery, Sean","Cannon, Dyan","Lumet, Sidney",1,No,seanConnery.png -1983,50,"Father Murphy, A Horse from Heaven",Comedy,"Olsen, Merlin","Cannon, Katharine","Claxton, William F.",28,No,NicholasCage.png -1989,80,Skull,Drama,"Bideman, Robert","Capone, Nadia","Bergman, Robert",19,No,NicholasCage.png -1987,91,"Quick & The Dead, The",Western,"Elliott, Sam","Capshaw, Kate","Day, Robert",40,No,NicholasCage.png -1984,94,Best Defense,Comedy,"Moore, Dudley","Capshaw, Kate","Huyck, Willard",75,No,NicholasCage.png -1984,99,Dreamscape,Science Fiction,"Quaid, Dennis","Capshaw, Kate","Ruben, Joseph",63,No,NicholasCage.png -1989,125,Black Rain,Action,"Douglas, Michael","Capshaw, Kate","Scott, Ridley",73,No,NicholasCage.png -1963,138,8 1/2,Drama,"Mastroianni, Marcello","Cardinale, Claudia","Fellini, Federico",80,Yes,NicholasCage.png -1935,64,One Frightened Night,Horror,"Ford, Wallace","Carlisle, Mary","Cabanne, Christy",33,No,NicholasCage.png -1988,103,"Year My Voice Broke, The",Drama,"Taylor, Noah","Carmen, Loene","Duigan, John",71,No,NicholasCage.png -1966,175,Is Paris Burning?,War,"Belmondo, Jean-Paul","Caron, Leslie","Clément, René",63,No,NicholasCage.png -1974,313,QB VII,Drama,"Hopkins, Anthony","Caron, Leslie","Gries, Tom",28,Yes,AnthonyHopkins.png -1977,104,"Island of Dr. Moreau, The",Horror,"Lancaster, Burt","Carrera, Barbara","Taylor, Don",54,No,burtLancaster.png -1983,104,Beyond the Limit,Drama,"Caine, Michael","Carrillo, Elpidia","Mackenzie, John",51,No,NicholasCage.png -1936,84,Secret Agent,Mystery,"Lorre, Peter","Carroll, Madeleine","Hitchcock, Alfred",50,No,alfredHitchcock.png -1986,71,Paramount Comedy Theater: Well-Developed,Comedy,"Mahler, Bruce","Carter, Judy",,40,No,NicholasCage.png -1972,71,"Big Bust Out, The",Action,"Kendall, Tony","Carter, Karen","Theumer, Ernst R. von",50,No,NicholasCage.png -1987,119,"Fourth Protocol, The",Mystery,"Caine, Michael","Cassidy, Joanna","Mackenzie, John",14,No,NicholasCage.png -1990,107,Gremlins 2: The New Batch,Comedy,"Galligan, Zach","Cates, Phoebe","Dante, Joe",61,No,NicholasCage.png -1982,92,Fast Times at Ridgemont High,Comedy,"Penn, Sean","Cates, Phoebe","Heckerling, Amy",65,No,NicholasCage.png -1987,,Mannequin,Comedy,"McCarthy, Andrew","Cattrall, Kim","Gottlieb, Michael",23,No,NicholasCage.png -1977,91,Rabid,Horror,"Moore, Frank","Chambers, Marilyn","Cronenberg, David",34,No,NicholasCage.png -1990,,"Party, The",Comedy,"Sellers, Peter","Champion, Marge","Edwards, Blake",32,No,NicholasCage.png -1989,90,"Vampire Raiders, Ninja Queen",Action,"Peterson, Chris","Chan, Agnes","Lambert, Bruce",15,No,NicholasCage.png -1970,26,Bloopers from Star Trek,Comedy,"Lawford, Peter","Channing, Carol",,22,No,NicholasCage.png -1943,99,Destroyer,Action,"Robinson, Edward G.","Chapman, Marguerite","Seiter, William A.",87,No,NicholasCage.png -1992,99,Party Girl,Comedy,"Taylor, Robert","Charisse, Cyd","Ray, Nicholas",85,No,NicholasCage.png -1989,113,Twin Peaks,Mystery,"MacLachlan, Kyle","Chen, Joan","Lynch, David",86,No,kyle.png -1987,103,Moonstruck,Comedy,"Cage, Nicholas",Cher,"Jewison, Norman",6,Yes,NicholasCage.png -1987,119,"Witches of Eastwick, The",Comedy,"Nicholson, Jack",Cher,"Miller, George",8,No,NicholasCage.png -1979,128,Moonraker,Action,"Moore, Roger","Chiles, Lois","Gilbert, Lewis",32,No,NicholasCage.png -1984,106,Beat Street,Drama,"Davis, Guy","Chong, Rae Dawn","Lathan, Stan",72,No,NicholasCage.png -1986,88,Running Out of Luck,Comedy,"Jagger, Mick","Chong, Rae Dawn",,16,No,NicholasCage.png -1989,90,Never on Tuesday,Drama,"Lauer, Andrew","Christian, Claudia","Rifkin, Adam",77,No,NicholasCage.png -1975,109,Shampoo,Comedy,"Beatty, Warren","Christie, Julie","Ashby, Hal",69,Yes,NicholasCage.png -1985,111,Power,Drama,"Hackman, Gene","Christie, Julie","Lumet, Sidney",43,No,NicholasCage.png -1965,122,Darling,Drama,"Harvey, Laurence","Christie, Julie","Schlesinger, John",44,Yes,NicholasCage.png -1963,120,"Ugly American, The",Drama,"Brando, Marlon","Church, Sandra","Englund, George",63,No,brando.png -1931,68,Ambassador Bill,Comedy,"Rogers, Will","Churchill, Marguerite","Taylor, Sam",66,No,NicholasCage.png -1931,110,"Big Trail, The",Western,"Wayne, John","Churchill, Marguerite","Walsh, Raoul",22,No,johnWayne.png -1967,111,Hombre,Western,"Newman, Paul","Cilento, Diane","Ritt, Martin",50,No,paulNewman.png -1968,103,Coogan's Bluff,Action,"Eastwood, Clint","Clark, Susan","Siegel, Don",57,No,clintEastwood.png -1989,91,Penn & Teller Get Killed,Comedy,"Penn, Jillette","Clarke, Caitlin","Penn, Arthur",12,No,NicholasCage.png -1987,118,Shy People,Drama,"Philbin, John","Clayburgh, Jill","Konchalovsky, Andrei",7,No,NicholasCage.png -1980,91,It's My Turn,Comedy,"Douglas, Michael","Clayburgh, Jill","Weill, Claudia",0,No,NicholasCage.png -1988,119,Dangerous Liaisons,Drama,"Malkovich, John","Close, Glenn","Frears, Stephen",77,No,MichellePfeiffer.png -1990,111,Reversal of Fortune,Drama,"Irons, Jeremy","Close, Glenn","Schroeder, Barbet",73,Yes,NicholasCage.png -1991,119,Meeting Venus,Comedy,"Arestrup, Niels","Close, Glenn","Szabó, István",74,No,NicholasCage.png -1946,105,Tomorrow Is Forever,Drama,"Welles, Orson","Colbert, Claudette",,65,No,NicholasCage.png -1987,101,Like Father Like Son,Comedy,"Cameron, Kirk","Colin, Margaret","Daniel, Rod",20,No,NicholasCage.png -1948,81,Rope,Drama,"Stewart, James","Collier, Constance","Hitchcock, Alfred",39,No,alfredHitchcock.png -1962,91,Road to Hong Kong,Comedy,"Hope, Bob","Collins, Joan","Panama, Norman",37,No,NicholasCage.png -1989,108,Shirley Valentine,Comedy,"Conti, Tom","Collins, Pauline","Gilbert, Lewis",51,No,NicholasCage.png -1992,135,City of Joy,Drama,"Swayze, Patrick","Collins, Pauline","Joffe, Roland",87,No,NicholasCage.png -1966,99,"Appaloosa, The",Western,"Brando, Marlon","Comer, Anjanette","Furie, Sidney J.",15,No,brando.png -1986,88,Seven Minutes in Heaven,Comedy,"Thames, Byron","Connelly, Jennifer","Feferman, Linda",49,No,NicholasCage.png -1991,96,"Hearts of Darkness, A Filmmaker's Apocalypse",Drama,"Bottoms, Sam","Coppola, Eleanor","Bahr, Fax",72,No,NicholasCage.png -1961,66,Tonight for Sure,Comedy,"Lee, Karla","Cornell, Laura","Coppola, Francis Ford",4,No,NicholasCage.png -1990,110,"White Hunter, Black Heart",Adventure,"Eastwood, Clint","Cornwell, Charlotte","Eastwood, Clint",66,No,clintEastwood.png -1962,110,Sundays & Cybele,Drama,"Kruger, Hardy","Courcel, Nicole","Bourguignon, Serge",11,Yes,NicholasCage.png -1989,90,Puppet Master,Science Fiction,"LeMat, Paul","Crampton, Barbara","Schmoeller, David",20,No,NicholasCage.png -1991,95,Night Gallery,Horror,"McDowall, Roddy","Crawford, Joan","Spielberg, Steven",31,No,NicholasCage.png -1989,103,Pet Sematary,Horror,"Gwynne, Fred","Crosby, Denise","Lambert, Mary",27,No,NicholasCage.png -1992,60,"America's Music, Gospel",Music,"Phipps, Wentley","Crouch, Sandra","Walton, Kip",13,No,NicholasCage.png -1977,123,Slap Shot,Comedy,"Newman, Paul","Crouse, Lindsay","Hill, George Roy",82,No,paulNewman.png -1987,109,O. C. & Stiggs,Comedy,"Jenkins, Daniel H.","Curtin, Jane","Altman, Robert",3,No,NicholasCage.png -1988,108,A Fish Called Wanda,Comedy,"Cleese, John","Curtis, Jamie Lee","Crichton, Charles",7,Yes,NicholasCage.png -1954,96,A Lesson in Love,Comedy,"Björnstrand, Gunnar","Dahlbeck, Eva","Bergman, Ingmar",48,No,Bergman.png -1957,82,Brink of Life,Drama,"Josephson, Erland","Dahlbeck, Eva","Bergman, Ingmar",57,No,Bergman.png -1986,120,Betty Blue,Drama,"Anglade, Jean-Hughes","Dalle, Béatrice","Beineix, Jean-Jacques",71,No,NicholasCage.png -1979,122,Hair,Music,"Savage, John","D'Angelo, Beverly","Forman, Milos",67,No,NicholasCage.png -1989,97,National Lampoon's Christmas Vacation,Comedy,"Chase, Chevy","D'Angelo, Beverly","S, Jeremiah",81,No,NicholasCage.png -1974,124,"Dersu Uzala, (The Hunter)",Adventure,"Solomin, Yuri","Danilchenko, Svetlana","Kurosawa, Akira",81,Yes,NicholasCage.png -1990,106,Alice,Comedy,"Baldwin, Alec","Danner, Blythe","Allen, Woody",22,No,woody.png -1980,90,"Fifth Floor, The",Mystery,"Hopkins, Bo","D'Arbanville, Patti","Avedis, Howard Hikmet",74,No,NicholasCage.png -1990,94,Snow Kill,Drama,"Knox, Terence","D'Arbanville, Patti","Wright, Thomas J.",35,No,NicholasCage.png -1971,74,"People, The",Drama,"Shatner, William","Darby, Kim","Coppola, Francis Ford",36,No,NicholasCage.png -1969,128,True Grit,Western,"Wayne, John","Darby, Kim","Hathaway, Henry",77,Yes,johnWayne.png -1942,18,"Battle of Midway, The",War,"Crisp, Donald","Darwell, Jane","Ford, John",75,No,johnFord.png -1948,103,Three Godfathers,Western,"Wayne, John","Darwell, Jane","Ford, John",72,No,johnWayne.png -1965,133,"Hush, Hush, Sweet Charlotte",Mystery,"Cotten, Joseph","Davis, Bette","Aldrich, Robert",68,No,NicholasCage.png -1946,110,A Stolen Life,Drama,"Ford, Glenn","Davis, Bette","Bernhardt, Curtis",20,No,glennFord.png -1939,96,"Old Maid, The",Drama,"Brent, George","Davis, Bette","Goulding, Edmund",18,No,NicholasCage.png -1950,138,All about Eve,Drama,"Sanders, George","Davis, Bette","Mankiewicz, Joseph L.",23,Yes,NicholasCage.png -1986,96,"Fly, The",Horror,"Goldblum, Jeff","Davis, Geena","Cronenberg, David",33,No,NicholasCage.png -1990,89,Quick Change,Comedy,"Murray, Bill","Davis, Geena","Franklin, Howard ",24,No,NicholasCage.png -1988,93,"Lair of the White Worm, The",Horror,"Grant, Hugh","Davis, Sammi","Russell, Ken",16,No,NicholasCage.png -1989,104,"Rainbow, The",Drama,"Hemmings, David","Davis, Sammi","Russell, Ken",53,No,NicholasCage.png -1956,120,"Man Who Knew Too Much, The",Mystery,"Stewart, James","Day, Doris","Hitchcock, Alfred",15,No,alfredHitchcock.png -1992,90,Beauty & the Beast,Science Fiction,"Marais, Jean","Day, Josette","Cocteau, Jean",14,No,NicholasCage.png -1940,120,Foreign Correspondent,Mystery,"McCrea, Joel","Day, Laraine","Hitchcock, Alfred",61,No,alfredHitchcock.png -1949,115,"Heiress, The",Drama,"Richardson, Ralph","De Havilland, Olivia","Wyler, William",81,Yes,NicholasCage.png -1986,120,"Boy Who Could Fly, The",Drama,"Underwood, Jay","Deakins, Lucy","Castle, Nick",25,No,NicholasCage.png -1975,89,"Terrorists, The",Action,"Connery, Sean","Dean, Isabel","Wrede, Caspar",4,No,seanConnery.png -1942,85,Wheel of Fortune,Drama,"Wayne, John","Dee, Frances","Auer, John H.",36,No,johnWayne.png -1989,120,Do the Right Thing,Drama,"Aiello, Danny","Dee, Ruby","Lee, Spike",5,No,NicholasCage.png -1990,93,"Court-Martial of Jackie Robinson, The",Drama,"Braugher, Andre","Dee, Ruby","Peerce, Larry",33,No,NicholasCage.png -1967,90,Elvira Madigan,Drama,"Berggren, Thommy","Degermark, Pia","Widerberg, Bo",28,No,NicholasCage.png -1992,86,Hurricane Smith,Action,"Weathers, Carl","Delaney, Cassandra","Budds, Colin",16,No,NicholasCage.png -1987,86,Fair Game,Action,"Ford, Peter","Delaney, Cassandra",,24,No,NicholasCage.png -1989,95,"Rape of the Sabines, The",Action,"Moore, Roger","Demongeot, Mylene",,83,No,NicholasCage.png -1983,99,Risky Business,Comedy,"Cruise, Tom","DeMornay, Rebecca","Brickman, Paul",28,No,NicholasCage.png -1980,103,I Love All of You (Je Vous Aime),Drama,"Depardieu, Gérard","Deneuve, Catherine","Berri, Claude",40,No,NicholasCage.png -1986,108,Love Songs,Drama,"Lambert, Christopher","Deneuve, Catherine","Chouraqui, Elie",15,No,NicholasCage.png -1983,114,Le Choix des Armes,Mystery,"Montand, Yves","Deneuve, Catherine","Comeau, Alain",15,No,NicholasCage.png -1981,135,Choice of Arms,Action,"Montand, Yves","Deneuve, Catherine","Corneau, Alan",87,No,NicholasCage.png -1977,107,March or Die,War,"Hackman, Gene","Deneuve, Catherine","Richards, Dick",59,No,NicholasCage.png -1980,135,"Last Metro, The",Drama,"Depardieu, Gérard","Deneuve, Catherine","Truffaut, François",66,No,NicholasCage.png -1986,120,Jean de Florette,Drama,"Montand, Yves","Depardieu, Elizabeth","Berri, Claude",87,Yes,NicholasCage.png -1989,127,Fat Man & Little Boy,Drama,"Newman, Paul","Dern, Laura","Joffe, Roland",86,No,paulNewman.png -1990,125,Wild at Heart,Drama,"Cage, Nicolas","Dern, Laura","Lynch, David",6,No,NicholasCage.png -1989,113,Family Business,Action,"Connery, Sean","DeSoto, Rosana","Lumet, Sidney",5,No,seanConnery.png -1988,103,Stand & Deliver,Drama,"Olmos, Edward James","DeSoto, Rosana","Menendez, Ramon",19,No,NicholasCage.png -1981,94,Looker,Science Fiction,"Finney, Albert","Dey, Susan","Crichton, Michael",62,No,NicholasCage.png -1989,89,Fire & Rain,Action,"Haid, Charles","Dickinson, Angie","Jameson, Jerry",10,No,NicholasCage.png -1990,56,"Best of Candid Camera, The",Comedy,"Allen, Woody","Dickinson, Angie",,12,No,woody.png -1940,83,Seven Sinners,Drama,"Wayne, John","Dietrich, Marlene","Garnett, Tay",24,No,johnWayne.png -1961,190,Judgment at Nuremberg,Drama,"Tracy, Spencer","Dietrich, Marlene","Kramer, Stanley",39,Yes,spencerTracy.png -1989,60,Minsky's Follies,Comedy,"Taylor, Rip","Diller, Phyllis",,12,No,NicholasCage.png -1990,97,"Novice, The",Comedy,"Sharif, Omar","Dombasle, Arielle",,72,No,NicholasCage.png -1987,130,Wings of Desire,Drama,"Ganz, Bruno","Dommartin, Solveig","Wenders, Wim",71,No,NicholasCage.png -1991,158,Until the End of the World,Drama,"Hurt, William","Dommartin, Solveig","Wenders, Wim",57,No,NicholasCage.png -1987,118,Castaway,Drama,"Reed, Oliver","Donohoe, Amanda","Roeg, Nicolas",41,No,NicholasCage.png -1993,30,"Alfred Hitchcock Presents, Sorcerer's Apprentice",Mystery,"Hitchcock, Alfred","Dors, Diana",,60,No,NicholasCage.png -1991,99,Delicatessen,Comedy,"Benezech, Pascal","Dougnac, Marie-Laure","Caro, Marc",78,No,NicholasCage.png -1979,110,"Great Train Robbery, The",Mystery,"Connery, Sean","Down, Lesley-Anne","Crichton, Michael",7,No,seanConnery.png -1991,110,Hanover Street,Drama,"Ford, Harrison","Down, Lesley-Anne","Hyams, Peter",81,No,NicholasCage.png -1991,102,Hunchback,Drama,"Hopkins, Anthony","Down, Lesley-Anne","Tuchner, Michael",33,No,AnthonyHopkins.png -1946,97,My Darling Clementine,Western,"Fonda, Henry","Downs, Cathy","Ford, John",12,No,johnFord.png -1950,86,Wagon Master,Western,"Johnson, Ben","Dru, Joanne","Ford, John",30,No,johnFord.png -1949,93,She Wore a Yellow Ribbon,Western,"Wayne, John","Dru, Joanne","Ford, John",84,No,johnWayne.png -1985,90,Fantasy Man,Comedy,"Hopkins, Harold","Drynan, Jeanie","Meagher, John",82,No,NicholasCage.png -1986,87,Monster in the Closet,Comedy,"Grant, Donald","DuBarry, Denise","Dahlin, Bob",39,No,NicholasCage.png -1992,85,Double Edge,Drama,"Eban, Abba","Dunaway, Faye","Kollek, Amos",69,No,clintEastwood.png -1976,116,Network,Comedy,"Finch, Peter","Dunaway, Faye","Lumet, Sidney",48,Yes,NicholasCage.png -1974,131,Chinatown,Drama,"Nicholson, Jack","Dunaway, Faye","Polanski, Roman",55,Yes,JackNicholson.png -1975,117,Three Days of the Condor,Drama,"Redford, Robert","Dunaway, Faye","Pollack, Sydney",87,No,NicholasCage.png -1977,134,Voyage of the Damned,Drama,"Sydow, Max von","Dunaway, Faye","Rosenberg, Stuart",34,No,NicholasCage.png -1987,97,Barfly,Drama,"Rourke, Mickey","Dunaway, Faye","Schroeder, Barbet",23,No,NicholasCage.png -1990,104,"Wait Until Spring, Bandini",Drama,"Mantegna, Joe","Dunaway, Faye",,20,No,NicholasCage.png -1947,118,Life with Father,Comedy,"Powell, William","Dunne, Irene","Curtiz, Michael",10,No,NicholasCage.png -1943,,A Guy Named Joe,Drama,"Tracy, Spencer","Dunne, Irene","Fleming, Victor",42,No,spencerTracy.png -1974,117,Stavisky,Drama,"Belmondo, Jean-Paul","Duperey, Anny","Resnais, Alain",1,No,NicholasCage.png -1981,117,Time Bandits,Comedy,"Cleese, John","Duvall, Shelley","Gilliam, Terry",5,No,NicholasCage.png -1980,144,"Shining, The",Horror,"Nicholson, Jack","Duvall, Shelley","Kubrick, Stanley",32,No,JackNicholson.png -1945,91,Flame of Barbary Coast,Western,"Wayne, John","Dvorak, Ann","Kane, Joseph",54,No,johnWayne.png -1993,92,"Naked Truth, The",Comedy,"Sellers, Peter","Eaton, Shirley",,34,No,NicholasCage.png -1979,92,"Brood, The",Horror,"Reed, Oliver","Eggar, Samantha","Cronenberg, David",51,No,NicholasCage.png -1970,123,"Molly Maguires, The",Action,"Connery, Sean","Eggar, Samantha","Ritt, Martin",3,No,seanConnery.png -1984,105,Beverly Hills Cop,Comedy,"Murphy, Eddie","Eilbacher, Lisa","Brest, Martin",41,No,NicholasCage.png -1991,86,Blind Man's Bluff,Mystery,"Urich, Robert","Eilbacher, Lisa","Quinn, James",64,No,NicholasCage.png -1961,140,La Dolce Vita,Drama,"Mastroianni, Marcello","Ekberg, Anita","Fellini, Federico",20,No,NicholasCage.png -1966,103,After the Fox,Comedy,"Sellers, Peter","Ekland, Britt","De Sica, Vittorio",60,No,NicholasCage.png -1974,127,"Man with the Golden Gun, The",Action,"Moore, Roger","Ekland, Britt","Hamilton, Guy",41,No,NicholasCage.png -1985,96,Marbella,Action,"Taylor, Rod","Ekland, Britt","Hermoso, Miguel",45,No,NicholasCage.png -1967,103,"Bobo, The",Comedy,"Sellers, Peter","Ekland, Britt","Parrish, Robert",80,No,NicholasCage.png -1993,53,"Big Bands, The",Music,"Beneke, Tex","Elgart, Les",,48,No,NicholasCage.png -1992,97,Killer Image.,Mystery,"Ironside, Michael","Errickson, Krista","Winning, David",8,No,NicholasCage.png -1987,94,Kandyland,Drama,"Laulette, Charles","Evenson, Kim","Schnitzer, Robert Allen",41,No,NicholasCage.png -1987,94,Campus Man,Drama,"Dye, John","Fairchild, Morgan","Casden, Ron",38,No,NicholasCage.png -1956,101,Jubal,Drama,"Ford, Glenn","Farr, Felicia","Daves, Delmer",32,No,glennFord.png -1985,84,"Purple Rose of Cairo, The",Comedy,"Aiello, Danny","Farrow, Mia","Allen, Woody",20,Yes,woody.png -1984,85,Broadway Danny Rose,Comedy,"Allen, Woody","Farrow, Mia","Allen, Woody",14,No,woody.png -1992,108,Husbands & Wives,Comedy,"Allen, Woody","Farrow, Mia","Allen, Woody",80,No,woody.png -1986,103,Hannah & Her Sisters,Comedy,"Caine, Michael","Farrow, Mia","Allen, Woody",8,Yes,woody.png -1979,115,Hurricane,Action,"Robards, Jason","Farrow, Mia","Troell, Jan",8,No,NicholasCage.png -1986,95,Between Two Women,Drama,"Nouri, Michael","Fawcett, Farrah","Avnet, John",52,No,NicholasCage.png -1981,96,"Cannonball Run, The",Comedy,"Reynolds, Burt","Fawcett, Farrah","Needham, Hal",80,No,NicholasCage.png -1936,70,Doughnuts & Society,Comedy,"Nugent, Eddie","Fazenda, Louise","Collins, Lewis D.",28,No,NicholasCage.png -1978,450,Holocaust,Drama,"Bottoms, Joseph","Feldshuh, Tovah","Chomsky, Marvin J.",1,No,NicholasCage.png -1990,103,Meridian,Science Fiction,"Jamieson, Malcolm","Fenn, Sherilyn","Band, Charles",47,No,NicholasCage.png -1992,90,Diary of a Hitman,Drama,"Whitaker, Forest","Fenn, Sherilyn","London, Roy",67,No,NicholasCage.png -1988,95,Gor,Action,"Reed, Oliver","Ferratti, Rebecca","Kiersch, Fritz",2,No,NicholasCage.png -1987,95,Surrender,Comedy,"Caine, Michael","Field, Sally","Belson, Jerry",84,No,NicholasCage.png -1984,112,Places in the Heart,Drama,"Harris, Ed","Field, Sally","Benton, Robert",83,Yes,NicholasCage.png -1991,106,Not Without My Daughter,Drama,"Molina, Alfred","Field, Sally","Gilbert, Brian",55,No,NicholasCage.png -1977,113,Heroes,Drama,"Winkler, Henry","Field, Sally","Kagan, Jeremy Paul",17,No,NicholasCage.png -1981,116,Absence of Malice,Drama,"Newman, Paul","Field, Sally","Pollack, Sydney",76,No,paulNewman.png -1979,110,Norma Rae,Drama,"Bridges, Beau","Field, Sally","Ritt, Martin",64,Yes,NicholasCage.png -1989,118,Steel Magnolias,Drama,"Skerritt, Tom","Field, Sally","Ross, Herbert",66,No,NicholasCage.png -1989,101,"Burbs, The",Comedy,"Hanks, Tom","Fisher, Carrie","Dante, Joe",42,No,NicholasCage.png -1980,124,"Empire Strikes Back, The",Science Fiction,"Hamill, Mark","Fisher, Carrie","Kershner, Irvin",33,No,NicholasCage.png -1977,121,Star Wars,Science Fiction,"Hamill, Mark","Fisher, Carrie","Lucas, George",44,No,NicholasCage.png -1983,132,Return of the Jedi,Science Fiction,"Hamill, Mark","Fisher, Carrie","Marquand, Richard",4,No,NicholasCage.png -1991,104,Hear My Song,Drama,"Dunbar, Adrian","Fitzgerald, Tara","Chelsom, Peter",72,No,NicholasCage.png -1956,99,Slightly Scarlet,Action,"Payne, John","Fleming, Rhonda","Dwan, Allan",52,No,NicholasCage.png -1957,120,Gunfight at the OK Corral,Western,"Lancaster, Burt","Fleming, Rhonda","Sturges, John",84,No,burtLancaster.png -1931,,"Range Feud, The",Western,"Wayne, John","Fleming, Susan","Lederman, Ross",51,No,johnWayne.png -1990,89,Bloodsucking Pharaohs in Pittsburgh,Comedy,"Dengel, Jake","Fletcher, Suzanne","Smithey, Alan",79,No,NicholasCage.png -1972,129,Roma,Drama,"Gonzales, Peter","Florence, Fiona","Fellini, Federico",75,No,NicholasCage.png -1979,122,"China Syndrome, The",Drama,"Douglas, Michael","Fonda, Jane","Bridges, James",43,No,NicholasCage.png -1986,100,"Morning After, The",Mystery,"Bridges, Jeff","Fonda, Jane","Lumet, Sidney",6,No,NicholasCage.png -1971,114,Klute,Drama,"Sutherland, Donald","Fonda, Jane","Pakula, Alan J.",15,Yes,NicholasCage.png -1979,113,"Electric Horseman, The",Comedy,"Redford, Robert","Fonda, Jane","Pollack, Sydney",34,No,NicholasCage.png -1965,97,Cat Ballou,Comedy,"Marvin, Lee","Fonda, Jane","Silverstein, Elliot",62,Yes,NicholasCage.png -1991,,Coming Home,Drama,"Voight, Jon","Fonda, Jane",,1,Yes,NicholasCage.png -1940,130,Rebecca,Drama,"Olivier, Laurence","Fontaine, Joan","Hitchcock, Alfred",78,Yes,alfredHitchcock.png -1944,96,Jane Eyre,Drama,"Welles, Orson","Fontaine, Joan","Stevenson, Robert",44,No,NicholasCage.png -1973,87,Stacey!,Action,"Randall, Anne","Ford, Anitra","Sidaris, Andy",31,No,NicholasCage.png -1992,85,Naked Obsession,Mystery,"Katt, William","Ford, Maria","Golden, Dan",26,No,NicholasCage.png -1989,83,"Stripped to Kill II, Live Girls",Mystery,"Lottimer, Ed","Ford, Maria","Ruben, Katt Shea",80,No,NicholasCage.png -1990,94,"Rain Killer, The",Mystery,"Sharkey, Ray","Ford, Maria","Stein, Ken",10,No,NicholasCage.png -1983,95,Valley Girl,Comedy,"Cage, Nicolas","Foreman, Deborah","Coolidge, Martha",30,No,NicholasCage.png -1991,118,"Silence of the Lambs, The",Mystery,"Hopkins, Anthony","Foster, Jodie","Demme, Jonathan",8,Yes,AnthonyHopkins.png -1988,98,Stealing Home,Drama,"Harmon, Mark","Foster, Jodie","Kampmann, Steven ",76,No,NicholasCage.png -1972,92,Napoleon & Samantha,Comedy,"Douglas, Michael","Foster, Jodie","McEveety, Bernard",33,No,NicholasCage.png -1988,,Five Corners,Drama,"Robbins, Tim","Foster, Jodie",,88,No,NicholasCage.png -1955,,"Blackboard Jungle, The",Drama,"Ford, Glenn","Francis, Anne","Brooks, Richard",66,No,glennFord.png -1989,103,My Left Foot,Drama,"Day-Lewis, Daniel","Fricker, Brenda","Sheridan, Jim",32,Yes,NicholasCage.png -1987,92,Back to the Beach,Comedy,"Avalon, Frankie","Funicello, Annette","Hobbs, Lyndall",45,No,NicholasCage.png -1934,85,"Painted Veil, The",Drama,"Marshall, Herbert","Garbo, Greta","Boleslawski, Richard",57,No,gretaGarbo.png -1931,74,Inspiration,Drama,"Apfel, Oscar","Garbo, Greta","Brown, Clarence",66,No,gretaGarbo.png -1930,92,Anna Christie,Drama,"Bickford, Charles","Garbo, Greta","Brown, Clarence",0,No,gretaGarbo.png -1926,109,"Flesh & the Devil, The",Drama,"Gilbert, John","Garbo, Greta","Brown, Clarence",72,No,gretaGarbo.png -1928,90,Woman of Affairs,Drama,"Gilbert, John","Garbo, Greta","Brown, Clarence",83,No,gretaGarbo.png -1935,96,Anna Karenina,Drama,"March, Fredric","Garbo, Greta","Brown, Clarence",35,Yes,gretaGarbo.png -1936,110,Camille,Drama,"Taylor, Robert","Garbo, Greta","Cukor, George",74,No,gretaGarbo.png -1931,91,Mata Hari,Drama,"Novarro, Ramon","Garbo, Greta","Fitzmaurice, George",67,No,gretaGarbo.png -1929,100,Wild Orchids,Drama,"Stone, Lewis","Garbo, Greta","Franklin, Sidney",70,No,gretaGarbo.png -1932,112,Grand Hotel,Drama,"Barrymore, John","Garbo, Greta","Goulding, Edmund",81,Yes,gretaGarbo.png -1931,84,"Susan Lennox, Her Fall & Rise",Drama,"Hale, Alan","Garbo, Greta","Leonard, Robert Z.",64,No,gretaGarbo.png -1939,108,Ninotchka,Comedy,"Douglas, Melvyn","Garbo, Greta","Lubitsch, Ernst",40,No,gretaGarbo.png -1933,97,Queen Christina,Drama,"Gilbert, John","Garbo, Greta","Mamoulian, Rouben",82,No,gretaGarbo.png -1928,96,"Mysterious Lady, The",Drama,"Nagel, Conrad","Garbo, Greta","Niblo, Fred",72,No,gretaGarbo.png -1925,125,Joyless Street,Drama,"Stuart, Henry","Garbo, Greta","Pabst, Georg Wilhelm",73,No,gretaGarbo.png -1929,74,"Single Standard, The",Drama,"Asther, Nils","Garbo, Greta","Robertson, John S.",73,No,gretaGarbo.png -1932,71,As You Desire Me,Drama,"Douglas, Melvyn","Garbo, Greta",,85,No,gretaGarbo.png -1930,76,Romance,Drama,"Stone, Lewis","Garbo, Greta",,62,No,gretaGarbo.png -1962,105,A Child Is Waiting,Drama,"Lancaster, Burt","Garland, Judy","Cassavetes, John",60,No,burtLancaster.png -1982,116,Tootsie,Comedy,"Hoffman, Dustin","Garr, Teri","Pollack, Sydney",8,Yes,NicholasCage.png -1989,86,Let It Ride,Comedy,"Dreyfuss, Richard","Garr, Teri","Pytka, Joe",88,No,NicholasCage.png -1953,120,Julius Caesar,Drama,"Brando, Marlon","Garson, Greer","Mankiewicz, Joseph L.",50,No,brando.png -1979,120,Nineteen Forty-One,Comedy,"Belushi, John","Gary, Lorraine","Spielberg, Steven",24,No,NicholasCage.png -1975,124,Jaws,Action,"Scheider, Roy","Gary, Lorraine","Spielberg, Steven",6,No,NicholasCage.png -1987,93,Hot Pursuit,Drama,"Cusack, John","Gazelle, Wendy","Lisberger, Steven",44,No,NicholasCage.png -1989,120,Triumph of the Spirit,Drama,"Dafoe, Willem","Gazelle, Wendy","Young, Robert M.",49,No,NicholasCage.png -1975,111,Brannigan,Drama,"Wayne, John","Geeson, Judy","Hickox, Douglas",64,No,johnWayne.png -1979,89,Buffet Froid,Comedy,"Depardieu, Gérard","Gence, Denise","Blier, Bertrand",75,No,NicholasCage.png -1986,122,Salvador,Drama,"Woods, James","Gibb, Cynthia","Stone, Oliver",77,No,NicholasCage.png -1959,102,"Horse Soldiers, The",Western,"Wayne, John","Gibson, Althea","Ford, John",76,No,johnWayne.png -1954,108,Long John Silver,Action,"Newton, Robert","Gilchrist, Connie","Haskin, Byron",56,No,NicholasCage.png -1961,134,"Hustler, The",Drama,"Newman, Paul","Gleason, Jackie","Rossen, Robert",43,Yes,paulNewman.png -1983,109,"Star Chamber, The",Drama,"Douglas, Michael","Gless, Sharon","Hyam, Peter",3,No,NicholasCage.png -1988,100,Clara's Heart,Drama,"Ontkean, Michael","Goldberg, Whoopi","Mulligan, Robert",60,No,NicholasCage.png -1987,102,Burglar,Comedy,"Goldthwait, Bob","Goldberg, Whoopi","Wilson, Hugh",44,No,NicholasCage.png -1986,120,Comic Relief,Comedy,"Crystal, Billy","Goldberg, Whoopi",,69,No,NicholasCage.png -1978,117,Bloodbrothers,Drama,"Sorvino, Paul","Goldoni, Lelia","Mulligan, Robert",11,No,NicholasCage.png -1988,134,Rain Man,Drama,"Hoffman, Dustin","Golino, Valeria","Levinson, Barry",8,Yes,NicholasCage.png -1966,95,Masculine Feminine,Drama,"Leaud, Jean-Pierre","Goya, Chantal","Godard, Jean-Luc",20,No,NicholasCage.png -1964,51,"Outer Limits, The",Science Fiction,"Perrin, Vic","Grahame, Gloria","Stanley, Paul",27,No,NicholasCage.png -1988,,Mama's Dirty Girls,Horror,"Currie, Sondra","Grahame, Gloria",,62,No,NicholasCage.png -1979,180,"Last Ride of the Dalton Gang, The",Western,"Palance, Jack","Greenbush, Lindsay","Curtis, Dan",62,No,NicholasCage.png -1991,,Why Me?,Comedy,"Lambert, Christopher","Greist, Kim",,74,No,NicholasCage.png -1932,66,Number Seventeen,Crime,"Lion, Leon M.","Grey, Anne","Hitchcock, Alfred",66,No,alfredHitchcock.png -1986,120,Manhunter,Drama,"Petersen, William L.","Griest, Kim","Mann, Michael",19,No,NicholasCage.png -1990,126,"Bonfire of the Vanities, The",Drama,"Hanks, Tom","Griffith, Melanie","De Palma, Brian",82,No,NicholasCage.png -1988,115,Working Girl,Comedy,"Ford, Harrison","Griffith, Melanie","Nichols, Mike",25,No,NicholasCage.png -1992,133,Shining Through,Mystery,"Douglas, Michael","Griffith, Melanie","Seltzer, David",11,No,NicholasCage.png -1991,76,Slumber Party Massacre III,Horror,"Christian, Keely","Grye, Brittain",,40,No,NicholasCage.png -1988,99,Tokyo Pop,Comedy,"Tadokoro, Yutaka","Hamilton, Carrie","Kuzui, Fran Rubel",2,No,NicholasCage.png -1991,136,Terminator 2,Action,"Schwarzenegger, Arnold","Hamilton, Linda","Cameron, James",8,No,T2.png -1984,108,"Terminator, The",Action,"Schwarzenegger, Arnold","Hamilton, Linda","Cameron, James",17,No,T2.png -1986,105,King Kong Lives!,Action,"Kerwin, Brian","Hamilton, Linda","Guillermin, John",20,No,NicholasCage.png -1969,125,Those Daring Young Men in Their Jaunty,Comedy,"Curtis, Tony","Hampshire, Susan",,59,No,NicholasCage.png -1991,186,At Play in the Fields of the Lord,Drama,"Berenger, Tom","Hannah, Daryl","Babenco, Hector",81,No,NicholasCage.png -1990,,Crazy People,Comedy,"Moore, Dudley","Hannah, Daryl","Bill, Tony",61,No,NicholasCage.png -1992,99,Memoirs of an Invisible Man,Comedy,"Chase, Chevy","Hannah, Daryl","Carpenter, John",58,No,NicholasCage.png -1985,100,"Clan of the Cave Bear, The",Drama,"Remar, James","Hannah, Daryl","Chapman, Michael",73,No,NicholasCage.png -1983,82,"Final Terror, The",Horror,"Zmed, Adrian","Hannah, Daryl","Davis, Andrew",24,No,NicholasCage.png -1984,93,Reckless,Drama,"Quinn, Aidan","Hannah, Daryl","Foley, James",14,No,NicholasCage.png -1989,,High Spirits,Comedy,"O'Toole, Peter","Hannah, Daryl","Jordan, Neil",53,No,NicholasCage.png -1987,107,Roxanne,Comedy,"Martin, Steve","Hannah, Daryl","Schepisi, Fred",66,No,NicholasCage.png -1982,117,Blade Runner,Action,"Ford, Harrison","Hannah, Daryl","Scott, Ridley",1,No,NicholasCage.png -1987,126,Wall Street,Drama,"Douglas, Michael","Hannah, Daryl","Stone, Oliver",6,Yes,NicholasCage.png -1992,111,Pope of Greenwich Village,Drama,"Rourke, Mickey","Hannah, Daryl",,58,No,NicholasCage.png -1989,89,After School,Drama,"Bottoms, Sam","Hannah, Page",,59,No,NicholasCage.png -1938,298,Flaming Frontiers,Western,"Brown, Johnny Mack","Hansen, Eleanor","Taylor, Ray",82,No,NicholasCage.png -1936,89,Libeled Lady,Comedy,"Powell, William","Harlow, Jean","Conway, Jack",86,No,NicholasCage.png -1976,99,Inserts,Drama,"Dreyfuss, Richard","Harper, Jessica","Byrum, John",85,No,NicholasCage.png -1988,88,"Blue Iguana, The",Drama,"McDermott, Dylan","Harper, Jessica","Lafia, John",65,No,NicholasCage.png -1983,93,Tender Mercies,Drama,"Duvall, Robert","Harper, Tess","Beresford, Bruce",61,Yes,NicholasCage.png -1987,96,Nights in White Satin,Drama,"Gilman, Kenneth","Harris, Priscilla","Barnard, Michael",5,No,NicholasCage.png -1989,87,Videodrome,Horror,"Woods, James","Harry, Deborah","Cronenberg, David",36,No,NicholasCage.png -1991,96,Intimate Stranger,Mystery,"Russo, James","Harry, Deborah","Holzman, Allan",23,No,NicholasCage.png -1986,110,Highlander,Science Fiction,"Lambert, Christopher","Hart, Roxanne","Mulcahy, Russell",8,No,NicholasCage.png -1987,93,Bodycount,Action,"White, Bernie","Hassett, Marilyn",,51,No,NicholasCage.png -1989,104,Tango & Cash,Action,"Stallone, Sylvester","Hatcher, Teri","Konchalovsky, Andrei",9,No,NicholasCage.png -1970,94,There's a Girl in My Soup,Comedy,"Sellers, Peter","Hawn, Goldie","Boulting, Roy",41,No,NicholasCage.png -1984,100,Swing Shift,Drama,"Russell, Kurt","Hawn, Goldie","Demme, Jonathan",81,No,NicholasCage.png -1978,112,Foul Play,Comedy,"Chase, Chevy","Hawn, Goldie","Higgins, Colin",46,No,NicholasCage.png -1982,109,Best Friends,Comedy,"Reynolds, Burt","Hawn, Goldie","Jewison, Norman",74,No,NicholasCage.png -1972,109,Butterflies Are Free,Drama,"Albert, Edward","Hawn, Goldie","Katselas, Milton",82,Yes,NicholasCage.png -1987,112,Overboard,Comedy,"Russell, Kurt","Hawn, Goldie","Marshall, Garry",6,No,NicholasCage.png -1974,103,"Girl from Petrovka, The",Drama,"Holbrook, Hal","Hawn, Goldie","Miller, Robert Ellis",23,No,NicholasCage.png -1992,102,Housesitter,Comedy,"Martin, Steve","Hawn, Goldie","Oz, Frank",14,No,NicholasCage.png -1986,106,Wildcats,Comedy,"Keach, James","Hawn, Goldie","Ritchie, Michael",22,No,NicholasCage.png -1984,100,Protocol,Comedy,"Sarandon, Chris","Hawn, Goldie","Ross, Herbert",53,No,NicholasCage.png -1980,102,Seems Like Old Times,Comedy,"Chase, Chevy","Hawn, Goldie","Sandrich, Jay",49,No,NicholasCage.png -1974,109,"Sugarland Express, The",Drama,"Johnson, Ben","Hawn, Goldie","Spielberg, Steven",28,No,NicholasCage.png -1980,110,Private Benjamin,Comedy,"Assante, Armand","Hawn, Goldie","Zieff, Howard",61,No,NicholasCage.png -1991,115,Deceived,Mystery,"Heard, John","Hawn, Goldie",,55,No,NicholasCage.png -1931,95,Arrowsmith,Drama,"Colman, Ronald","Hayes, Helen","Ford, John",84,No,johnFord.png -1972,78,Say Goodbye Maggie Cole,Drama,"McGavin, Darren","Hayward, Susan","Taylor, Jud",84,No,NicholasCage.png -1964,132,Circus World,Drama,"Wayne, John","Hayworth, Rita","Hathaway, Henry",29,No,johnWayne.png -1952,98,Affair in Trinidad,Drama,"Ford, Glenn","Hayworth, Rita","Sherman, Vincent",49,No,glennFord.png -1948,87,Lady from Shanghai,Mystery,"Welles, Orson","Hayworth, Rita","Welles, Orson",16,No,NicholasCage.png -1940,81,Lady in Question,Drama,"Aherne, Brian","Hayworth, Rita","Vidor, Charles",57,No,NicholasCage.png -1946,110,Gilda,Drama,"Ford, Glenn","Hayworth, Rita","Vidor, Charles",57,No,glennFord.png -1948,98,"Loves of Carmen, The",Drama,"Ford, Glenn","Hayworth, Rita","Vidor, Charles",48,No,glennFord.png -1990,105,Dick Tracy,Comedy,"Beatty, Warren","Headley, Glenne","Beatty, Warren",84,No,NicholasCage.png -1964,130,Marnie,Drama,"Connery, Sean","Hedren, Tippi","Hitchcock, Alfred",2,No,seanConnery.png -1987,85,Hot Child in the City,Mystery,"Prysirr, Geof","Hendrix, Leah Ayres","Florea, John",0,No,NicholasCage.png -1984,90,Johnny Dangerously,Comedy,"Piscopo, Joe","Henner, Marilu","Heckerling, Amy",3,No,NicholasCage.png -1985,95,Stark,Mystery,"Surovy, Nicolas","Henner, Marilu","Holcomb, Rod",27,No,NicholasCage.png -1949,84,Three Strange Loves,Drama,"Malmsten, Birger","Henning, Eva","Bergman, Ingmar",87,No,Bergman.png -1964,170,My Fair Lady,Music,"Harrison, Rex","Hepburn, Audrey","Cukor, George",10,Yes,NicholasCage.png -1960,123,"Unforgiven, The",Drama,"Lancaster, Burt","Hepburn, Audrey","Huston, John",32,No,burtLancaster.png -1976,106,Robin & Marian,Action,"Connery, Sean","Hepburn, Audrey","Lester, Richard",6,No,seanConnery.png -1961,109,"Children's Hour, The",Drama,"Garner, James","Hepburn, Audrey","Wyler, William",60,No,NicholasCage.png -1956,121,"Rainmaker, The",Drama,"Lancaster, Burt","Hepburn, Katharine","Anthony, Joseph",21,No,katharineHepburn.png -1952,95,Pat & Mike,Comedy,"Tracy, Spencer","Hepburn, Katharine","Cukor, George",48,No,spencerTracy.png -1968,134,"Lion in Winter, THe",Drama,"O'Toole, Peter","Hepburn, Katharine","Harvey, Anthony",78,Yes,katharineHepburn.png -1991,132,"Sea of Grass, The",Western,"Tracy, Spencer","Hepburn, Katharine","Kazan, Elia",75,No,spencerTracy.png -1967,108,Guess Who's Coming to Dinner,Drama,"Tracy, Spencer","Hepburn, Katharine","Kramer, Stanley",50,Yes,spencerTracy.png -1957,153,Desk Set,Comedy,"Tracy, Spencer","Hepburn, Katharine","Lang, Walter",51,No,spencerTracy.png -1975,107,Rooster Cogburn,Western,"Wayne, John","Hepburn, Katharine","Miller, Stuart",76,No,johnWayne.png -1981,109,On Golden Pond,Drama,"Fonda, Henry","Hepburn, Katharine","Rydell, Mark",23,Yes,katharineHepburn.png -1991,101,Adam's Rib,Comedy,"Tracy, Spencer","Hepburn, Katharine",,62,No,spencerTracy.png -1991,116,Boom Town,Drama,"Tracy, Spencer","Hepburn, Katharine",,73,No,katharineHepburn.png -1991,145,Dragon Seed,Drama,"Tracy, Spencer","Hepburn, Katharine",,34,No,katharineHepburn.png -1991,115,Little Women,Drama,"Tracy, Spencer","Hepburn, Katharine",,22,No,katharineHepburn.png -1991,113,"Philadelphia Story, The",Comedy,"Tracy, Spencer","Hepburn, Katharine",,25,No,katharineHepburn.png -1991,112,Without Love,Comedy,"Tracy, Spencer","Hepburn, Katharine",,66,No,katharineHepburn.png -1991,113,Woman of the Year,Comedy,"Tracy, Spencer","Hepburn, Katharine",,12,No,spencerTracy.png -1992,95,Juice,Drama,"Shakur, Tupac","Herron, Cindy","Dickerson, Ernest R.",31,No,NicholasCage.png -1986,114,Hoosiers,Drama,"Hackman, Gene","Hershey, Barbara","Anspaugh, David",2,No,NicholasCage.png -1987,112,Tin Men,Comedy,"Dreyfuss, Richard","Hershey, Barbara","Levinson, Barry",50,No,NicholasCage.png -1988,163,"Last Temptation of Christ, The",Drama,"Dafoe, Willem","Hershey, Barbara","Scorsese, Martin",32,No,NicholasCage.png -1991,99,Paris Trout,Drama,"Hopper, Dennis","Hershey, Barbara",,53,No,NicholasCage.png -1988,87,Souvenir,Drama,"Plummer, Christopher","Hicks, Catherine","Reeve, Geoffrey",42,No,NicholasCage.png -1966,120,A Man for All Seasons,Drama,"Shaw, Robert","Hiller, Wendy","Zinnemann, Fred",20,Yes,NicholasCage.png -1986,90,Knights & Emeralds,Drama,"Leadbitter, Bill","Hills, Beverly","Emes, Ian",,No,NicholasCage.png -1989,83,Masque of the Red Death,Horror,"MacNee, Patrick","Hoak, Clare","Brand, Larry",9,No,NicholasCage.png -1943,265,"Adventures of Smilin' Jack, The",Mystery,"Brown, Tom","Hobart, Rose","Taylor, Ray",77,No,NicholasCage.png -1992,88,Adventures in Dinosaur City,Action,"Katz, Omri","Hoffman, Shawn","Thompson, Brett",19,No,NicholasCage.png -1987,95,"Allnighter, The",Comedy,"Terlesky, John","Hoffs, Susanna","Hoffs, Tamar Simon",71,No,NicholasCage.png -1980,99,Caddyshack,Comedy,"Chase, Chevy","Holcomb, Sarah","Ramis, Harold",70,No,NicholasCage.png -1973,102,Tom Sawyer,Music,"Whitaker, Johnny","Holm, Celeste","Taylor, Don",11,No,NicholasCage.png -1987,94,"Rita, Sue & Bob Too",Comedy,"Finneran, Siohban","Holmes, Michelle","Clarke, Alan",5,No,NicholasCage.png -1947,56,Hawk of Powder River,Western,"Dean, Eddie","Holt, Jennifer","Taylor, Ray",61,No,NicholasCage.png -1928,148,Tempest,Drama,"Barrymore, John","Horn, Camilla","Taylor, Sam",33,No,NicholasCage.png -1986,90,Running Mates,Drama,"Webb, Greg","Howard, Barbara","Neff, Thomas L.",63,No,NicholasCage.png -1987,105,Prettykill,Drama,"Birney, David","Hubley, Season","Kaczender, George",71,No,NicholasCage.png -1934,80,Judge Priest,Drama,"Rogers, Will","Hudson, Rochelle","Ford, John",9,No,johnFord.png -1950,104,Harvey,Comedy,"Stewart, James","Hull, Josephine","Koster, Henry",42,No,NicholasCage.png -1991,89,If Looks Could Kill,Action,"Grieco, Richard","Hunt, Linda","Wilmington, Michael",10,No,NicholasCage.png -1987,94,Raising Arizona,Comedy,"Cage, Nicolas","Hunter, Holly","Coen, Joel",23,No,NicholasCage.png -1989,114,Once Around,Comedy,"Dreyfuss, Richard","Hunter, Holly","Hallström, Lasse",68,No,NicholasCage.png -1980,110,Loulou,Drama,"Depardieu, Gérard","Huppert, Isabelle","Pialat, Maurice",65,No,NicholasCage.png -1982,136,"World According to Garp, The",Drama,"Williams, Robin","Hurt, Mary Beth","Hill, George Roy",59,No,NicholasCage.png -1980,106,Virus,Science Fiction,"Kennedy, George","Hussey, Olivia","Fukasaku, Kinji",62,No,NicholasCage.png -1940,127,Northwest Passage,Action,"Tracy, Spencer","Hussey, Ruth","Vidor, King",51,No,spencerTracy.png -1987,112,Gardens of Stone,Drama,"Caan, James","Huston, Anjelica","Coppola, Francis Ford",27,No,NicholasCage.png -1989,121,"Enemies, a Love Story",Drama,"Silver, Ron","Huston, Anjelica","Mazursky, Paul",5,No,NicholasCage.png -1992,102,"Addams Family, The",Comedy,"Julia, Raul","Huston, Anjelica","Sonnenfeld, B.",8,No,NicholasCage.png -1932,65,Freaks,Horror,"Ford, Wallace","Hyams, Leila","Browning, Tod",61,No,NicholasCage.png -1991,108,Necessary Roughness,Comedy,"Bakula, Scott","Ireland, Kathy","Dragoti, Stan",60,No,NicholasCage.png -1990,93,A Show of Force,Drama,"Garcia, Andy","Irving, Amy","Barreto, Bruno",1,No,NicholasCage.png -1980,129,"Competition, The",Drama,"Dreyfuss, Richard","Irving, Amy","Oliansky, Joel",45,No,NicholasCage.png -1988,97,Crossing Delancey,Comedy,"Riegert, Peter","Irving, Amy","Silver, Joan Micklin",6,No,NicholasCage.png -1982,120,"State of Things, The",Drama,"Kime, Jeffrey",Isabelle Weingarten.,"Wenders, Wim",73,No,NicholasCage.png -1987,89,Business As Usual,Comedy,"Thaw, John","Jackson, Glenda","Barrett, Lezli-An",17,No,NicholasCage.png -1973,103,A Touch of Class,Comedy,"Segal, George","Jackson, Glenda","Frank, Melvin",79,Yes,NicholasCage.png -1970,129,Women in Love.,Drama,"Bates, Alan","Jackson, Glenda","Russell, Ken",18,No,NicholasCage.png -1988,89,Salome's Last Dance,Comedy,"Johns, Stratford","Jackson, Glenda","Russell, Ken",76,No,NicholasCage.png -1986,100,Casino,Mystery,"Connors, Mike","Jackson, Sherry","Chaffey, Don",5,No,NicholasCage.png -1955,108,Smiles of a Summer Night,Comedy,"Björnstrand, Gunnar","Jacobsson, Ulla","Bergman, Ingmar",58,No,Bergman.png -1989,90,New Year's Day,Comedy,"Jaglom, Henry","Jakobsen, Maggie","Jaglom, Henry",88,No,NicholasCage.png -1981,132,Mephisto,Drama,"Brandauer, Klaus Maria","Janda, Krystyna","Szabó, István",80,Yes,NicholasCage.png -1927,60,Easy Virtue,Mystery,"Dyall, Franklin","Jeans, Isabel","Hitchcock, Alfred",45,No,alfredHitchcock.png -1937,59,Swing It Sailor!,Comedy,"Ford, Wallace","Jewell, Isabel",,6,No,NicholasCage.png -1991,83,Strictly Business,Comedy,"Davidson, Tommy","Johnson, Anne-Marie","Hooks, Kevin",3,No,NicholasCage.png -1983,90,Blame It on Rio,Comedy,"Caine, Michael","Johnson, Michelle","Donen, Stanley",10,No,NicholasCage.png -1987,86,Straight to Hell,Action,"Hopper, Dennis","Jones, Grace","Cox, Alex",47,No,NicholasCage.png -1990,131,A View to a Kill,Action,"Moore, Roger","Jones, Grace",,44,No,NicholasCage.png -1986,100,American Anthem,Drama,"Gaylord, Mitch","Jones, Janet","Magnoli, Albert",74,No,NicholasCage.png -1963,99,Bedtime Story,Comedy,"Brando, Marlon","Jones, Shirley","Levy, Ralph",7,No,brando.png -1991,117,"Courtship of Eddie's Father, The",Comedy,"Howard, Ron","Jones, Shirley",,43,No,NicholasCage.png -1988,102,"Night Train to Katmandu, THe",Action,"Roberts, Pernell","Jovovich, Milla","Wiemer, Robert",43,No,NicholasCage.png -1948,100,Port of Call,Drama,"Eklund, Bengt","Jönsson, Nine-Christine","Bergman, Ingmar",29,No,Bergman.png -1973,103,Paper Moon,Comedy,"O'Neal, Ryan","Kahn, Madeline","Bogdanovich, Peter",3,Yes,NicholasCage.png -1983,97,Yellowbeard,Comedy,"Chapman, Graham","Kahn, Madeline","Damski, Mel",34,No,NicholasCage.png -1975,91,Adventures of Sherlock Holmes' Smarter,Comedy,"Wilder, Gene","Kahn, Madeline","Wilder, Gene",42,No,NicholasCage.png -1990,108,Flashback,Comedy,"Hopper, Dennis","Kane, Carol","Amurri, Franco",19,No,NicholasCage.png -1977,89,"World's Greatest Lover, The",Comedy,"Wilder, Gene","Kane, Carol","Wilder, Gene",42,No,NicholasCage.png -1955,67,Killer's Kiss,Mystery,"Silvera, Frank","Kane, Irene","Kubrick, Stanley",66,No,NicholasCage.png -1988,103,"Deceivers, The",Action,"Brosnan, Pierce","Kapoor, Shashi","Meyer, Nicholas",14,No,NicholasCage.png -1983,97,Breathless,Action,"Gere, Richard","Kaprisky, Valerie","McBride, Jim",51,No,NicholasCage.png -1989,145,Born on the Fourth of July,Drama,"Cruise, Tom","Kava, Caroline","Stone, Oliver",8,Yes,NicholasCage.png -1991,120,Awakenings,Drama,"De Niro, Robert","Kavner, Julie","Marshall, Penny",8,No,NicholasCage.png -1977,94,Annie Hall,Comedy,"Allen, Woody","Keaton, Diane","Allen, Woody",68,Yes,woody.png -1979,96,Manhattan,Comedy,"Allen, Woody","Keaton, Diane","Allen, Woody",82,Yes,woody.png -1981,195,Reds,Drama,"Beatty, Warren","Keaton, Diane","Beatty, Warren",76,Yes,NicholasCage.png -1986,105,Crimes of the Heart,Comedy,"Shepard, Sam","Keaton, Diane","Beresford, Bruce",84,No,NicholasCage.png -1977,136,Looking for Mr. Goodbar,Drama,"Atherton, William","Keaton, Diane","Brooks, Richard",54,No,NicholasCage.png -1972,175,"Godfather, The",Drama,"Brando, Marlon","Keaton, Diane","Coppola, Francis Ford",8,Yes,brando.png -1974,201,"Godfather, Pt 2., The",Drama,"Pacino, Al","Keaton, Diane","Coppola, Francis Ford",8,Yes,NicholasCage.png -1976,109,"I Will, I Will...For Now",Comedy,"Gould, Elliott","Keaton, Diane","Panama, Norman",6,No,NicholasCage.png -1972,86,"Play It Again, Sam",Comedy,"Allen, Woody","Keaton, Diane","Ross, Herbert",81,No,woody.png -1975,82,Love & Death,Comedy,"Allen, Woody","Keaton, Diane",,84,No,woody.png -1973,88,Sleeper,Comedy,"Allen, Woody","Keaton, Diane",,59,No,woody.png -1970,130,Fellini Satyricon,Drama,"Potter, Martin","Keller, Hiram","Fellini, Federico",88,No,NicholasCage.png -1980,117,"Formula, The",Mystery,"Scott, George C.","Keller, Marthe","Avildsen, John G.",82,No,NicholasCage.png -1977,143,Black Sunday,Drama,"Shaw, Robert","Keller, Marthe","Frankenheimer, John",76,No,NicholasCage.png -1977,124,Bobby Deerfield,Drama,"Pacino, Al","Keller, Marthe","Pollack, Sydney",36,No,NicholasCage.png -1972,98,Last of the Red Hot Lovers,Comedy,"Arkin, Alan","Kellerman, Sally","Saks, Gene",40,No,NicholasCage.png -1953,116,Mogambo,Action,"Gable, Clark","Kelly, Grace","Ford, John",71,No,johnFord.png -1955,103,To Catch a Thief,Mystery,"Grant, Cary","Kelly, Grace","Hitchcock, Alfred",69,No,alfredHitchcock.png -1954,113,Rear Window,Mystery,"Stewart, James","Kelly, Grace","Hitchcock, Alfred",25,No,alfredHitchcock.png -1945,69,Woman Who Came Back,Drama,"Kruger, Otto","Kelly, Nancy","Colmes, Walter",26,No,NicholasCage.png -1939,101,Stanley & Livingstone,Action,"Tracy, Spencer","Kelly, Nancy","King, Henry",11,No,spencerTracy.png -1956,129,"Bad Seed, The",Horror,"Jones, Henry","Kelly, Nancy","LeRoy, Mervyn",69,No,NicholasCage.png -1989,113,Lethal Weapon 2,Action,"Gibson, Mel","Kensit, Patsy","Donner, Richard",69,No,NicholasCage.png -1992,79,Blame It on the Bellboy,Comedy,"Moore, Dudley","Kensit, Patsy","Herman, Mark",69,No,NicholasCage.png -1927,62,"Drop Kick, The",Drama,"Barthelmess, Richard","Kent, Barbara","Webb, Millard",,No,NicholasCage.png -1978,145,"Superman, The Movie",Action,"Brando, Marlon","Kidder, Margot","Donner, Richard",87,No,brando.png -1987,90,Superman IV: The Quest for Peace,Action,"Reeve, Christopher","Kidder, Margot","Furie, Sidney J.",77,No,NicholasCage.png -1970,90,Quackser Fortune Has a Cousin in the Bronx,Comedy,"Wilder, Gene","Kidder, Margot","Waris, Hussein",49,No,NicholasCage.png -1989,96,Dead Calm,Mystery,"Neill, Sam","Kidman, Nicole","Noyce, Phillip",1,No,NicholasCage.png -1990,107,Days of Thunder,Action,"Cruise, Tom","Kidman, Nicole","Scott, Tony",3,No,NicholasCage.png -1987,101,My Life As a Dog,Comedy,"Glanzelius, Anton","Kinnaman, Melinda","Hallström, Lasse",21,No,NicholasCage.png -1983,,"Moon in the Gutter, The",Action,"Depardieu, Gérard","Kinski, Nastassia","Beineix, Jean-Jacques",29,No,NicholasCage.png -1984,150,"Paris, Texas",Drama,"Stanton, Harry Dean","Kinski, Nastassia","Wenders, Wim",27,No,NicholasCage.png -1984,96,Unfaithfully Yours,Comedy,"Moore, Dudley","Kinski, Nastassia","Zieff, Howard",73,No,NicholasCage.png -1987,95,Bullseye!,Comedy,"Caine, Michael","Kirkland, Sally","Winner, Michael",8,No,NicholasCage.png -1989,104,Erik the Viking,Action,"Robbins, Tim","Kitt, Eartha","Jones, Terry",25,No,NicholasCage.png -1987,90,Dragonard,Drama,"Reed, Oliver","Kitt, Eartha","Kikoine, Gerard",71,No,NicholasCage.png -1986,90,Hard Choices,Drama,"McCleery, Gary","Klenck, Margaret","King, Rick",41,No,NicholasCage.png -1969,102,"Rain People, The",Drama,"Caan, James","Knight, Shirley","Coppola, Francis Ford",78,No,NicholasCage.png -1984,106,A Year of the Quiet Sun,Drama,"Wilson, Scott","Komorowska, Maja","Zanussi, Krzystoff",78,No,NicholasCage.png -1935,54,"Desert Trail, The",Western,"Wayne, John","Kornman, Mary","Collins, Lewis D.",50,No,johnWayne.png -1990,98,Almost an Angel,Comedy,"Hogan, Paul","Kozlowski, Linda","Cornell, John",14,No,NicholasCage.png -1986,98,Crocodile Dundee,Comedy,"Hogan, Paul","Kozlowski, Linda","Faiman, Peter",66,No,NicholasCage.png -1977,127,"American Friend, The",Mystery,"Hopper, Dennis","Kreuzer, Lisa","Wenders, Wim",35,No,NicholasCage.png -1989,119,See You in the Morning,Drama,"Bridges, Jeff","Krige, Alice","Pakula, Alan J.",53,No,NicholasCage.png -1987,88,"Arrogant, The",Drama,"Graham, Gary","Kristel, Sylvia","Blot, Philippe",62,No,NicholasCage.png -1989,86,Dracula's Widow,Horror,"Sommer, Josef","Kristel, Sylvia","Coppola, Christopher",55,No,NicholasCage.png -1987,90,Ninja Masters of Death,Action,"Peterson, Chris","Kruize, Kelly","Lambert, Bruce",15,No,NicholasCage.png -1990,110,Mystery Train,Comedy,"Nagase, Masatoshi","Kudoh, Youki","Jarmusch, Jim",23,No,NicholasCage.png -1978,114,Go Tell the Spartans,War,"Lancaster, Burt","Kumagai, Denice","Post, Ted",67,No,burtLancaster.png -1986,89,True Stories,Comedy,"Goodman, John","Kurtz, Swoosie","Byrne, David",79,No,NicholasCage.png -1953,94,Ugetsu Monogatari,Drama,"Mori, Masayuki","Kyô, Machiki","Mizoguchi, Kenji",82,No,NicholasCage.png -1969,80,Rebel Rousers,Action,"Nicholson, Jack","Ladd, Diane","Cohen, Martin B.",44,No,JackNicholson.png -1988,98,Plain Clothes,Comedy,"Howard, Arliss","Ladd, Diane","Coolidge, Martha",4,No,NicholasCage.png -1981,119,"Whose Life Is It, Anyway?",Drama,"Dreyfuss, Richard","Lahti, Christine","Badham, John",62,No,NicholasCage.png -1988,116,Running on Empty,Drama,"Hirsch, Judd","Lahti, Christine","Lumet, Sidney",2,No,NicholasCage.png -1990,101,Funny about Love,Comedy,"Wilder, Gene","Lahti, Christine","Nimoy, Leonard",60,No,NicholasCage.png -1985,118,"A Chorus Line, The Movie",Music,"Douglas, Michael","Landers, Audrey","Attenborough, Richard",71,No,NicholasCage.png -1986,84,Stewardess School,Comedy,"Most, Donald","Landers, Judy","Blancato, Ken",28,No,NicholasCage.png -1987,109,"Big Town, The",Drama,"Dillon, Matt","Lane, Diane","Bolt, Ben",11,No,NicholasCage.png -1983,94,Rumble Fish,Drama,"Dillon, Matt","Lane, Diane","Coppola, Francis Ford",4,No,NicholasCage.png -1983,91,"Outsiders, The",Drama,"Howell, C. Thomas","Lane, Diane","Coppola, Francis Ford",56,No,NicholasCage.png -1990,94,Priceless Beauty,Science Fiction,"Lambert, Christopher","Lane, Diane","Finch, Charles",7,No,NicholasCage.png -1989,93,Streets of Fire,Action,"Paré, Michael","Lane, Diane","Hill, Walter",65,No,NicholasCage.png -1990,115,Men Don't Leave,Drama,"Howard, Arliss","Lange, Jessica","Brickman, Paul",66,No,NicholasCage.png -1988,127,Everybody's All American,Romance,"Quaid, Dennis","Lange, Jessica","Hackford, Taylor",62,No,NicholasCage.png -1992,128,Cape Fear,Mystery,"De Niro, Robert","Lange, Jessica","Scorsese, Martin",7,No,NicholasCage.png -1992,121,"Postman Always Rings Twice, The",Mystery,"Nicholson, Jack","Lange, Jessica",,24,No,NicholasCage.png -1949,58,Crashing Thru,Western,"Wilson, Whip","Larson, Christine","Taylor, Ray",19,No,NicholasCage.png -1978,109,Get Out Your Handkerchiefs,Comedy,"Depardieu, Gérard","Laure, Carole","Blier, Bertrand",78,Yes,NicholasCage.png -1971,137,"Boy Friend, THe",Music,"Gable, Christopher","Lawson, Twiggy","Russell, Ken",8,No,NicholasCage.png -1990,100,Hard To Kill,Action,"Seagal, Steven","LeBrock, Kelly","Malmuth, Bruce",49,No,NicholasCage.png -1960,109,Psycho,Horror,"Perkins, Anthony","Leigh, Janet","Hitchcock, Alfred",56,No,alfredHitchcock.png -1957,112,Jet Pilot,Action,"Wayne, John","Leigh, Janet","Sternberg, Josef von",43,No,johnWayne.png -1987,95,Under Cover,Mystery,"Neidorf, David","Leigh, Jennifer Jason","Stockwell, John",36,No,NicholasCage.png -1951,122,A Streetcar Named Desire,Drama,"Brando, Marlon","Leigh, Vivien","Kazan, Elia",75,Yes,brando.png -1986,93,"Golden Child, The",Comedy,"Murphy, Eddie","Lewis, Charlotte","Ritchie, Michael",86,No,NicholasCage.png -1971,84,"Statue, The",Drama,"Niven, David","Lisi, Virna","Amateau, Rod",80,No,NicholasCage.png -1985,128,Christopher Columbus,Drama,"Byrne, Gabriel","Lisi, Virna","Lattuada, Alberto",69,No,NicholasCage.png -1989,116,In Country,Drama,"Willis, Bruce","Lloyd, Emily","Jewison, Norman",76,No,NicholasCage.png -1978,132,"Wild Geese, The",Action,"Burton, Richard","Lloyd, Rosalind","McLaglen, Andrew V.",21,No,NicholasCage.png -1974,90,"Second Coming of Suzanne., The",Drama,"Dreyfuss, Richard","Locke, Sondra","Barry, Michael",21,No,NicholasCage.png -1980,116,Bronco Billy,Westerns,"Eastwood, Clint","Locke, Sondra","Eastwood, Clint",57,No,clintEastwood.png -1977,109,"Gauntlet, The",Action,"Eastwood, Clint","Locke, Sondra","Eastwood, Clint",18,No,clintEastwood.png -1986,105,Ratboy,Drama,"Townsend, Robert","Locke, Sondra","Locke, Sondra",1,No,NicholasCage.png -1938,96,Lady Vanishes,Mystery,"Redgrave, Michael","Lockwood, Margaret","Hitchcock, Alfred",27,No,alfredHitchcock.png -1987,95,"Kitchen Toto, THe",Drama,"Peck, Bob","Logan, Phyllis","Hook, Harry",41,No,NicholasCage.png -1959,88,Carlton-Browne of the F.O.,Comedy,Terry-Thomas,"Lohr, Marie","Boulting, Roy",63,No,NicholasCage.png -1929,68,Racketeer,Drama,"Armstrong, Robert","Lombard, Carole","Higgin, Howard",2,No,NicholasCage.png -1941,95,Mr. & Mrs. Smith,Comedy,"Montgomery, Robert","Lombard, Carole","Hitchcock, Alfred",3,No,alfredHitchcock.png -1986,132,Alrededor de Medianoche,Drama,Francois Cluzet,Lonette McKee,"Rayfield, David",47,No,NicholasCage.png -1982,101,Losin' It,Comedy,"Cruise, Tom","Long, Shelley","Hanson, Curtis",4,No,NicholasCage.png -1987,114,Into the Homeland,Action,"Boothe, Powers","Longstreth, Emily","Glatter, Lesli Linka",34,No,NicholasCage.png -1991,60,Boxing Babes,Action,"Nichol, Robin","Lords, Traci","Dell, Stewart",9,No,NicholasCage.png -1991,94,Shock 'em Dead,Horror,"Donahue, Troy","Lords, Traci","Freed, Mark",31,No,NicholasCage.png -1960,101,Heller in Pink Tights,Drama,"Quinn, Anthony","Loren, Sophia","Cukor, George",52,No,sophiaLoren.png -1961,100,Two Women,Drama,"Belmondo, Jean-Paul","Loren, Sophia","De Sica, Vittorio",83,Yes,sophiaLoren.png -1954,107,"Gold of Naples, The",Drama,"De Sica, Vittorio","Loren, Sophia","De Sica, Vittorio",40,No,sophiaLoren.png -1963,118,"Yesterday, Today & Tomorrow",Comedy,"Mastroianni, Marcello","Loren, Sophia","De Sica, Vittorio",73,Yes,sophiaLoren.png -1957,109,Legend of the Lost,Action,"Wayne, John","Loren, Sophia","Hathaway, Henry",84,No,sophiaLoren.png -1978,111,Brass Target,Action,"Cassavetes, John","Loren, Sophia","Hough, John",53,No,sophiaLoren.png -1964,188,"Fall of the Roman Empire, The",Drama,"Boyd, Stphen","Loren, Sophia","Mann, Anthony",62,No,sophiaLoren.png -1961,172,El Cid,Drama,"Heston, Charlton","Loren, Sophia","Mann, Anthony",10,No,sophiaLoren.png -1958,114,Desire under the Elms,Drama,"Perkins, Anthony","Loren, Sophia","Mann, Delbert",13,No,sophiaLoren.png -1953,92,Two Nights with Cleo,Drama,"Sordi, Alberto","Loren, Sophia","Mattoli, Mario",54,No,sophiaLoren.png -1959,,"Black Orchid, The",Drama,"Quinn, Anthony","Loren, Sophia","Ritt, Martin",54,No,sophiaLoren.png -1977,91,Angela,Drama,"Railsback, Steve","Loren, Sophia","Sagal, Boris",80,No,sophiaLoren.png -1977,105,A Special Day,Drama,"Mastroianni, Marcello","Loren, Sophia","Scola, Ettore",80,Yes,sophiaLoren.png -1979,112,Blood Feud,Action,"Mastroianni, Marcello","Loren, Sophia","Wertmuller, Lina",52,No,sophiaLoren.png -1991,145,"Sophia Loren, Her Own Story",Drama,"Gavin, John","Loren, Sophia",,49,No,sophiaLoren.png -1990,,Running Away,Drama,"Loggia, Robert","Loren, Sophia",,2,No,sophiaLoren.png -1991,130,Man of La Mancha,Music,"O'Toole, Peter","Loren, Sophia",,55,No,sophiaLoren.png -1992,116,Operation Crossbow,Action,"Peppard, George","Loren, Sophia",,1,No,sophiaLoren.png -1986,141,Courage,Drama,"Williams, Billy Dee","Loren, Sophia",,56,No,sophiaLoren.png -1986,94,RAD,Action,"Allen, Bill","Loughlin, Lori","Needham, Hal",75,No,NicholasCage.png -1992,98,Secret Admirer,Comedy,"Howell, C. Thomas","Loughlin, Lori",,55,No,NicholasCage.png -1979,85,Cocaine Cowboys,Action,"Palance, Jack","Love, Suzanna","Lommel, Ulli",17,No,NicholasCage.png -1991,118,Test Pilot,Drama,"Gable, Clark","Loy, Myrna",,13,No,NicholasCage.png -1943,64,"Ape Man, The",Horror,"Ford, Wallace","Lugosi, Bela","Beaudine, William",83,No,NicholasCage.png -1986,125,"Mission, The",Drama,"De Niro, Robert","Lunghi, Cherie","Joffe, Roland",20,No,NicholasCage.png -1991,102,Curly Sue,Comedy,"Belushi, Jim","Lynch, Kelly","Hughes, John",2,No,NicholasCage.png -1962,150,Lolita,Drama,"Mason, James","Lyon, Sue","Kubrick, Stanley",80,No,NicholasCage.png -1989,101,"Sex, Lies, and Videotape",Drama,"Spader, James","MacDowell, Andie","Soderbergh, Steven",70,Yes,NicholasCage.png -1990,107,Green Card,Comedy,"Depardieu, Gérard","MacDowell, Andie","Weir, Peter",25,No,NicholasCage.png -1988,95,Gator Bait II,Action,"Muzzcat, Paul","MacKenzie, Jan","Sebastian, Beverly",73,No,NicholasCage.png -1979,129,Being There,Comedy,"Sellers, Peter","MacLaine, Shirley","Ashby, Hal",31,Yes,NicholasCage.png -1983,132,Terms of Endearment,Drama,"Nicholson, Jack","MacLaine, Shirley","Brooks, James L.",32,Yes,JackNicholson.png -1967,99,Woman Times Seven,Comedy,"Sellers, Peter","MacLaine, Shirley","De Sica, Vittorio",36,No,NicholasCage.png -1968,,"Bliss of Mrs. Blossom, The",Comedy,"Booth, James","MacLaine, Shirley","McGrath, Joseph",86,No,NicholasCage.png -1990,101,Postcards from the Edge,Comedy,"Quaid, Dennis","MacLaine, Shirley","Nichols, Mike",63,No,NicholasCage.png -1970,105,Two Mules for Sister Sara,Western,"Eastwood, Clint","MacLaine, Shirley","Siegel, Don",36,No,clintEastwood.png -1992,84,Dragonfight,Drama,"Z'Dar, Robert","MacLaren, Fawna",,71,No,NicholasCage.png -1939,85,Back Door to Heaven,Drama,"Ford, Wallace","MacMahon, Aline","Howard, William K.",83,No,NicholasCage.png -1988,100,"Ciao Italia, Madonna Live from Italy",Music,,Madonna,"De Winter, Harry",74,No,NicholasCage.png -1991,118,"Madonna, Truth or Dare",Music,,Madonna,"Keshishian, Alek",54,No,NicholasCage.png -1992,60,A Certain Sacrifice,Music,"Pattnosh, Jeremy",Madonna,"Lewicki, Steven Jon",24,No,NicholasCage.png -1991,40,"National Enquirer, The Untold Story",Music,"White, Vanna",Madonna,,65,No,NicholasCage.png -1990,60,"Immaculate Collection, The",Music,,Madonna,,32,No,NicholasCage.png -1987,50,"Madonna Live, The Virgin Tour",Music,,Madonna,,75,No,NicholasCage.png -1990,5,"Madonna, Justify My Love",Music,,Madonna,,77,No,NicholasCage.png -1991,16,"Madonna, Like a Virgin",Music,,Madonna,,63,No,NicholasCage.png -1988,83,Hot to Trot,Comedy,"Goldthwait, Bob","Madsen, Virginia","Dinner, Michael",78,No,NicholasCage.png -1986,103,Fire with Fire,Drama,"Sheffer, Craig","Madsen, Virginia","Gibbins, Duncan",9,No,NicholasCage.png -1990,120,Hot Spot,Drama,"Johnson, Don","Madsen, Virginia","Hopper, Dennis",70,No,NicholasCage.png -1974,124,Amarcord,Drama,"Noel, Magali","Maggio, Pupella","Fellini, Federico",50,Yes,NicholasCage.png -1988,85,Casablanca Express,Action,"Connery, Jason","Maneri, Luisa","Martino, Sergio",33,No,NicholasCage.png -1980,94,Out of the Blue,Drama,"Hopper, Dennis","Manz, Linda","Hopper, Dennis",4,No,NicholasCage.png -1949,110,Sands of Iwo Jima,War,"Wayne, John","Mara, Adele","Dwan, Allan",72,No,johnWayne.png -1981,104,"Hand, The",Horror,"Caine, Michael","Marcovicci, Andrea","Stone, Oliver",44,No,NicholasCage.png -1989,81,Deep Cover,Mystery,"Conti, Tom","Markham, Kika","Loncraine, Richard",15,No,NicholasCage.png -1955,92,Il Bidone,Drama,"Crawford, Broderick","Masina, Guilietta","Fellini, Federico",70,No,NicholasCage.png -1986,130,El Guerrero Solitario,Drama,"Eastwood, Clint","Mason, Marsha","Eastwood, Clint",77,No,clintEastwood.png -1986,130,Heartbreak Ridge,War,"Eastwood, Clint","Mason, Marsha","Eastwood, Clint",61,No,clintEastwood.png -1977,110,"Goodbye Girl, The",Comedy,"Dreyfuss, Richard","Mason, Marsha","Ross, Herbert",6,Yes,NicholasCage.png -1991,113,Audrey Rose,Drama,"Hopkins, Anthony","Mason, Marsha",,62,No,AnthonyHopkins.png -1981,86,Polyester,Comedy,Divine,"Massey, Edith",,68,No,NicholasCage.png -1991,144,Robin Hood: Prince of Thieves,Action,"Costner, Kevin","Mastrantonio, Mary Elizabeth","Costner, Kevin",8,No,NicholasCage.png -1992,101,White Sands,Drama,"Dafoe, Willem","Mastrantonio, Mary Elizabeth","Donaldson, Roger",38,No,NicholasCage.png -1986,119,"Color of Money, The",Drama,"Newman, Paul","Mastrantonio, Mary Elizabeth","Scorsese, Martin",6,Yes,paulNewman.png -1986,119,Children of a Lesser God,Drama,"Hurt, William","Matlin, Marlee","Haines, Randa",20,Yes,NicholasCage.png -1986,,Matador,Comedy,"Banderas, Antonio","Maura, Carmen","Almodóvar, Pedro",34,No,NicholasCage.png -1989,88,Women on the Verge of a Nervous Breakdown,Comedy,"Banderas, Antonio","Maura, Carmen","Almodóvar, Pedro",65,No,NicholasCage.png -1980,86,Pepi Luci Bom,Comedy,"Rotaeta, Félix","Maura, Carmen","Almodóvar, Pedro",66,No,NicholasCage.png -1989,100,"Forgotten, The",Mystery,"Carradine, Keith","Maynard, Mimi","Keach, James",69,No,NicholasCage.png -1992,89,"Flame & the Arrow, The",Action,"Lancaster, Burt","Mayo, Virginia",,0,No,burtLancaster.png -1990,92,After the Shock,Drama,"Kotto, Yaphet","McClanahan, Rue","Sherman, Gary",28,No,NicholasCage.png -1990,110,Modern Love,Comedy,"Benson, Robby","McClanahan, Rue",,18,No,NicholasCage.png -1992,95,Riff Raff,Comedy,"Carlyle, Robert","McCourt, Emer","Loach, Ken",71,No,NicholasCage.png -1967,81,"Glory Stompers, The",Action,"Hopper, Dennis","McCrea, Jody","Lanza, Anthony M.",27,No,NicholasCage.png -1990,181,Dances with Wolves,Western,"Costner, Kevin","McDonnell, Mary","Costner, Kevin",8,Yes,NicholasCage.png -1987,130,Matewan,Drama,"Jones, James Earl","McDonnell, Mary","Sayles, John",81,No,NicholasCage.png -1988,120,Mississippi Burning,Drama,"Hackman, Gene","McDormand, Frances","Parker, Alan",41,Yes,NicholasCage.png -1975,130,"Eiger Sanction, The",Action,"Eastwood, Clint","McGee, Vonetta","Eastwood, Clint",69,No,clintEastwood.png -1988,109,Unsettled Land,Drama,"Shea, John","McGillis, Kelly","Barbash, Uri",75,No,NicholasCage.png -1991,98,Cat Chaser,Drama,"Weller, Peter","McGillis, Kelly","Ferrera, Abel",6,No,NicholasCage.png -1988,110,"Accused, The",Drama,"Coulson, Bernie","McGillis, Kelly","Kaplan, Jonathan",71,Yes,NicholasCage.png -1989,109,Winter People,Drama,"Russell, Kurt","McGillis, Kelly","Kotcheff, Ted",30,No,NicholasCage.png -1983,101,"Reuben, Reuben",Comedy,"Conti, Tom","McGillis, Kelly","Miller, Robert Ellis",2,No,NicholasCage.png -1987,102,Made in Heaven,Fantasy,"Hutton, Timothy","McGillis, Kelly","Rudolph, Alan",57,No,NicholasCage.png -1986,109,Top Gun,Action,"Cruise, Tom","McGillis, Kelly","Scott, Tony",8,No,NicholasCage.png -1985,112,Witness,Drama,"Ford, Harrison","McGillis, Kelly","Weir, Peter",59,No,NicholasCage.png -1988,111,"House on Carroll Street, The",Mystery,"Daniels, Jeff","McGillis, Kelly",,6,No,NicholasCage.png -1984,109,Racing with the Moon,Drama,"Penn, Sean","McGovern, Elizabeth","Benjamin, Richard",50,No,NicholasCage.png -1983,98,Lovesick,Comedy,"Moore, Dudley","McGovern, Elizabeth","Brickman, Marshall",51,No,NicholasCage.png -1988,106,She's Having a Baby,Comedy,"Hughes, Kevin Bacon","McGovern, Elizabeth",,18,No,NicholasCage.png -1965,199,"Greatest Story Ever Told, The",Drama,"Sydow, Max von","McGuire, Dorothy","Stevens, George",26,No,NicholasCage.png -1989,105,Hawks,Drama,"Dalton, Timothy","McTeer, Janet","Miller, Robert Ellis",11,No,NicholasCage.png -1981,91,So Fine,Comedy,"O'Neal, Ryan","Melato, Mariangela","Bergman, Andrew",17,No,NicholasCage.png -1957,89,Paths of Glory,Drama,"Douglas, Kirk","Menjou, Adolphe","Kubrick, Stanley",47,No,NicholasCage.png -1964,120,Tom Jones,Drama,"Ustinov, Peter","Mercouri, Melina","Dassin, Jules",39,Yes,NicholasCage.png -1975,103,"Sunshine Boys, The",Comedy,"Burns, George","Meredith, Lee","Ross, Herbert",35,Yes,NicholasCage.png -1988,98,Caddyshack 2,Comedy,"Mason, Jackie","Merrill, Dina","Arkush, Allan",34,No,NicholasCage.png -1990,117,Internal Affairs,Drama,"Gere, Richard","Metcalf, Laurie","Figgis, Mike",3,No,NicholasCage.png -1991,206,JFK,Drama,"Costner, Kevin","Metcalf, Laurie","Stone, Oliver",78,No,NicholasCage.png -1991,97,New Jack City,Action,"Snipes, Wesley",Michael Michele,"Van Peebles, Mario",80,No,NicholasCage.png -1991,87,Scenes from a Mall,Comedy,"Allen, Woody","Midler, Bette",,8,No,woody.png -1987,118,Hope & Glory,War,"Hayman, David","Miles, Sarah","Boorman, John",3,No,NicholasCage.png -1970,194,Ryan's Daughter,Drama,"Mitchum, Robert","Miles, Sarah","Lean, David",81,Yes,NicholasCage.png -1973,127,"Man Who Loved Cat Dancing, The",Western,"Reynolds, Burt","Miles, Sarah","Sarafian, Richard C.",40,No,NicholasCage.png -1962,123,"Man Who Shot Liberty Valance, The",Western,"Stewart, James","Miles, Vera","Ford, John",85,No,johnFord.png -1989,102,Dead-Bang,Action,"Johnson, Don","Miller, Penelope Ann","Frankenheimer, John",9,No,NicholasCage.png -1988,90,Big Top Pee-wee,Comedy,"Reubens, Paul","Miller, Penelope Ann","Kleiser, Randal",17,No,NicholasCage.png -1960,103,"Time Machine, The",Science Fiction,"Taylor, Rod","Mimieux, Yvette","Pal, George",88,No,NicholasCage.png -1972,128,Cabaret,Drama,"Grey, Joel","Minnelli, Liza","Fosse, Bob",59,Yes,NicholasCage.png -1981,97,Arthur,Comedy,"Moore, Dudley","Minnelli, Liza","Gordon, Steve",79,Yes,NicholasCage.png -1976,97,A Matter of Time,Drama,"Boyer, Charles","Minnelli, Liza","Minnelli, Vincente",70,No,NicholasCage.png -1977,137,"New York, New York",Drama,"De Niro, Robert","Minnelli, Liza","Scorsese, Martin",8,No,NicholasCage.png -1989,89,"Nightmare on Elm Street, Pt. 5, The Dream Child",Horror,"Englund, Robert","Minter, Kelly Jo","Hopkins, Stephen",41,No,NicholasCage.png -1980,100,"Fiendish Plot of Dr. Fu Manchu, The",Comedy,"Sellers, Peter","Mirren, Helen","Haggard, Piers",29,No,NicholasCage.png -1991,240,Four American Composers,Music,"Cage, John","Monk, Meredith","Greenaway, Peter",3,No,NicholasCage.png -1950,112,"Asphalt Jungle, The",Action,"Hayden, Sterling","Monroe, Marilyn","Huston, John",77,No,NicholasCage.png -1992,61,Ladies of the Chorus,Music,"Garr, Eddie","Monroe, Marilyn","Karlson, Phil",60,No,NicholasCage.png -1953,95,How to Marry a Millionaire,Comedy,"Powell, William","Monroe, Marilyn","Negulesco, Jean",65,No,NicholasCage.png -1983,,Hollywood Out-Takes & Rare Footage,Comedy,"Bogart, Humphrey","Monroe, Marilyn",,27,No,NicholasCage.png -1991,94,Nothing But Trouble,Comedy,"Candy, John","Moore, Demi","Aykroyd, Dan",25,No,NicholasCage.png -1987,109,Wisdom,Action,"Estevez, Emilio","Moore, Demi","Estevez, Emilio",25,No,NicholasCage.png -1986,94,One Crazy Summer,Comedy,"Cusack, John","Moore, Demi","Holland, Savage Steve",61,No,NicholasCage.png -1989,110,We're No Angels,Comedy,"De Niro, Robert","Moore, Demi","Jordan, Neil",51,No,NicholasCage.png -1984,102,No Small Affair,Comedy,"Cryer, Jon","Moore, Demi","Schatzberg, Jerry",10,No,NicholasCage.png -1990,127,Ghost,Science Fiction,"Swayze, Patrick","Moore, Demi","Zucker, Jerry",6,Yes,NicholasCage.png -1986,113,About Last Night,Drama,"Lowe, Rob","Moore, Demi","Zwick, Edward",66,No,NicholasCage.png -1982,107,Six Weeks,Drama,"Moore, Dudley","Moore, Mary Tyler","Bill, Tony",73,No,NicholasCage.png -1948,89,Return of October,Comedy,"Ford, Glenn","Moore, Terry","Lewis, Joseph H.",35,No,glennFord.png -1952,99,"Come Back, Little Sheba",Drama,"Lancaster, Burt","Moore, Terry","Mann, Daniel",50,Yes,burtLancaster.png -1974,117,Going Places,Drama,"Depardieu, Gérard","Moreau, Jeanne","Blier, Bertrand",66,No,NicholasCage.png -1970,99,Monte Walsh,Western,"Marvin, Lee","Moreau, Jeanne","Fraker, William A.",29,No,NicholasCage.png -1955,100,Mr. Arkadin,Drama,"Welles, Orson","Mori, Paola","Welles, Orson",80,No,NicholasCage.png -1988,,White of the Eye,Mystery,"Keith, David","Moriarty, Cathy","Cammell, Donald",48,No,NicholasCage.png -1968,90,"Producers, The",Comedy,"Wilder, Gene","Mostel, Zero","Brooks, Mel",33,No,NicholasCage.png -1976,94,"Front, The",Drama,"Allen, Woody","Mostel, Zero","Ritt, Martin",70,No,woody.png -1987,86,House of the Rising Sun,Drama,"Annese, Frank","Moyer, Tawny","Gold, Greg",45,No,NicholasCage.png -1988,91,In a Shallow Grave,Drama,"Biehn, Michael","Mueller, Maureen","Bowser, Kenneth",72,No,NicholasCage.png -1974,111,Mc Q,Action,"Wayne, John","Muldaur, Diana","Sturges, John",73,No,johnWayne.png -1941,85,Lady from Louisiana,Drama,"Wayne, John","Munson, Ona","Vorhaus, Bernard",38,No,johnWayne.png -1990,102,Wait Until Spring Bandini,Drama,"Mantegna, Joe","Muti, Ornella","Deruddere, Dominique",29,No,NicholasCage.png -1940,105,"Long Voyage Home, The",Drama,"Wayne, John","Natwick, Mildred","Ford, John",88,No,johnWayne.png -1955,100,"Trouble with Harry, The",Mystery,"Forsythe, John","Natwick, Mildred","Hitchcock, Alfred",28,No,alfredHitchcock.png -1987,60,Encounters,Drama,"Von Bergan, Raven","Navarro, Monica","Marder, Bruce",44,No,NicholasCage.png -1963,112,Hud,Drama,"Newman, Paul","Neal, Patricia","Ritt, Martin",2,Yes,paulNewman.png -1951,111,Operation Pacific,War,"Wayne, John","Neal, Patricia",,5,No,johnWayne.png -1987,83,Surf Nazis Must Die,Horror,"Brenner, Barry","Neely, Gail","George, Peter",50,No,NicholasCage.png -1956,124,Teahouse of the August Moon,Drama,"Brando, Marlon","Negami, Jun","Mann, Daniel",11,No,brando.png -1992,88,Back in the U.S.S.R.,Action,"Whaley, Frank","Negoda, Natalya",,61,No,NicholasCage.png -1970,91,"Man Who Haunted Himself, The",Drama,"Moore, Roger","Neil, Hildegard","Dearden, Basil",75,No,NicholasCage.png -1991,108,Prisoner of Honor.,Drama,"Dreyfuss, Richard","Neilson, Catherine","Russell, Ken",58,No,NicholasCage.png -1988,83,Control,Drama,"Lancaster, Burt","Nelligan, Kate",,27,No,burtLancaster.png -1923,57,Desert Rider,Western,"Hoxie, Jack","Nelson, Evelyn","Bradbury, Robert N.",,No,NicholasCage.png -1980,109,Wholly Moses!,Comedy,"Moore, Dudley","Newman, Laraine","Weis, Gary",25,No,NicholasCage.png -1991,110,Star Trek VI: The Undiscovered Country,Science Fiction,"Shatner, William","Nichols, Nichelle","Meyer, Nicholas",11,No,NicholasCage.png -1989,107,Star Trek V: The Final Frontier,Action,"Shatner, William","Nichols, Nichelle","Shatner, William ",87,No,NicholasCage.png -1991,85,Circuitry Man,Action,"Metzler, Jim","Nicholson, Dana W.","Lovy, Steven",78,No,NicholasCage.png -1986,87,Cobra,Action,"Stallone, Sylvester","Nielsen, Brigitte","Cosmatos, George P.",57,No,NicholasCage.png -1987,103,Beverly Hills Cop II,Comedy,"Murphy, Eddie","Nielsen, Brigitte","Scott, Tony",37,No,NicholasCage.png -1990,90,Red Sonja,Action,"Schwarzenegger, Arnold","Nielsen, Brigitte",,40,No,NicholasCage.png -1950,93,To Joy,Drama,"Olin, Stig","Nilsson, Maj-Britt","Bergman, Ingmar",65,No,Bergman.png -1992,112,Macbeth,Drama,"Welles, Orson","Nolan, Jeanette",,45,No,NicholasCage.png -1958,128,Vertigo,Drama,"Stewart, James","Novak, Kim","Hitchcock, Alfred",10,No,alfredHitchcock.png -1987,91,Young Love: Lemon Popsicle Seven,Comedy,"Katzur, Yftach","Noy, Zachi","Bennett, Walter",47,No,NicholasCage.png -1946,93,Crack-Up,Mystery,"Marshall, Herbert","O'Brien, Pat","Reis, Irving",25,No,NicholasCage.png -1941,57,Bury Me Not on the Lone Prairie,Western,"Brown, Johnny Mack","O'Day, Nell","Taylor, Ray",85,No,NicholasCage.png -1940,57,Law & Order,Western,"Brown, Johnny Mack","O'Day, Nell","Taylor, Ray",87,No,NicholasCage.png -1941,56,Man from Montana,Western,"Brown, Johnny Mack","O'Day, Nell","Taylor, Ray",85,No,NicholasCage.png -1992,137,"Long Gray Line, The",Drama,"Power, Tyrone","O'Hara, Maureen","Ford, John",26,No,johnFord.png -1950,105,Rio Grande,Western,"Wayne, John","O'Hara, Maureen","Ford, John",64,No,johnWayne.png -1957,107,"Wings of Eagles, The",Drama,"Wayne, John","O'Hara, Maureen","Ford, John",29,No,johnWayne.png -1939,94,Jamaica Inn,Drama,"Laughton, Charles","O'Hara, Maureen","Hitchcock, Alfred",75,No,alfredHitchcock.png -1971,110,Big Jake,Action,"Wayne, John","O'Hara, Maureen","Sherman, George",68,No,johnWayne.png -1992,153,"Quiet Man, The",Drama,"Wayne, John","O'Hara, Maureen",,74,No,johnWayne.png -1983,72,After the Rehearsal,Drama,"Josephson, Erland","Olin, Lena","Bergman, Ingmar",0,No,Bergman.png -1952,90,Big Jim McLain,Western,"Wayne, John","Olson, Nancy","Ludwig, Edward",14,No,johnWayne.png -1969,101,Smith!,Western,"Ford, Glenn","Olson, Nancy","O'Herlihy, Michael",62,No,glennFord.png -1953,79,"Wild One, The",Drama,"Brando, Marlon","O'Malley, Pat","Benedek, Laslo",26,No,brando.png -1929,129,"Manxman, The",Drama,"Brisson, Carl","Ondra, Anny","Hitchcock, Alfred",65,No,alfredHitchcock.png -1978,126,International Velvet,Drama,"Hopkins, Anthony","O'Neal, Tatum","Forbes, Bryan",40,No,AnthonyHopkins.png -1981,104,Scanners,Horror,"Lack, Stephen","O'Neill, Jennifer","Cronenberg, David",32,No,NicholasCage.png -1986,98,Trick or Treat,Horror,"Price, Marc","Orgolini, Lisa","Smith, Charles Martin",47,No,NicholasCage.png -1982,92,48 Hrs.,Action,"Nolte, Nick","O'Toole, Annette","Hill, Walter",67,No,NicholasCage.png -1985,108,"Trip to Bountiful, The",Drama,"Heard, John","Page, Geraldine","Masterson, Peter",62,Yes,NicholasCage.png -1955,116,Mister Roberts,Comedy,"Fonda, Henry","Palmer, Betsy","Ford, John",8,Yes,johnFord.png -1969,127,Z,Drama,"Montand, Yves","Papas, Irene",Costa-Gavras,72,Yes,NicholasCage.png -1987,139,Maurice,Drama,"Wilby, James","Parfitt, Judy","Ivory, James",45,No,NicholasCage.png -1969,114,Hamlet,Drama,"Williamson, Nicol","Parfitt, Judy","Richardson, Tony",39,No,NicholasCage.png -1991,117,La Femme Nikita,Drama,"Karyo, Tcheky","Parillaud, Anne","Besson, Luc",6,No,NicholasCage.png -1993,95,Honeymoon in Vegas,Comedy,"Caan, James","Parker, Sarah Jessica","Bergman, Andrew",53,No,NicholasCage.png -1988,90,Going for the Gold,Action,"Edwards, Anthony","Parker, Sarah Jessica","Taylor, Dan",10,No,clintEastwood.png -1976,128,Shout at the Devil,Action,"Marvin, Lee","Parkins, Barbara","Hunt, Peter R.",0,No,NicholasCage.png -1986,94,A Smoky Mountain Christmas,Music,"Majors, Lee","Parton, Dolly","Winkler, Henry",23,No,NicholasCage.png -1984,95,Getting Physical,Drama,"Naughton, David","Paul, Alexandra","Stern, Steven Hilliard",75,No,NicholasCage.png -1990,95,Torn Apart,Drama,"Pasdar, Adrian","Peck, Cecilia","Fisher, Jack",8,No,NicholasCage.png -1986,112,From the Hip,Comedy,"Nelson, Judd","Perkins, Elizabeth","Clark, Bob",36,No,NicholasCage.png -1984,102,"Ratings Game, The",Comedy,"DeVito, Danny","Perlman, Rhea","DeVito, Danny",21,No,NicholasCage.png -1992,100,Class Act,Drama,"Reid, Christopher","Perlman, Rhea",,88,No,NicholasCage.png -1986,89,Water,Comedy,"Caine, Michael","Perrine, Valerie","Clement, Dick",47,No,NicholasCage.png -1978,88,Silent Movie,Comedy,"Brooks, Mel","Peters, Bernadette","Brooks, Mel",27,No,NicholasCage.png -1989,122,Pink Cadillac,Comedy,"Eastwood, Clint","Peters, Bernadette","Eastwood, Clint",12,No,clintEastwood.png -1979,94,"Jerk, The",Comedy,"Martin, Steve","Peters, Bernadette","Reiner, Carl",22,No,NicholasCage.png -1980,180,Wild Times,,"Elliott, Sam","Peyser, Penny","Compton, Richard",75,No,NicholasCage.png -1986,107,Sweet Liberty,Comedy,"Alda, Alan","Pfeiffer, Michelle","Alda, Alan",12,No,MichellePfeiffer.png -1982,115,Grease II,Music,"Caulfield, Maxwell","Pfeiffer, Michelle","Birch, Patricia",64,No,MichellePfeiffer.png -1989,104,Married to the Mob,Comedy,"Modine, Matthew","Pfeiffer, Michelle","Demme, Jonathan",8,No,MichellePfeiffer.png -1985,121,Ladyhawke,Adventure,"Broderick, Matthew","Pfeiffer, Michelle","Donner, Richard",68,No,MichellePfeiffer.png -1989,114,"Fabulous Baker Boys, The",Drama,"Bridges, Jeff","Pfeiffer, Michelle","Kloves, Steve",66,No,MichellePfeiffer.png -1985,115,Into the Night,Comedy,"Goldblum, Jeff","Pfeiffer, Michelle","Landis, John",62,No,MichellePfeiffer.png -1991,124,"Russia House, The",Drama,"Connery, Sean","Pfeiffer, Michelle","Schepisi, Fred",3,No,MichellePfeiffer.png -1988,116,Tequila Sunrise,Mystery,"Gibson, Mel","Pfeiffer, Michelle","Towne, Robert",50,No,MichellePfeiffer.png -1989,74,B. A. D. Cats,Action,"Morrow, Vic","Pfeiffer, Michelle",,87,No,MichellePfeiffer.png -1971,108,"Last Movie, The",Drama,"Hopper, Dennis","Phillips, Michelle","Hopper, Dennis",22,No,NicholasCage.png -1973,106,Dillinger,Drama,"Oates, Warren","Phillips, Michelle","Milius, John",83,No,NicholasCage.png -1988,360,Little Dorrit,Drama,"Jacobi, Derek","Pickering, Sarah","Edzard, Christine",12,No,NicholasCage.png -1927,78,My Best Girl,Drama,"Rogers, Charles","Pickford, Mary","Taylor, Sam",31,No,NicholasCage.png -1989,93,Seizure,Horror,"Frid, Jonathan","Pickles, Christina","Stone, Oliver",59,No,NicholasCage.png -1990,89,A Chorus of Disapproval,Comedy,"Irons, Jeremy","Pigg, Alexandra","Winner, Michael",0,No,NicholasCage.png -1962,119,Rome Adventure,Drama,"Donahue, Tony","Pleshette, Suzanne","Daves, Delmer",39,No,NicholasCage.png -1992,121,Drowning by Numbers,Mystery,"Hill, Bernard","Plowright, Joan","Greenaway, Peter",28,No,NicholasCage.png -1991,88,Born to Ride,Action,"Stamos, John","Polo, Teri","Baker, Graham",59,No,NicholasCage.png -1988,94,Her Alibi,Comedy,"Selleck, Tom","Porizkova, Paulina","Beresford, Bruce",80,No,NicholasCage.png -1988,96,Glitz,Mystery,"Smits, Jimmy","Post, Markie",,9,No,NicholasCage.png -1990,95,Dangerous Pursuit,Mystery,"Harrison, Gregory","Powers, Alexandra","Stern, Sandor",88,No,NicholasCage.png -1962,123,Experiment in Terror,Mystery,"Ford, Glenn","Powers, Stefanie","Edwards, Blake",77,No,glennFord.png -1972,105,"Hideaways, The",Comedy,"Doran, Johnny","Prager, Sally","Cook, Fielder",42,No,NicholasCage.png -1965,108,What's New Pussycat,Comedy,"O'Toole, Peter","Prentiss, Paula","Donner, Clive",83,No,NicholasCage.png -1965,108,What's New Pussycat?,Comedy,"Sellers, Peter","Prentiss, Paula","Donner, Clive",46,No,NicholasCage.png -1983,98,Packin' It In,Comedy,"Benjamin, Richard","Prentiss, Paula","Taylor, Jud",8,No,NicholasCage.png -1988,90,"Naked Gun: From the Files of Police Squad!, THe",Comedy,"Nielsen, Leslie","Presley, Priscilla","Zucker, David",9,No,NicholasCage.png -1990,106,In Too Deep,Drama,"Race, Hugo","Press, Santha","Tatoulis, Colin South, John",50,No,NicholasCage.png -1988,107,Twins,Comedy,"Schwarzenegger, Arnold","Preston, Kelly","Reitman, Ivan",23,No,NicholasCage.png -1988,94,"Experts, The",Comedy,"Travolta, John","Preston, Kelly","Thomas, Dave",67,No,NicholasCage.png -1989,94,Naked Lie,Drama,"Lucking, William","Principal, Victoria","Colla, Richard A.",7,No,NicholasCage.png -1987,87,Mistress,Drama,"Rachins, Allan","Principal, Victoria","Tuchner, Michael",36,No,NicholasCage.png -1992,,Pleasure Palace,Action,"Sharif, Omar","Principal, Victoria",,45,No,NicholasCage.png -1970,100,Adam at 6 A.M.,Drama,"Douglas, Michael","Purcell, Lee","Scheerer, Robert",3,No,NicholasCage.png -1990,93,Web of Deceit,Drama,"Read, James","Purl, Linda","Stern, Sandor",6,No,NicholasCage.png -1991,119,New York Stories,Comedy,"Allen, Woody","Questel, Mae","Coppola, Francis Ford",6,No,NicholasCage.png -1987,97,"Dreams Lost, Dreams Found",Drama,"Robb, David","Quinlan, Kathleen","Patterson, Willi",66,No,NicholasCage.png -1987,103,Au Revoir les Enfants,Drama,"Manesse, Gaspard","Racette, Francine","Malle, Louis",35,No,NicholasCage.png -1989,122,Quo Vadis,Drama,"Brandauer, Klaus Maria","Raines, Cristina","Rossi, Franco",6,No,NicholasCage.png -1949,100,"Fighting Kentuckian, The",Action,"Wayne, John","Ralston, Vera","Waggner, George",74,No,johnWayne.png -1974,104,Zardoz,Science Fiction,"Connery, Sean","Rampling, Charlotte","Boorman, John",6,No,seanConnery.png -1989,84,Police Academy 6: City under Siege,Comedy,"Smith, Bubba","Ramsey, Marion","Bonerz, Peter",29,No,NicholasCage.png -1988,90,Police Academy 5: Assignment Miami Beach,Comedy,"Gaynes, George","Ramsey, Marion","Myerson, Alan",59,No,NicholasCage.png -1986,84,Police Academy 3: Back in Training,Comedy,"Guttenberg, Steve","Ramsey, Marion","Paris, Jerry",6,No,NicholasCage.png -1991,60,"America's Music, Blues",Music,"Hopkins, Linda","Redd, Vi","Walton, Kip",54,No,NicholasCage.png -1977,100,Julia,Drama,"Fonda, Vanessa","Redgrave, Jane","Zinnemann, Fred",75,Yes,NicholasCage.png -1971,111,"Devils, The",Drama,"Reed, Oliver","Redgrave, Vanessa","Russell, Ken",69,No,NicholasCage.png -1984,90,Ransom,Drama,"Ford, Glenn","Reed, Donna","Segal, Alex",73,No,glennFord.png -1990,97,Cadillac Man,Comedy,"Williams, Robin","Reed, Pamela","Donaldson, Roger",28,No,NicholasCage.png -1986,104,"Best of Times, The",Comedy,"Williams, Robin","Reed, Pamela","Spottiswoode, Roger",88,No,NicholasCage.png -1985,135,Death of a Salesman,Drama,"Hoffman, Dustin","Reid, Kate","Schlöndorff, Volker",13,No,NicholasCage.png -1993,104,It Started with a Kiss,Drama,"Ford, Glenn","Reynolds, Debbie",,80,No,glennFord.png -1989,88,"Money, The",Drama,"Luckinbill, Laurence","Richards, Elizabeth",,29,No,NicholasCage.png -1987,153,Empire of the Sun,Drama,"Malkovich, John","Richardson, Miranda","Spielberg, Steven",6,No,NicholasCage.png -1991,102,"Comfort of Strangers, The",Mystery,"Walken, Christopher","Richardson, Natasha","Schrader, Paul",5,No,NicholasCage.png -1969,135,On Her Majesty's Secret Service,Action,"Lazenby, George","Rigg, Diana","Hunt, Peter R.",66,No,NicholasCage.png -1986,96,Pretty in Pink,Drama,"Stanton, Harry Dean","Ringwald, Molly","Deutch, Howard",75,No,NicholasCage.png -1987,90,PK. & the Kid.,Drama,"LeMat, Paul","Ringwald, Molly",,49,No,NicholasCage.png -1943,60,"Lone Star Trail, The",Western,"Brown, Johnny Mack","Ritter, Tex","Taylor, Ray",27,No,NicholasCage.png -1986,98,Summer,Comedy,"Gauthier, Vincent","Riviere, Marie","Rohmer, Eric",11,No,NicholasCage.png -1987,93,"Planes, Trains & Automobiles",Comedy,"Martin, Steve","Robbins, Laila","Hughes, John",73,No,NicholasCage.png -1990,119,Pretty Woman,Comedy,"Gere, Richard","Roberts, Julia","Marshall, Garry",43,No,NicholasCage.png -1991,111,Flatliners,Drama,"Sutherland, Kiefer","Roberts, Julia","Schumacher, Joel",19,No,NicholasCage.png -1991,142,Hook,Action,"Williams, Robin","Roberts, Julia","Spielberg, Steven",4,No,NicholasCage.png -1940,56,Riders of Pasco Basin,Western,"Brown, Johnny Mack","Robinson, Frances","Taylor, Ray",17,No,NicholasCage.png -1992,53,"Gotta Dance, Gotta Sing",Music,"Astaire, Fred","Rogers, Ginger",,20,No,NicholasCage.png -1990,106,Desperate Hours,Mystery,"Rourke, Mickey","Rogers, Mimi","Cimino, Michael",58,No,NicholasCage.png -1986,111,Gung Ho,Comedy,"Keaton, Michael","Rogers, Mimi","Howard, Ron",59,No,NicholasCage.png -1992,96,Shooting Elizabeth,Mystery,"Goldblum, Jeff","Rogers, Mimi","Taylor, Baz",5,No,NicholasCage.png -1951,101,Strangers on a Train,Mystery,"Granger, Farley","Roman, Ruth","Hitchcock, Alfred",17,No,alfredHitchcock.png -1979,198,"Sacketts, The",Western,"Elliott, Sam","Roman, Ruth","Totten, Robert",86,No,NicholasCage.png -1991,87,To Die Standing,Action,"De Young, Cliff","Rose, Jamie","Morneau, Louis",53,No,NicholasCage.png -1980,92,Rodeo Girl,Drama,"Hopkins, Bo","Ross, Katharine","Cooper, Jackie",80,No,NicholasCage.png -1969,110,Butch Cassidy & the Sundance Kid,Western,"Newman, Paul","Ross, Katharine","Hill, George Roy",29,Yes,paulNewman.png -1968,121,Hellfighters,Action,"Wayne, John","Ross, Katharine","McLaglen, Andrew V.",22,No,johnWayne.png -1980,92,"Final Countdown, The",Action,"Douglas, Kirk","Ross, Katharine","Taylor, Don",35,No,NicholasCage.png -1986,120,Blue Velvet,Mystery,"MacLachlan, Kyle","Rossellini, Isabella","Lynch, David",6,No,lynch.png -1989,110,Cousins,Comedy,"Danson, Ted","Rossellini, Isabella",,28,No,NicholasCage.png -1976,90,Black & White in Color,Comedy,"Carmet, Jean","Rouvel, Catherine","Annaud, Jean-Jacques",24,Yes,NicholasCage.png -1988,81,Another Woman,Drama,"Hackman, Gene","Rowlands, Gena","Allen, Woody",7,No,woody.png -1992,128,Night on Earth,Drama,"Benigni, Roberto","Rowlands, Gena","Jarmusch, Jim",24,No,NicholasCage.png -1988,92,Permanent Record,Drama,"Boyce, Alan","Rubin, Jennifer","Silver, Marisa",42,No,NicholasCage.png -1992,138,"Fisher King, The",Drama,"Williams, Robin","Ruehl, Mercedes","Gilliam, Terry",8,Yes,NicholasCage.png -1991,98,Another You,Comedy,"Pryor, Richard","Ruehl, Mercedes","Phillips, Maurice",75,No,NicholasCage.png -1958,167,"Young Lions, The",Drama,"Brando, Marlon","Rush, Barbara","Dmytryk, Edward",10,No,NicholasCage.png -1988,89,Cheerleader Camp,Horror,"Garrett, Leif","Russell, Betsy","Quinn, John",79,No,NicholasCage.png -1990,98,Trapper County War,Action,"Hudson, Ernie","Russell, Betsy",,5,No,NicholasCage.png -1947,96,Angel & the Badman,Western,"Wayne, John","Russell, Gail","Grant, James Edward",84,No,johnWayne.png -1990,109,Impulse,Mystery,"Fahey, Jeff","Russell, Theresa","Locke, Sondra",23,No,NicholasCage.png -1988,91,Track Twenty-Nine,Drama,"Oldman, Gary","Russell, Theresa","Roeg, Nicolas",48,No,NicholasCage.png -1991,110,Freejack,Action,"Estevez, Emilio","Russo, Rene","Richardson, Tony",26,No,NicholasCage.png -1939,109,"John Wayne Matinee Double Feature, No. 1",Western,"Wayne, John","Rutherford, Ann",,30,No,johnWayne.png -1988,81,"Smallest Show on Earth, The",Comedy,"Sellers, Peter","Rutherford, Margaret","Dearden, Basil",24,No,NicholasCage.png -1987,120,Innerspace,Science Fiction,"Quaid, Dennis","Ryan, Meg","Dante, Joe",41,No,NicholasCage.png -1988,97,"Presidio, The",Action,"Connery, Sean","Ryan, Meg","Hyams, Peter",4,No,seanConnery.png -1990,102,Joe Versus the Volcano,Comedy,"Hanks, Tom","Ryan, Meg","Patrick, John",17,No,NicholasCage.png -1991,135,"Doors, The",Drama,"Kilmer, Val","Ryan, Meg","Stone, Oliver",60,No,NicholasCage.png -1990,98,"Welcome Home, Roxy Carmichael",Comedy,"Daniels, Jeff","Ryder, Winona","Abrahams, Jim",41,No,NicholasCage.png -1972,99,Cancel My Reservation,Comedy,"Hope, Bob","Saint, Eva Marie","Bogart, Paul",60,No,NicholasCage.png -1991,135,North by Northwest,Mystery,"Grant, Cary","Saint, Eva Marie","Hitchcock, Alfred",20,No,alfredHitchcock.png -1966,127,"Russians Are Coming, the Russians Are, The",Comedy,"Reiner, Carl","Saint, Eva Marie","Jewison, Norman",79,Yes,NicholasCage.png -1992,213,Exodus,Drama,"Newman, Paul","Saint, Eva Marie","Preminger, Otto",13,No,paulNewman.png -1982,128,"Ballad of Narayama, The",Drama,"Ogata, Ken","Sakamoto, Sumiko","Imamura, Shohei",88,No,NicholasCage.png -1985,96,Out of the Darkness,Mystery,"Sheen, Martin","Salt, Jennifer","Taylor, Jud",86,No,NicholasCage.png -1971,90,"Garden of the Finzi-Continis, The",Drama,"Capolicchio, Lino","Sanda, Dominique","De Sica, Vittorio",42,Yes,NicholasCage.png -1974,105,Steppenwolf,Drama,"Sydow, Max von","Sanda, Dominique","Haines, Fred",20,No,NicholasCage.png -1973,100,"Mackintosh Man, The",Action,"Newman, Paul","Sanda, Dominique","Huston, John",65,No,paulNewman.png -1968,105,Partner,Drama,"Clementi, Pierre","Sandrelli, Stefania","Bertolucci, Bernardo",26,No,NicholasCage.png -1970,107,"Conformist, The",Drama,"Trintignant, Jean-Louis","Sandrelli, Stefania","Bertolucci, Bernardo",72,No,NicholasCage.png -1971,102,Dirty Harry,Drama,"Eastwood, Clint","Santoni, Reni","Siegel, Don",72,No,clintEastwood.png -1986,103,Ferris Bueller's Day Off,Comedy,"Broderick, Matthew","Sara, Mia","Hughes, John",12,No,NicholasCage.png -1986,89,Legend,Science Fiction,"Cruise, Tom","Sara, Mia","Scott, Ridley",42,No,NicholasCage.png -1984,110,"Buddy System, The",Drama,"Dreyfuss, Richard","Sarandon, Susan","Jordan, Glenn",48,No,NicholasCage.png -1989,97,A Dry White Season,Drama,"Sutherland, Donald","Sarandon, Susan","Palcy, Euzhan",71,No,NicholasCage.png -1975,105,"Rocky Horror Picture Show, The",Music,"Gray, Charles","Sarandon, Susan","Sharman, Jim",59,No,NicholasCage.png -1968,360,War & Peace,Drama,"Tikhonov, Vyacheslav","Savelyeva, Lyudmila","Bondarchuk, Sergei",80,Yes,NicholasCage.png -1992,96,Defense of the Realm,Drama,"Elliott, Denholm","Scacchi, Greta",,79,No,NicholasCage.png -1991,90,Basil The Rat,Comedy,"Cleese, John","Scales, Prunella",,9,No,NicholasCage.png -1979,90,"Fawlty Towers, Gourmet Night, Waldorf Salad & The Kipper & the Corpse",Comedy,"Cleese, John","Scales, Prunella",,46,No,NicholasCage.png -1991,80,Going Under,Comedy,"Pullman, Bill","Schaal, Wendy","Travis, Mark W.",30,No,NicholasCage.png -1990,83,U S. Sub Standard.,Comedy,"Pullman, Bill","Schaal, Wendy",,27,No,NicholasCage.png -1990,,Hells Angels on Wheels,Action,"Nicholson, Jack","Scharf, Sabrina",,1,No,NicholasCage.png -1975,118,"Passenger, The",Drama,"Nicholson, Jack","Schneider, Maria","Antonioni, Michelangelo",32,No,JackNicholson.png -1973,127,Last Tango in Paris,Drama,"Brando, Marlon","Schneider, Maria","Bertolucci, Bernardo",28,No,brando.png -1987,155,Indigo Autumn & Lilac Dream,Drama,"Singer, Marc","Schrage, Lisa","Gillard, Stuart",72,No,NicholasCage.png -1924,95,"Kriemhild's Revenge, The Nibelungenlied",Drama,"Loos, Theodor","Schön, Margarete","Lang, Fritz",74,No,NicholasCage.png -1966,102,Johnny Tiger,Drama,"Taylor, Robert","Scott, Brenda","Wendkos, Paul",69,No,NicholasCage.png -1986,90,Head Office,Comedy,"Reinhold, Judge","Seymour, Jane","Finkleman, Ken",88,No,NicholasCage.png -1990,,Live & Let Die,Action,"Moore, Roger","Seymour, Jane",,62,No,NicholasCage.png -1972,100,Le Charme Discret de la Bourgeoisie,Comedy,"Rey, Fernando","Seyrig, Delphine","Bunuel, Luis",4,Yes,NicholasCage.png -1986,83,Blue City,Action,"Nelson, Judd","Sheedy, Ally","Manning, Michelle",38,No,NicholasCage.png -1983,123,Bad Boys,Drama,"Penn, Sean","Sheedy, Ally","Rosenthal, Rick",7,No,NicholasCage.png -1986,82,"Whoopee Boys, The",Comedy,"O'Keefe, Michael","Shelley, Carole","Byrum, John",54,No,NicholasCage.png -1971,118,"Last Picture Show, The",Drama,"Bottoms, Timothy","Shepherd, Cybill","Bogdanovich, Peter",62,Yes,NicholasCage.png -1988,93,"Diamond Trap, The",Drama,"Hessman, Howard","Shields, Brooke","Taylor, Don",58,No,NicholasCage.png -1981,115,Endless Love,Drama,"Hewitt, Martin","Shields, Brooke","Zeffirelli, Franco",20,No,NicholasCage.png -1976,90,Rocky,Drama,"Stallone, Sylvester","Shire, Talia","Avildsen, John G.",78,Yes,NicholasCage.png -1988,103,Cocktail,Drama,"Cruise, Tom","Shue, Elisabeth","Donaldson, Roger",13,No,NicholasCage.png -1936,77,Sabotage,Mystery,"Homolka, Oskar","Sidney, Sylvia","Hitchcock, Alfred",74,No,alfredHitchcock.png -1977,105,Madame Rosa,Drama,"Youb, Samy Ben","Signoret, Simone","Mizrahi, Moshe",11,Yes,NicholasCage.png -1985,56,Fozzie's Muppet Scrapbook,Comedy,"Berle, Milton","Sills, Beverly",,86,No,NicholasCage.png -1954,110,Desiree,Drama,"Brando, Marlon","Simmons, Jean","Koster, Henry",22,No,brando.png -1960,185,Spartacus,Drama,"Douglas, Kirk","Simmons, Jean","Kubrick, Stanley",67,Yes,NicholasCage.png -1955,150,Guys & Dolls,Comedy,"Brando, Marlon","Simmons, Jean","Mankiewicz, Joseph L.",70,Yes,brando.png -1992,95,Until They Sail,Drama,"Newman, Paul","Simmons, Jean","Wise, Robert",77,No,paulNewman.png -1988,116,Coming to America,Comedy,"Murphy, Eddie","Sinclair, Madge","Landis, John",11,No,NicholasCage.png -1963,93,Lilies of the Field,Drama,"Poitier, Sidney","Skala, Lilia","Poitier, Sidney",36,Yes,NicholasCage.png -1987,99,River's Edge,Drama,"Glover, Crispin","Skye, Ione","Hunter, Tim",3,No,NicholasCage.png -1986,93,Ruthless People,Comedy,"DeVito, Danny","Slater, Helen","Abrahams, Jim",84,No,NicholasCage.png -1987,110,"Secret of My Success, The",Comedy,"Fox, Michael J.","Slater, Helen","Ross, Herbert",5,No,NicholasCage.png -1965,128,"Shop on Main Street, The",Drama,"Kroner, Josef","Slivoka, Hana","Kadar, Jan",37,Yes,NicholasCage.png -1988,101,Funny Farm,Comedy,"Chase, Chevy","Smith, Madolyn","Hill, George Roy",30,No,NicholasCage.png -1988,120,"Lonely Passion of Judith Hearne, The",Drama,"Hoskins, Bob","Smith, Maggie","Clayton, Jack",24,No,NicholasCage.png -1978,103,California Suite,Comedy,"Caine, Michael","Smith, Maggie","Ross, Herbert",11,Yes,NicholasCage.png -1986,97,Maximum Overdrive,Horror,"Estevez, Emilio","Smith, Yeardley","King, Stephen",40,No,NicholasCage.png -1985,116,Pale Rider,Western,"Eastwood, Clint","Snodgress, Carrie","Eastwood, Clint",45,No,clintEastwood.png -1990,88,"Kissing Place, The",Drama,"Birney, Meredith Baxter","Snow, Victoria","Wharmby, Tony",41,No,NicholasCage.png -1986,90,French Lesson,Comedy,"Sterling, Alexandre","Snowden, Jane","Gilbert, Brian",29,No,NicholasCage.png -1985,88,Roller Blade,Action,"Hutchinson, Jeff","Solari, Suzanne","Jackson, Donald G",31,No,NicholasCage.png -1964,101,A Shot in the Dark,Comedy,"Sellers, Peter","Sommer, Elke","Edwards, Blake",51,No,NicholasCage.png -1979,88,"Treasure Seekers, The",Action,"Whitman, Stuart","Sommer, Elke",,2,No,NicholasCage.png -1982,122,Missing,Drama,"Lemmon, Jack","Spacek, Sissy",Costa-Gavras,30,No,NicholasCage.png -1989,99,Picasso Trigger,Action,"Bond, Steve","Speir, Dona","Sidaris, Andy",20,No,NicholasCage.png -1987,97,Hard Ticket to Hawaii,Action,"Moss, Ronn","Speir, Dona","Sidaris, Andy",36,No,NicholasCage.png -1990,,Diamonds are Forever,Action,"Connery, Sean","St. John, Jill","Hamilton, Guy",8,No,seanConnery.png -1933,72,Baby Face,Drama,"Brent, George","Stanwyck, Barbara","Green, Alfred E.",66,No,NicholasCage.png -1992,95,"Violent Men, The",Action,"Ford, Glenn","Stanwyck, Barbara","Mate, Rudolph",25,No,glennFord.png -1985,117,Cocoon,Science Fiction,"Ameche, Don","Stapleton, Maureen","Howard, Ron",45,Yes,NicholasCage.png -1986,96,Clockwise,Comedy,"Cleese, John","Steadman, Alison","Morahan, Christopher",10,No,NicholasCage.png -1993,103,Romantic Comedy,Comedy,"Moore, Dudley","Steenburgen, Mary",,8,No,NicholasCage.png -1981,111,Outland,Science Fiction,"Connery, Sean","Sternhagen, Frances","Hyams, Peter",7,No,seanConnery.png -1967,114,Hang 'em High,Western,"Eastwood, Clint","Stevens, Inger","Post, Ted",67,No,clintEastwood.png -1992,123,Basic Instinct,Mystery,"Douglas, Michael","Stone, Sharon","Verhoeven, Paul",41,No,NicholasCage.png -1990,113,Total Recall,Action,"Schwarzenegger, Arnold","Stone, Sharon","Verhoeven, Paul",8,No,NicholasCage.png -1987,115,Stakeout,Comedy,"Dreyfuss, Richard","Stowe, Madeleine","Badham, John",13,No,NicholasCage.png -1992,104,"Unnamable II, The Statement of Randolph Carter, The",Drama,"Rhys-Davies, John","Strain, Julie","Ouellette, Jean-Paul",36,No,NicholasCage.png -1967,85,"Trip, The",Drama,"Fonda, Peter","Strasberg, Susan","Corman, Roger",64,No,NicholasCage.png -1987,135,Ironweed,Drama,"Nicholson, Jack","Streep, Meryl","Babenco, Hector",32,No,merylStreep.png -1979,,Kramer vs. Kramer,Drama,"Hoffman, Dustin","Streep, Meryl","Benton, Robert",8,Yes,merylStreep.png -1988,,Still of the Night,Mystery,"Scheider, Roy","Streep, Meryl","Benton, Robert",42,No,merylStreep.png -1991,112,Defending Your Life,Comedy,"Brooks, Albert","Streep, Meryl","Brooks, Albert",75,No,merylStreep.png -1978,183,"Deer Hunter, The",Drama,"De Niro, Robert","Streep, Meryl","Cimino, Michael",82,Yes,merylStreep.png -1984,106,Falling in Love,Drama,"De Niro, Robert","Streep, Meryl","Grosbard, Ulu",31,No,merylStreep.png -1986,108,Heartburn,Comedy,"Nicholson, Jack","Streep, Meryl","Nichols, Mike",57,No,JackNicholson.png -1983,131,Silkwood,Drama,"Russell, Kurt","Streep, Meryl","Nichols, Mike",52,No,merylStreep.png -1982,151,Sophie's Choice,Drama,"Kline, Kevin","Streep, Meryl","Pakula, Alan J.",64,Yes,merylStreep.png -1985,161,Out of Africa,Drama,"Redford, Robert","Streep, Meryl","Pollack, Sydney",88,Yes,merylStreep.png -1981,127,"French Lieutenant's Woman, The",Drama,"Irons, Jeremy","Streep, Meryl","Reisz, Karel",37,No,merylStreep.png -1985,124,Plenty,Drama,"Dance, Charles","Streep, Meryl","Schepisi, Fred",9,No,merylStreep.png -1988,122,A Cry in the Dark,Drama,"Neill, Sam","Streep, Meryl","Schepisi, Fred",67,No,merylStreep.png -1989,99,She-Devil,Comedy,"Begley, Ed, Jr.","Streep, Meryl","Seidelman, Susan",43,No,merylStreep.png -1992,103,Death Becomes Her,Drama,"Willis, Bruce","Streep, Meryl","Zemeckis, Robert",61,No,merylStreep.png -1991,28,Kids & Pesticides,Drama,"Whyatt, Robin","Streep, Meryl",,36,No,merylStreep.png -1970,129,On a Clear Day You Can See Forever,Music,"Montand, Yves","Streisand, Barbra","Minnelli, Vincente",67,No,NicholasCage.png -1987,100,Nuts,Drama,"Dreyfuss, Richard","Streisand, Barbra","Ritt, Martin",52,No,NicholasCage.png -1983,134,Yentl,Music,"Patinkin, Mandy","Streisand, Barbra","Streisand, Barbra",46,No,NicholasCage.png -1968,151,Funny Girl,Music,"Sharif, Omar","Streisand, Barbra","Wyler, William",30,Yes,NicholasCage.png -1990,97,Fellow Traveller,Drama,"Travanti, Daniel J.","Stubbs, Imogen","Towns, Philip Saville",39,No,NicholasCage.png -1970,140,Dodesukaden,Drama,"Zushi, Yoshitaka","Sugai, Kin","Kurosawa, Akira",75,No,NicholasCage.png -1987,,"Sicilian, The",Drama,"Lambert, Christopher","Sukowa, Barbara","Cimino, Michael",41,No,NicholasCage.png -1941,117,So Ends Our Night,Drama,"March, Fredric","Sullavan, Margaret","Cromwell, John",2,No,NicholasCage.png -1984,102,Sword of the Valiant,Action,"O'Keeffe, Miles","Sutton, Emma","Weeks, Stephen",5,No,NicholasCage.png -1949,78,"Devil's Wanton, The",Drama,"Malmsten, Birger","Svedlund, Doris","Bergman, Ingmar",66,No,Bergman.png -1989,99,Driving Miss Daisy,Drama,"Freeman, Morgan","Tandy, Jessica","Beresford, Bruce",6,Yes,NicholasCage.png -1991,111,"Seventh Cross, The",Drama,"Tracy, Spencer","Tandy, Jessica",,35,No,spencerTracy.png -1983,105,Between Friends,Drama,"Ramer, Henry","Taylor, Elizabeth","Antonio, Lou",54,No,elizabethTaylor.png -1957,173,Raintree County,Drama,"Clift, Montgomery","Taylor, Elizabeth","Dmytryk, Edward",74,No,elizabethTaylor.png -1975,101,"Driver's Seat, The",Drama,"Bannen, Ian","Taylor, Elizabeth","Griffi, Giuseppe Patroni",72,No,elizabethTaylor.png -1967,109,Reflections in a Golden Eye,Drama,"Brando, Marlon","Taylor, Elizabeth","Huston, John",81,No,elizabethTaylor.png -1972,110,"X, Y & Zee",Drama,"Caine, Michael","Taylor, Elizabeth","Hutton, Brian G.",87,No,elizabethTaylor.png -1968,109,Secret Ceremony,Drama,"Mitchum, Robert","Taylor, Elizabeth","Losey, Joseph",60,No,elizabethTaylor.png -1963,243,Cleopatra,Drama,"Burton, Richard","Taylor, Elizabeth","Mankiewicz, Joseph L.",80,No,elizabethTaylor.png -1950,,Father of the Bride,Comedy,"Taylor, Rod","Taylor, Elizabeth","Minnelli, Vincente",54,No,elizabethTaylor.png -1992,130,Who's Afraid of Virginia Woolf?,Drama,"Burton, Richard","Taylor, Elizabeth","Nichols, Mike",82,Yes,elizabethTaylor.png -1977,110,A Little Night Music,Music,"Cariou, Len","Taylor, Elizabeth","Prince, Harold",61,No,elizabethTaylor.png -1956,201,Giant,Drama,"Hudson, Rock","Taylor, Elizabeth","Stevens, George",61,Yes,elizabethTaylor.png -1985,94,"Rumor Mill, The",Drama,"Dysart, Richard A.","Taylor, Elizabeth","Trikonis, Gus",62,No,elizabethTaylor.png -1943,90,Lassie Come Home,Drama,"McDowall, Roddy","Taylor, Elizabeth","Wilcox, Fred M",79,No,elizabethTaylor.png -1993,76,Return Engagement,Drama,"Bottoms, Joseph","Taylor, Elizabeth",,26,No,elizabethTaylor.png -1972,108,Hammersmith Is Out,Drama,"Burton, Richard","Taylor, Elizabeth",,80,No,elizabethTaylor.png -1991,60,Super Duper Bloopers,Comedy,"Cooper, Gary","Taylor, Elizabeth",,21,No,elizabethTaylor.png -1991,,"Elizabeth Taylor Collection, The",Drama,"Fisher, Eddie","Taylor, Elizabeth",,21,No,elizabethTaylor.png -1973,99,Ash Wednesday,Drama,"Fonda, Henry","Taylor, Elizabeth",,54,No,elizabethTaylor.png -1991,117,"Last Time I Saw Paris, The",Drama,"Johnson, Van","Taylor, Elizabeth",,13,No,elizabethTaylor.png -1931,125,Cimarron,Western,"Dix, Richard","Taylor, Estelle","Ruggles, Wesley",44,Yes,NicholasCage.png -1992,83,Apache Woman,Western,"Bridges, Lloyd","Taylor, Joan","Corman, Roger",32,No,NicholasCage.png -1984,,Gary Numan - Berzerker,Music,"Webb, John","Taylor, Karen",,60,No,NicholasCage.png -1988,101,Mystic Pizza,Comedy,"Moses, William","Taylor, Lili","Petrie, Donald",74,No,NicholasCage.png -1991,95,Dogfight,Action,"Phoenix, River","Taylor, Lili","Savoca, Nancy",66,No,NicholasCage.png -1935,234,"Adventures of Rex & Rinty, The",Western,Rex the Wonder Horse,"Taylor, Norma","Beebe, Ford",87,No,NicholasCage.png -1988,60,Daphnis & Chloe,Music,"Morrow, Carl","Taylor, Victoria","Wimhurst, Jolyon",85,No,NicholasCage.png -1980,97,Marathon,Comedy,"Newhart, Bob","Taylor-Young, Leigh","Cooper, Jackie",76,No,NicholasCage.png -1948,127,Fort Apache,Western,"Fonda, Henry","Temple, Shirley","Ford, John",4,No,johnFord.png -1937,100,Wee Willie Winkie,Drama,"Romero, Cesar","Temple, Shirley","Ford, John",78,No,johnFord.png -1987,91,Big Shots,Action,"Busker, Ricky","Thayer, Brynn","Mandel, Robert",5,No,NicholasCage.png -1988,85,Doin' Time on Planet Earth,Comedy,"Strouse, Nocholas","Thompson, Andrea","Matthau, Charles",44,Yes,NicholasCage.png -1983,91,All the Right Moves,Drama,"Cruise, Tom","Thompson, Lea","Chapman, Michael",65,No,NicholasCage.png -1987,93,Some Kind of Wonderful,Drama,"Stoltz, Eric","Thompson, Lea","Deutch, Howard",16,No,NicholasCage.png -1990,87,"All New Tales from the Crypt, A Trilogy",Horror,"Walsh, M. Emmet","Thompson, Lea","Deutch, Howard",33,No,NicholasCage.png -1985,116,Back to the Future,Comedy,"Fox, Michael J.","Thompson, Lea","Zemeckis, Robert",9,No,NicholasCage.png -1963,80,Winter Light,Drama,"Björnstrand, Gunnar","Thulin, Ingrid","Bergman, Ingmar",2,No,Bergman.png -1963,95,"Silence, The",Drama,"Malmsten, Birger","Thulin, Ingrid","Bergman, Ingmar",79,No,Bergman.png -1959,100,"Magician, The",Drama,"Sydow, Max von","Thulin, Ingrid","Bergman, Ingmar",3,No,Bergman.png -1961,154,"Four Horsemen of the Apocalypse, The",Drama,"Ford, Glenn","Thulin, Ingrid","Minnelli, Vincente",71,No,glennFord.png -1986,99,Critical Condition,Comedy,"Pryor, Richard","Ticotin, Rachel","Apted, Michael",41,No,NicholasCage.png -1989,88,Center of the Web,Mystery,"Curtis, Tony","Tilton, Charlene",,42,No,NicholasCage.png -1990,110,Border Shootout,Action,"Ford, Glenn","Tilton, Charlene",,7,No,glennFord.png -1989,109,Lean on Me,Drama,"Freeman, Morgan","Todd, Beverly","Avildsen, John G.",51,No,NicholasCage.png -1986,221,On Wings of Eagles,Drama,"Lancaster, Burt","Towers, Constance","McLaglen, Andrew V.",53,No,burtLancaster.png -1941,94,Texas,Western,"Holden, William","Trevor, Claire","Marshall, George",79,No,NicholasCage.png -1939,80,Allegheny Uprising,Drama,"Wayne, John","Trevor, Claire","Seiter, William A.",53,No,johnWayne.png -1940,95,Dark Command,Western,"Wayne, John","Trevor, Claire","Walsh, Raoul",52,No,johnWayne.png -1986,103,Peggy Sue Got Married,Drama,"Cage, Nicolas","Turner, Kathleen","Coppola, Francis Ford",62,No,NicholasCage.png -1989,84,"Dear America, Letters Home from Vietnam",War,"De Niro, Robert","Turner, Kathleen","Couturie, Bill",57,No,NicholasCage.png -1985,130,Prizzi's Honor,Comedy,"Nicholson, Jack","Turner, Kathleen","Huston, John",25,Yes,JackNicholson.png -1983,90,"Man with Two Brains, The",Comedy,"Martin, Steve","Turner, Kathleen","Reiner, Carl",68,No,NicholasCage.png -1984,101,Crimes of Passion,Drama,"Perkins, Anthony","Turner, Kathleen","Russell, Ken",4,No,NicholasCage.png -1985,106,"Jewel of the Nile, The",Action,"Douglas, Michael","Turner, Kathleen","Teague, Lewis",68,No,NicholasCage.png -1984,106,Romancing the Stone,Action,"Douglas, Michael","Turner, Kathleen","Zemeckis, Robert ",83,No,NicholasCage.png -1988,121,"Accidental Tourist, The",Comedy,"Hurt, William","Turner, Kathleen",,56,Yes,NicholasCage.png -1955,117,"Sea Chase, The",War,"Wayne, John","Turner, Lana","Farrow, John",4,No,johnWayne.png -1958,98,"Another Time, Another Place",Drama,"Connery, Sean","Turner, Lana",,4,No,seanConnery.png -1988,90,Cannibal Women in the Avocado Jungle of Death,Comedy,"Primus, Barry","Tweed, Shannon","Lawton, J.F.",56,No,NicholasCage.png -1986,91,Mr Love.,Comedy,"Jackson, Barry","Tyzack, Margaret","Battersby, Roy",10,No,NicholasCage.png -1968,139,2001: A Space Odyssey,Science Fiction,"Dullea, Keir","Tyzack, Margaret","Kubrick, Stanley",83,No,NicholasCage.png -1966,81,Persona,Drama,"Björnstrand, Gunnar","Ullman, Liv","Bergman, Ingmar",81,Yes,Bergman.png -1973,,Scenes from a Marriage,Drama,"Josephson, Erland","Ullman, Liv","Bergman, Ingmar",3,Yes,Bergman.png -1968,88,Hour of the Wolf,Drama,"Sydow, Max von","Ullman, Liv","Bergman, Ingmar",37,No,Bergman.png -1969,101,"Passion of Anna, The",Drama,"Sydow, Max von","Ullman, Liv","Bergman, Ingmar",6,No,Bergman.png -1984,96,Dangerous Moves,Drama,"Caron, Leslie","Ullman, Liv","Dembo, Richard",7,Yes,NicholasCage.png -1957,147,Sayonara,Drama,"Brando, Marlon","Umeki, Miyoshi","Logan, Joshua",19,Yes,brando.png -1968,158,Where Eagles Dare,War,"Burton, Richard","Ure, Mary","Hulton, Brian G.",57,No,NicholasCage.png -1985,95,Teen Wolf,Drama,"Fox, Michael J.","Ursitti, Susan","Daniel, Rod",58,No,NicholasCage.png -1990,88,Amazon,Action,"Davi, Robert","Vaananen, Kari","Kaurismäki, Mika",30,No,NicholasCage.png -1973,,"Paper Chase, The",Drama,"Bottoms, Timothy","Wagner, Lindsay","Bridges, James",7,Yes,NicholasCage.png -1959,88,"Virgin Spring, The",Drama,"Sydow, Max von","Valberg, Brigitta","Bergman, Ingmar",8,Yes,Bergman.png -1970,97,Spider's Stratagem,Drama,"Brogi, Giulio","Valli, Alida","Bertolucci, Bernardo",45,No,NicholasCage.png -1971,102,Play Misty for Me,Mystery,"Eastwood, Clint","Walter, Jessica","Eastwood, Clint",47,No,clintEastwood.png -1981,88,Going Ape,Comedy,"Danza, Tony","Walter, Jessica","Kronsberg, Jeremy Joe",65,No,NicholasCage.png -1967,127,Cool Hand Luke,Drama,"Newman, Paul","Van Fleet, Jo","Rosenberg, Stuart",49,Yes,paulNewman.png -1988,89,Phantom of the Ritz,Horror,"Bergman, Peter","Van Valkenburgh, Deborah","Plone, Allen",85,No,NicholasCage.png -1990,85,Crash & Burn,Science Fiction,"Ganus, Paul","Ward, Megan","Band, Charles",75,No,NicholasCage.png -1991,114,After Dark My Sweet,Mystery,"Patric, Jason","Ward, Rachel","Foley, James",33,No,NicholasCage.png -1992,121,Christopher Columbus: The Discovery,Adventure,"Brando, Marlon","Ward, Rachel","Glen, John",39,No,NicholasCage.png -1986,109,Young Sherlock Holmes,Mystery,"Rowe, Nicholas","Ward, Sophie","Levinson, Barry",16,No,NicholasCage.png -1991,104,Doc Hollywood,Comedy,"Fox, Michael J.","Warner, Julie","Caton-Jones, Michael",64,No,NicholasCage.png -1988,96,Baja Oklahoma,Comedy,"Coyote, Peter","Warren, Lesley Ann","Roth, Bobby",71,No,NicholasCage.png -1986,137,Aliens,Science Fiction,"Biehn, Michael","Weaver, Sigourney","Cameron, James",82,No,weaver.png -1992,115,Alien Three,Science Fiction,"Dutton, Charles","Weaver, Sigourney","Fincher, David",59,No,weaver.png -1997,109,Alien: resurrection,Science Fiction,"Perlman, Ron","Weaver, Sigourney","Jeunet, Jean-Pierre",60,No,weaver.png -1979,117,Alien,Science Fiction,"Skerritt, Tom","Weaver, Sigourney","Scott, Ridley",83,No,weaver.png -1985,97,One Woman or Two,Comedy,"Depardieu, Gérard","Weaver, Sigourney","Vigne, Daniel",64,No,weaver.png -1984,96,Soggy Bottom U. S. A.,Comedy,"Johnson, Ben","Wedgeworth, Ann","Flicker, Theodore J.",50,No,NicholasCage.png -1973,96,Bang the Drum Slowly,Drama,"Moriarty, Michael","Wedgeworth, Ann","Hancock, John D.",73,No,NicholasCage.png -1974,82,"Catamount Killing, The",Action,"Buchholz, Horst","Wedgeworth, Ann","Zanussi, Krzystoff",84,No,NicholasCage.png -1972,92,Fuzz,Action,"Reynolds, Burt","Welch, Raquel","Colla, Richard A.",37,No,NicholasCage.png -1966,101,"Shoot Loud, Louder, I Don't Understand!",Mystery,"Mastroianni, Marcello","Welch, Raquel","De Filippo, Eduardo",70,No,NicholasCage.png -1967,107,Bedazzled,Comedy,"Cook, Peter","Welch, Raquel","Donen, Stanley",67,No,NicholasCage.png -1977,120,"Prince & the Pauper, The",Action,"Reed, Oliver","Welch, Raquel","Fleischer, Richard",86,No,NicholasCage.png -1969,110,One Hundred Rifles,Western,"Reynolds, Burt","Welch, Raquel","Gries, Tom",48,No,NicholasCage.png -1975,90,"Wild Party, The",Drama,"Dukes, David","Welch, Raquel","Ivory, James",75,No,NicholasCage.png -1968,106,Bandolero!,Western,"Stewart, James","Welch, Raquel","McLaglen, Andrew V.",9,No,NicholasCage.png -1973,119,"Last of Sheila, The",Mystery,"Coburn, James","Welch, Raquel","Ross, Herbert",39,No,NicholasCage.png -1972,87,Hannie Caulder,Drama,"Borgnine, Ernest","Welch, Raquel",,9,No,NicholasCage.png -1990,,Sounds of the Seventies...& the Beat Goes,Music,"Jones, Tom","Welch, Raquel",,13,No,NicholasCage.png -1988,161,Bird,Drama,"Whitaker, Forest","Venora, Diane","Eastwood, Clint",24,No,NicholasCage.png -1955,60,Meet Millie,Drama,"Halop, Florence","Verdugo, Elena",,82,No,NicholasCage.png -1987,88,Hell Comes to Frogtown,Science Fiction,"LeFlore, Julius","Verrell, Cec","Jackson, Donald G",74,No,NicholasCage.png -1966,126,"Fortune Cookie, The",Comedy,"Lemmon, Jack","West, Judi","Wilder, Billy",3,Yes,NicholasCage.png -1990,92,"Sun Shines Bright, The",Action,"Winninger, Charles","Whelan, Arleen","Ford, John",46,No,johnFord.png -1987,106,"Squeeze, The",Action,"Keach, Stacy","White, Carol","Apted, Michael",23,No,NicholasCage.png -1970,91,Start the Revolution Without Me,Comedy,"Wilder, Gene","Whitelaw, Billie","Yorkin, Bud",62,No,NicholasCage.png -1989,107,Major League,Comedy,"Sheen, Charlie","Whitton, Margaret","Ward, David S.",64,No,NicholasCage.png -1990,108,"Bright Lights, Big City",Drama,"Fox, Michael J.","Wiest, Dianne","Bridges, James",30,No,NicholasCage.png -1987,97,"Lost Boys, The",Horror,"Patric, Jason","Wiest, Dianne","Schumacher, Joel",67,No,NicholasCage.png -1989,93,Cookie,Comedy,"Falk, Peter","Wiest, Dianne","Seidelman, Susan",43,No,NicholasCage.png -1974,114,"Conversation, The",Drama,"Hackman, Gene","Williams, Cindy","Coppola, Francis Ford",59,Yes,NicholasCage.png -1973,112,American Graffiti,Comedy,"Dreyfuss, Richard","Williams, Cindy","Lucas, George",39,Yes,NicholasCage.png -1953,96,Dangerous When Wet,Music,"Lamas, Fernando","Williams, Esther","Walters, Charles",67,No,NicholasCage.png -1980,111,Stir Crazy,Comedy,"Pryor, Richard","Williams, JoBeth","Poitier, Sidney",40,No,NicholasCage.png -1989,91,Young Einstein,Comedy,"Serious, Yahoo","Wilson, Pee-Wee","Serious, Yahoo",47,No,NicholasCage.png -1956,83,"Killing, The",Drama,"Hayden, Sterling","Windsor, Marie","Kubrick, Stanley",51,No,NicholasCage.png -1973,102,"Cahill, United States Marshal",Western,"Wayne, John","Windsor, Marie","McLaglen, Andrew V.",12,No,johnWayne.png -1989,90,"Savage Intruder, The",Horror,"Garfield, John David","Wing, Virginia","Wolfe, Donald",24,No,NicholasCage.png -1992,139,"Sheltering Sky, The",Drama,"Malkovich, John","Winger, Debra","Bertolucci, Bernardo",64,No,NicholasCage.png -1982,125,An Officer & a Gentleman,Drama,"Gere, Richard","Winger, Debra","Hackford, Taylor",1,Yes,NicholasCage.png -1987,101,Black Widow,Mystery,"Hopper, Dennis","Winger, Debra","Rafelson, Bob",54,No,NicholasCage.png -1986,116,Legal Eagles,Comedy,"Redford, Robert","Winger, Debra","Reitman, Ivan",39,No,NicholasCage.png -1970,90,Bloody Mama,Action,"Stroud, Don","Winters, Shelley","Corman, Roger",17,No,NicholasCage.png -1965,106,A Patch of Blue,Drama,"Poitier, Sidney","Winters, Shelley","Green, Guy",51,No,NicholasCage.png -1955,109,I Died a Thousand Times,Drama,"Palance, Jack","Winters, Shelley","Heisler, Stuart",23,No,NicholasCage.png -1977,90,Tentacles,Horror,"Huston, John","Winters, Shelley","Hellman, Oliver",62,No,NicholasCage.png -1968,100,"Scalphunters, The",Western,"Lancaster, Burt","Winters, Shelley","Pollack, Sydney",33,No,burtLancaster.png -1992,96,A Day in October,Drama,"Sweeney, D. B.","Wolf, Kelly","Madsen, Kenneth",76,No,NicholasCage.png -1964,102,A Fistful of Dollars,Westerns,"Eastwood, Clint","Volonte, Gian Maria","Leone, Sergio",61,No,clintEastwood.png -1985,94,My Science Project,Comedy,"Stockwell, John","Von Zerneck, Danielle","Betnel, Jonathan",84,No,NicholasCage.png -1991,160,"Great Race, The",Comedy,"Moore, Dudley","Wood, Natalie","Edwards, Blake",88,No,NicholasCage.png -1956,119,"Searchers, The",Western,"Wayne, John","Wood, Natalie","Ford, John",9,No,johnWayne.png -1979,105,Meteor,Action,"Connery, Sean","Wood, Natalie","Neame, Ronald",5,No,seanConnery.png -1955,111,Rebel Without a Cause,Drama,"Dean, James","Wood, Natalie","Ray, Nicholas",82,No,NicholasCage.png -1961,153,West Side Story,Music,"Beymer, Richard","Wood, Natalie","Wise, Robert",38,Yes,NicholasCage.png -1970,110,Trash,Comedy,"Dallesandro, Joe","Woodlawn, Holly","Morrissey, Paul",68,No,NicholasCage.png -1966,95,A Big Hand for the Little Lady,Comedy,"Fonda, Henry","Woodward, Joanne","Cook, Fielder",12,No,NicholasCage.png -1966,104,A Fine Madness,Comedy,"Connery, Sean","Woodward, Joanne","Kershner, Irvin",6,No,seanConnery.png -1987,134,"Glass Menagerie, The",Drama,"Malkovich, John","Woodward, Joanne","Newman, Paul",68,No,NicholasCage.png -1989,117,Harry & Son,Drama,"Newman, Paul","Woodward, Joanne","Newman, Paul",57,No,paulNewman.png -1968,102,"Rachel, Rachel",Drama,"Olson, James","Woodward, Joanne","Newman, Paul",32,No,NicholasCage.png -1961,98,Paris Blues,Drama,"Newman, Paul","Woodward, Joanne","Ritt, Martin",54,No,paulNewman.png -1960,135,"Fugitive Kind, The",Drama,"Brando, Marlon","Woodward, Joanne",,3,No,brando.png -1993,,Mr. & Mrs. Bridge,Drama,"Newman, Paul","Woodward, Joanne",,29,No,paulNewman.png -1991,144,State of Grace,Drama,"Penn, Sean","Wright, Robin","Joanou, Phil",49,No,NicholasCage.png -1943,108,Shadow of a Doubt,Drama,"Cotten, Joseph","Wright, Teresa","Hitchcock, Alfred",32,No,alfredHitchcock.png -1950,85,"Men, The",Drama,"Brando, Marlon","Wright, Teresa","Zinnemann, Fred",27,No,brando.png -1950,110,Stage Fright,Mystery,"Wilding, Michael","Wyman, Jane","Hitchcock, Alfred",72,No,alfredHitchcock.png -1947,103,Magic Town,Drama,"Stewart, James","Wyman, Jane","Wellman, William",4,No,NicholasCage.png -1975,93,That Lucky Touch,Action,"Moore, Roger","York, Susannah","Miles, Christopher",85,No,NicholasCage.png -1949,90,Lust for Gold,Drama,"Ford, Glenn","Young, Gig","Simon, S. Sylvan",57,No,glennFord.png -1987,103,Heat,Mystery,"Reynolds, Burt","Young, Karen","Jameson, Jerry",69,No,NicholasCage.png -1993,75,Employee's Entrance,Drama,"William, Warren","Young, Loretta",,0,No,NicholasCage.png -1947,87,Night Is My Future,Drama,"Malmsten, Birger","Zetterling, Mai","Bergman, Ingmar",17,No,Bergman.png -1990,92,"Witches, The",Science Fiction,"Fisher, Jasen","Zetterling, Mai","Roeg, Nicolas",18,No,NicholasCage.png -1953,94,Vera Cruz,Action,"Cooper, Gary",,"Aldrich, Robert",71,No,NicholasCage.png -1954,91,Apache,Western,"Lancaster, Burt",,"Aldrich, Robert",78,No,burtLancaster.png -1977,146,Twilight's Last Gleaming,Drama,"Lancaster, Burt",,"Aldrich, Robert",84,No,burtLancaster.png -1979,119,"Frisco Kid, The",Comedy,"Wilder, Gene",,"Aldrich, Robert",10,No,NicholasCage.png -1954,30,Bank on the Stars,Drama,"Paar, Jack",,"Allen, Craig",,No,NicholasCage.png -1987,100,Law of Desire,Drama,"Maura, Carmen",,"Almodóvar, Pedro",73,No,NicholasCage.png -1966,103,"Quiller Memorandum, The",Mystery,"Segal, George",,"Anderson, Michael",34,No,NicholasCage.png -1962,183,"Longest Day, The",War,"Wayne, John",,"Annakin, Ken",7,No,johnWayne.png -1986,128,"Name of the Rose, The",Drama,"Connery, Sean",,"Annaud, Jean-Jacques",8,No,seanConnery.png -1988,92,Bloodsport,Action,"Van Damme, Jean-Claude",,"Arnold, Newt",78,No,NicholasCage.png -1986,85,Torment,Horror,"Gilbert, Taylor",,"Aslanian, Samson",8,No,NicholasCage.png -1988,138,Pelle the Conqueror,Drama,"Sydow, Max von",,"August, Bille",14,Yes,NicholasCage.png -1981,118,Taps,Drama,"Hutton, Timothy",,"Becker, Harold",84,No,NicholasCage.png -1991,102,"Freshman, The",Comedy,"Brando, Marlon",,"Bergman, Andrew",32,No,brando.png -1987,164,"Last Emperor, The",Drama,"Lone, John",,"Bertolucci, Bernardo",1,Yes,NicholasCage.png -1962,100,"Grim Reaper, The",Drama,"Rulu, Francesco",,"Bertolucci, Bernardo",35,No,NicholasCage.png -1983,90,Le Dernier Combat,Drama,"Jolivet, Pierre",,"Besson, Luc",72,No,NicholasCage.png -1989,91,Too Beautiful for You,Drama,"Depardieu, Gérard",,"Blier, Bertrand",35,No,NicholasCage.png -1991,105,"Fire, Ice & Dynamite",Action,"Moore, Roger",,"Bogner, Willy",72,No,NicholasCage.png -1963,113,Heavens Above,Comedy,"Sellers, Peter",,"Boulting, John",38,No,NicholasCage.png -1961,141,One Eyed Jacks,Western,"Malden, Karl",,"Brando, Marlon",26,No,brando.png -1937,61,"Swing It, Sailor!",Comedy,"Ford, Wallace",,"Cannon, Raymond",83,No,NicholasCage.png -1987,94,"Wolf at the Door, The",Drama,"Sutherland, Donald",,"Carlsen, Henning",68,No,NicholasCage.png -1936,87,Modern Times,Comedy,"Chaplin, Charles",,"Chaplin, Charles",4,No,NicholasCage.png -1991,114,Thunderbolt & Lightfoot,Action,"Eastwood, Clint",,"Cimino, Michael",16,No,clintEastwood.png -1931,87,A Nous la Liberte,Drama,"Marchand, Henri",,"Clair, Rene",60,No,NicholasCage.png -1979,95,Scum,Action,"Winstone, Ray",,"Clarke, Alan",68,No,NicholasCage.png -1984,90,"Inside Man, The",Action,"Hopper, Dennis",,"Clegg, Tom",45,No,NicholasCage.png -1979,153,Apocalypse Now,Drama,"Brando, Marlon",,"Coppola, Francis Ford",8,No,brando.png -1990,94,"Bellboy & the Playgirls, The",Drama,"Wilkinson, June",,"Coppola, Francis Ford",7,No,NicholasCage.png -1963,81,"Terror, The",Horror,"Karloff, Boris",,"Corman, Roger",88,No,NicholasCage.png -1963,86,"Raven, The",Horror,"Price, Vincent",,"Corman, Roger",85,No,NicholasCage.png -1975,87,They Came from Within,Horror,"Hampton, Paul",,"Cronenberg, David",21,No,NicholasCage.png -1986,97,"Boy in Blue, The",Drama,"Cage, Nicolas",,"Dale, Cynthia",63,No,NicholasCage.png -1991,87,Killer Tomatoes Strike Back,Comedy,"Astin, John",,"De Bello, John",24,No,NicholasCage.png -1979,87,Attack of the Killer Tomatoes,Comedy,"Wilson, George",,"De Bello, John",47,No,NicholasCage.png -1987,119,"Untouchables, The",Drama,"Connery, Sean",,"De Palma, Brian",7,Yes,seanConnery.png -1986,91,Wise Guys,Comedy,"Piscopo, Joe",,"De Palma, Brian",16,No,NicholasCage.png -1989,90,American Autobahn,Drama,"Jalenak, Jan",,"Degas, Andre",75,No,NicholasCage.png -1990,94,"Final Alliance, The",Action,"Hasselhoff, David",,"Di Leo, Mario",10,No,NicholasCage.png -1984,130,"Bounty, The",Drama,"Gibson, Mel",,"Donaldson, Roger",25,No,NicholasCage.png -1974,89,"Little Prince, The",Music,"Kiley, Richard",,"Donen, Stanley",31,No,NicholasCage.png -1975,94,Posse,Western,"Douglas, Kirk",,"Douglas, Kirk",76,No,NicholasCage.png -1982,136,Firefox,Action,"Eastwood, Clint",,"Eastwood, Clint",64,No,clintEastwood.png -1987,91,Penitentiary III,Action,"Kennedy, Leon Isaac",,"Fanaka, Jamaa",82,No,NicholasCage.png -1993,,Ginger & Fred,Comedy,"Mastroianni, Marcello",,"Fellini, Federico",29,No,NicholasCage.png -1966,107,"Wrong Box, The",Comedy,"Mills, John",,"Forbes, Bryan",40,No,NicholasCage.png -1990,86,Wagonmaster,Western,"Johnson, Ben",,"Ford, John",1,No,johnFord.png -1945,135,They Were Expendable,War,"Montgomery, Robert",,"Ford, John",88,No,johnFord.png -1991,125,"Last Hurrah, The",Drama,"Tracy, Spencer",,"Ford, John",46,No,spencerTracy.png -1949,59,Law of the Golden West,Western,"Hale, Monte",,"Ford, Philip",1,No,NicholasCage.png -1949,60,Pioneer Marshal,Western,"Hale, Monte",,"Ford, Philip",8,No,NicholasCage.png -1949,60,Ranger of the Cherokee Strip,Western,"Hale, Monte",,"Ford, Philip",31,No,NicholasCage.png -1950,60,Vanishing Westerner,Western,"Hale, Monte",,"Ford, Philip",6,No,NicholasCage.png -1948,59,Bandits of Dark Canyon,Western,"Lane, Allan",,"Ford, Philip",72,No,NicholasCage.png -1948,60,"Bold Frontiersman, The",Western,"Lane, Allan",,"Ford, Philip",18,No,NicholasCage.png -1948,59,"Wild Frontier, The",Western,"Lane, Allan",,"Ford, Philip",61,No,NicholasCage.png -1968,73,"Firemen's Ball, The",Comedy,"Vostrcil, Jan",,"Forman, Milos",8,No,NicholasCage.png -1983,112,Local Hero,Comedy,"Riegert, Peter",,"Forsyth, Bill",54,No,NicholasCage.png -1971,104,"French Connection, The",Drama,"Hackman, Gene",,"Friedkin, William",88,Yes,NicholasCage.png -1985,114,To Live & Die in L. A.,Action,"Stockwell, Dean",,"Friedkin, William",70,No,NicholasCage.png -1961,113,Ferry to Hong Kong,Drama,"Welles, Orson",,"Gilbert, Lewis",77,No,NicholasCage.png -1983,69,"Eddie Murphy, Delirious",Comedy,"Murphy, Eddie",,"Gower, Bruce",6,No,NicholasCage.png -1984,77,"Secret Policeman's Private Parts, The",Comedy,"Cleese, John",,"Graef, Roger",36,No,NicholasCage.png -1958,83,Up the Creek,Comedy,"Sellers, Peter",,"Guest, Val",54,No,NicholasCage.png -1982,111,Yol,Drama,"Akan, Tarik",,"Guney, Yilmaz",53,No,NicholasCage.png -1989,150,Sara Dane,Drama,"Hopkins, Harold",,"Hardy, Rod",75,No,NicholasCage.png -1988,84,Night Tide,Drama,"Muir, Gavin",,"Harrington, Curtis",50,No,NicholasCage.png -1953,92,His Majesty O'Keefe,Action,"Lancaster, Burt",,"Haskin, Byron",3,No,burtLancaster.png -1960,122,North to Alaska,Western,"Wayne, John",,"Hathaway, Henry",31,No,johnWayne.png -1966,76,Flight to Fury,Action,"Nicholson, Jack",,"Hellman, Monte",70,No,NicholasCage.png -1966,82,Ride in the Whirlwind,Western,"Nicholson, Jack",,"Hellman, Monte",26,No,NicholasCage.png -1970,93,Powderkeg,Western,"Taylor, Rod",,"Heyes, Douglas",26,No,NicholasCage.png -1953,95,I Confess,Drama,"Clift, Montgomery",,"Hitchcock, Alfred",63,No,alfredHitchcock.png -1935,88,"Thirty-Nine Steps, The",Science Fiction,"Donat, Robert",,"Hitchcock, Alfred",8,No,alfredHitchcock.png -1969,126,Topaz,Mystery,"Forsythe, John",,"Hitchcock, Alfred",12,No,alfredHitchcock.png -1930,95,Murder,Mystery,"Marshall, Herbert",,"Hitchcock, Alfred",50,No,alfredHitchcock.png -1954,123,Dial M for Murder,Mystery,"Milland, Ray",,"Hitchcock, Alfred",52,No,alfredHitchcock.png -1937,80,Young & Innocent,Mystery,"Pilbeam, Nova",,"Hitchcock, Alfred",43,No,alfredHitchcock.png -1976,95,Creature from Black Lake,Horror,"Elam, Jack",,"Houck, Joy, Jr.",88,No,NicholasCage.png -1981,124,Chariots of Fire,Drama,"Cross, Ben",,"Hudson, Hugh",6,Yes,NicholasCage.png -1982,81,Monty Python Live at the Hollywood Bowl,Comedy,"Chapman, Graham",,"Hughes, Terry",81,No,NicholasCage.png -1975,129,"Man Who Would Be King, The",Drama,"Connery, Sean",,"Huston, John",6,No,seanConnery.png -1981,117,Victory,Drama,"Stallone, Sylvester",,"Huston, John",39,No,NicholasCage.png -1970,146,Kelly's Heroes,War,"Eastwood, Clint",,"Hutton, Brian G.",84,No,clintEastwood.png -1989,109,Next of Kin,Mystery,"Swayze, Patrick",,"Irvin, John",63,No,NicholasCage.png -1990,96,Chattahoochee,Drama,"Oldman, Gary",,"Jackson, Mick",30,No,NicholasCage.png -1985,82,"Angelic Conversation, The",Comedy,"Reynolds, Paul",,"Jarman, Derek",41,No,NicholasCage.png -1986,107,Down by Law,Comedy,"Waits, Tom",,"Jarmusch, Jim",49,No,NicholasCage.png -1984,141,"Killing Fields, The",Drama,"Waterston, Sam",,"Joffe, Roland",6,Yes,NicholasCage.png -1992,85,Survival Zone,Action,"Ford, Terence",,"Jones, Chris",25,No,NicholasCage.png -1979,94,Monty Python's Life of Brian,Comedy,"Chapman, Graham",,"Jones, Terry",11,No,NicholasCage.png -1983,107,Monty Python's the Meaning of Life,Comedy,"Cleese, John",,"Jones, Terry",33,No,NicholasCage.png -1971,121,"Red Tent, The",Action,"Finch, Peter",,"Kalatozov, Mikhail",7,No,NicholasCage.png -1945,82,Dakota,Western,"Wayne, John",,"Kane, Joseph",27,No,johnWayne.png -1952,112,Viva Zapata!,Drama,"Brando, Marlon",,"Kazan, Elia",86,Yes,brando.png -1968,133,"Green Berets, The",War,"Wayne, John",,"Kellogg, Ray",36,No,johnWayne.png -1990,90,Big Bad John,Action,"English, Doug",,"Kennedy, Burt",84,No,NicholasCage.png -1937,71,"Ticket of Leave Man, The",Mystery,"Slaughter, Tod",,"King, George",45,No,NicholasCage.png -1956,106,"D-Day, The Sixth of June",War,"Taylor, Robert",,"Koster, Henry",84,No,NicholasCage.png -1974,121,"Apprenticeship of Duddy Kravitz, The",Drama,"Dreyfuss, Richard",,"Kotcheff, Ted",64,Yes,NicholasCage.png -1971,138,A Clockwork Orange,Science Fiction,"McDowell, Malcolm",,"Kubrick, Stanley",83,Yes,NicholasCage.png -1991,117,Full Metal Jacket,War,"Modine, Matthew",,"Kubrick, Stanley",45,No,NicholasCage.png -1943,82,Sanshiro Sugata,Drama,"Fujita, Susumu",,"Kurosawa, Akira",85,No,NicholasCage.png -1991,97,Rhapsody in August,Drama,"Gere, Richard",,"Kurosawa, Akira",50,No,NicholasCage.png -1946,110,No Regrets for Our Youth,Drama,"Hara, Setsuko",,"Kurosawa, Akira",31,No,NicholasCage.png -1960,152,"Bad Sleep Well, The",Drama,"Mifune, Toshiro",,"Kurosawa, Akira",65,No,NicholasCage.png -1951,166,"Idiot, The",Drama,"Mifune, Toshiro",,"Kurosawa, Akira",40,No,NicholasCage.png -1951,83,Rashomon,Drama,"Mifune, Toshiro",,"Kurosawa, Akira",59,Yes,NicholasCage.png -1962,96,Sanjuro,Mystery,"Mifune, Toshiro",,"Kurosawa, Akira",6,No,NicholasCage.png -1955,200,Seven Samurai,Drama,"Mifune, Toshiro",,"Kurosawa, Akira",9,No,NicholasCage.png -1957,110,Throne of Blood,Drama,"Mifune, Toshiro",,"Kurosawa, Akira",60,No,NicholasCage.png -1961,110,Yojimbo,Action,"Mifune, Toshiro",,"Kurosawa, Akira",60,No,NicholasCage.png -1980,161,Kagemusha,Drama,"Nakadai, Tatsuya",,"Kurosawa, Akira",74,Yes,NicholasCage.png -1952,134,Ikiru,Drama,"Shimura, Takashi",,"Kurosawa, Akira",36,No,NicholasCage.png -1987,90,Empire of Spiritual Ninja,Action,"Berlin, Tom",,"Lambert, Bruce",26,No,NicholasCage.png -1986,90,"Ninja, the Violent Sorcerer",Action,,,"Lambert, Bruce",,No,NicholasCage.png -1926,139,Metropolis,Science Fiction,"Abel, Alfred",,"Lang, Fritz",49,No,NicholasCage.png -1946,106,Cloak & Dagger,Mystery,"Cooper, Gary",,"Lang, Fritz",55,No,NicholasCage.png -1920,137,Spiders,Drama,"De Vogy, Carl",,"Lang, Fritz",29,No,NicholasCage.png -1954,90,Human Desire,Drama,"Ford, Glenn",,"Lang, Fritz",27,No,glennFord.png -1928,130,Spies,Drama,"Klein-Rogge, Rudolf",,"Lang, Fritz",49,No,NicholasCage.png -1933,120,"Testament of Dr. Mabuse, The",Drama,"Klein-Rogge, Rudolf",,"Lang, Fritz",4,No,NicholasCage.png -1991,95,Fury,Drama,"Tracy, Spencer",,"Lang, Fritz",48,No,spencerTracy.png -1990,129,Mo' Better Blues,Drama,"Washington, Denzel",,"Lee, Spike",78,No,NicholasCage.png -1989,30,Matt Talbot,Drama,"Ford, Seamus",,"Lennon, Biddy W.",35,No,NicholasCage.png -1989,55,"Will Rogers, Look Back in Laughter",Comedy,"Williams, Robin",,"Leo, Malcolm",6,No,NicholasCage.png -1991,130,For a Few Dollars More,Westerns,"Eastwood, Clint",,"Leone, Sergio",34,No,clintEastwood.png -1944,139,Thirty Seconds over Tokyo,War,"Tracy, Spencer",,"LeRoy, Mervyn",45,No,spencerTracy.png -1982,93,Class of 1984,Drama,"King, Perry",,"Lester, Mark L.",23,No,NicholasCage.png -1974,109,Juggernaut,Action,"Harris, Richard",,"Lester, Richard",63,No,NicholasCage.png -1987,120,"Good Morning, Vietnam",Comedy,"Williams, Robin",,"Levinson, Barry",37,No,NicholasCage.png -1945,94,Blood on the Sun,Drama,"Cagney, James",,"Lloyd, Frank",76,No,NicholasCage.png -1969,161,Paint Your Wagon,Music,"Marvin, Lee",,"Logan, Joshua",46,No,NicholasCage.png -1964,105,Ensign Pulver,Comedy,"Walker, Robert, Jr.",,"Logan, Joshua",16,No,NicholasCage.png -1976,92,Street People,Action,"Moore, Roger",,"Lucidi, Maurizio",25,No,NicholasCage.png -1984,83,"Manhunt, The",Action,"Borgnine, Ernest",,"Ludman, Larry",34,No,NicholasCage.png -1987,85,Operation Nam,War,"Wayne, John Ethan",,"Ludman, Larry",37,No,NicholasCage.png -1944,100,"Fighting Seabees, The",War,"Wayne, John",,"Ludwig, Edward",35,No,johnWayne.png -1988,75,Let It Rock,Drama,"Hopper, Dennis",,"Lynch, David",32,No,lynch.png -1978,90,Eraserhead,Horror,"Nance, John",,"Lynch, David",2,No,lynch.png -1955,87,"Ladykillers, The",Comedy,"Guinness, Alec",,"Mackendrick, Alexander",28,No,NicholasCage.png -1957,97,Sweet Smell of Success,Drama,"Lancaster, Burt",,"Mackendrick, Alexander",12,No,burtLancaster.png -1971,88,And Now for Something Completely Different,Comedy,"Cleese, John",,"MacNaughton, Ian",44,No,NicholasCage.png -1984,92,Crackers,Action,"Sutherland, Donald",,"Malle, Louis",17,No,NicholasCage.png -1991,89,Green Glove,Drama,"Ford, Glenn",,"Mate, Rudolph",54,No,glennFord.png -1970,89,Menace on the Mountain,Action,"Crowley, Pat",,"McEveety, Vincent",69,No,NicholasCage.png -1940,90,In Old California,Western,"Wayne, John",,"McGann, William",27,No,johnWayne.png -1967,85,"Thirty Is a Dangerous Age, Cynthia",Comedy,"Moore, Dudley",,"McGrath, Joseph",28,No,NicholasCage.png -1980,99,Ffolkes,Action,"Moore, Roger",,"McLaglen, Andrew V.",62,No,NicholasCage.png -1970,111,Chisum,Western,"Wayne, John",,"McLaglen, Andrew V.",72,No,johnWayne.png -1990,135,"Hunt for Red October, The",Drama,"Connery, Sean",,"McTiernan, John",8,No,seanConnery.png -1966,123,Closely Watched Trains,Drama,"Neckar, Vaclav",,"Menzel, Jiri",75,Yes,NicholasCage.png -1973,91,Executive Action,Drama,"Lancaster, Burt",,"Miller, David",6,No,burtLancaster.png -1942,101,Flying Tigers,Action,"Wayne, John",,"Miller, David",61,No,johnWayne.png -1991,87,Father's Little Dividend,Comedy,"Tracy, Spencer",,"Minnelli, Vincente",52,No,spencerTracy.png -1982,92,An Evening with Robin Williams,Comedy,"Williams, Robin",,"Mischer, Don",68,No,NicholasCage.png -1987,90,Eddie Murphy Raw,Comedy,"Murphy, Eddie",,"Murphy, Eddie",51,No,NicholasCage.png -1989,118,Harlem Nights,Comedy,"Murphy, Eddie",,"Murphy, Eddie",11,No,NicholasCage.png -1973,93,Santee,Western,"Ford, Glenn",,"Nelson, Gary",47,No,glennFord.png -1987,90,"Good Father, The",Drama,"Hopkins, Anthony",,"Newell, Mike",42,No,AnthonyHopkins.png -1971,115,Sometimes a Great Notion,Drama,"Newman, Paul",,"Newman, Paul",7,No,paulNewman.png -1970,117,Catch Twenty-Two,Comedy,"Arkin, Alan",,"Nichols, Mike",50,No,NicholasCage.png -1988,90,Dark Age,Action,"Jarratt, John",,"Nicholson, Arch",3,No,NicholasCage.png -1981,94,Deadline,Mystery,"Newman, Barry",,"Nicholson, Arch",9,No,paulNewman.png -1935,60,Mysterious Mr. Wong,Mystery,"Lugosi, Bela",,"Nigh, William",71,No,NicholasCage.png -1988,92,A Month in the Country,Drama,"Firth, Colin",,"O'Connor, Pat",57,No,NicholasCage.png -1990,97,"Prom Night III, The Last Kiss",Horror,"Conlon, Tim",,"Oliver, Ron",29,No,NicholasCage.png -1990,,"Blood in, Blood Out",Drama,"Penn, Sean",,"Olmos, Edward James",88,No,NicholasCage.png -1989,94,"Wrong Arm of the Law, The",Comedy,"Sellers, Peter",,"Owen, Cliff",25,No,NicholasCage.png -1987,116,Orphans,Drama,"Finney, Albert",,"Pakula, Alan J.",21,No,NicholasCage.png -1976,139,All the President's Men,Drama,"Redford, Robert",,"Pakula, Alan J.",45,Yes,NicholasCage.png -1987,73,J-Men Forever,Action,"Bergman, Peter",,"Patterson, Richard",59,No,NicholasCage.png -1969,144,"Wild Bunch, The",Western,"Holden, William",,"Peckinpah, Sam",50,No,NicholasCage.png -1988,92,Judgement in Berlin,Drama,"Sheen, Martin",,"Penn, Leo",13,No,NicholasCage.png -1993,,"Hot Line, The",Comedy,"Boyer, Charles",,"Perier, Etienne",70,No,NicholasCage.png -1988,100,Rocket Gibraltar,Drama,"Lancaster, Burt",,"Petrie, Daniel",26,No,burtLancaster.png -1975,112,"Yakuza, The",Action,"Mitchum, Robert",,"Pollack, Sydney",16,No,NicholasCage.png -1972,116,Jeremiah Johnson,Drama,"Redford, Robert",,"Pollack, Sydney",88,No,NicholasCage.png -1970,112,Burn!,Drama,"Brando, Marlon",,"Pontecorvo, Gillo",75,No,brando.png -1973,122,Magnum Force,Action,"Eastwood, Clint",,"Post, Ted",28,No,clintEastwood.png -1989,86,Cyborg,Action,"Van Damme, Jean-Claude",,"Pyun, Albert",31,No,NicholasCage.png -1979,108,"Prisoner of Zenda, The",Comedy,"Sellers, Peter",,"Quine, Richard",12,No,NicholasCage.png -1983,86,Scream,Horror,"Martin, Pepper",,"Quisenberry, Byron",24,No,NicholasCage.png -1986,140,"Assault, The",Drama,"Lint, Derek De",,"Rademakers, Fons",71,Yes,NicholasCage.png -1951,102,Flying Leathernecks,Action,"Wayne, John",,"Ray, Nicholas",23,No,johnWayne.png -1985,92,What Comes Around,Drama,"Reed, Jerry",,"Reed, Jerry",49,No,NicholasCage.png -1980,123,Mon Oncle D'Amerique,Comedy,Roger-Pierre,,"Resnais, Alain",71,No,NicholasCage.png -1972,92,"Culpepper Cattle Company, The",Western,"Grimes, Gary",,"Richards, Dick",29,No,NicholasCage.png -1983,102,"Survivors, The",Comedy,"Matthau, Walter",,"Ritchie, Michael",52,No,NicholasCage.png -1984,96,Roadhouse Sixty-Six,Action,"Dafoe, Willem",,"Robinson, John Mark",20,No,NicholasCage.png -1991,60,"Burning Poles, Cecil Taylor in Performance",Music,"Taylor, Cecil",,"Rochlin, Sheldon",82,No,NicholasCage.png -1987,98,Russkies,Action,"Hubley, Whip",,"Rosenthal, Rick",87,No,NicholasCage.png -1990,96,My Blue Heaven,Comedy,"Martin, Steve",,"Ross, Herbert",63,No,NicholasCage.png -1990,103,Altered States,Science Fiction,"Hurt, William",,"Russell, Ken",22,No,NicholasCage.png -1972,128,"Cowboys, The",Western,"Wayne, John",,"Rydell, Mark",58,No,johnWayne.png -1985,95,"Code Name, Emerald",Drama,"Harris, Ed",,"Sanger, Jonathan",22,No,NicholasCage.png -1970,170,Patton,War,"Scott, George C.",,"Schaffner, Franklin J.",8,Yes,NicholasCage.png -1969,123,Midnight Cowboy,Drama,"Hoffman, Dustin",,"Schlesinger, John",33,Yes,NicholasCage.png -1985,131,"Falcon & the Snowman, The",Drama,"Hutton, Timothy",,"Schlesinger, John",61,No,NicholasCage.png -1976,112,Maitresse,Drama,"Ogier, Bulle",,"Schroeder, Barbet",39,No,NicholasCage.png -1987,86,Disorderlies,Comedy,"Boys, The Fat",,"Schultz, Michael",69,No,NicholasCage.png -1991,,Raging Bull,Drama,"De Niro, Robert",,"Scorsese, Martin",25,No,NicholasCage.png -1991,60,Garrison Keillor's Home,Comedy,"Keillor, Garrison",,"Sevush, Herb",6,No,NicholasCage.png -1938,55,Overland Stage Raiders,Western,"Wayne, John",,"Sherman, George",83,No,johnWayne.png -1938,55,Pals of the Saddle,Western,"Wayne, John",,"Sherman, George",33,No,johnWayne.png -1982,92,Alone in the Dark,Horror,"Schultz, Dwight",,"Sholder, Jack",75,No,NicholasCage.png -1971,109,"Beguiled, The",Drama,"Eastwood, Clint",,"Siegel, Don",60,No,clintEastwood.png -1979,112,Escape from Alcatraz,Drama,"Eastwood, Clint",,"Siegel, Don",22,No,clintEastwood.png -1948,88,Criss Cross,Drama,"Lancaster, Burt",,"Siodmak, Robert",77,No,burtLancaster.png -1976,132,Midway,War,"Heston, Charlton",,"Smight, Jack",36,No,NicholasCage.png -1990,126,Indiana Jones & the Last Crusade,Action,"Ford, Harrison",,"Spielberg, Steven",8,No,NicholasCage.png -1993,90,Duel,Mystery,"Weaver, Dennis",,"Spielberg, Steven",48,No,NicholasCage.png -1991,193,Separate but Equal,Drama,"Poitier, Sidney",,"Stevens, George, Jr.",56,No,NicholasCage.png -1924,123,Gosta Berling's Saga,Drama,"Hanson, Lars",,"Stiller, Mauritz",63,No,NicholasCage.png -1986,120,Platoon,Drama,"Sheen, Charlie",,"Stone, Oliver",8,Yes,NicholasCage.png -1963,89,"Crawling Hand, The",Science Fiction,"Breck, Peter",,"Strock, Herbert L.",79,No,NicholasCage.png -1971,100,Willy Wonka & the Chocolate Factory,Music,"Wilder, Gene",,"Stuart, Mel",65,No,NicholasCage.png -1971,88,Joe Kidd,Western,"Eastwood, Clint",,"Sturges, John",79,No,clintEastwood.png -1985,104,"Santa Claus, The Movie",Comedy,"Moore, Dudley",,"Szwarc, Jeannot",19,No,NicholasCage.png -1938,96,Boys Town,Drama,"Tracy, Spencer",,"Taurog, Norman",21,Yes,spencerTracy.png -1990,59,"Erasure, Live Wild!",Music,,,"Taylor, Gavin",48,No,NicholasCage.png -1982,150,A Question of Honor,Drama,"Gazzara, Ben",,"Taylor, Jud",80,No,NicholasCage.png -1947,61,Check Your Guns,Western,"Dean, Eddie",,"Taylor, Ray",80,No,NicholasCage.png -1947,56,West to Glory,Western,"Dean, Eddie",,"Taylor, Ray",43,No,NicholasCage.png -1937,60,"Throwback, The",Western,"Jones, Buck",,"Taylor, Ray",53,No,NicholasCage.png -1992,54,Border Feud,Action,"LaRue, Lash",,"Taylor, Ray",43,No,NicholasCage.png -1947,58,"Fighting Vigilantes, The",Western,"LaRue, Lash",,"Taylor, Ray",21,No,NicholasCage.png -1947,53,Law of the Lash,Western,"LaRue, Lash",,"Taylor, Ray",66,No,NicholasCage.png -1949,66,Outlaw Country,Western,"LaRue, Lash",,"Taylor, Ray",62,No,NicholasCage.png -1992,53,Return of the Lash,Action,"LaRue, Lash",,"Taylor, Ray",78,No,NicholasCage.png -1937,60,Mystery of the Hooded Horsemen,Western,"Ritter, Tex",,"Taylor, Ray",52,No,NicholasCage.png -1937,60,Tex Rides with the Boy Scouts,Western,"Ritter, Tex",,"Taylor, Ray",17,No,NicholasCage.png -1949,59,Shadows of the West,Western,"Wilson, Whip",,"Taylor, Ray",40,No,NicholasCage.png -1991,102,Instant Karma,Comedy,"Cassidy, David",,"Taylor, Roderick",47,No,NicholasCage.png -1957,73,Time Lock,Drama,"Connery, Sean",,"Thomas, Gerald",5,No,seanConnery.png -1953,79,Appointment in Honduras,Drama,"Ford, Glenn",,"Tourneur, Jacques",7,No,glennFord.png -1982,136,Danton,Drama,"Depardieu, Gérard",,"Wajda, Andrzej",5,No,NicholasCage.png -1960,164,"Alamo, The",Action,"Wayne, John",,"Wayne, John",29,No,johnWayne.png -1986,91,"La Chevre, (The Goat)",Drama,"Depardieu, Gérard",,"Veber, Francis",24,No,NicholasCage.png -1985,109,Les Comperes,Comedy,"Richard, Pierre",,"Veber, Francis",54,No,NicholasCage.png -1990,128,Dead Poets Society,Drama,"Williams, Robin",,"Weir, Peter",8,Yes,NicholasCage.png -1952,93,"Othello, The Lost Masterpiece",Drama,"Welles, Orson",,"Welles, Orson",23,No,NicholasCage.png -1949,119,"Battleground, The",War,"Johnson, Van",,"Wellman, William",7,No,NicholasCage.png -1976,176,Kings of the Road (In the Course of Time),Drama,"Vogler, Rudiger",,"Wenders, Wim",41,No,NicholasCage.png -1990,98,Hiroshima,Drama,"Nelson, Judd",,"Werner, Peter",17,No,NicholasCage.png -1982,111,"Return of Martin Guerre, The",Drama,"Depardieu, Gérard",,"Vigne, Daniel",51,No,NicholasCage.png -1956,97,Somebody up There Likes Me,Drama,"Newman, Paul",,"Wise, Robert",56,No,paulNewman.png -1955,57,Jack Benny Show,Comedy,"Benny, Jack",,,51,No,NicholasCage.png -1962,182,Mutiny on the Bounty,Action,"Brando, Marlon",,,35,No,brando.png -1989,,"Death Valley Days, Deadly Decision",Western,"Caan, James",,,9,No,NicholasCage.png -1986,60,Monty Python's Flying Circus,Comedy,"Chapman, Graham",,,4,No,NicholasCage.png -1986,60,"Monty Python's Flying Circus, Vol 1.",Comedy,"Chapman, Graham",,,24,No,NicholasCage.png -1986,59,"Monty Python's Flying Circus, Vol 2.",Comedy,"Chapman, Graham",,,79,No,NicholasCage.png -1986,58,"Monty Python's Flying Circus, Vol 3.",Comedy,"Chapman, Graham",,,63,No,NicholasCage.png -1990,,Valkenvania,Comedy,"Chase, Chevy",,,82,No,NicholasCage.png -1982,101,"Secret Policeman's Other Ball, The",Comedy,"Cleese, John",,,86,No,NicholasCage.png -1981,127,"Taming of the Shrew, The",Drama,"Cleese, John",,,2,No,NicholasCage.png -1964,,From Russia with Love,Action,"Connery, Sean",,,6,No,seanConnery.png -1993,108,"Offence, The",Mystery,"Connery, Sean",,,6,No,seanConnery.png -1992,60,Hollywood Mavericks,Comedy,"Coppola, Francis Ford",,,22,No,NicholasCage.png -1990,60,Live at Harrah's,Comedy,"Cosby, Bill",,,6,No,NicholasCage.png -1992,52,"Persuaders, The Overture, The",Mystery,"Curtis, Tony",,,40,No,NicholasCage.png -1977,255,Nineteen Hundred,Drama,"De Niro, Robert",,,82,No,NicholasCage.png -1989,90,"Van, The",Comedy,"DeVito, Danny",,,5,No,NicholasCage.png -1972,15,My Country Right or Wrong,War,"Douglas, Michael",,,21,No,NicholasCage.png -1991,,"Clint Eastwood Collection, The",Westerns,"Eastwood, Clint",,,11,No,clintEastwood.png -1991,,"Complete Dirty Harry, Magnum Force, The",Action,"Eastwood, Clint",,,53,No,clintEastwood.png -1992,92,"Dead Pool, The",Action,"Eastwood, Clint",,,26,No,clintEastwood.png -1992,163,"Good, the Bad & the Ugly, The",Westerns,"Eastwood, Clint",,,68,No,clintEastwood.png -1959,60,"Rawhide, Premiere Episode",Western,"Eastwood, Clint",,,54,No,clintEastwood.png -1992,118,Tightrope,Mystery,"Eastwood, Clint",,,55,No,clintEastwood.png -1987,95,Hearts of Fire,Drama,"Everett, Rupert",,,25,No,NicholasCage.png -1992,165,How the West Was Won,Western,"Fonda, Henry",,,45,No,NicholasCage.png -1992,,"Mummy's Hand, The",Mystery,"Foran, Dick",,,54,No,NicholasCage.png -1993,88,Great White Death,Action,"Ford, Glenn",,,26,No,glennFord.png -1986,119,"Mosquito Coast, The",Drama,"Ford, Harrison",,,54,No,NicholasCage.png -1993,102,Today We Kill....Tomorrow We Die,Western,"Ford, Montgomery",,,25,No,NicholasCage.png -1991,,Tormenta Sobre Arizona,Drama,"Ford, Wallace",,,81,No,NicholasCage.png -1989,116,Back to the Future II,Comedy,"Fox, Michael J.",,,65,No,NicholasCage.png -1959,60,"Maverick, Duel at Sundown",Western,"Garner, James",,,26,No,NicholasCage.png -1983,,Shakespeare Series,Drama,"Gielgud, John",,,23,No,NicholasCage.png -1973,105,Deadly Trackers,Western,"Harris, Richard",,,54,No,NicholasCage.png -1992,72,"American Film Institute, Alfred Hitchcock",Mystery,"Hitchcock, Alfred",,,70,No,NicholasCage.png -1990,,A Married Man,Drama,"Hopkins, Anthony",,,79,No,AnthonyHopkins.png -1982,208,Othello,Drama,"Hopkins, Anthony",,,84,No,AnthonyHopkins.png -1975,85,"Only Way Home, The",Drama,"Hopkins, Bo",,,60,No,NicholasCage.png -1953,120,Tales of Tomorrow,Horror,"Karloff, Boris",,,0,No,NicholasCage.png -1991,128,Inherit the Wind,Drama,"Kelly, Gene",,,18,No,NicholasCage.png -1990,45,This Is Horror,Horror,"King, Stephen",,,3,No,NicholasCage.png -1992,112,Conversation Piece,Drama,"Lancaster, Burt",,,1,No,burtLancaster.png -1992,105,"Crimson Pirate, The",Action,"Lancaster, Burt",,,60,No,burtLancaster.png -1992,83,"Devil's Disciple, The",Mystery,"Lancaster, Burt",,,65,No,burtLancaster.png -1992,166,"Hallelujah Trail, The",Drama,"Lancaster, Burt",,,6,No,burtLancaster.png -1992,133,"Train, The",Action,"Lancaster, Burt",,,68,No,burtLancaster.png -1986,49,Jay Leno: The American Dream,Comedy,"Leno, Jay",,,67,No,NicholasCage.png -1990,92,Primal Rage,Mystery,"Lowe, Patrick",,,3,No,NicholasCage.png -1990,50,"Industrial Symphony, The Dream of the Broken-Hearted",Music,"Lynch, David",,,49,No,lynch.png -1986,52,Howie Mandel's North American Watusi Tour,Comedy,"Mandel, Howie",,,65,No,NicholasCage.png -1989,90,"Branford Marsalis, Steep",Music,"Marsalis, Branford",,,52,No,NicholasCage.png -1991,98,L. A. Story,Comedy,"Martin, Steve",,,81,No,NicholasCage.png -1986,60,Steve Martin Live!,Comedy,"Martin, Steve",,,3,No,NicholasCage.png -1974,60,"Steve Martin, The Funnier Side of Eastern Canada",Comedy,"Martin, Steve",,,34,No,NicholasCage.png -1993,,"Runaway Barge, The",Action,"Matheson, Tim",,,38,No,NicholasCage.png -1992,101,Romulus & the Sabines,Action,"Moore, Roger",,,76,No,NicholasCage.png -1989,,"Saint, The",Mystery,"Moore, Roger",,,29,No,NicholasCage.png -1983,91,Strange Brew,Comedy,"Moranis, Rick",,,24,No,NicholasCage.png -1990,98,Another Forty-Eight Hours,Action,"Murphy, Eddie",,,54,No,NicholasCage.png -1989,,"Best of Eddie Murphy, Saturday Night Live, The",Comedy,"Murphy, Eddie",,,56,No,NicholasCage.png -1991,99,What about Bob?,Comedy,"Murray, Bill",,,6,No,NicholasCage.png -1953,91,"Mummy's Revenge, The",Horror,"Naschy, Paul",,,56,No,NicholasCage.png -1992,121,Harper,Mystery,"Newman, Paul",,,86,No,paulNewman.png -1992,102,"Left Handed Gun, The",Western,"Newman, Paul",,,26,No,paulNewman.png -1989,,Once upon a Wheel,Action,"Newman, Paul",,,40,No,paulNewman.png -1992,136,"Prize, The",Drama,"Newman, Paul",,,66,No,paulNewman.png -1968,,"Secret War of Harry Frigg, The",Comedy,"Newman, Paul",,,28,No,paulNewman.png -1990,,"Two Jakes, The",Mystery,"Nicholson, Jack",,,3,No,NicholasCage.png -1989,61,Exile in Concert,Music,"Pennington, J. P.",,,12,No,NicholasCage.png -1987,60,Joe Piscopo New Jersey Special,Comedy,"Piscopo, Joe",,,14,No,NicholasCage.png -1991,60,"Joe Piscopo Video, The",Comedy,"Piscopo, Joe",,,44,No,NicholasCage.png -1989,,"Death Valley Days, No Gun Behind His Badge",Western,"Reagan, Ronald",,,1,No,NicholasCage.png -1988,96,Salsa: The Motion Picture,Drama,"Rosa, Robby",,,26,No,NicholasCage.png -1991,80,Hollywood's Greatest War Movies,War,"Scott, George C.",,,41,No,NicholasCage.png -1991,91,Out for Justice,Action,"Seagal, Steven",,,2,No,NicholasCage.png -1956,27,"Case of the Mukkinese Battle Horn, The",Comedy,"Sellers, Peter",,,45,No,NicholasCage.png -1953,75,"Goon Show Movie, The",Comedy,"Sellers, Peter",,,80,No,NicholasCage.png -1975,95,"Great McGonagall, The",Comedy,"Sellers, Peter",,,72,No,NicholasCage.png -1991,101,I'm All Right Jack,Comedy,"Sellers, Peter",,,23,No,NicholasCage.png -1991,101,"Magic Christian, The",Comedy,"Sellers, Peter",,,75,No,NicholasCage.png -1960,91,Never Let Go,Action,"Sellers, Peter",,,5,No,NicholasCage.png -1991,121,"Pink Panther, The",Comedy,"Sellers, Peter",,,77,No,NicholasCage.png -1991,84,Two-Way Stretch,Comedy,"Sellers, Peter",,,7,No,NicholasCage.png -1988,65,"Face at the Window, The",Horror,"Slaughter, Tod",,,79,No,NicholasCage.png -1958,92,Tom Thumb,Science Fiction,"Tamblyn, Russ",,,30,No,NicholasCage.png -1989,90,Beartooth,Action,"Taylor, Dub",,,70,No,NicholasCage.png -1979,90,James Taylor in Concert,Music,"Taylor, James",,,38,No,NicholasCage.png -1942,253,Gangbusters,Drama,"Taylor, Kent",,,31,No,NicholasCage.png -1992,,El Rublo de las Dos Caras,Action,"Taylor, Robert",,,83,No,NicholasCage.png -1992,87,"Law & Jake Wade, The",Drama,"Taylor, Robert",,,68,No,NicholasCage.png -1967,105,Chuka,Western,"Taylor, Rod",,,47,No,NicholasCage.png -1980,93,Cry of the Innocent,Drama,"Taylor, Rod",,,13,No,NicholasCage.png -1991,108,Edison the Man,Drama,"Tracy, Spencer",,,19,No,spencerTracy.png -1991,101,Keeper of the Flame,Drama,"Tracy, Spencer",,,76,No,spencerTracy.png -1991,92,"Spencer Tracy Legacy, The",Comedy,"Tracy, Spencer",,,44,No,spencerTracy.png -1957,60,"Cheyenne, The Iron Trail",Western,"Walker, Clint",,,1,No,NicholasCage.png -1992,56,"Dawn Rider, The",Western,"Wayne, John",,,44,No,johnWayne.png -1993,,"Duke, The Films of John Wayne",Western,"Wayne, John",,,70,No,johnWayne.png -1939,55,Frontier Horizon,Western,"Wayne, John",,,73,No,johnWayne.png -1934,54,Hell Town,Western,"Wayne, John",,,23,No,johnWayne.png -1932,,Hurricane Express,Western,"Wayne, John",,,7,No,johnWayne.png -1932,210,"Hurricane Express, The",Action,"Wayne, John",,,68,No,johnWayne.png -1965,165,In Harm's Way,War,"Wayne, John",,,66,No,johnWayne.png -1991,,"John Wayne Collection, Red River, The",War,"Wayne, John",,,49,No,johnWayne.png -1992,,John Wayne Collector's Limited Edition,War,"Wayne, John",,,3,No,johnWayne.png -1991,,John Wayne Four Pack,Western,"Wayne, John",,,58,No,johnWayne.png -1939,112,"John Wayne Matinee Double Feature, No. 2",Western,"Wayne, John",,,3,No,johnWayne.png -1939,110,"John Wayne Matinee Double Feature, No. 3",Western,"Wayne, John",,,24,No,johnWayne.png -1938,110,"John Wayne Matinee Double Feature, No. 4",Western,"Wayne, John",,,28,No,johnWayne.png -1990,,John Wayne Six Pack,Western,"Wayne, John",,,87,No,johnWayne.png -1991,,"John Wayne Western Greats, Rio Bravo",Western,"Wayne, John",,,22,No,johnWayne.png -1991,56,King of the Pecos,Western,"Wayne, John",,,78,No,johnWayne.png -1992,59,Lawless Frontier,Western,"Wayne, John",,,8,No,johnWayne.png -1991,52,"Lawless Frontier, The",Western,"Wayne, John",,,35,No,johnWayne.png -1991,56,"Lawless Nineties, The",Western,"Wayne, John",,,3,No,johnWayne.png -1934,54,Lucky Texan,Western,"Wayne, John",,,48,No,johnWayne.png -1992,112,McQ,Action,"Wayne, John",,,5,No,johnWayne.png -1993,,Neath Arizona Skies,Western,"Wayne, John",,,73,No,johnWayne.png -1991,54,Neath the Arizona Skies,Western,"Wayne, John",,,28,No,johnWayne.png -1991,53,Randy Rides Alone,Western,"Wayne, John",,,75,No,johnWayne.png -1993,58,Range Feud,Western,"Wayne, John",,,77,No,johnWayne.png -1992,134,Red River,Western,"Wayne, John",,,16,No,johnWayne.png -1991,52,Riders of Destiny,Western,"Wayne, John",,,30,No,johnWayne.png -1990,,Sagebrush Trail,Western,"Wayne, John",,,23,No,johnWayne.png -1932,226,"Shadow of the Eagle, The",Action,"Wayne, John",,,19,No,johnWayne.png -1989,103,Blood & Guns,Action,"Welles, Orson",,,43,No,NicholasCage.png -1988,78,Hot Money,Drama,"Welles, Orson",,,19,No,NicholasCage.png -1977,75,Comedy Tonight,Comedy,"Williams, Robin",,,18,No,NicholasCage.png -1991,65,Robin Williams,Comedy,"Williams, Robin",,,4,No,NicholasCage.png \ No newline at end of file diff --git a/cdk-api-gateway-cors-whitelist/example-pattern.json b/cdk-api-gateway-cors-whitelist/example-pattern.json index 369c12006b..9b35bc4153 100644 --- a/cdk-api-gateway-cors-whitelist/example-pattern.json +++ b/cdk-api-gateway-cors-whitelist/example-pattern.json @@ -38,7 +38,7 @@ "text": ["make build-and-deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["make teardown"] diff --git a/cdk-api-gateway-cors-whitelist/src/cors-function/go.mod b/cdk-api-gateway-cors-whitelist/src/cors-function/go.mod index 80d09b921f..30a47ef0f5 100644 --- a/cdk-api-gateway-cors-whitelist/src/cors-function/go.mod +++ b/cdk-api-gateway-cors-whitelist/src/cors-function/go.mod @@ -7,9 +7,10 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.18.19 github.com/aws/aws-sdk-go-v2/service/ssm v1.36.0 github.com/sirupsen/logrus v1.9.0 - github.com/stretchr/testify v1.8.2 ) +require github.com/stretchr/testify v1.8.2 // indirect + require ( github.com/DataDog/datadog-agent/pkg/obfuscate v0.41.1 // indirect github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.42.0-rc.1 // indirect @@ -17,41 +18,29 @@ require ( github.com/DataDog/go-tuf v0.3.0--fix-localmeta-fork // indirect github.com/DataDog/sketches-go v1.4.1 // indirect github.com/Microsoft/go-winio v0.5.2 // indirect - github.com/andybalholm/brotli v1.0.4 // indirect - github.com/aws/aws-sdk-go v1.44.168 // indirect - github.com/aws/aws-sdk-go-v2/service/kms v1.19.2 // indirect - github.com/aws/aws-xray-sdk-go v1.8.0 // indirect - github.com/cenkalti/backoff/v4 v4.2.0 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/dgraph-io/ristretto v0.1.0 // indirect github.com/dustin/go-humanize v1.0.0 // indirect github.com/golang/glog v1.0.0 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/uuid v1.3.0 // indirect - github.com/klauspost/compress v1.15.13 // indirect github.com/philhofer/fwd v1.1.1 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect - github.com/sony/gobreaker v0.5.0 // indirect github.com/tinylib/msgp v1.1.6 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.35.0 // indirect go.uber.org/atomic v1.10.0 // indirect go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/text v0.7.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f // indirect - google.golang.org/grpc v1.45.0 // indirect - google.golang.org/protobuf v1.28.0 // indirect + google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect + google.golang.org/grpc v1.53.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect gopkg.in/DataDog/dd-trace-go.v1 v1.48.0 inet.af/netaddr v0.0.0-20220617031823-097006376321 // indirect ) require ( - github.com/DataDog/datadog-lambda-go v1.9.0 github.com/aws/aws-sdk-go-v2 v1.17.7 github.com/aws/aws-sdk-go-v2/credentials v1.13.18 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1 // indirect @@ -63,9 +52,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.18.7 // indirect github.com/aws/smithy-go v1.13.5 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/sys v0.5.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/cdk-api-gateway-cors-whitelist/src/cors-function/go.sum b/cdk-api-gateway-cors-whitelist/src/cors-function/go.sum index 2f4a7ad87d..24d2ca5668 100644 --- a/cdk-api-gateway-cors-whitelist/src/cors-function/go.sum +++ b/cdk-api-gateway-cors-whitelist/src/cors-function/go.sum @@ -1,6 +1,3 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DataDog/datadog-agent/pkg/obfuscate v0.41.1 h1:AHZu7lzfW6amjOLkbjioAxT+pKiiwD6KdkR0VfT3pMw= github.com/DataDog/datadog-agent/pkg/obfuscate v0.41.1/go.mod h1:DNHeRExTGWQoMgmOgcDtNENOEHN/tYJIicmAUgW1nXk= github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.42.0-rc.1 h1:Rmz52Xlc5k3WzAHzD0SCH4USCzyti7EbK4HtrHys3ME= @@ -8,8 +5,6 @@ github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.42.0-rc.1/go.mod h1:V github.com/DataDog/datadog-go/v5 v5.1.1/go.mod h1:KhiYb2Badlv9/rofz+OznKoEF5XKTonWyhx5K83AP8E= github.com/DataDog/datadog-go/v5 v5.2.0 h1:kSptqUGSNK67DgA+By3rwtFnAh6pTBxJ7Hn8JCLZcKY= github.com/DataDog/datadog-go/v5 v5.2.0/go.mod h1:XRDJk1pTc00gm+ZDiBKsjh7oOOtJfYfglVCmFb8C2+Q= -github.com/DataDog/datadog-lambda-go v1.9.0 h1:XtvshiRTzDNVQQAkNuMqUDMyCcfJJ0eLSLCOOKB52jQ= -github.com/DataDog/datadog-lambda-go v1.9.0/go.mod h1:TlnzDhuHlkedDvDYEc9Yo+15iAbYakYaIIeySC+Yguw= github.com/DataDog/go-tuf v0.3.0--fix-localmeta-fork h1:yBq5PrAtrM4yVeSzQ+bn050+Ysp++RKF1QmtkL4VqvU= github.com/DataDog/go-tuf v0.3.0--fix-localmeta-fork/go.mod h1:yA5JwkZsHTLuqq3zaRgUQf35DfDkpOZqgtBqHKpwrBs= github.com/DataDog/sketches-go v1.4.1 h1:j5G6as+9FASM2qC36lvpvQAj9qsv/jUs3FtO8CwZNAY= @@ -18,14 +13,8 @@ github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpz github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/aws/aws-lambda-go v1.39.1 h1:UcuX9O3JqhQyP/rxPJEpTUUSehzqkNpwKKRFa9N+ozk= github.com/aws/aws-lambda-go v1.39.1/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8qK17ewzbQMM= -github.com/aws/aws-sdk-go v1.44.168 h1:/NNDLkjcgW8UrvAUk7QvQS9yzo/CFu9Zp4BCiPHoV+E= -github.com/aws/aws-sdk-go v1.44.168/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= -github.com/aws/aws-sdk-go-v2 v1.17.2/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2 v1.17.7 h1:CLSjnhJSTSogvqUGhIC6LqFKATMRexcxLZ0i/Nzk9Eg= github.com/aws/aws-sdk-go-v2 v1.17.7/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2/config v1.18.19 h1:AqFK6zFNtq4i1EYu+eC7lcKHYnZagMn6SW171la0bGw= @@ -34,18 +23,15 @@ github.com/aws/aws-sdk-go-v2/credentials v1.13.18 h1:EQMdtHwz0ILTW1hoP+EwuWhwCG1 github.com/aws/aws-sdk-go-v2/credentials v1.13.18/go.mod h1:vnwlwjIe+3XJPBYKu1et30ZPABG3VaXJYr8ryohpIyM= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1 h1:gt57MN3liKiyGopcqgNzJb2+d9MJaKT/q1OksHNXVE4= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.1/go.mod h1:lfUx8puBRdM5lVVMQlwt2v+ofiG/X6Ms+dy0UkG/kXw= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.26/go.mod h1:2E0LdbJW6lbeU4uxjum99GZzI0ZjDpAb0CoSCM0oeEY= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31 h1:sJLYcS+eZn5EeNINGHSCRAwUJMFVqklwkH36Vbyai7M= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.31/go.mod h1:QT0BqUvX1Bh2ABdTGnjqEjvjzrCfIniM9Sc8zn9Yndo= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.20/go.mod h1:/+6lSiby8TBFpTVXZgKiN/rCfkYXEGvhlM4zCgPpt7w= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25 h1:1mnRASEKnkqsntcxHaysxwgVoUUp5dkiB+l3llKnqyg= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.25/go.mod h1:zBHOPwhBc3FlQjQJE/D3IfPWiWaQmT06Vq9aNukDo0k= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32 h1:p5luUImdIqywn6JpQsW3tq5GNOxKmOnEpybzPx+d1lk= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.32/go.mod h1:XGhIBZDEgfqmFIugclZ6FU7v75nHhBDtzuB4xB/tEi4= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25 h1:5LHn8JQ0qvjD9L9JhMtylnkcw7j05GDZqM9Oin6hpr0= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.25/go.mod h1:/95IA+0lMnzW6XzqYJRpjjsAbKEORVeO0anQqjd2CNU= -github.com/aws/aws-sdk-go-v2/service/kms v1.19.2 h1:pgOVfu7E6zBddKGks4TvL4YuFsL/oTpiWDIzs4WPLjY= -github.com/aws/aws-sdk-go-v2/service/kms v1.19.2/go.mod h1:XH60PhgtbXDXFBzJ2auE6bpIELxAYTnoVFFwPtG8JwY= +github.com/aws/aws-sdk-go-v2/service/sqs v1.0.0 h1:k+iXUEMp688JqUcxb4/bzt7xgJX4TLqahrwgWA/qO6E= github.com/aws/aws-sdk-go-v2/service/ssm v1.36.0 h1:L1gK0SF7Filotf8Jbhiq0Y+rKVs/W1av8MH0+AXPrAg= github.com/aws/aws-sdk-go-v2/service/ssm v1.36.0/go.mod h1:nCdeJmEFby1HKwKhDdKdVxPOJQUNht7Ngw+ejzbzvDU= github.com/aws/aws-sdk-go-v2/service/sso v1.12.6 h1:5V7DWLBd7wTELVz5bPpwzYy/sikk0gsgZfj40X+l5OI= @@ -54,93 +40,66 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6 h1:B8cauxOH1W1v7rd8RdI/MWno github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.6/go.mod h1:Lh/bc9XUf8CfOY6Jp5aIkQtN+j1mc+nExc+KXj9jx2s= github.com/aws/aws-sdk-go-v2/service/sts v1.18.7 h1:bWNgNdRko2x6gqa0blfATqAZKZokPIeM1vfmQt2pnvM= github.com/aws/aws-sdk-go-v2/service/sts v1.18.7/go.mod h1:JuTnSoeePXmMVe9G8NcjjwgOKEfZ4cOjMuT2IBT/2eI= -github.com/aws/aws-xray-sdk-go v1.8.0 h1:0xncHZ588wB/geLjbM/esoW3FOEThWy2TJyb4VXfLFY= -github.com/aws/aws-xray-sdk-go v1.8.0/go.mod h1:7LKe47H+j3evfvS1+q0wzpoaGXGrF3mUsfM+thqVO+A= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= -github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/flynn/go-docopt v0.0.0-20140912013429-f6dd2ebbb31e/go.mod h1:HyVoz1Mz5Co8TFO8EupIdlcpwShBmY98dkT2xeHkvEI= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.15.13 h1:NFn1Wr8cfnenSJSA46lLq4wHCcBzKTSjnBIexDMMOV0= -github.com/klauspost/compress v1.15.13/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -152,24 +111,22 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/secure-systems-lab/go-securesystemslib v0.3.1/go.mod h1:o8hhjkbNl2gOamKUA/eNW3xUrntHT9L4W89W1nfj43U= github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE= github.com/secure-systems-lab/go-securesystemslib v0.4.0/go.mod h1:FGBZgq2tXWICsxWQW1msNf49F0Pf2Op5Htayx335Qbs= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= -github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -185,15 +142,8 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw= github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.35.0 h1:wwkR8mZn2NbigFsaw2Zj5r+xkmzjbrA/lyTmiSlal/Y= -github.com/valyala/fasthttp v1.35.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE= @@ -204,48 +154,25 @@ go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760/go.mod h1: golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -267,91 +194,60 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f h1:izedQ6yVIc5mZsRuXzmSreCOlzI0lCU1HpG8yEdMiKw= -google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/DataDog/dd-trace-go.v1 v1.48.0 h1:AZhmo9zstDWWD7qG7g+2W7x4X7FYuGJcwRIIEjsLiEY= gopkg.in/DataDog/dd-trace-go.v1 v1.48.0/go.mod h1:z+Zm99hL8zep83JLkTbknOxSMJnNvAggmL6mnUtDhWE= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= inet.af/netaddr v0.0.0-20220617031823-097006376321 h1:B4dC8ySKTQXasnjDTMsoCMf1sQG4WsMej0WXaHxunmU= inet.af/netaddr v0.0.0-20220617031823-097006376321/go.mod h1:OIezDfdzOgFhuw4HuWapWq2e9l0H9tK4F1j+ETRtF3k= diff --git a/cdk-apigw-authorizer-sqs/example-pattern.json b/cdk-apigw-authorizer-sqs/example-pattern.json index 4a584b6b77..c0a417dc41 100644 --- a/cdk-apigw-authorizer-sqs/example-pattern.json +++ b/cdk-apigw-authorizer-sqs/example-pattern.json @@ -41,7 +41,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cdk-appsync-sns/example-pattern.json b/cdk-appsync-sns/example-pattern.json index 364b4c5c9d..ddc99e8afd 100644 --- a/cdk-appsync-sns/example-pattern.json +++ b/cdk-appsync-sns/example-pattern.json @@ -33,7 +33,7 @@ "text": ["npx aws-cdk deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: npx aws-cdk destroy."] diff --git a/cdk-appsync-sqs/pattern-cdk-appsync-sqs.json b/cdk-appsync-sqs/pattern-cdk-appsync-sqs.json index 1e39d18e15..67ec3bca50 100644 --- a/cdk-appsync-sqs/pattern-cdk-appsync-sqs.json +++ b/cdk-appsync-sqs/pattern-cdk-appsync-sqs.json @@ -16,7 +16,7 @@ ] }, "deploy": { - "text": ["See the Github repo for detailed instructions"] + "text": ["See the GitHub repo for detailed instructions"] }, "gitHub": { "template": { diff --git a/cdk-closed-loop-serverless-control/cdk-closed-loop-serverless-control-pattern.json b/cdk-closed-loop-serverless-control/cdk-closed-loop-serverless-control-pattern.json index 5ec4b025c1..4ff48ec52b 100644 --- a/cdk-closed-loop-serverless-control/cdk-closed-loop-serverless-control-pattern.json +++ b/cdk-closed-loop-serverless-control/cdk-closed-loop-serverless-control-pattern.json @@ -43,7 +43,7 @@ }, "testing": { "text": [ - "See README.md file in the Github repo for detailed testing instructions." + "See README.md file in the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cdk-cognito-apigateway-lambda/example-pattern.json b/cdk-cognito-apigateway-lambda/example-pattern.json index 2fa4e2cc56..9a75a44e0d 100644 --- a/cdk-cognito-apigateway-lambda/example-pattern.json +++ b/cdk-cognito-apigateway-lambda/example-pattern.json @@ -28,12 +28,12 @@ }, "deploy": { "text": [ - "See the Github repo for detailed deployment instructions." + "See the GitHub repo for detailed deployment instructions." ] }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cdk-eventbridge-appsync-oauth/pattern-cdk-eventbridge-appsync.json b/cdk-eventbridge-appsync-oauth/pattern-cdk-eventbridge-appsync.json index 5ae45f157e..0e70ea0866 100644 --- a/cdk-eventbridge-appsync-oauth/pattern-cdk-eventbridge-appsync.json +++ b/cdk-eventbridge-appsync-oauth/pattern-cdk-eventbridge-appsync.json @@ -16,7 +16,7 @@ ] }, "deploy": { - "text": ["See the Github repo for detailed instructions"] + "text": ["See the GitHub repo for detailed instructions"] }, "gitHub": { "template": { diff --git a/cdk-eventbridge-appsync/pattern-cdk-eventbridge-appsync.json b/cdk-eventbridge-appsync/pattern-cdk-eventbridge-appsync.json index dfcff98cb5..b70843498f 100644 --- a/cdk-eventbridge-appsync/pattern-cdk-eventbridge-appsync.json +++ b/cdk-eventbridge-appsync/pattern-cdk-eventbridge-appsync.json @@ -16,7 +16,7 @@ ] }, "deploy": { - "text": ["See the Github repo for detailed instructions"] + "text": ["See the GitHub repo for detailed instructions"] }, "gitHub": { "template": { diff --git a/cdk-eventbridge-mesh/example-pattern.json b/cdk-eventbridge-mesh/example-pattern.json index ece9d18a61..26668d5dd9 100644 --- a/cdk-eventbridge-mesh/example-pattern.json +++ b/cdk-eventbridge-mesh/example-pattern.json @@ -34,7 +34,7 @@ "text": ["make deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["make destroy"] diff --git a/cdk-eventbridge-stepfunction-sqs/example-pattern.json b/cdk-eventbridge-stepfunction-sqs/example-pattern.json index 49f3a91c9d..09cf629a62 100644 --- a/cdk-eventbridge-stepfunction-sqs/example-pattern.json +++ b/cdk-eventbridge-stepfunction-sqs/example-pattern.json @@ -39,7 +39,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cdk-kinesis-poison-pill/example-pattern.json b/cdk-kinesis-poison-pill/example-pattern.json index 082a83271c..b658fbdc93 100644 --- a/cdk-kinesis-poison-pill/example-pattern.json +++ b/cdk-kinesis-poison-pill/example-pattern.json @@ -34,7 +34,7 @@ "text": ["make deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["make destroy"] diff --git a/cdk-lambda-appsync/pattern-cdk-lambda-appsync.json b/cdk-lambda-appsync/pattern-cdk-lambda-appsync.json index 06eb398530..86899d0c88 100644 --- a/cdk-lambda-appsync/pattern-cdk-lambda-appsync.json +++ b/cdk-lambda-appsync/pattern-cdk-lambda-appsync.json @@ -16,7 +16,7 @@ ] }, "deploy": { - "text": ["See the Github repo for detailed instructions"] + "text": ["See the GitHub repo for detailed instructions"] }, "gitHub": { "template": { diff --git a/cdk-s3-sqs-lambda-dynamodb/README.md b/cdk-s3-sqs-lambda-dynamodb/README.md deleted file mode 100644 index 40901fe674..0000000000 --- a/cdk-s3-sqs-lambda-dynamodb/README.md +++ /dev/null @@ -1,83 +0,0 @@ -# AWS pattern for s3->sqs->lambda->dynamodb - -This CDK pattern creates a serverless data processing workflow using s3, sqs, lambda and dynamodb - - -Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. - -## Requirements - -* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. -* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured -* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -* [Getting started with the AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html) - -## Deployment Instructions - - 1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: - ``` - git clone https://github.com/aws-samples/serverless-patterns - ``` - 2. Change directory to the pattern directory: - ``` - cd cdk-s3-sqs-lambda-dynamodb - ``` - 3. Install dependencies: - ``` - npm install - ``` - - 4. Synthesize CloudFormation template from the AWS CDK app: - ``` - cdk synth - ``` - 5. Deploy the stack to your default AWS account and region. This command should deploy the serverless workflow to your AWS account. - ``` - cdk deploy - ``` - 6. Browse to the AWS cloudformation console to verify successful deployment of the stack - -## How it works - -The AWS CDK template deploys s3 bucket, SQS queue, lambda and DynamoDB table - -SQS queue is configured to listen to s3 create object events - -lambda is configured to read messages from the SQS queues, process them and insert records into DynamoDB table named EmployeeInfo - - - -## Testing - -Use the [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) to invoke the step function. The function name is in the outputs of the AWS CDK deployment - - 1. Upload the sample csv file from data folder to the s3 bucket name from the output of cloudformation stack - ``` - s3 cp ./data/test.csv s3://BUCKET_NAME/test.csv - ``` - 2. View S3 bucket to see files created in the previously empty bucket: You should see a file has been saved to the S3 bucket: - ``` - Verify the lambda function cloudwatch logs for successfull processing of csv file - - Verify the dynamodb table that the record was inserted - - ``` - -## Cleanup - - 1. Delete the stack - ``` - cdk destroy - ``` - 2. Confirm the stack has been deleted - ``` - aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'ENTER_STACK_NAME')].StackStatus" - ``` - -## Useful commands - - * `cdk ls` list all stacks in the app - * `cdk synth` emits the synthesized CloudFormation template - * `cdk deploy` deploy this stack to your default AWS account/region - * `cdk diff` compare deployed stack with current state - * `cdk docs` open CDK documentation diff --git a/cdk-s3-sqs-lambda-dynamodb/data/test.csv b/cdk-s3-sqs-lambda-dynamodb/data/test.csv deleted file mode 100644 index 67f517b7f8..0000000000 --- a/cdk-s3-sqs-lambda-dynamodb/data/test.csv +++ /dev/null @@ -1,3 +0,0 @@ -id,name,email -1,shashi,shashi@abc.com -2,neha,neha@abc.com \ No newline at end of file diff --git a/cdk-s3-sqs-lambda-dynamodb/lambda/index.py b/cdk-s3-sqs-lambda-dynamodb/lambda/index.py deleted file mode 100644 index d448664dba..0000000000 --- a/cdk-s3-sqs-lambda-dynamodb/lambda/index.py +++ /dev/null @@ -1,37 +0,0 @@ -import csv -import boto3 -import json - -s3 = boto3.client('s3') -dynamodb = boto3.resource('dynamodb') -table_name = 'employeeInfoNew' - -def lambda_handler(event, context): - table = dynamodb.Table(table_name) - # Iterate through the records in the event - for record in event['Records']: - body = json.loads(record['body']) - bucket = body['Records'][0]['s3']['bucket']['name'] - key = body['Records'][0]['s3']['object']['key'] - # Fetch the CSV file from S3 - response = s3.get_object(Bucket=bucket, Key=key) - csv_data = response['Body'].read().decode('utf-8').splitlines() - - # Parse the CSV file - csv_reader = csv.DictReader(csv_data) - for row in csv_reader: - - name = row['name'] - email = row['email'] - - item = { - - 'name': row['name'], - 'email': row['email'] - } - - print(f"Received record: name={name}, email={email}") - # Insert the item into DynamoDB table - table.put_item(Item=item) - - print('CSV processing completed.') diff --git a/cdk-s3-sqs-lambda-dynamodb/lib/my-cdk-project-stack.ts b/cdk-s3-sqs-lambda-dynamodb/lib/my-cdk-project-stack.ts deleted file mode 100644 index d0b9d303b1..0000000000 --- a/cdk-s3-sqs-lambda-dynamodb/lib/my-cdk-project-stack.ts +++ /dev/null @@ -1,75 +0,0 @@ -import * as cdk from 'aws-cdk-lib'; -import { Construct } from 'constructs'; -import * as s3 from 'aws-cdk-lib/aws-s3'; -import * as sqs from 'aws-cdk-lib/aws-sqs'; -import * as s3n from 'aws-cdk-lib/aws-s3-notifications'; -import * as lambda from 'aws-cdk-lib/aws-lambda'; -import * as sqsevts from 'aws-cdk-lib/aws-lambda-event-sources'; -import * as iam from 'aws-cdk-lib/aws-iam' -import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'; - -export class MyCdkProjectStack extends cdk.Stack { - constructor(scope: Construct, id: string, props?: cdk.StackProps) { - super(scope, id, props); - // Create an S3 bucket - const bucket = new s3.Bucket(this, 'MyBucket', { - removalPolicy: cdk.RemovalPolicy.DESTROY, - }); - - // Create an SQS queue - const queue = new sqs.Queue(this, 'MyQueue', { - visibilityTimeout: cdk.Duration.seconds(300), - }); - - // Add S3 object created event notification to the SQS queue - bucket.addObjectCreatedNotification(new s3n.SqsDestination(queue)); - - // Create DynamoDB table - const table = new dynamodb.Table(this, 'EmployeeInfoTable', { - tableName: 'employeeInfoNew', - partitionKey: { name: 'email', type: dynamodb.AttributeType.STRING }, - removalPolicy: cdk.RemovalPolicy.DESTROY, - }); - - // Create a Lambda function - const lambdaFn = new lambda.Function(this, 'MyLambdaFunction', { - runtime: lambda.Runtime.PYTHON_3_8, - handler: 'index.lambda_handler', - code: lambda.Code.fromAsset('lambda'), - }); - - // Add SQS queue as an event source for the Lambda function - lambdaFn.addEventSource(new sqsevts.SqsEventSource(queue)); - - // Grant S3 read permission to the Lambda function - const s3ReadWritePolicy = new iam.PolicyStatement({ - effect: iam.Effect.ALLOW, - actions: ['s3:GetObject'], - resources: [bucket.bucketArn + '/*'], - }); - lambdaFn.addToRolePolicy(s3ReadWritePolicy); - - // Grant read/write permissions to the DynamoDB table - table.grantReadWriteData(lambdaFn); - - // Output the resource names - new cdk.CfnOutput(this, 'BucketNameOutput', { - value: bucket.bucketName, - description: 'S3 Bucket Name', - }); - new cdk.CfnOutput(this, 'QueueNameOutput', { - value: queue.queueName, - description: 'SQS Queue Name', - }); - new cdk.CfnOutput(this, 'TableNameOutput', { - value: table.tableName, - description: 'DynamoDB Table Name', - }); - new cdk.CfnOutput(this, 'FunctionNameOutput', { - value: lambdaFn.functionName, - description: 'Lambda Function Name', - }); - - - } -} diff --git a/cdk-s3-sqs-lambda-dynamodb/package.json b/cdk-s3-sqs-lambda-dynamodb/package.json deleted file mode 100644 index 5a8392197e..0000000000 --- a/cdk-s3-sqs-lambda-dynamodb/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "my-cdk-project", - "version": "0.1.0", - "bin": { - "my-cdk-project": "bin/my-cdk-project.js" - }, - "scripts": { - "build": "tsc", - "watch": "tsc -w", - "test": "jest", - "cdk": "cdk" - }, - "devDependencies": { - "@types/jest": "^27.5.2", - "@types/node": "10.17.27", - "@types/prettier": "2.6.0", - "aws-cdk": "2.46.0", - "jest": "^27.5.1", - "ts-jest": "^27.1.4", - "ts-node": "^10.9.1", - "typescript": "~3.9.7" - }, - "dependencies": { - "aws-cdk-lib": "2.46.0", - "constructs": "^10.0.0", - "csv-parser": "^3.0.0", - "source-map-support": "^0.5.21" - } -} diff --git a/cdk-sfn-s3/example-pattern.json b/cdk-sfn-s3/example-pattern.json index d3cdfbd8bb..55837f5f1d 100644 --- a/cdk-sfn-s3/example-pattern.json +++ b/cdk-sfn-s3/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cdk-sns-sqs-eventbridge-pipes-stepfunctions/example-pattern.json b/cdk-sns-sqs-eventbridge-pipes-stepfunctions/example-pattern.json index 53582ca698..56c0c8ae0d 100644 --- a/cdk-sns-sqs-eventbridge-pipes-stepfunctions/example-pattern.json +++ b/cdk-sns-sqs-eventbridge-pipes-stepfunctions/example-pattern.json @@ -34,7 +34,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy."] diff --git a/cdk-sns-sqs-lambda-dynamodb-dotnet/example-pattern.json b/cdk-sns-sqs-lambda-dynamodb-dotnet/example-pattern.json index 43a56e900f..712dc418ef 100644 --- a/cdk-sns-sqs-lambda-dynamodb-dotnet/example-pattern.json +++ b/cdk-sns-sqs-lambda-dynamodb-dotnet/example-pattern.json @@ -28,17 +28,17 @@ }, "deploy": { "text": [ - "See the Github repo for detailed deployment instructions." + "See the GitHub repo for detailed deployment instructions." ] }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { "text": [ - "See the Github repo for detailed deployment instructions." + "See the GitHub repo for detailed deployment instructions." ] }, "authors": [ diff --git a/claim-check-pattern-cdk/example-pattern.json b/claim-check-pattern-cdk/example-pattern.json index 6353c2acc7..aaccc126fe 100644 --- a/claim-check-pattern-cdk/example-pattern.json +++ b/claim-check-pattern-cdk/example-pattern.json @@ -36,7 +36,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cloudfront-apigw-http-api-lambda-rust/example-pattern.json b/cloudfront-apigw-http-api-lambda-rust/example-pattern.json index 9e4d5d6a01..092dbbb358 100644 --- a/cloudfront-apigw-http-api-lambda-rust/example-pattern.json +++ b/cloudfront-apigw-http-api-lambda-rust/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cloudfront-apigw-rest-api-lambda-dynamodb-sam/example-pattern.json b/cloudfront-apigw-rest-api-lambda-dynamodb-sam/example-pattern.json index e30529a31d..59cebfc79d 100644 --- a/cloudfront-apigw-rest-api-lambda-dynamodb-sam/example-pattern.json +++ b/cloudfront-apigw-rest-api-lambda-dynamodb-sam/example-pattern.json @@ -35,7 +35,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cloudfront-cff-s3-staticsite-dotnet/README.md b/cloudfront-cff-s3-staticsite-dotnet/README.md index c94be18111..cc20d0bfdc 100644 --- a/cloudfront-cff-s3-staticsite-dotnet/README.md +++ b/cloudfront-cff-s3-staticsite-dotnet/README.md @@ -13,8 +13,8 @@ Important: this application uses various AWS services and there are costs associ * [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -* [.Net Core](https://dotnet.microsoft.com/en-us/download/dotnet) - - 3.1 for the CDK Stack - https://dotnet.microsoft.com/en-us/download/dotnet/3.1 +* [.NET 6](https://dotnet.microsoft.com/en-us/download/dotnet) + - 3.1 for the CDK Stack - https://dotnet.microsoft.com/en-us/download/dotnet/6.0 * [Docker](https://docs.docker.com/get-docker/) installed and running * [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) (AWS CDK) installed * [MKDOCS](https://www.mkdocs.org/user-guide/installation/) installed diff --git a/cloudfront-cff-s3-staticsite-dotnet/example-pattern.json b/cloudfront-cff-s3-staticsite-dotnet/example-pattern.json index e518becc6b..bab321a6d0 100644 --- a/cloudfront-cff-s3-staticsite-dotnet/example-pattern.json +++ b/cloudfront-cff-s3-staticsite-dotnet/example-pattern.json @@ -34,17 +34,17 @@ }, "deploy": { "text": [ - "See the Github repo for detailed deployment instructions." + "See the GitHub repo for detailed deployment instructions." ] }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { "text": [ - "See the Github repo for detailed cleanup instructions." + "See the GitHub repo for detailed cleanup instructions." ] }, "authors": [ diff --git a/cloudfront-cff-s3-staticsite-dotnet/src/Mdsite/Mdsite.csproj b/cloudfront-cff-s3-staticsite-dotnet/src/Mdsite/Mdsite.csproj index a8c1de23b5..ef7f4900ac 100644 --- a/cloudfront-cff-s3-staticsite-dotnet/src/Mdsite/Mdsite.csproj +++ b/cloudfront-cff-s3-staticsite-dotnet/src/Mdsite/Mdsite.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net6.0 Major diff --git a/cloudfront-failover-apigw-http-api-lambda-rust/example-pattern.json b/cloudfront-failover-apigw-http-api-lambda-rust/example-pattern.json index 8faea33ed3..9ba527d0e9 100644 --- a/cloudfront-failover-apigw-http-api-lambda-rust/example-pattern.json +++ b/cloudfront-failover-apigw-http-api-lambda-rust/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cloudfront-lambda-edge-s3-cdk/example-patterns.json b/cloudfront-lambda-edge-s3-cdk/example-patterns.json index bbd2cfc99d..e74e1b7214 100644 --- a/cloudfront-lambda-edge-s3-cdk/example-patterns.json +++ b/cloudfront-lambda-edge-s3-cdk/example-patterns.json @@ -51,7 +51,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cloudfront-lambda-url-java/example-pattern.json b/cloudfront-lambda-url-java/example-pattern.json index 87ec539b8e..df4e74bb37 100644 --- a/cloudfront-lambda-url-java/example-pattern.json +++ b/cloudfront-lambda-url-java/example-pattern.json @@ -36,7 +36,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cloudfront-lambda-url-rust/example-pattern.json b/cloudfront-lambda-url-rust/example-pattern.json index 77d6ee5932..d9c27887ee 100644 --- a/cloudfront-lambda-url-rust/example-pattern.json +++ b/cloudfront-lambda-url-rust/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cloudfront-le-apigw-cdk/example-pattern.json b/cloudfront-le-apigw-cdk/example-pattern.json index 700078e097..26984339de 100644 --- a/cloudfront-le-apigw-cdk/example-pattern.json +++ b/cloudfront-le-apigw-cdk/example-pattern.json @@ -33,17 +33,17 @@ }, "deploy": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "authors": [ diff --git a/cloudfront-s3-cdk-dotnet/example-pattern.json b/cloudfront-s3-cdk-dotnet/example-pattern.json index 99b159f2e6..f6c4758ad1 100644 --- a/cloudfront-s3-cdk-dotnet/example-pattern.json +++ b/cloudfront-s3-cdk-dotnet/example-pattern.json @@ -27,7 +27,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cloudfront-waf-s3-cdk/README.md b/cloudfront-waf-s3-cdk/README.md index 7abe4df6f5..f7baed90ff 100644 --- a/cloudfront-waf-s3-cdk/README.md +++ b/cloudfront-waf-s3-cdk/README.md @@ -14,7 +14,7 @@ Important: this application uses various AWS services and there are costs associ * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured * [AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/cli.html) installed and configured * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -* [.Net Core 3.1 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/3.1) installed +* [.NET 6](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) installed ## Deployment Instructions diff --git a/cloudfront-waf-s3-cdk/src/src.csproj b/cloudfront-waf-s3-cdk/src/src.csproj index 5ff45379e1..17e8f9efa1 100644 --- a/cloudfront-waf-s3-cdk/src/src.csproj +++ b/cloudfront-waf-s3-cdk/src/src.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net6.0 enable enable latest diff --git a/cloudmap-fargate-terraform/example-pattern.json b/cloudmap-fargate-terraform/example-pattern.json index e7b3386203..4cde32d66b 100644 --- a/cloudmap-fargate-terraform/example-pattern.json +++ b/cloudmap-fargate-terraform/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cloudtrail-lambda-dynamo-cdk/example-pattern.json b/cloudtrail-lambda-dynamo-cdk/example-pattern.json index a627cae100..b1e191d30f 100644 --- a/cloudtrail-lambda-dynamo-cdk/example-pattern.json +++ b/cloudtrail-lambda-dynamo-cdk/example-pattern.json @@ -35,7 +35,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cloudtrail-lambda-slack-sam/example-pattern.json b/cloudtrail-lambda-slack-sam/example-pattern.json index 578535f576..2cc596d1ab 100644 --- a/cloudtrail-lambda-slack-sam/example-pattern.json +++ b/cloudtrail-lambda-slack-sam/example-pattern.json @@ -39,7 +39,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cloudwatch-logs-subscription-firehose-cdk/example-pattern.json b/cloudwatch-logs-subscription-firehose-cdk/example-pattern.json index cf3fc1d427..5e0d56f8ae 100644 --- a/cloudwatch-logs-subscription-firehose-cdk/example-pattern.json +++ b/cloudwatch-logs-subscription-firehose-cdk/example-pattern.json @@ -39,12 +39,12 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { "text": [ - "See the Github repo for detailed cleanup instructions." + "See the GitHub repo for detailed cleanup instructions." ] }, "authors": [ diff --git a/cloudwatch-logs-subscription-lambda-cdk/example-pattern.json b/cloudwatch-logs-subscription-lambda-cdk/example-pattern.json index 43a47de9cc..6459cf737b 100644 --- a/cloudwatch-logs-subscription-lambda-cdk/example-pattern.json +++ b/cloudwatch-logs-subscription-lambda-cdk/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cloudwatch-logs-subscription-lambda-sam/example-pattern.json b/cloudwatch-logs-subscription-lambda-sam/example-pattern.json index 5edb32db15..c6c048cce5 100644 --- a/cloudwatch-logs-subscription-lambda-sam/example-pattern.json +++ b/cloudwatch-logs-subscription-lambda-sam/example-pattern.json @@ -34,7 +34,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cloudwatch-rum-cognito/.pattern.json b/cloudwatch-rum-cognito/.pattern.json index af67c6393a..5caff8622c 100644 --- a/cloudwatch-rum-cognito/.pattern.json +++ b/cloudwatch-rum-cognito/.pattern.json @@ -60,7 +60,7 @@ "testing": { "headline": "Testing", "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cognito-restapi-vpclink-cdk/example-pattern.json b/cognito-restapi-vpclink-cdk/example-pattern.json index bbe7d4f8cd..295324d7e0 100644 --- a/cognito-restapi-vpclink-cdk/example-pattern.json +++ b/cognito-restapi-vpclink-cdk/example-pattern.json @@ -45,7 +45,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cognito-restapi/README.md b/cognito-restapi/README.md index dde5accd6d..44891a07ce 100644 --- a/cognito-restapi/README.md +++ b/cognito-restapi/README.md @@ -6,7 +6,7 @@ It assumes that the Cognito User Pool already exists and takes the Cognito User Note: when deploying this pattern, *CAPABILITY_IAM* is required. -Learn more about this pattern at Serverless Land Patterns: [https://serverlessland.com/patterns/cognito-restapi](https://serverlessland.com/patterns/cognito-restapi) +Learn more about this pattern at Serverless Land Patterns: [https://serverlessland.com/patterns/apigw-cognito-authorizer-sam-nodejs](https://serverlessland.com/patterns/apigw-cognito-authorizer-sam-nodejs) Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. @@ -43,7 +43,7 @@ Important: this application uses various AWS services and there are costs associ ## Testing -The stack will output the **api endpoint**. Use *curl* to make an HTTP request to the API Gateway endpoint that includes a Header with the authorization toekn to test the Cognito User Pools Authorizer. +The stack will output the **api endpoint**. Use *curl* to make an HTTP request to the API Gateway endpoint that includes a Header with the authorization token to test the Cognito User Pools Authorizer. ``` curl -i https://{apiId}.execute-api.{region}.amazonaws.com/Prod -H "authorizationToken: {tokenProvidedByCognito}" diff --git a/cognito-restapi/example-pattern.json b/cognito-restapi/example-pattern.json index 60039426f8..9114ad3b1c 100644 --- a/cognito-restapi/example-pattern.json +++ b/cognito-restapi/example-pattern.json @@ -34,7 +34,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cognito-sns-sms-origination-id/README.md b/cognito-sns-sms-origination-id/README.md new file mode 100644 index 0000000000..6bc8462c37 --- /dev/null +++ b/cognito-sns-sms-origination-id/README.md @@ -0,0 +1,155 @@ +# Amazon Cognito - SNS SMS Originating Identity +This pattern will provide guidance on how to use Amazon Cognito’s lambda triggers to select origination IDs to suit your needs and send sms message using Amazon SNS SMS + +Amazon Cognito sends SMS on your behalf by publishing the request to send SMS for MFA and “other” use cases. Cognito uses the default settings when choosing origination IDs. If you cannot secure the same origination ID, or if you operate with multiple origination IDs with your account, you will need to select the correct origination ID for your Cognito use cases. This pattern will provide guidance on how to use Cognito’s lambda triggers to select origination IDs to suit your needs. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/cognito-sns-sms-origination-id + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) installed +* [Node and NPM](https://nodejs.org/en/download/) installed +* [AWS Cloud Devolpment Kit](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html) installed + +## Getting Started +The entire solution is built on CDK typescript and CustomSMSSender Lambda function in Node.js 18.x. The instructions below shows the prerequisities, deployment instructions and testing steps. + + +### Prerequisites +* Amazon SNS SMS account is moved out of [SMS Sandbox](https://aws.amazon.com/blogs/compute/introducing-the-sms-sandbox-for-amazon-sns/) +* Dedicated OriginationNumber or SenderID registered with [Aamazon SNS](https://docs.aws.amazon.com/sns/latest/dg/channels-sms-originating-identities-origination-numbers.html) + + + +### Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +2. Change directory to the pattern directory: + ``` + cd cognito-sns-sms-origination-id + ``` +3. Configure origination identities for countries you want use sending sms messages from cognito: + + ``` + cd lambda/code + ``` + + * Open orignationIdConfig.json file and edit origination identities as per country. + + ``` + { + "US": { + "OriginationNumber": "+12223334444" + }, + "CA": { + "OriginationNumber": "123456" + }, + "IN": { + "SenderID": "MySenderID" + } + } + ``` + **Note:** OriginationNumber can be Long code or Short code and need to be registered prior with Amazon SNS serivce. + +4. Change directory and install CustomSMSSender Lambda function Node.js dependencies + ``` + cd - && cd lambda/layer/nodejs + ``` + + ``` + npm init -y + npm install --save google-libphonenumber @aws-crypto/client-node base64-js + ``` + +5. Change directory to main directory cognito-sns-sms-origination-id: + ``` + cd - + ``` +6. Run below command to install AWS CDK required dependancies + ``` + npm install + ``` +7. This project uses typescript as client language for AWS CDK. Run the below command to compile typescript to javascript + ``` + npm run build + ``` +8. Deploy the stack: + ``` + cdk deploy --all + ``` + + **Note:** Make sure you deploy this solution in AWS Region you have dedicated [OriginationNumber](https://docs.aws.amazon.com/sns/latest/dg/channels-sms-originating-identities-origination-numbers.html) or SenderID registered with Aamazon SNS for the country you want to send SMS messages. + + +### How it works +Amazon Cognito can be customized using lambda triggers for different flows. This solution uses the [custom SMS sender lambda trigger](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-custom-sms-sender.html) to dynamically identify the country code using phone number library and set the origination id from the origination configuration file and send SMS message. + + +#### Architecture Diagram +architecture + +### Testing + +How to test this pattern and send SMS message from Amazon Cognito CustomSMSSender Lambda function. + + **Note:** Make sure you edited orignationIdConfig.json in Step 3 and added dedicated [OriginationNumber](https://docs.aws.amazon.com/sns/latest/dg/channels-sms-originating-identities-origination-numbers.html) or SenderID registered with Aamazon SNS for the country you want to test. If there is no OriginationNumber or SenderID configured for the country, Amazon SNS will use default settings. + +For example: +* If you want to Send SMS messages to US phonenumber you will need have Toll-Free Number or 10DLC or Short Code registered with Aamazon SNS + +Testing mechanisms using AWS CLI: + +In this test we will trigger Amazon Cognito "CustomSMSSender_SignUp" [event](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-custom-sms-sender.html#trigger-source) from Amazon Cognito and send MFA SMS code + +1. Run the below CLI command, replace the --client-id with cognitocustomsmssenderclientappid from CDK output, and update other inputs fields: TestUserEmail, TestUserName, TestPhonenumber, AWSRegion + + ``` + aws cognito-idp sign-up --client-id --username --password --user-attributes Name="email",Value="" Name="name",Value="" Name="phone_number",Value="" --region + ``` + **Example:** + ``` + aws cognito-idp sign-up --client-id 781hakalbosbt4tu0g6s236uk9 --username jane@example.com --password Test@654321 --user-attributes Name="email",Value="jane@example.com" Name="name",Value="Jane" Name="phone_number",Value="+12223334444" --region us-west-2 + ``` + **Note:** Password Requirements 8-character minimum length and at least 1 number, 1 lowercase letter, and 1 special character + + CLI command response: + ``` + { + "UserConfirmed": false, + "CodeDeliveryDetails": { + "Destination": "+********2076", + "DeliveryMedium": "SMS", + "AttributeName": "phone_number" + }, + "UserSub": "e68c3847-f1bb-4399-a642-361bcee259c5" + } + ``` +2. You can verify the CustomSMSSender Lambda function cognito-customsmssender CloudWatch log to check origination identities used to SMS messages + + Go to Lambda function cognito-customsmssender CloudWatch logs + + * **Sample log with origination identities used from orignationIdConfig.json file** + architecture + + + * **Sample log if no origination identities configured orignationIdConfig.json file for destination country** + architecture + +### Cleanup + +1. Run the given command to delete the resources that were created. It might take some time for the CloudFormation stack to get deleted + ``` + cdk destroy --all + ``` + +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 \ No newline at end of file diff --git a/cognito-sns-sms-origination-id/bin/cognito-sns-sms-origination-identity.d.ts b/cognito-sns-sms-origination-id/bin/cognito-sns-sms-origination-identity.d.ts new file mode 100644 index 0000000000..c5f71d112a --- /dev/null +++ b/cognito-sns-sms-origination-id/bin/cognito-sns-sms-origination-identity.d.ts @@ -0,0 +1,2 @@ +#!/usr/bin/env node +import 'source-map-support/register'; diff --git a/cognito-sns-sms-origination-id/bin/cognito-sns-sms-origination-identity.js b/cognito-sns-sms-origination-id/bin/cognito-sns-sms-origination-identity.js new file mode 100644 index 0000000000..e228056a25 --- /dev/null +++ b/cognito-sns-sms-origination-id/bin/cognito-sns-sms-origination-identity.js @@ -0,0 +1,20 @@ +#!/usr/bin/env node +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +require("source-map-support/register"); +const cdk = require("aws-cdk-lib"); +const cognito_sns_sms_origination_identity_stack_1 = require("../lib/cognito-sns-sms-origination-identity-stack"); +const app = new cdk.App(); +new cognito_sns_sms_origination_identity_stack_1.CognitoSnsSmsOriginationIdentityStack(app, 'CognitoSnsSmsOriginationIdentityStack', { +/* If you don't specify 'env', this stack will be environment-agnostic. + * Account/Region-dependent features and context lookups will not work, + * but a single synthesized template can be deployed anywhere. */ +/* Uncomment the next line to specialize this stack for the AWS Account + * and Region that are implied by the current CLI configuration. */ +// env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, +/* Uncomment the next line if you know exactly what Account and Region you + * want to deploy the stack to. */ +// env: { account: '123456789012', region: 'us-east-1' }, +/* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ +}); +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29nbml0by1zbnMtc21zLW9yaWdpbmF0aW9uLWlkZW50aXR5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiY29nbml0by1zbnMtc21zLW9yaWdpbmF0aW9uLWlkZW50aXR5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLHVDQUFxQztBQUNyQyxtQ0FBbUM7QUFDbkMsa0hBQTBHO0FBRTFHLE1BQU0sR0FBRyxHQUFHLElBQUksR0FBRyxDQUFDLEdBQUcsRUFBRSxDQUFDO0FBQzFCLElBQUksa0ZBQXFDLENBQUMsR0FBRyxFQUFFLHVDQUF1QyxFQUFFO0FBQ3RGOztpRUFFaUU7QUFFakU7bUVBQ21FO0FBQ25FLDZGQUE2RjtBQUU3RjtrQ0FDa0M7QUFDbEMseURBQXlEO0FBRXpELDhGQUE4RjtDQUMvRixDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIjIS91c3IvYmluL2VudiBub2RlXG5pbXBvcnQgJ3NvdXJjZS1tYXAtc3VwcG9ydC9yZWdpc3Rlcic7XG5pbXBvcnQgKiBhcyBjZGsgZnJvbSAnYXdzLWNkay1saWInO1xuaW1wb3J0IHsgQ29nbml0b1Nuc1Ntc09yaWdpbmF0aW9uSWRlbnRpdHlTdGFjayB9IGZyb20gJy4uL2xpYi9jb2duaXRvLXNucy1zbXMtb3JpZ2luYXRpb24taWRlbnRpdHktc3RhY2snO1xuXG5jb25zdCBhcHAgPSBuZXcgY2RrLkFwcCgpO1xubmV3IENvZ25pdG9TbnNTbXNPcmlnaW5hdGlvbklkZW50aXR5U3RhY2soYXBwLCAnQ29nbml0b1Nuc1Ntc09yaWdpbmF0aW9uSWRlbnRpdHlTdGFjaycsIHtcbiAgLyogSWYgeW91IGRvbid0IHNwZWNpZnkgJ2VudicsIHRoaXMgc3RhY2sgd2lsbCBiZSBlbnZpcm9ubWVudC1hZ25vc3RpYy5cbiAgICogQWNjb3VudC9SZWdpb24tZGVwZW5kZW50IGZlYXR1cmVzIGFuZCBjb250ZXh0IGxvb2t1cHMgd2lsbCBub3Qgd29yayxcbiAgICogYnV0IGEgc2luZ2xlIHN5bnRoZXNpemVkIHRlbXBsYXRlIGNhbiBiZSBkZXBsb3llZCBhbnl3aGVyZS4gKi9cblxuICAvKiBVbmNvbW1lbnQgdGhlIG5leHQgbGluZSB0byBzcGVjaWFsaXplIHRoaXMgc3RhY2sgZm9yIHRoZSBBV1MgQWNjb3VudFxuICAgKiBhbmQgUmVnaW9uIHRoYXQgYXJlIGltcGxpZWQgYnkgdGhlIGN1cnJlbnQgQ0xJIGNvbmZpZ3VyYXRpb24uICovXG4gIC8vIGVudjogeyBhY2NvdW50OiBwcm9jZXNzLmVudi5DREtfREVGQVVMVF9BQ0NPVU5ULCByZWdpb246IHByb2Nlc3MuZW52LkNES19ERUZBVUxUX1JFR0lPTiB9LFxuXG4gIC8qIFVuY29tbWVudCB0aGUgbmV4dCBsaW5lIGlmIHlvdSBrbm93IGV4YWN0bHkgd2hhdCBBY2NvdW50IGFuZCBSZWdpb24geW91XG4gICAqIHdhbnQgdG8gZGVwbG95IHRoZSBzdGFjayB0by4gKi9cbiAgLy8gZW52OiB7IGFjY291bnQ6ICcxMjM0NTY3ODkwMTInLCByZWdpb246ICd1cy1lYXN0LTEnIH0sXG5cbiAgLyogRm9yIG1vcmUgaW5mb3JtYXRpb24sIHNlZSBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vY2RrL2xhdGVzdC9ndWlkZS9lbnZpcm9ubWVudHMuaHRtbCAqL1xufSk7Il19 \ No newline at end of file diff --git a/cognito-sns-sms-origination-id/bin/cognito-sns-sms-origination-identity.ts b/cognito-sns-sms-origination-id/bin/cognito-sns-sms-origination-identity.ts new file mode 100644 index 0000000000..89633e5304 --- /dev/null +++ b/cognito-sns-sms-origination-id/bin/cognito-sns-sms-origination-identity.ts @@ -0,0 +1,21 @@ +#!/usr/bin/env node +import 'source-map-support/register'; +import * as cdk from 'aws-cdk-lib'; +import { CognitoSnsSmsOriginationIdentityStack } from '../lib/cognito-sns-sms-origination-identity-stack'; + +const app = new cdk.App(); +new CognitoSnsSmsOriginationIdentityStack(app, 'CognitoSnsSmsOriginationIdentityStack', { + /* If you don't specify 'env', this stack will be environment-agnostic. + * Account/Region-dependent features and context lookups will not work, + * but a single synthesized template can be deployed anywhere. */ + + /* Uncomment the next line to specialize this stack for the AWS Account + * and Region that are implied by the current CLI configuration. */ + // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, + + /* Uncomment the next line if you know exactly what Account and Region you + * want to deploy the stack to. */ + // env: { account: '123456789012', region: 'us-east-1' }, + + /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ +}); \ No newline at end of file diff --git a/cognito-sns-sms-origination-id/cdk.json b/cognito-sns-sms-origination-id/cdk.json new file mode 100644 index 0000000000..eb16e93442 --- /dev/null +++ b/cognito-sns-sms-origination-id/cdk.json @@ -0,0 +1,51 @@ +{ + "app": "npx ts-node --prefer-ts-exts bin/cognito-sns-sms-origination-identity.ts", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "**/*.d.ts", + "**/*.js", + "tsconfig.json", + "package*.json", + "yarn.lock", + "node_modules", + "test" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-redshift:columnId": true, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true + } +} diff --git a/cognito-sns-sms-origination-id/example-pattern.json b/cognito-sns-sms-origination-id/example-pattern.json new file mode 100644 index 0000000000..2f3f7a42aa --- /dev/null +++ b/cognito-sns-sms-origination-id/example-pattern.json @@ -0,0 +1,66 @@ +{ + "title": "Amazon Cognito - SNS SMS Origination Id", + "description": "This pattern creates an Amazon Cognito userpool and an AWS Lambda function.", + "language": "TypeScript", + "level": "300", + "framework": "CDK", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern deploys an Amazon Cognito userpool and an AWS lambda function with required polices. In general, Amazon Cognito sends SMS on your behalf by publishing the request to send SMS for MFA and other use cases. Cognito uses the default settings when choosing origination IDs. If you cannot secure the same origination ID, or if you operate with multiple origination IDs with your account, you will need to select the correct origination ID for your Cognito use cases. This solution will provide guidance on how to use Cognito with lambda triggers to select origination IDs to suit your needs." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/cognito-sns-sms-origination-id", + "templateURL": "serverless-patterns/cognito-sns-sms-origination-id", + "projectFolder": "cognito-sns-sms-origination-id", + "templateFile": "lib/cognito-sns-sms-origination-identity-stack.ts" + } + }, + "resources": { + "bullets": [ + { + "text": "Amazon Cognito", + "link": "https://aws.amazon.com/cognito/" + }, + { + "text": "Amazon SNS", + "link": "https://aws.amazon.com/sns/" + }, + { + "text": "Custom SMS sender Lambda trigger", + "link": "https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-custom-sms-sender.html" + } + ] + }, + "deploy": { + "text": [ + "cdk deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: cdk destroy." + ] + }, + "authors": [ + { + "name": "Vinodh Kannan Sadayamuthu", + "image": "https://i.postimg.cc/sx0BqLmv/vinodh.jpg", + "bio": "Vinodh is a Specialist Solution Architect at AWS.", + "linkedin": "vinodh-aws" + }, + { + "name": "Sarath Kumar K.S", + "image": "https://i.postimg.cc/5NhyzLRd/Sarath.png", + "bio": "Sarath is a Senior Technical Account Manager at AWS.", + "linkedin": "kssarathkumar" + } + ] +} \ No newline at end of file diff --git a/cognito-sns-sms-origination-id/images/cognito-sns-sms-id.jpg b/cognito-sns-sms-origination-id/images/cognito-sns-sms-id.jpg new file mode 100644 index 0000000000..755a9723ee Binary files /dev/null and b/cognito-sns-sms-origination-id/images/cognito-sns-sms-id.jpg differ diff --git a/cognito-sns-sms-origination-id/images/no-orignationIdConfig.png b/cognito-sns-sms-origination-id/images/no-orignationIdConfig.png new file mode 100644 index 0000000000..89c8f69dc6 Binary files /dev/null and b/cognito-sns-sms-origination-id/images/no-orignationIdConfig.png differ diff --git a/cognito-sns-sms-origination-id/images/orignationIdConfig-used.png b/cognito-sns-sms-origination-id/images/orignationIdConfig-used.png new file mode 100644 index 0000000000..9a359ac3e2 Binary files /dev/null and b/cognito-sns-sms-origination-id/images/orignationIdConfig-used.png differ diff --git a/cdk-s3-sqs-lambda-dynamodb/jest.config.js b/cognito-sns-sms-origination-id/jest.config.js similarity index 100% rename from cdk-s3-sqs-lambda-dynamodb/jest.config.js rename to cognito-sns-sms-origination-id/jest.config.js diff --git a/cognito-sns-sms-origination-id/lambda/code/index.mjs b/cognito-sns-sms-origination-id/lambda/code/index.mjs new file mode 100644 index 0000000000..b1bbf84ddc --- /dev/null +++ b/cognito-sns-sms-origination-id/lambda/code/index.mjs @@ -0,0 +1,133 @@ +// ES Modules import +import { SNSClient, PublishCommand } from "@aws-sdk/client-sns"; +import b64 from 'base64-js'; +import libphonenumber from 'google-libphonenumber'; +import encryptionSdk from '@aws-crypto/client-node'; +//Configure orignation number or sender id as per destination country iso code +import { createRequire } from "module"; +const require = createRequire(import.meta.url); +const orignationIdConfig = require("./orignationIdConfig.json"); +//Set up an encryption client with an explicit commitment policy: REQUIRE_ENCRYPT_ALLOW_DECRYPT +const { encrypt, decrypt } = encryptionSdk.buildClient(encryptionSdk.CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT); +const generatorKeyId = process.env.KEY_ALIAS; +const keyIds = [process.env.KEY_ARN]; +const keyring = new encryptionSdk.KmsKeyringNode({ generatorKeyId, keyIds }) +// SNS Client +const client = new SNSClient(); +async function snsPublish(message, phoneNumber) { + //SNS SMS Parameters + const [messageAttributes] = + await Promise.all([ + getSMSMessageAttributes(phoneNumber) + ]); + const input = { // PublishInput + Message: message, + PhoneNumber: phoneNumber.toString(), + MessageAttributes: messageAttributes + }; + const command = new PublishCommand(input); + const response = await client.send(command); + return response +} +// Function to get ISO Country Code from Phone Number +// Phone Number should be E.164 format +function getISOCode(phoneNumber) { + // Get an instance of `PhoneNumberUtil`. + const phoneUtil = libphonenumber.PhoneNumberUtil.getInstance(); + // Parse number with country code and keep raw input. + const number = phoneUtil.parseAndKeepRawInput(phoneNumber); + //Get ISO Code from getRegionCodeForNumber + const countryCode = phoneUtil.getRegionCodeForNumber(number); + console.log("SMS Message Sent to Country Code:" + countryCode); + return countryCode; +} +// Function to set SNS SMS Message Attributes as per origination configuration and destination country code +function getSMSMessageAttributes(phoneNumber) { + //SNS SMS MessageAttributes + let senderId; + let originationNumber; + //Default SMS Message Attributes if no orgination Id configured + let smsMessageAttributes = { + 'AWS.SNS.SMS.SMSType': { + DataType: 'String', /* required */ + StringValue: 'Transactional' + } + }; + // Get ISO country code of the phoneNumber + let isoCountryCode = getISOCode(phoneNumber); + // Check if origination configuration has orgination identites configured for destination country code + if (isoCountryCode in orignationIdConfig) { + let originationId = Object.keys(orignationIdConfig[isoCountryCode]); + let originationType = originationId[0]; + // Check if ISO country code has orignation number or sender id + switch (originationType) { + case "SenderID": + // Set Sender Id + senderId = orignationIdConfig[isoCountryCode]['SenderID']; + console.log("Sender Id used:" + senderId); + // Create publish parameters with Sender Id + smsMessageAttributes = { + 'AWS.SNS.SMS.SMSType': { + DataType: 'String', /* required */ + StringValue: 'Transactional' + }, + 'AWS.SNS.SMS.SenderID': { + DataType: 'String', /* required */ + StringValue: senderId + } + }; + return smsMessageAttributes; + case "OriginationNumber": + //Set originationNumber + originationNumber = orignationIdConfig[isoCountryCode]['OriginationNumber']; + console.log("Origination number used:" + originationNumber); + // Create publish parameters with Sender Id + smsMessageAttributes = { + 'AWS.SNS.SMS.SMSType': { + DataType: 'String', /* required */ + StringValue: 'Transactional' + }, + 'AWS.MM.SMS.OriginationNumber': { + DataType: 'String', /* required */ + StringValue: originationNumber + } + }; + return smsMessageAttributes; + default: + //Default SMS message attributes if no orgination Id configured + console.log("No Origination number used:" + originationNumber); + return smsMessageAttributes; + } + }else { + //Default SMS message attributes if no orgination Id configured + console.log("No origination identity configured for country code:"+ isoCountryCode); + return smsMessageAttributes; + } +} +//main lambda handler +export const handler = async (event) => { + // Log triggerSource + console.log("TriggerSource:" + event.triggerSource) + //SMS Attributes + let plainTextCode; + let smsMessage; + let destinationPhoneNumber; + //Decrypt the secret code using encryption SDK. + if (event.request.code) { + const { plaintext, messageHeader } = await decrypt(keyring, b64.toByteArray(event.request.code)); + //PlainTextCode now contains the decrypted secret. + plainTextCode = plaintext; + //Get destination phonenumber + destinationPhoneNumber = event.request.userAttributes.phone_number; + } + //Include the temporary MFA code in the sms message. + smsMessage = "Your OTP Code from Custom SMS Sender: " + plainTextCode; + //Send SMS Messaging + const [result] = + await Promise.all([ + snsPublish(smsMessage, destinationPhoneNumber) + ]); + // Print SNS SMS Message Id + console.log("SNS Publish MessageId:"+result.MessageId); + return result; +}; diff --git a/cognito-sns-sms-origination-id/lambda/code/orignationIdConfig.json b/cognito-sns-sms-origination-id/lambda/code/orignationIdConfig.json new file mode 100644 index 0000000000..60748c304a --- /dev/null +++ b/cognito-sns-sms-origination-id/lambda/code/orignationIdConfig.json @@ -0,0 +1,8 @@ +{ + "US": { + "OriginationNumber": "+18668246244" + }, + "IN": { + "SenderID": "MySenderID" + } +} \ No newline at end of file diff --git a/cognito-sns-sms-origination-id/lib/cognito-sns-sms-origination-identity-stack.d.ts b/cognito-sns-sms-origination-id/lib/cognito-sns-sms-origination-identity-stack.d.ts new file mode 100644 index 0000000000..5c79c98225 --- /dev/null +++ b/cognito-sns-sms-origination-id/lib/cognito-sns-sms-origination-identity-stack.d.ts @@ -0,0 +1,5 @@ +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +export declare class CognitoSnsSmsOriginationIdentityStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps); +} diff --git a/cognito-sns-sms-origination-id/lib/cognito-sns-sms-origination-identity-stack.js b/cognito-sns-sms-origination-id/lib/cognito-sns-sms-origination-identity-stack.js new file mode 100644 index 0000000000..82cb0b5f96 --- /dev/null +++ b/cognito-sns-sms-origination-id/lib/cognito-sns-sms-origination-identity-stack.js @@ -0,0 +1,136 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.CognitoSnsSmsOriginationIdentityStack = void 0; +const cdk = require("aws-cdk-lib"); +const aws_cdk_lib_1 = require("aws-cdk-lib"); +const kms = require("aws-cdk-lib/aws-kms"); +const cognito = require("aws-cdk-lib/aws-cognito"); +const lambda = require("aws-cdk-lib/aws-lambda"); +const iam = require("aws-cdk-lib/aws-iam"); +const aws_cognito_1 = require("aws-cdk-lib/aws-cognito"); +class CognitoSnsSmsOriginationIdentityStack extends cdk.Stack { + constructor(scope, id, props) { + super(scope, id, props); + // Kms key for Cognito custom sms sender + const cognito_custom_sms_sender_kmskey = new kms.Key(this, 'cognito-customsmssender-kmskey', { + removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY, + pendingWindow: aws_cdk_lib_1.Duration.days(7), + alias: 'alias/customsmssenderKey', + description: 'KMS key for encrypting cognito code', + enableKeyRotation: false, + }); + // Permissions required by Cognito encrypt code using KMS key + cognito_custom_sms_sender_kmskey.addToResourcePolicy(new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + principals: [new iam.ServicePrincipal('cognito-idp.amazonaws.com')], + actions: ['kms:CreateGrant', 'kms:Encrypt'], + resources: ['*'], + })); + //Permissions required by AWS Lambda function decrypt code from cognito + const cognito_custom_sms_sender_lambda_permissions = { + "Version": "2012-10-17", + "Statement": [ + { + "sid": "cognito-customsmssender-sns-permission", + "Effect": "Allow", + "Action": [ + "sns:publish" + ], + "Resource": "*" + }, + { + "sid": "cognito-customsmssender-kms-permission", + "Effect": "Allow", + "Action": [ + "kms:Decrypt" + ], + "Resource": "*" + } + ] + }; + //Creating an IAM role for AWS Lambda function + const lambda_role = new iam.Role(this, 'cognito-customsmssender-lambda-role', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), + description: 'Lambda Execution Role for Cognito Custom SMS Sender', + roleName: 'cognito-custom-sms-sender-lambda-role', + managedPolicies: [iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole')] + }); + //Creating an AWS Lambda Role Policy + const lambda_policy = new iam.Policy(this, 'cognito-customsmssender-lambda-policy', { + document: iam.PolicyDocument.fromJson(cognito_custom_sms_sender_lambda_permissions) + }); + lambda_policy.attachToRole(lambda_role); + //Creating an AWS Lambda layers + const cognito_custom_sms_sender_lambda_layer = new lambda.LayerVersion(this, 'cognito-customsmssender-nodejs-lib', { + code: new lambda.AssetCode('lambda/layer/'), + compatibleRuntimes: [lambda.Runtime.NODEJS_18_X], + compatibleArchitectures: [lambda.Architecture.X86_64, lambda.Architecture.ARM_64], + removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY, + }); + //Creating CustomSMSSender Lambda function + const cognito_custom_sms_sender_lambda_function = new lambda.Function(this, 'cognito-customsmssender-lambda-function', { + runtime: lambda.Runtime.NODEJS_18_X, + code: lambda.Code.fromAsset('lambda/code'), + description: 'Customer SMS Sender Lambda function', + role: lambda_role, + handler: 'index.handler', + layers: [cognito_custom_sms_sender_lambda_layer], + environment: { + 'KMS_KEY_ARN': cognito_custom_sms_sender_kmskey.keyArn + } + }); + // Creating cognito user pool + const cognito_custom_sms_sender_userpool = new cognito.UserPool(this, 'custom-sms-sender-userpool', { + userPoolName: 'custom-sms-sender-userpool', + selfSignUpEnabled: true, + signInCaseSensitive: false, + signInAliases: { + email: true, + phone: true, + }, + autoVerify: { + email: true, + phone: true + }, + standardAttributes: { + fullname: { + required: true, + mutable: true, + }, + email: { + required: true, + mutable: true, + }, + phoneNumber: { + required: true, + mutable: true, + } + }, + mfa: cognito.Mfa.REQUIRED, + mfaSecondFactor: { + sms: true, + otp: false, + }, + passwordPolicy: { + minLength: 8, + requireLowercase: true, + requireDigits: true, + requireSymbols: true, + }, + removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY, + accountRecovery: cognito.AccountRecovery.EMAIL_ONLY, + customSenderKmsKey: cognito_custom_sms_sender_kmskey + }); + //Enable CustomSMSSender Lambda trigger + cognito_custom_sms_sender_userpool.addTrigger(aws_cognito_1.UserPoolOperation.CUSTOM_SMS_SENDER, cognito_custom_sms_sender_lambda_function); + //Creating Cognito pool app client + const cognito_custom_sms_sender_client_app = cognito_custom_sms_sender_userpool.addClient("cognito-custom-sms-sender-app-client", { + userPoolClientName: "cognito-custom-sms-sender-client-app-id" + }); + new aws_cdk_lib_1.CfnOutput(this, "cognito-custom-sms-sender-client-app-id", { + value: cognito_custom_sms_sender_client_app.userPoolClientId, + }); + } +} +exports.CognitoSnsSmsOriginationIdentityStack = CognitoSnsSmsOriginationIdentityStack; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29nbml0by1zbnMtc21zLW9yaWdpbmF0aW9uLWlkZW50aXR5LXN0YWNrLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiY29nbml0by1zbnMtc21zLW9yaWdpbmF0aW9uLWlkZW50aXR5LXN0YWNrLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLG1DQUFtQztBQUNuQyw2Q0FBaUU7QUFDakUsMkNBQTJDO0FBQzNDLG1EQUFtRDtBQUNuRCxpREFBaUQ7QUFDakQsMkNBQTJDO0FBRzNDLHlEQUE0RDtBQUc1RCxNQUFhLHFDQUFzQyxTQUFRLEdBQUcsQ0FBQyxLQUFLO0lBQ2xFLFlBQVksS0FBZ0IsRUFBRSxFQUFVLEVBQUUsS0FBc0I7UUFDOUQsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFFeEIseUNBQXlDO1FBQ3ZDLE1BQU0sZ0NBQWdDLEdBQUcsSUFBSSxHQUFHLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxnQ0FBZ0MsRUFBQztZQUMxRixhQUFhLEVBQUUsMkJBQWEsQ0FBQyxPQUFPO1lBQ3BDLGFBQWEsRUFBRSxzQkFBUSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7WUFDL0IsS0FBSyxFQUFFLDBCQUEwQjtZQUNqQyxXQUFXLEVBQUUscUNBQXFDO1lBQ2xELGlCQUFpQixFQUFFLEtBQUs7U0FDekIsQ0FBQyxDQUFDO1FBRUwsNkRBQTZEO1FBQzdELGdDQUFnQyxDQUFDLG1CQUFtQixDQUM5QyxJQUFJLEdBQUcsQ0FBQyxlQUFlLENBQUM7WUFDdEIsTUFBTSxFQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsS0FBSztZQUN2QixVQUFVLEVBQUUsQ0FBQyxJQUFJLEdBQUcsQ0FBQyxnQkFBZ0IsQ0FBQywyQkFBMkIsQ0FBQyxDQUFDO1lBQ25FLE9BQU8sRUFBRSxDQUFDLGlCQUFpQixFQUFFLGFBQWEsQ0FBQztZQUMzQyxTQUFTLEVBQUUsQ0FBQyxHQUFHLENBQUM7U0FDakIsQ0FBQyxDQUNILENBQUM7UUFFTix1RUFBdUU7UUFDckUsTUFBTSw0Q0FBNEMsR0FBRztZQUMvQyxTQUFTLEVBQUUsWUFBWTtZQUN2QixXQUFXLEVBQUU7Z0JBQ1Q7b0JBQ0ksS0FBSyxFQUFDLHdDQUF3QztvQkFDOUMsUUFBUSxFQUFFLE9BQU87b0JBQ2pCLFFBQVEsRUFBRTt3QkFDTixhQUFhO3FCQUNoQjtvQkFDRCxVQUFVLEVBQUUsR0FBRztpQkFDbEI7Z0JBQ0Q7b0JBQ0ksS0FBSyxFQUFDLHdDQUF3QztvQkFDOUMsUUFBUSxFQUFFLE9BQU87b0JBQ2pCLFFBQVEsRUFBRTt3QkFDTixhQUFhO3FCQUNoQjtvQkFDRCxVQUFVLEVBQUUsR0FBRztpQkFDbEI7YUFDSjtTQUNKLENBQUE7UUFFRiw4Q0FBOEM7UUFDN0MsTUFBTSxXQUFXLEdBQUcsSUFBSSxHQUFHLENBQUMsSUFBSSxDQUFFLElBQUksRUFBRSxxQ0FBcUMsRUFBQztZQUM1RSxTQUFTLEVBQUUsSUFBSSxHQUFHLENBQUMsZ0JBQWdCLENBQUMsc0JBQXNCLENBQUM7WUFDM0QsV0FBVyxFQUFDLHFEQUFxRDtZQUNqRSxRQUFRLEVBQUMsdUNBQXVDO1lBQ2hELGVBQWUsRUFBQyxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsd0JBQXdCLENBQUMsMENBQTBDLENBQUMsQ0FBQztTQUN6RyxDQUFDLENBQUM7UUFFTCxvQ0FBb0M7UUFDbEMsTUFBTSxhQUFhLEdBQUcsSUFBSSxHQUFHLENBQUMsTUFBTSxDQUFDLElBQUksRUFBQyx1Q0FBdUMsRUFBQztZQUNoRixRQUFRLEVBQUUsR0FBRyxDQUFDLGNBQWMsQ0FBQyxRQUFRLENBQUMsNENBQTRDLENBQUM7U0FDcEYsQ0FBQyxDQUFDO1FBQ0gsYUFBYSxDQUFDLFlBQVksQ0FBQyxXQUFXLENBQUMsQ0FBQTtRQUV6QywrQkFBK0I7UUFDN0IsTUFBTSxzQ0FBc0MsR0FBRyxJQUFJLE1BQU0sQ0FBQyxZQUFZLENBQUMsSUFBSSxFQUFFLG9DQUFvQyxFQUFDO1lBQ2hILElBQUksRUFBRSxJQUFJLE1BQU0sQ0FBQyxTQUFTLENBQUMsZUFBZSxDQUFDO1lBQzNDLGtCQUFrQixFQUFDLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUM7WUFDL0MsdUJBQXVCLEVBQUUsQ0FBQyxNQUFNLENBQUMsWUFBWSxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUMsWUFBWSxDQUFDLE1BQU0sQ0FBQztZQUNqRixhQUFhLEVBQUUsMkJBQWEsQ0FBQyxPQUFPO1NBQ3JDLENBQUMsQ0FBQztRQUVMLDBDQUEwQztRQUN4QyxNQUFNLHlDQUF5QyxHQUFHLElBQUksTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFJLEVBQUUseUNBQXlDLEVBQUU7WUFDckgsT0FBTyxFQUFFLE1BQU0sQ0FBQyxPQUFPLENBQUMsV0FBVztZQUNuQyxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsYUFBYSxDQUFDO1lBQzFDLFdBQVcsRUFBQyxxQ0FBcUM7WUFDakQsSUFBSSxFQUFDLFdBQVc7WUFDaEIsT0FBTyxFQUFFLGVBQWU7WUFDeEIsTUFBTSxFQUFDLENBQUMsc0NBQXNDLENBQUM7WUFDL0MsV0FBVyxFQUFFO2dCQUNYLGFBQWEsRUFBRSxnQ0FBZ0MsQ0FBQyxNQUFNO2FBQ3ZEO1NBQ0YsQ0FBQyxDQUFDO1FBRUwsNkJBQTZCO1FBQzdCLE1BQU0sa0NBQWtDLEdBQUcsSUFBSSxPQUFPLENBQUMsUUFBUSxDQUFDLElBQUksRUFBRSw0QkFBNEIsRUFBRTtZQUNsRyxZQUFZLEVBQUUsNEJBQTRCO1lBQzFDLGlCQUFpQixFQUFFLElBQUk7WUFDdkIsbUJBQW1CLEVBQUUsS0FBSztZQUMxQixhQUFhLEVBQUU7Z0JBQ2IsS0FBSyxFQUFFLElBQUk7Z0JBQ1gsS0FBSyxFQUFFLElBQUk7YUFDWjtZQUNELFVBQVUsRUFBRTtnQkFDVixLQUFLLEVBQUUsSUFBSTtnQkFDWCxLQUFLLEVBQUUsSUFBSTthQUNaO1lBQ0Qsa0JBQWtCLEVBQUU7Z0JBQ2xCLFFBQVEsRUFBRTtvQkFDUixRQUFRLEVBQUUsSUFBSTtvQkFDZCxPQUFPLEVBQUUsSUFBSTtpQkFDZDtnQkFDRCxLQUFLLEVBQUU7b0JBQ0wsUUFBUSxFQUFFLElBQUk7b0JBQ2QsT0FBTyxFQUFFLElBQUk7aUJBQ2Q7Z0JBQ0QsV0FBVyxFQUFFO29CQUNYLFFBQVEsRUFBRSxJQUFJO29CQUNkLE9BQU8sRUFBRSxJQUFJO2lCQUNkO2FBQ0Y7WUFDRCxHQUFHLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxRQUFRO1lBQ3pCLGVBQWUsRUFBRTtnQkFDZixHQUFHLEVBQUUsSUFBSTtnQkFDVCxHQUFHLEVBQUUsS0FBSzthQUNYO1lBQ0QsY0FBYyxFQUFFO2dCQUNkLFNBQVMsRUFBRSxDQUFDO2dCQUNaLGdCQUFnQixFQUFFLElBQUk7Z0JBQ3RCLGFBQWEsRUFBRSxJQUFJO2dCQUNuQixjQUFjLEVBQUUsSUFBSTthQUNyQjtZQUNELGFBQWEsRUFBRSwyQkFBYSxDQUFDLE9BQU87WUFDcEMsZUFBZSxFQUFFLE9BQU8sQ0FBQyxlQUFlLENBQUMsVUFBVTtZQUNuRCxrQkFBa0IsRUFBRSxnQ0FBZ0M7U0FDckQsQ0FBQyxDQUFDO1FBRUgsdUNBQXVDO1FBQ3ZDLGtDQUFrQyxDQUFDLFVBQVUsQ0FBQywrQkFBaUIsQ0FBQyxpQkFBaUIsRUFBRSx5Q0FBeUMsQ0FBQyxDQUFDO1FBRTlILG1DQUFtQztRQUNuQyxNQUFNLG9DQUFvQyxHQUFHLGtDQUFrQyxDQUFDLFNBQVMsQ0FBQyxzQ0FBc0MsRUFBRTtZQUNoSSxrQkFBa0IsRUFBRSx5Q0FBeUM7U0FDOUQsQ0FBQyxDQUFDO1FBRUgsSUFBSSx1QkFBUyxDQUFDLElBQUksRUFBRSx5Q0FBeUMsRUFBRTtZQUM3RCxLQUFLLEVBQUUsb0NBQW9DLENBQUMsZ0JBQWdCO1NBQzdELENBQUMsQ0FBQztJQUVQLENBQUM7Q0FDRjtBQXpJRCxzRkF5SUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBjZGsgZnJvbSAnYXdzLWNkay1saWInO1xuaW1wb3J0IHsgQ2ZuT3V0cHV0LCBEdXJhdGlvbiwgUmVtb3ZhbFBvbGljeSB9IGZyb20gJ2F3cy1jZGstbGliJztcbmltcG9ydCAqIGFzIGttcyBmcm9tICdhd3MtY2RrLWxpYi9hd3Mta21zJztcbmltcG9ydCAqIGFzIGNvZ25pdG8gZnJvbSAnYXdzLWNkay1saWIvYXdzLWNvZ25pdG8nO1xuaW1wb3J0ICogYXMgbGFtYmRhIGZyb20gJ2F3cy1jZGstbGliL2F3cy1sYW1iZGEnO1xuaW1wb3J0ICogYXMgaWFtIGZyb20gJ2F3cy1jZGstbGliL2F3cy1pYW0nO1xuXG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJztcbmltcG9ydCB7IFVzZXJQb29sT3BlcmF0aW9uIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWNvZ25pdG8nO1xuXG5cbmV4cG9ydCBjbGFzcyBDb2duaXRvU25zU21zT3JpZ2luYXRpb25JZGVudGl0eVN0YWNrIGV4dGVuZHMgY2RrLlN0YWNrIHtcbiAgY29uc3RydWN0b3Ioc2NvcGU6IENvbnN0cnVjdCwgaWQ6IHN0cmluZywgcHJvcHM/OiBjZGsuU3RhY2tQcm9wcykge1xuICAgIHN1cGVyKHNjb3BlLCBpZCwgcHJvcHMpO1xuXG4gICAgLy8gS21zIGtleSBmb3IgQ29nbml0byBjdXN0b20gc21zIHNlbmRlciBcbiAgICAgIGNvbnN0IGNvZ25pdG9fY3VzdG9tX3Ntc19zZW5kZXJfa21za2V5ID0gbmV3IGttcy5LZXkodGhpcywgJ2NvZ25pdG8tY3VzdG9tc21zc2VuZGVyLWttc2tleScse1xuICAgICAgICByZW1vdmFsUG9saWN5OiBSZW1vdmFsUG9saWN5LkRFU1RST1ksXG4gICAgICAgIHBlbmRpbmdXaW5kb3c6IER1cmF0aW9uLmRheXMoNyksXG4gICAgICAgIGFsaWFzOiAnYWxpYXMvY3VzdG9tc21zc2VuZGVyS2V5JyxcbiAgICAgICAgZGVzY3JpcHRpb246ICdLTVMga2V5IGZvciBlbmNyeXB0aW5nIGNvZ25pdG8gY29kZScsXG4gICAgICAgIGVuYWJsZUtleVJvdGF0aW9uOiBmYWxzZSxcbiAgICAgIH0pO1xuXG4gICAgLy8gUGVybWlzc2lvbnMgcmVxdWlyZWQgYnkgQ29nbml0byBlbmNyeXB0IGNvZGUgdXNpbmcgS01TIGtleVxuICAgIGNvZ25pdG9fY3VzdG9tX3Ntc19zZW5kZXJfa21za2V5LmFkZFRvUmVzb3VyY2VQb2xpY3koXG4gICAgICAgICAgbmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgICAgICAgZWZmZWN0OmlhbS5FZmZlY3QuQUxMT1csXG4gICAgICAgICAgICBwcmluY2lwYWxzOiBbbmV3IGlhbS5TZXJ2aWNlUHJpbmNpcGFsKCdjb2duaXRvLWlkcC5hbWF6b25hd3MuY29tJyldLFxuICAgICAgICAgICAgYWN0aW9uczogWydrbXM6Q3JlYXRlR3JhbnQnLCAna21zOkVuY3J5cHQnXSxcbiAgICAgICAgICAgIHJlc291cmNlczogWycqJ10sXG4gICAgICAgICAgfSlcbiAgICAgICAgKTtcblxuICAgIC8vUGVybWlzc2lvbnMgcmVxdWlyZWQgYnkgQVdTIExhbWJkYSBmdW5jdGlvbiBkZWNyeXB0IGNvZGUgZnJvbSBjb2duaXRvXG4gICAgICBjb25zdCBjb2duaXRvX2N1c3RvbV9zbXNfc2VuZGVyX2xhbWJkYV9wZXJtaXNzaW9ucyA9IHtcbiAgICAgICAgICAgIFwiVmVyc2lvblwiOiBcIjIwMTItMTAtMTdcIixcbiAgICAgICAgICAgIFwiU3RhdGVtZW50XCI6IFtcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIFwic2lkXCI6XCJjb2duaXRvLWN1c3RvbXNtc3NlbmRlci1zbnMtcGVybWlzc2lvblwiLFxuICAgICAgICAgICAgICAgICAgICBcIkVmZmVjdFwiOiBcIkFsbG93XCIsXG4gICAgICAgICAgICAgICAgICAgIFwiQWN0aW9uXCI6IFtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwic25zOnB1Ymxpc2hcIlxuICAgICAgICAgICAgICAgICAgICBdLFxuICAgICAgICAgICAgICAgICAgICBcIlJlc291cmNlXCI6IFwiKlwiXG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgIFwic2lkXCI6XCJjb2duaXRvLWN1c3RvbXNtc3NlbmRlci1rbXMtcGVybWlzc2lvblwiLFxuICAgICAgICAgICAgICAgICAgICBcIkVmZmVjdFwiOiBcIkFsbG93XCIsXG4gICAgICAgICAgICAgICAgICAgIFwiQWN0aW9uXCI6IFtcbiAgICAgICAgICAgICAgICAgICAgICAgIFwia21zOkRlY3J5cHRcIlxuICAgICAgICAgICAgICAgICAgICBdLFxuICAgICAgICAgICAgICAgICAgICBcIlJlc291cmNlXCI6IFwiKlwiXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgXVxuICAgICAgICB9XG5cbiAgICAgICAvL0NyZWF0aW5nIGFuIElBTSByb2xlIGZvciBBV1MgTGFtYmRhIGZ1bmN0aW9uXG4gICAgICAgIGNvbnN0IGxhbWJkYV9yb2xlID0gbmV3IGlhbS5Sb2xlICh0aGlzLCAnY29nbml0by1jdXN0b21zbXNzZW5kZXItbGFtYmRhLXJvbGUnLHtcbiAgICAgICAgICBhc3N1bWVkQnk6IG5ldyBpYW0uU2VydmljZVByaW5jaXBhbCgnbGFtYmRhLmFtYXpvbmF3cy5jb20nKSxcbiAgICAgICAgICBkZXNjcmlwdGlvbjonTGFtYmRhIEV4ZWN1dGlvbiBSb2xlIGZvciBDb2duaXRvIEN1c3RvbSBTTVMgU2VuZGVyJyxcbiAgICAgICAgICByb2xlTmFtZTonY29nbml0by1jdXN0b20tc21zLXNlbmRlci1sYW1iZGEtcm9sZScsXG4gICAgICAgICAgbWFuYWdlZFBvbGljaWVzOltpYW0uTWFuYWdlZFBvbGljeS5mcm9tQXdzTWFuYWdlZFBvbGljeU5hbWUoJ3NlcnZpY2Utcm9sZS9BV1NMYW1iZGFCYXNpY0V4ZWN1dGlvblJvbGUnKV1cbiAgICAgICAgfSk7XG4gIFxuICAgICAgLy9DcmVhdGluZyBhbiBBV1MgTGFtYmRhIFJvbGUgUG9saWN5XG4gICAgICAgIGNvbnN0IGxhbWJkYV9wb2xpY3kgPSBuZXcgaWFtLlBvbGljeSh0aGlzLCdjb2duaXRvLWN1c3RvbXNtc3NlbmRlci1sYW1iZGEtcG9saWN5Jyx7XG4gICAgICAgICAgZG9jdW1lbnQ6IGlhbS5Qb2xpY3lEb2N1bWVudC5mcm9tSnNvbihjb2duaXRvX2N1c3RvbV9zbXNfc2VuZGVyX2xhbWJkYV9wZXJtaXNzaW9ucylcbiAgICAgICAgfSk7XG4gICAgICAgIGxhbWJkYV9wb2xpY3kuYXR0YWNoVG9Sb2xlKGxhbWJkYV9yb2xlKVxuXG4gICAgICAvL0NyZWF0aW5nIGFuIEFXUyBMYW1iZGEgbGF5ZXJzXG4gICAgICAgIGNvbnN0IGNvZ25pdG9fY3VzdG9tX3Ntc19zZW5kZXJfbGFtYmRhX2xheWVyID0gbmV3IGxhbWJkYS5MYXllclZlcnNpb24odGhpcywgJ2NvZ25pdG8tY3VzdG9tc21zc2VuZGVyLW5vZGVqcy1saWInLHtcbiAgICAgICAgICBjb2RlOiBuZXcgbGFtYmRhLkFzc2V0Q29kZSgnbGFtYmRhL2xheWVyLycpLFxuICAgICAgICAgIGNvbXBhdGlibGVSdW50aW1lczpbbGFtYmRhLlJ1bnRpbWUuTk9ERUpTXzE4X1hdLFxuICAgICAgICAgIGNvbXBhdGlibGVBcmNoaXRlY3R1cmVzOiBbbGFtYmRhLkFyY2hpdGVjdHVyZS5YODZfNjQsIGxhbWJkYS5BcmNoaXRlY3R1cmUuQVJNXzY0XSxcbiAgICAgICAgICByZW1vdmFsUG9saWN5OiBSZW1vdmFsUG9saWN5LkRFU1RST1ksXG4gICAgICAgIH0pO1xuICAgICAgXG4gICAgICAvL0NyZWF0aW5nIEN1c3RvbVNNU1NlbmRlciBMYW1iZGEgZnVuY3Rpb25cbiAgICAgICAgY29uc3QgY29nbml0b19jdXN0b21fc21zX3NlbmRlcl9sYW1iZGFfZnVuY3Rpb24gPSBuZXcgbGFtYmRhLkZ1bmN0aW9uKHRoaXMsICdjb2duaXRvLWN1c3RvbXNtc3NlbmRlci1sYW1iZGEtZnVuY3Rpb24nLCB7XG4gICAgICAgICAgcnVudGltZTogbGFtYmRhLlJ1bnRpbWUuTk9ERUpTXzE4X1ggLFxuICAgICAgICAgIGNvZGU6IGxhbWJkYS5Db2RlLmZyb21Bc3NldCgnbGFtYmRhL2NvZGUnKSxcbiAgICAgICAgICBkZXNjcmlwdGlvbjonQ3VzdG9tZXIgU01TIFNlbmRlciBMYW1iZGEgZnVuY3Rpb24nLFxuICAgICAgICAgIHJvbGU6bGFtYmRhX3JvbGUsXG4gICAgICAgICAgaGFuZGxlcjogJ2luZGV4LmhhbmRsZXInLFxuICAgICAgICAgIGxheWVyczpbY29nbml0b19jdXN0b21fc21zX3NlbmRlcl9sYW1iZGFfbGF5ZXJdLFxuICAgICAgICAgIGVudmlyb25tZW50OiB7XG4gICAgICAgICAgICAnS01TX0tFWV9BUk4nOiBjb2duaXRvX2N1c3RvbV9zbXNfc2VuZGVyX2ttc2tleS5rZXlBcm5cbiAgICAgICAgICB9ICAgICAgICAgICAgICAgIFxuICAgICAgICB9KTtcblxuICAgICAgLy8gQ3JlYXRpbmcgY29nbml0byB1c2VyIHBvb2xcbiAgICAgIGNvbnN0IGNvZ25pdG9fY3VzdG9tX3Ntc19zZW5kZXJfdXNlcnBvb2wgPSBuZXcgY29nbml0by5Vc2VyUG9vbCh0aGlzLCAnY3VzdG9tLXNtcy1zZW5kZXItdXNlcnBvb2wnLCB7XG4gICAgICAgIHVzZXJQb29sTmFtZTogJ2N1c3RvbS1zbXMtc2VuZGVyLXVzZXJwb29sJyxcbiAgICAgICAgc2VsZlNpZ25VcEVuYWJsZWQ6IHRydWUsXG4gICAgICAgIHNpZ25JbkNhc2VTZW5zaXRpdmU6IGZhbHNlLFxuICAgICAgICBzaWduSW5BbGlhc2VzOiB7XG4gICAgICAgICAgZW1haWw6IHRydWUsXG4gICAgICAgICAgcGhvbmU6IHRydWUsXG4gICAgICAgIH0sXG4gICAgICAgIGF1dG9WZXJpZnk6IHtcbiAgICAgICAgICBlbWFpbDogdHJ1ZSxcbiAgICAgICAgICBwaG9uZTogdHJ1ZVxuICAgICAgICB9LFxuICAgICAgICBzdGFuZGFyZEF0dHJpYnV0ZXM6IHtcbiAgICAgICAgICBmdWxsbmFtZToge1xuICAgICAgICAgICAgcmVxdWlyZWQ6IHRydWUsXG4gICAgICAgICAgICBtdXRhYmxlOiB0cnVlLFxuICAgICAgICAgIH0sXG4gICAgICAgICAgZW1haWw6IHtcbiAgICAgICAgICAgIHJlcXVpcmVkOiB0cnVlLFxuICAgICAgICAgICAgbXV0YWJsZTogdHJ1ZSxcbiAgICAgICAgICB9LFxuICAgICAgICAgIHBob25lTnVtYmVyOiB7XG4gICAgICAgICAgICByZXF1aXJlZDogdHJ1ZSxcbiAgICAgICAgICAgIG11dGFibGU6IHRydWUsXG4gICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICBtZmE6IGNvZ25pdG8uTWZhLlJFUVVJUkVELFxuICAgICAgICBtZmFTZWNvbmRGYWN0b3I6IHtcbiAgICAgICAgICBzbXM6IHRydWUsXG4gICAgICAgICAgb3RwOiBmYWxzZSxcbiAgICAgICAgfSxcbiAgICAgICAgcGFzc3dvcmRQb2xpY3k6IHtcbiAgICAgICAgICBtaW5MZW5ndGg6IDgsXG4gICAgICAgICAgcmVxdWlyZUxvd2VyY2FzZTogdHJ1ZSxcbiAgICAgICAgICByZXF1aXJlRGlnaXRzOiB0cnVlLFxuICAgICAgICAgIHJlcXVpcmVTeW1ib2xzOiB0cnVlLFxuICAgICAgICB9LFxuICAgICAgICByZW1vdmFsUG9saWN5OiBSZW1vdmFsUG9saWN5LkRFU1RST1ksXG4gICAgICAgIGFjY291bnRSZWNvdmVyeTogY29nbml0by5BY2NvdW50UmVjb3ZlcnkuRU1BSUxfT05MWSxcbiAgICAgICAgY3VzdG9tU2VuZGVyS21zS2V5OiBjb2duaXRvX2N1c3RvbV9zbXNfc2VuZGVyX2ttc2tleVxuICAgICAgfSk7XG5cbiAgICAgIC8vRW5hYmxlIEN1c3RvbVNNU1NlbmRlciBMYW1iZGEgdHJpZ2dlclxuICAgICAgY29nbml0b19jdXN0b21fc21zX3NlbmRlcl91c2VycG9vbC5hZGRUcmlnZ2VyKFVzZXJQb29sT3BlcmF0aW9uLkNVU1RPTV9TTVNfU0VOREVSLCBjb2duaXRvX2N1c3RvbV9zbXNfc2VuZGVyX2xhbWJkYV9mdW5jdGlvbik7XG5cbiAgICAgIC8vQ3JlYXRpbmcgQ29nbml0byBwb29sIGFwcCBjbGllbnQgXG4gICAgICBjb25zdCBjb2duaXRvX2N1c3RvbV9zbXNfc2VuZGVyX2NsaWVudF9hcHAgPSBjb2duaXRvX2N1c3RvbV9zbXNfc2VuZGVyX3VzZXJwb29sLmFkZENsaWVudChcImNvZ25pdG8tY3VzdG9tLXNtcy1zZW5kZXItYXBwLWNsaWVudFwiLCB7XG4gICAgICAgIHVzZXJQb29sQ2xpZW50TmFtZTogXCJjb2duaXRvLWN1c3RvbS1zbXMtc2VuZGVyLWNsaWVudC1hcHAtaWRcIlxuICAgICAgfSk7XG4gIFxuICAgICAgbmV3IENmbk91dHB1dCh0aGlzLCBcImNvZ25pdG8tY3VzdG9tLXNtcy1zZW5kZXItY2xpZW50LWFwcC1pZFwiLCB7XG4gICAgICAgIHZhbHVlOiBjb2duaXRvX2N1c3RvbV9zbXNfc2VuZGVyX2NsaWVudF9hcHAudXNlclBvb2xDbGllbnRJZCxcbiAgICAgIH0pO1xuICAgICBcbiAgfSAgICAgIFxufVxuIl19 \ No newline at end of file diff --git a/cognito-sns-sms-origination-id/lib/cognito-sns-sms-origination-identity-stack.ts b/cognito-sns-sms-origination-id/lib/cognito-sns-sms-origination-identity-stack.ts new file mode 100644 index 0000000000..ebd3288296 --- /dev/null +++ b/cognito-sns-sms-origination-id/lib/cognito-sns-sms-origination-identity-stack.ts @@ -0,0 +1,152 @@ +import * as cdk from 'aws-cdk-lib'; +import { CfnOutput, Duration, RemovalPolicy } from 'aws-cdk-lib'; +import * as kms from 'aws-cdk-lib/aws-kms'; +import * as cognito from 'aws-cdk-lib/aws-cognito'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import * as iam from 'aws-cdk-lib/aws-iam'; + +import { Construct } from 'constructs'; +import { UserPoolOperation } from 'aws-cdk-lib/aws-cognito'; + + +export class CognitoSnsSmsOriginationIdentityStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + // Kms key for Cognito custom sms sender + const cognito_custom_sms_sender_kmskey = new kms.Key(this, 'cognito-customsmssender-kmskey',{ + removalPolicy: RemovalPolicy.DESTROY, + pendingWindow: Duration.days(7), + alias: 'alias/customsmssenderKey', + description: 'KMS key for encrypting cognito code', + enableKeyRotation: false, + }); + + // Permissions required by Cognito encrypt code using KMS key + cognito_custom_sms_sender_kmskey.addToResourcePolicy( + new iam.PolicyStatement({ + effect:iam.Effect.ALLOW, + principals: [new iam.ServicePrincipal('cognito-idp.amazonaws.com')], + actions: ['kms:CreateGrant', 'kms:Encrypt'], + resources: ['*'], + }) + ); + + //Permissions required by AWS Lambda function decrypt code from cognito + const cognito_custom_sms_sender_lambda_permissions = { + "Version": "2012-10-17", + "Statement": [ + { + "sid":"cognito-customsmssender-sns-permission", + "Effect": "Allow", + "Action": [ + "sns:publish" + ], + "Resource": "*" + }, + { + "sid":"cognito-customsmssender-kms-permission", + "Effect": "Allow", + "Action": [ + "kms:Decrypt" + ], + "Resource": "*" + } + ] + } + + //Creating an IAM role for AWS Lambda function + const lambda_role = new iam.Role (this, 'cognito-customsmssender-lambda-role',{ + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), + description:'Lambda Execution Role for Cognito Custom SMS Sender', + roleName:'cognito-custom-sms-sender-lambda-role', + managedPolicies:[iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole')] + }); + + //Creating an AWS Lambda Role Policy + const lambda_policy = new iam.Policy(this,'cognito-customsmssender-lambda-policy',{ + document: iam.PolicyDocument.fromJson(cognito_custom_sms_sender_lambda_permissions) + }); + lambda_policy.attachToRole(lambda_role) + + //Creating an AWS Lambda layers + const cognito_custom_sms_sender_lambda_layer = new lambda.LayerVersion(this, 'cognito-customsmssender-nodejs-lib',{ + code: new lambda.AssetCode('lambda/layer/'), + layerVersionName:'cognito-customsmssender-nodejs-lib', + compatibleRuntimes:[lambda.Runtime.NODEJS_18_X], + compatibleArchitectures: [lambda.Architecture.X86_64, lambda.Architecture.ARM_64], + removalPolicy: RemovalPolicy.DESTROY, + }); + + //Creating CustomSMSSender Lambda function + const cognito_custom_sms_sender_lambda_function = new lambda.Function(this, 'cognito-customsmssender-lambda-function', { + runtime: lambda.Runtime.NODEJS_18_X , + functionName:'cognito-customsmssender', + code: lambda.Code.fromAsset('lambda/code'), + description:'Customer SMS Sender Lambda function', + role:lambda_role, + handler: 'index.handler', + layers:[cognito_custom_sms_sender_lambda_layer], + environment: { + 'KEY_ALIAS': 'customsmssenderKey', + 'KEY_ARN': cognito_custom_sms_sender_kmskey.keyArn + } + }); + + // Creating cognito user pool + const cognito_custom_sms_sender_userpool = new cognito.UserPool(this, 'custom-sms-sender-userpool', { + userPoolName: 'custom-sms-sender-userpool', + selfSignUpEnabled: true, + signInCaseSensitive: false, + signInAliases: { + email: true, + phone: true, + }, + autoVerify: { + email: true, + phone: true + }, + standardAttributes: { + fullname: { + required: true, + mutable: true, + }, + email: { + required: true, + mutable: true, + }, + phoneNumber: { + required: true, + mutable: true, + } + }, + mfa: cognito.Mfa.REQUIRED, + mfaSecondFactor: { + sms: true, + otp: false, + }, + passwordPolicy: { + minLength: 8, + requireLowercase: true, + requireDigits: true, + requireSymbols: true, + }, + removalPolicy: RemovalPolicy.DESTROY, + accountRecovery: cognito.AccountRecovery.EMAIL_ONLY, + customSenderKmsKey: cognito_custom_sms_sender_kmskey + }); + + //Enable CustomSMSSender Lambda trigger + cognito_custom_sms_sender_userpool.addTrigger(UserPoolOperation.CUSTOM_SMS_SENDER, cognito_custom_sms_sender_lambda_function); + + //Creating Cognito pool app client + const cognito_custom_sms_sender_client_app = cognito_custom_sms_sender_userpool.addClient("cognito-custom-sms-sender-app-client", { + userPoolClientName: "cognito-custom-sms-sender-client-app-id" + }); + + new CfnOutput(this, "cognito-custom-sms-sender-client-app-id", { + value: cognito_custom_sms_sender_client_app.userPoolClientId, + }); + + } +} diff --git a/cognito-sns-sms-origination-id/package.json b/cognito-sns-sms-origination-id/package.json new file mode 100644 index 0000000000..a2cb38cef5 --- /dev/null +++ b/cognito-sns-sms-origination-id/package.json @@ -0,0 +1,27 @@ +{ + "name": "cognito-sns-sms-origination-identity", + "version": "0.1.0", + "bin": { + "cognito-sns-sms-origination-identity": "bin/cognito-sns-sms-origination-identity.js" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "jest", + "cdk": "cdk" + }, + "devDependencies": { + "@types/jest": "^29.4.0", + "@types/node": "18.14.6", + "jest": "^29.5.0", + "ts-jest": "^29.0.5", + "aws-cdk": "2.77.0", + "ts-node": "^10.9.1", + "typescript": "~4.9.5" + }, + "dependencies": { + "aws-cdk-lib": "2.77.0", + "constructs": "^10.0.0", + "source-map-support": "^0.5.21" + } +} diff --git a/aurora-serverless-s3-ingestion/cdk/aurora_serverless_ingestion/__init__.py b/cognito-sns-sms-origination-id/test/cognito-sns-sms-origination-identity.test.d.ts similarity index 100% rename from aurora-serverless-s3-ingestion/cdk/aurora_serverless_ingestion/__init__.py rename to cognito-sns-sms-origination-id/test/cognito-sns-sms-origination-identity.test.d.ts diff --git a/cognito-sns-sms-origination-id/test/cognito-sns-sms-origination-identity.test.js b/cognito-sns-sms-origination-id/test/cognito-sns-sms-origination-identity.test.js new file mode 100644 index 0000000000..e4df126134 --- /dev/null +++ b/cognito-sns-sms-origination-id/test/cognito-sns-sms-origination-identity.test.js @@ -0,0 +1,17 @@ +"use strict"; +// import * as cdk from 'aws-cdk-lib'; +// import { Template } from 'aws-cdk-lib/assertions'; +// import * as CognitoSnsSmsOriginationIdentity from '../lib/cognito-sns-sms-origination-identity-stack'; +// example test. To run these tests, uncomment this file along with the +// example resource in lib/cognito-sns-sms-origination-identity-stack.ts +test('SQS Queue Created', () => { + // const app = new cdk.App(); + // // WHEN + // const stack = new CognitoSnsSmsOriginationIdentity.CognitoSnsSmsOriginationIdentityStack(app, 'MyTestStack'); + // // THEN + // const template = Template.fromStack(stack); + // template.hasResourceProperties('AWS::SQS::Queue', { + // VisibilityTimeout: 300 + // }); +}); +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29nbml0by1zbnMtc21zLW9yaWdpbmF0aW9uLWlkZW50aXR5LnRlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjb2duaXRvLXNucy1zbXMtb3JpZ2luYXRpb24taWRlbnRpdHkudGVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsc0NBQXNDO0FBQ3RDLHFEQUFxRDtBQUNyRCx5R0FBeUc7QUFFekcsdUVBQXVFO0FBQ3ZFLHdFQUF3RTtBQUN4RSxJQUFJLENBQUMsbUJBQW1CLEVBQUUsR0FBRyxFQUFFO0lBQy9CLCtCQUErQjtJQUMvQixjQUFjO0lBQ2Qsa0hBQWtIO0lBQ2xILGNBQWM7SUFDZCxnREFBZ0Q7SUFFaEQsd0RBQXdEO0lBQ3hELDZCQUE2QjtJQUM3QixRQUFRO0FBQ1IsQ0FBQyxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBpbXBvcnQgKiBhcyBjZGsgZnJvbSAnYXdzLWNkay1saWInO1xuLy8gaW1wb3J0IHsgVGVtcGxhdGUgfSBmcm9tICdhd3MtY2RrLWxpYi9hc3NlcnRpb25zJztcbi8vIGltcG9ydCAqIGFzIENvZ25pdG9TbnNTbXNPcmlnaW5hdGlvbklkZW50aXR5IGZyb20gJy4uL2xpYi9jb2duaXRvLXNucy1zbXMtb3JpZ2luYXRpb24taWRlbnRpdHktc3RhY2snO1xuXG4vLyBleGFtcGxlIHRlc3QuIFRvIHJ1biB0aGVzZSB0ZXN0cywgdW5jb21tZW50IHRoaXMgZmlsZSBhbG9uZyB3aXRoIHRoZVxuLy8gZXhhbXBsZSByZXNvdXJjZSBpbiBsaWIvY29nbml0by1zbnMtc21zLW9yaWdpbmF0aW9uLWlkZW50aXR5LXN0YWNrLnRzXG50ZXN0KCdTUVMgUXVldWUgQ3JlYXRlZCcsICgpID0+IHtcbi8vICAgY29uc3QgYXBwID0gbmV3IGNkay5BcHAoKTtcbi8vICAgICAvLyBXSEVOXG4vLyAgIGNvbnN0IHN0YWNrID0gbmV3IENvZ25pdG9TbnNTbXNPcmlnaW5hdGlvbklkZW50aXR5LkNvZ25pdG9TbnNTbXNPcmlnaW5hdGlvbklkZW50aXR5U3RhY2soYXBwLCAnTXlUZXN0U3RhY2snKTtcbi8vICAgICAvLyBUSEVOXG4vLyAgIGNvbnN0IHRlbXBsYXRlID0gVGVtcGxhdGUuZnJvbVN0YWNrKHN0YWNrKTtcblxuLy8gICB0ZW1wbGF0ZS5oYXNSZXNvdXJjZVByb3BlcnRpZXMoJ0FXUzo6U1FTOjpRdWV1ZScsIHtcbi8vICAgICBWaXNpYmlsaXR5VGltZW91dDogMzAwXG4vLyAgIH0pO1xufSk7XG4iXX0= \ No newline at end of file diff --git a/cognito-sns-sms-origination-id/test/cognito-sns-sms-origination-identity.test.ts b/cognito-sns-sms-origination-id/test/cognito-sns-sms-origination-identity.test.ts new file mode 100644 index 0000000000..ba0446d1fe --- /dev/null +++ b/cognito-sns-sms-origination-id/test/cognito-sns-sms-origination-identity.test.ts @@ -0,0 +1,17 @@ +// import * as cdk from 'aws-cdk-lib'; +// import { Template } from 'aws-cdk-lib/assertions'; +// import * as CognitoSnsSmsOriginationIdentity from '../lib/cognito-sns-sms-origination-identity-stack'; + +// example test. To run these tests, uncomment this file along with the +// example resource in lib/cognito-sns-sms-origination-identity-stack.ts +test('SQS Queue Created', () => { +// const app = new cdk.App(); +// // WHEN +// const stack = new CognitoSnsSmsOriginationIdentity.CognitoSnsSmsOriginationIdentityStack(app, 'MyTestStack'); +// // THEN +// const template = Template.fromStack(stack); + +// template.hasResourceProperties('AWS::SQS::Queue', { +// VisibilityTimeout: 300 +// }); +}); diff --git a/cdk-s3-sqs-lambda-dynamodb/tsconfig.json b/cognito-sns-sms-origination-id/tsconfig.json similarity index 94% rename from cdk-s3-sqs-lambda-dynamodb/tsconfig.json rename to cognito-sns-sms-origination-id/tsconfig.json index 9f8e8beabd..fc44377a1e 100644 --- a/cdk-s3-sqs-lambda-dynamodb/tsconfig.json +++ b/cognito-sns-sms-origination-id/tsconfig.json @@ -1,9 +1,9 @@ { "compilerOptions": { - "target": "ES2018", + "target": "ES2020", "module": "commonjs", "lib": [ - "es2018" + "es2020" ], "declaration": true, "strict": true, diff --git a/cognito-user-pool/example-pattern.json b/cognito-user-pool/example-pattern.json index 475fc6ac6b..5b2dd0253b 100644 --- a/cognito-user-pool/example-pattern.json +++ b/cognito-user-pool/example-pattern.json @@ -35,7 +35,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/content-filter-pattern-kinesis-cdk/example-pattern.json b/content-filter-pattern-kinesis-cdk/example-pattern.json index b2a306f578..a62d86707b 100644 --- a/content-filter-pattern-kinesis-cdk/example-pattern.json +++ b/content-filter-pattern-kinesis-cdk/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cw-sns-secretsmanager/example-pattern.json b/cw-sns-secretsmanager/example-pattern.json index 70520e2d1a..714fbea7d4 100644 --- a/cw-sns-secretsmanager/example-pattern.json +++ b/cw-sns-secretsmanager/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/cwlogs-lambda-sns-sqs-cdk/example-pattern.json b/cwlogs-lambda-sns-sqs-cdk/example-pattern.json index 438baa9295..009610f86a 100644 --- a/cwlogs-lambda-sns-sqs-cdk/example-pattern.json +++ b/cwlogs-lambda-sns-sqs-cdk/example-pattern.json @@ -30,7 +30,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["cdk destroy"] diff --git a/data/test.csv b/data/test.csv deleted file mode 100644 index 67f517b7f8..0000000000 --- a/data/test.csv +++ /dev/null @@ -1,3 +0,0 @@ -id,name,email -1,shashi,shashi@abc.com -2,neha,neha@abc.com \ No newline at end of file diff --git a/dynamodb-eventbridge-java/example-pattern.json b/dynamodb-eventbridge-java/example-pattern.json index 4d9c9c84ac..a3a963147c 100644 --- a/dynamodb-eventbridge-java/example-pattern.json +++ b/dynamodb-eventbridge-java/example-pattern.json @@ -32,7 +32,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/dynamodb-eventbridge-transformer/example-pattern.json b/dynamodb-eventbridge-transformer/example-pattern.json index 90b5ba7660..f26fc25e5b 100644 --- a/dynamodb-eventbridge-transformer/example-pattern.json +++ b/dynamodb-eventbridge-transformer/example-pattern.json @@ -34,7 +34,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/dynamodb-lambda-cdk-kotlin/example-pattern.json b/dynamodb-lambda-cdk-kotlin/example-pattern.json index 1ea0767352..af8f465b6f 100644 --- a/dynamodb-lambda-cdk-kotlin/example-pattern.json +++ b/dynamodb-lambda-cdk-kotlin/example-pattern.json @@ -22,12 +22,12 @@ }, "deploy": { "text": [ - "See the Github repo for detailed deploy instructions." + "See the GitHub repo for detailed deploy instructions." ] }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/dynamodb-pipes-sqs-cdk/example-pattern.json b/dynamodb-pipes-sqs-cdk/example-pattern.json index e7e36d672b..56a0631fe1 100644 --- a/dynamodb-pipes-sqs-cdk/example-pattern.json +++ b/dynamodb-pipes-sqs-cdk/example-pattern.json @@ -43,7 +43,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/dynamodb-seed-data-on-create-cdk/example-pattern.json b/dynamodb-seed-data-on-create-cdk/example-pattern.json index 734035a2f3..06d18d45de 100644 --- a/dynamodb-seed-data-on-create-cdk/example-pattern.json +++ b/dynamodb-seed-data-on-create-cdk/example-pattern.json @@ -37,7 +37,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/dynamodb-stream-lambda-cdk-dotnet/cdk/src/Cdk/Cdk.csproj b/dynamodb-stream-lambda-cdk-dotnet/cdk/src/Cdk/Cdk.csproj index 7677b7d8c8..ae92f16ab3 100644 --- a/dynamodb-stream-lambda-cdk-dotnet/cdk/src/Cdk/Cdk.csproj +++ b/dynamodb-stream-lambda-cdk-dotnet/cdk/src/Cdk/Cdk.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net6.0 Major diff --git a/dynamodb-stream-lambda-cdk-dotnet/example-pattern.json b/dynamodb-stream-lambda-cdk-dotnet/example-pattern.json index ac6e82426d..edcb6ec4ed 100644 --- a/dynamodb-stream-lambda-cdk-dotnet/example-pattern.json +++ b/dynamodb-stream-lambda-cdk-dotnet/example-pattern.json @@ -27,7 +27,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/dynamodb-streams-appsync-subscription/README.md b/dynamodb-streams-appsync-subscription/README.md new file mode 100644 index 0000000000..1a5b11c4e2 --- /dev/null +++ b/dynamodb-streams-appsync-subscription/README.md @@ -0,0 +1,76 @@ +# Amazon DynamoDB Streams to AppSync Subscription + +This pattern creates an AppSync API allowing you to listen to new items being created on a specific DynamoDB table through AWS AppSync subscriptions. With this pattern, a Lambda function will be invoked that will in turn call an AppSync mutation with data source set as `None`. An AppSync subscription will be subscribed to that mutation allowing end users to be notified of a newly created DynamoDB item. + +Learn more about this pattern at ServerlessLand Patterns: https://serverlessland.com/patterns/dynamodb-streams-appsync-subscription + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +2. Change directory to the pattern directory: + ``` + cd dynamodb-streams-appsync-subscription + ``` +3. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: + ``` + sam build + sam deploy --guided + ``` +4. During the prompts: + * Enter a stack name + * Enter the desired AWS Region + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run `sam deploy --guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +## How it works + +This template creates an AppSync api. Any new item being created on the DynamoDB table created with this example will invoke a Lambda function that will in turn call an AppSync mutation with data source set as `None`. An AppSync subscription will be subscribed to that mutation allowing end users to be notified of a newly created DynamoDB item. + +More information is available [here](https://repost.aws/knowledge-center/appsync-notify-subscribers-real-time). + +## Testing + +The easiest way to test the AppSync API is with the AppSync console at https://console.aws.amazon.com/appsync/home#/apis (change to your appropriate region) + +1. Click on the API you created and visit the Queries tab +2. Subscribe to `onCreateItem` as follows + ```graphql + subscription MySubscription { + onCreateItem (PK: "thePKImSubscribingTo") { + PK + SK + data + } + } + ``` + ![query](./images/subscription-input.png) +3. Open the [DynamoDB console](https://console.aws.amazon.com/dynamodbv2/home?#item-explorer) in another tab. +4. Create a new item with a `PK` equal to `thePKImSubscribingTo`. Choose any value for `SK` AND `data` for this example to work. + ![query](./images/dynamodb.png) +5. Click on `Create item` \ +6. The item should appear in your AppSync console opened in the other tab. + ![query](./images/subscription-result.png) + +## Cleanup + +1. Delete the stack + ```bash + sam delete + ``` +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/dynamodb-streams-appsync-subscription/example-pattern.json b/dynamodb-streams-appsync-subscription/example-pattern.json new file mode 100644 index 0000000000..ee788125e3 --- /dev/null +++ b/dynamodb-streams-appsync-subscription/example-pattern.json @@ -0,0 +1,59 @@ +{ + "title": "DynamoDB Streams to AppSync subscription", + "description": "Listen to DynamoDB changes using AppSync subscriptions", + "language": "TypeScript", + "level": "300", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "In some of your projects, you might want to be able to listen to changes made on your database that are not performed through client-side mutations.", + "This sample project creates an AppSync API allowing you to listen to new items being created on a specific DynamoDB table through AWS AppSync subscriptions.", + "With this pattern, a Lambda function will be invoked for every new item created on a DynamoDB table. It will in turn call an AppSync mutation with data source set as `None`. An AppSync subscription will be subscribed to that mutation allowing end users to be notified of a newly created DynamoDB item." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/dynamodb-streams-appsync-subscription", + "templateURL": "serverless-patterns/dynamodb-streams-appsync-subscription", + "projectFolder": "dynamodb-streams-appsync-subscription", + "templateFile": "dynamodb-streams-appsync-subscription/template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "AWS Appsync", + "link": "https://aws.amazon.com/appsync/" + }, + { + "text": "AWS Re:Post - Listen to external DB changes using AppSync", + "link": "https://repost.aws/knowledge-center/appsync-notify-subscribers-real-time" + } + ] + }, + "deploy": { + "text": [ + "sam deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "name": "Alexis Philippart de Foy", + "image": "https://media.licdn.com/dms/image/C4D03AQFyN5GLrFk-0g/profile-displayphoto-shrink_800_800/0/1586853008031?e=1693440000&v=beta&t=0BtjX92oZxhHUrrIzh55wah8V1OiwOAOyigPtfAcZdo", + "bio": " I am a Startup Solutions Architect, Serverless enthusiast and former Startup Founder.", + "linkedin": "apdf", + "twitter": "@aphilippartd" + } + ] +} \ No newline at end of file diff --git a/dynamodb-streams-appsync-subscription/images/dynamodb.png b/dynamodb-streams-appsync-subscription/images/dynamodb.png new file mode 100644 index 0000000000..977a9cc791 Binary files /dev/null and b/dynamodb-streams-appsync-subscription/images/dynamodb.png differ diff --git a/dynamodb-streams-appsync-subscription/images/subscription-input.png b/dynamodb-streams-appsync-subscription/images/subscription-input.png new file mode 100644 index 0000000000..629cdbb747 Binary files /dev/null and b/dynamodb-streams-appsync-subscription/images/subscription-input.png differ diff --git a/dynamodb-streams-appsync-subscription/images/subscription-result.png b/dynamodb-streams-appsync-subscription/images/subscription-result.png new file mode 100644 index 0000000000..f41535b4e8 Binary files /dev/null and b/dynamodb-streams-appsync-subscription/images/subscription-result.png differ diff --git a/dynamodb-streams-appsync-subscription/sam_graphql_api/resolvers/functions/onCreateItem.js b/dynamodb-streams-appsync-subscription/sam_graphql_api/resolvers/functions/onCreateItem.js new file mode 100644 index 0000000000..ea392dbb34 --- /dev/null +++ b/dynamodb-streams-appsync-subscription/sam_graphql_api/resolvers/functions/onCreateItem.js @@ -0,0 +1,2 @@ +export function request(_) { return {}; } +export function response(ctx) { return ctx.args; } \ No newline at end of file diff --git a/dynamodb-streams-appsync-subscription/sam_graphql_api/schema.graphql b/dynamodb-streams-appsync-subscription/sam_graphql_api/schema.graphql new file mode 100644 index 0000000000..a0018e0e7a --- /dev/null +++ b/dynamodb-streams-appsync-subscription/sam_graphql_api/schema.graphql @@ -0,0 +1,22 @@ +type Item @aws_iam { + PK: String! + SK: String! + data: String! +} + +type Mutation { + onCreateItem ( + PK: String!, + SK: String!, + data: String! + ): Item @aws_iam +} + +type Subscription { + onCreateItem(PK: String, SK: String, data: String): Item + @aws_subscribe(mutations: ["onCreateItem"]) +} + +type Query { + getItem(PK: String!, SK: String!): Item +} \ No newline at end of file diff --git a/dynamodb-streams-appsync-subscription/src/DDBStreamFunction/app.ts b/dynamodb-streams-appsync-subscription/src/DDBStreamFunction/app.ts new file mode 100644 index 0000000000..cb029871c4 --- /dev/null +++ b/dynamodb-streams-appsync-subscription/src/DDBStreamFunction/app.ts @@ -0,0 +1,62 @@ + +import { DynamoDBStreamEvent, DynamoDBRecord } from "aws-lambda" +import { unmarshall } from "@aws-sdk/util-dynamodb" +import * as crypto from '@aws-crypto/sha256-js' +import { defaultProvider } from '@aws-sdk/credential-provider-node' +import { SignatureV4 } from '@aws-sdk/signature-v4' +import { HttpRequest } from '@aws-sdk/protocol-http' + +interface Message { + PK: string + SK: string + data: string +} + +export const handler = async (event: DynamoDBStreamEvent): Promise => { + let messages = [] + await Promise.all(event.Records.map(async (record: DynamoDBRecord) => { + let payload = unmarshall(record.dynamodb.NewImage) + messages.push(payload) + })) + + await Promise.all(messages.map(message => appsync(message))) +} + +const { Sha256 } = crypto +const { APP_SYNC_API, AWS_REGION } = process.env + +const query = /* GraphQL */ ` + mutation ON_CREATE_ITEM($PK: String!, $SK: String!, $data: String!) { + onCreateItem(PK: $PK, SK: $SK, data: $data) { + PK + SK + data + } + } +` +const appsync = async (variables: Message): Promise => { + const endpoint = new URL(APP_SYNC_API) + + const signer = new SignatureV4({ + credentials: defaultProvider(), + region: AWS_REGION, + service: 'appsync', + sha256: Sha256 + }) + + const requestToBeSigned = new HttpRequest({ + method: 'POST', + headers: { + 'Content-Type': 'application/json', + host: endpoint.host + }, + hostname: endpoint.host, + body: JSON.stringify({ query, variables }), + path: endpoint.pathname + }) + + const signed = await signer.sign(requestToBeSigned) + const request = new Request(APP_SYNC_API, signed) + + await fetch(request) +} \ No newline at end of file diff --git a/dynamodb-streams-appsync-subscription/src/DDBStreamFunction/package.json b/dynamodb-streams-appsync-subscription/src/DDBStreamFunction/package.json new file mode 100644 index 0000000000..7c08267b88 --- /dev/null +++ b/dynamodb-streams-appsync-subscription/src/DDBStreamFunction/package.json @@ -0,0 +1,24 @@ +{ + "name": "dynamodb-streams-lambda-appsync-sam-node", + "version": "1.0.0", + "description": "Broadcast dynamoDB changes to appsync subscriptions", + "scripts": { + "build": "tsc" + }, + "author": "Alexis Philippart de Foy", + "license": "ISC", + "devDependencies": { + "@aws-sdk/credential-provider-node": "^3.358.0", + "@aws-sdk/protocol-http": "^3.357.0", + "@aws-sdk/signature-v4": "^3.357.0", + "@aws-sdk/util-dynamodb": "^3.266.1", + "@types/aws-lambda": "^8.10.110", + "@types/node": "^18.13.0", + "aws-lambda": "^1.0.7", + "esbuild": "0.18.7", + "typescript": "^4.9.5" + }, + "dependencies": { + "@aws-crypto/sha256-js": "^4.0.0" + } +} diff --git a/dynamodb-streams-appsync-subscription/src/DDBStreamFunction/tsconfig.json b/dynamodb-streams-appsync-subscription/src/DDBStreamFunction/tsconfig.json new file mode 100644 index 0000000000..9f5e87c735 --- /dev/null +++ b/dynamodb-streams-appsync-subscription/src/DDBStreamFunction/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compileOnSave": true, + "compilerOptions": { + "module": "commonjs", + "moduleResolution": "node", + "declaration": false, + "noImplicitAny": false, + "noImplicitThis": false, + "removeComments": true, + "target": "ES2022", + "inlineSourceMap": true, + "esModuleInterop": true, + "noImplicitOverride": false, + "experimentalDecorators": true + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/dynamodb-streams-appsync-subscription/template.yaml b/dynamodb-streams-appsync-subscription/template.yaml new file mode 100644 index 0000000000..4fbfb76d78 --- /dev/null +++ b/dynamodb-streams-appsync-subscription/template.yaml @@ -0,0 +1,109 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: Serverless patterns - Appsync DynamoDB Streams Subscription + +Resources: +########################################################################## +# DynamoDB Table # +########################################################################## + DDBTable: + Type: "AWS::DynamoDB::Table" + Properties: + BillingMode: PAY_PER_REQUEST + AttributeDefinitions: + - AttributeName: PK + AttributeType: S + - AttributeName: SK + AttributeType: S + KeySchema: + - AttributeName: PK + KeyType: HASH + - AttributeName: SK + KeyType: RANGE + StreamSpecification: + StreamViewType: NEW_IMAGE + + +########################################################################## +# AppSync API # +########################################################################## + AppSyncApi: + Type: AWS::Serverless::GraphQLApi + Properties: + SchemaUri: ./sam_graphql_api/schema.graphql + ApiKeys: + TestApiKey: + Description: Test Api Key + Auth: + Type: API_KEY + Additional: + - Type: AWS_IAM + Functions: + onCreateItemFunction: + Runtime: + Name: APPSYNC_JS + Version: 1.0.0 + DataSource: None + CodeUri: ./sam_graphql_api/resolvers/functions/onCreateItem.js + Resolvers: + Mutation: + onCreateItem: + Runtime: + Name: APPSYNC_JS + Version: "1.0.0" + Pipeline: + - onCreateItemFunction + + +########################################################################## +# Lambda Function # +########################################################################## + DDBStreamFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: src/DDBStreamFunction + Handler: app.handler + Runtime: nodejs18.x + Architectures: ["arm64"] + Timeout: 30 + MemorySize: 1024 + Policies: + - Statement: + - Effect: Allow + Action: appsync:GraphQL + Resource: !Sub ${AppSyncApi}/types/Mutation/* + Events: + DynamoStream: + Type: DynamoDB + Properties: + BatchSize: 100 + ParallelizationFactor: 10 + StartingPosition: LATEST + MaximumRetryAttempts: 2 + BisectBatchOnFunctionError: true + MaximumRecordAgeInSeconds: 120 + FilterCriteria: + Filters: + - Pattern: '{"eventName": ["INSERT"]}' + Stream: !GetAtt DDBTable.StreamArn + Environment: + Variables: + APP_SYNC_API: !GetAtt AppSyncApi.GraphQLUrl + Metadata: + BuildMethod: esbuild + BuildProperties: + External: + - '@aws-sdk/util-dynamodb' + - '@aws-sdk/credential-provider-node' + - '@aws-sdk/signature-v4' + - '@aws-sdk/protocol-http' + Minify: true + Target: "es2022" + Sourcemap: true + EntryPoints: + - app.ts + DDBStreamFunctionLogGroup: + Type: AWS::Logs::LogGroup + Properties: + RetentionInDays: 1 + LogGroupName: !Sub /aws/lambda/${DDBStreamFunction} \ No newline at end of file diff --git a/dynamodb-streams-lambda-dynamodb/README.md b/dynamodb-streams-lambda-dynamodb/README.md index 2f8fa339c0..1a95c3d017 100644 --- a/dynamodb-streams-lambda-dynamodb/README.md +++ b/dynamodb-streams-lambda-dynamodb/README.md @@ -16,7 +16,7 @@ Learn more about this pattern at Serverless Land Patterns: https://serverlesslan * [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -* [.NET 6](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) installed +* [.NET 7](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) installed * [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) (AWS CDK) installed ## Deployment Instructions diff --git a/dynamodb-streams-lambda-dynamodb/example-pattern.json b/dynamodb-streams-lambda-dynamodb/example-pattern.json index c2412a1c2c..e651c91667 100644 --- a/dynamodb-streams-lambda-dynamodb/example-pattern.json +++ b/dynamodb-streams-lambda-dynamodb/example-pattern.json @@ -26,7 +26,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/dynamodb-streams-lambda-eventbridge-sam-node/example-pattern.json b/dynamodb-streams-lambda-eventbridge-sam-node/example-pattern.json index de6f420c2f..2671e9a007 100644 --- a/dynamodb-streams-lambda-eventbridge-sam-node/example-pattern.json +++ b/dynamodb-streams-lambda-eventbridge-sam-node/example-pattern.json @@ -41,7 +41,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/dynamodb-streams-lambda-eventbridge-sam-rust/example-pattern.json b/dynamodb-streams-lambda-eventbridge-sam-rust/example-pattern.json index c3b7d7550b..d681eaf880 100644 --- a/dynamodb-streams-lambda-eventbridge-sam-rust/example-pattern.json +++ b/dynamodb-streams-lambda-eventbridge-sam-rust/example-pattern.json @@ -41,7 +41,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/dynamodb-streams-lambda-terraform-java/example-pattern.json b/dynamodb-streams-lambda-terraform-java/example-pattern.json index 11d5dfa98b..f0bb2d8907 100644 --- a/dynamodb-streams-lambda-terraform-java/example-pattern.json +++ b/dynamodb-streams-lambda-terraform-java/example-pattern.json @@ -32,7 +32,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/ecs-windows-cdk-dotnet/README.md b/ecs-windows-cdk-dotnet/README.md index df7aa0ebff..c7f8124aaa 100644 --- a/ecs-windows-cdk-dotnet/README.md +++ b/ecs-windows-cdk-dotnet/README.md @@ -16,7 +16,7 @@ Learn more about this pattern at Serverless Land Patterns: https://serverlesslan * [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -* [.NET 6](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) installed +* [.NET 7](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) installed * [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) (AWS CDK) installed ## Deployment Instructions diff --git a/ecs-windows-cdk-dotnet/example-pattern.json b/ecs-windows-cdk-dotnet/example-pattern.json index 4078fad6c1..29eef2282f 100644 --- a/ecs-windows-cdk-dotnet/example-pattern.json +++ b/ecs-windows-cdk-dotnet/example-pattern.json @@ -26,7 +26,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/elemental-mediaconnect-medialive-mediapackage-cdk-ts/example-pattern.json b/elemental-mediaconnect-medialive-mediapackage-cdk-ts/example-pattern.json index 586b008f9f..00821d6029 100644 --- a/elemental-mediaconnect-medialive-mediapackage-cdk-ts/example-pattern.json +++ b/elemental-mediaconnect-medialive-mediapackage-cdk-ts/example-pattern.json @@ -36,7 +36,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/elemental-medialive-mediapackage-cdk-ts/example-pattern.json b/elemental-medialive-mediapackage-cdk-ts/example-pattern.json index 23328c727f..3ee1fd3063 100644 --- a/elemental-medialive-mediapackage-cdk-ts/example-pattern.json +++ b/elemental-medialive-mediapackage-cdk-ts/example-pattern.json @@ -20,17 +20,17 @@ }, "deploy": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "authors": [ diff --git a/elemental-mediapackage-cloudfront-cdk-ts/example-pattern.json b/elemental-mediapackage-cloudfront-cdk-ts/example-pattern.json index eeafea9ac1..2efdb22962 100644 --- a/elemental-mediapackage-cloudfront-cdk-ts/example-pattern.json +++ b/elemental-mediapackage-cloudfront-cdk-ts/example-pattern.json @@ -29,7 +29,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/event-bridge-scheduler-lambda-cdk-dotnet/example-pattern.json b/event-bridge-scheduler-lambda-cdk-dotnet/example-pattern.json index e18740aaed..2719535b10 100644 --- a/event-bridge-scheduler-lambda-cdk-dotnet/example-pattern.json +++ b/event-bridge-scheduler-lambda-cdk-dotnet/example-pattern.json @@ -26,7 +26,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-api-destinations/10-stripe/example-pattern.json b/eventbridge-api-destinations/10-stripe/example-pattern.json new file mode 100644 index 0000000000..e38c4d47fd --- /dev/null +++ b/eventbridge-api-destinations/10-stripe/example-pattern.json @@ -0,0 +1,77 @@ +{ + "title": "EventBridge API Destinations to Stripe", + "description": "Create an API destination in EventBridge for Stripe APIs.", + "level": "300", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern configures an EventRule, that routes to the EventBridge API destination in EventBridge for Stripe to create a new product object API.", + "Once deployed, the pattern allows a user to send events easily through to create specific goods or services for supporting the product experience at Stripe. The pattern creates the following resources: ", + "EventBridge Event Bus", + "EventBridge Rule", + "EventBridge Connection", + "EventBridge API Destination", + "IAM Role", + "SQS Queue", + "SQS Queue Policy", + "The EventBridge Rule uses the API Destination and Connection as a target, SQS Queue as a Dead Letter Queue (DLQ), and the IAM role as its role. The created IAM role has the permission to allow InvokeApiDestination access to the EventBridge Rule.", + "If using the custom named IAM role in this pattern, it requires the explicit acknowledgement that the stack template contains certain capabilities in order for AWS CloudFormation to create the stack. When using the AWS SAM CLI, the --capabilities=CAPABILITY_NAMED_IAM parameter can be added to the deployment command to explicitly acknowledge.", + "To send events, go to the EventBridge Event Bus details. Select the Send events button. Select the Event Bus created by the pattern, \"StripeEventBus\". Enter \"MyStripeTestApp\" for the source. Enter \"MyStripeTestAppDetailType\" for the detail type. Enter Event details in json format for a creating a product (https://stripe.com/docs/api/products) API call.", + "The Stripe API uses API keys to authenticate requests (https://stripe.com/docs/api/authentication.) Before using the pattern, obtain an API Key from Stripe and update the CloudFormation template parameter \"StripeAPIKeyValue\" using Bearer auth." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/eventbridge-api-destinations", + "templateURL": "serverless-patterns/eventbridge-api-destinations", + "projectFolder": "eventbridge-api-destinations", + "templateFile": "eventbridge-api-destinations/10-stripe/template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Stripe Docs", + "link": "https://stripe.com/docs" + }, + { + "text": "Stripe API Documentation", + "link": "https://stripe.com/docs/api" + }, + { + "text": "Using API destinations with Amazon EventBridge", + "link": "https://aws.amazon.com/blogs/compute/using-api-destinations-with-amazon-eventbridge/" + }, + { + "text": "Use Amazon EventBridge to Build Decoupled, Event-Driven Architectures", + "link": "https://serverlessland.com/learn/eventbridge" + } + ] + }, + "deploy": { + "text": [ + "cd ./10-stripe", + "sam deploy --guided" + ] + }, + "testing": { + "text": [ + "1. From a command line in this directory, send a test event to EventBridge simulating a \"Payment failed\" event: aws events put-events --entries file://testEvent.json" + ] + }, + "cleanup": { + "text": [ + "1. Delete the stack: sam delete --stack-name STACK_NAME.", + "2. Confirm the stack has been deleted: aws cloudformation list-stacks --query \"StackSummaries[?contains(StackName,'STACK_NAME')].StackStatus\"" + ] + }, + "authors": [ + { + "name": "Diana \"Di\" Dow", + "image": "https://badgephotos.corp.amazon.com/?uid=didow", + "bio": "AWS Technical Account Manager", + "linkedin": "https://www.linkedin.com/in/diana-d-17a713/" + } + ] +} \ No newline at end of file diff --git a/eventbridge-api-destinations/10-stripe/template.yaml b/eventbridge-api-destinations/10-stripe/template.yaml new file mode 100644 index 0000000000..54a7b9ea4c --- /dev/null +++ b/eventbridge-api-destinations/10-stripe/template.yaml @@ -0,0 +1,161 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: Create an API destination in EventBridge for Stripe Create Product API + +Parameters: + CreateAProductAPI: + Type: String + Default: 'https://api.stripe.com/v1/products' + StripeAPIKeyName: + NoEcho: false + Type: String + Default: 'Authorization' + StripeAPIKeyValue: + NoEcho: false + Type: String + Default: 'Bearer <>' + StripeAPIEventBridgeRole: + NoEcho: false + Type: String + Default: 'EventBridgeIAMRole' + + +Resources: + + # Define the EventBridge Event Bus + StripeEventBus: + Type: AWS::Events::EventBus + Properties: + Name: "StripeEventBus" + + # Define the Connection + StripeConnection: + Type: AWS::Events::Connection + Properties: + AuthorizationType: API_KEY + Description: 'My connection with a username and password' + AuthParameters: + ApiKeyAuthParameters : + ApiKeyName: !Ref StripeAPIKeyName + ApiKeyValue: !Ref StripeAPIKeyValue + + # Define the API Destination + CreateAProductAPIDestination: + Type: AWS::Events::ApiDestination + Properties: + Name: 'CreateAProductAPIDestination' + ConnectionArn: !GetAtt StripeConnection.Arn + InvocationEndpoint: !Ref CreateAProductAPI + HttpMethod: POST + InvocationRateLimitPerSecond: 10 + + # Define the EventBridge Target Role + EventBridgeTargetRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: + - events.amazonaws.com + Action: + - sts:AssumeRole + Policies: + - PolicyName: AllowAPIdestinationAccess + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: 'events:InvokeApiDestination' + Resource: !GetAtt CreateAProductAPIDestination.Arn + RoleName: !Ref StripeAPIEventBridgeRole + + # Define the SQS queue + StripeDLQueue: + Type: AWS::SQS::Queue + + # Define the Event Rule to filter for events + EventRuleCreateAProduct: + Type: AWS::Events::Rule + Properties: + Description: "EventRule" + State: "ENABLED" + EventBusName: !Ref StripeEventBus + EventPattern: + source: + - "PartnerApp" + detail-type: + - "PartnerAppDetailType" + Targets: + - Arn: !GetAtt CreateAProductAPIDestination.Arn + RoleArn: !GetAtt EventBridgeTargetRole.Arn + Id: "CreateAProduct" + HttpParameters: + QueryStringParameters: + name : "$.detail.name" + description: "$.detail.description" + InputPath : "$.detail" + RetryPolicy: + MaximumRetryAttempts: 0 + MaximumEventAgeInSeconds: 60 + DeadLetterConfig: + Arn: !GetAtt StripeDLQueue.Arn + + # Allow EventBridge to invoke SQS + StripeDLQueueEventBridgePolicy: + Type: AWS::SQS::QueuePolicy + Properties: + PolicyDocument: + Statement: + - Effect: Allow + Principal: + Service: events.amazonaws.com + Action: SQS:SendMessage + Resource: !GetAtt StripeDLQueue.Arn + Queues: + - Ref: StripeDLQueue + +Outputs: + StripeEventBusName: + Description: Application EventBus Name + Value: !Ref StripeEventBus + StripeEventBusArn: + Description: Application EventBus ARN + Value: !GetAtt StripeEventBus.Arn + + EventRuleCreateAProductName: + Description: Create A Product Event Rule + Value: !Ref EventRuleCreateAProduct + EventRuleCreateAProductArn: + Description: Create A Product Event Rule ARN + Value: !GetAtt EventRuleCreateAProduct.Arn + + StripeConnectionName: + Description: Stripe Connection + Value: !Ref StripeConnection + StripeConnectionArn: + Description: Stripe Connection ARN + Value: !GetAtt StripeConnection.Arn + + CreateAProductAPIDestinationName: + Description: Create A Product API Destination + Value: !Ref CreateAProductAPIDestination + CreateAProductAPIDestinationArn: + Description: Create A Product API Destination ARN + Value: !GetAtt CreateAProductAPIDestination.Arn + + EventBridgeTargetRole: + Description: EventBridge Target Role + Value: !Ref EventBridgeTargetRole + EventBridgeTargetRoleArn: + Description: EventBridge Target Role ARN + Value: !GetAtt EventBridgeTargetRole.Arn + + StripeDLQueue: + Description: Stripe Dead Letter Queue + Value: !Ref StripeDLQueue + StripeDLQueueArn: + Description: Stripe Dead Letter Queue ARN + Value: !GetAtt StripeDLQueue.Arn diff --git a/eventbridge-api-destinations/10-stripe/testEvent.json b/eventbridge-api-destinations/10-stripe/testEvent.json new file mode 100644 index 0000000000..5e60ec059e --- /dev/null +++ b/eventbridge-api-destinations/10-stripe/testEvent.json @@ -0,0 +1,8 @@ +[ + { + "DetailType": "StripeAppDetailType", + "EventBusName": "StripeEventBus", + "Source": "StripeApp", + "Detail": "{\"name\":\"Mystic 8 Button\",\"description\":\"A modern Magic 8 ball that can be used as a desk toy.\"}" + } +] \ No newline at end of file diff --git a/eventbridge-api-destinations/README.md b/eventbridge-api-destinations/README.md index e2a4504ebe..2adffacb1c 100644 --- a/eventbridge-api-destinations/README.md +++ b/eventbridge-api-destinations/README.md @@ -22,6 +22,7 @@ Important: this application uses various AWS services and there are costs associ * To run example #6, an account with [Freshdesk](https://support.freshdesk.com/support/login). Follow the instructions at [Getting Started](https://developers.freshdesk.com/api/#getting-started) and note the unique URL for your API destination endpoint to deploy the solution. * To run example #7, an account with [DataDog](hhttps://www.datadoghq.com). Follow the instructions to [Add an API key or client token](https://docs.datadoghq.com/account_management/api-app-keys/#add-an-api-key-or-client-token) and note the api key. * To run example #9, an account with [Shopify](https://www.shopify.com/). Follow the instructions to [Create an app and configure Admin API Access scopes](https://shopify.dev/apps/auth/admin-app-access-tokens#step-1-create-and-install-the-app). Make sure to note the Admin Key. +* To run example #10, an account with [Stripe](https://dashboard.stripe.com/login). Follow the instructions to [Set up your development environment](https://stripe.com/docs/development/quickstart) and note the api key. ## Deployment Instructions 1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: @@ -41,6 +42,7 @@ Important: this application uses various AWS services and there are costs associ - To run the freshdesk API destination example, cd to `6-freshdesk`. - To run the Datadog API Destination example, cd to `7-datadog`. - To run the Shopify API Destination example, cd to `9-shopify`. +- To run the Stripe API Destination example, cd to `10-stripe`. 1. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: ``` sam deploy --guided @@ -68,6 +70,7 @@ aws events put-events --entries file://testEvent.json 7. For the freshdesk example use the testEvent.json within the 6-freshdesk directory 8. For the datadog example use the testEvent.json within the 7-datadog directory 9. For the shopify example use the testEvent.json within the 9-shopify directory +10. For the shopify example use the testEvent.json within the 10-stripe directory ``` aws events put-events --entries file://3-sumologic/testEvent.json ``` @@ -83,6 +86,6 @@ aws events put-events --entries file://3-sumologic/testEvent.json aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'STACK_NAME')].StackStatus" ``` ---- -Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: MIT-0 diff --git a/eventbridge-cloudevents-transformer-cdk-typescript/README.md b/eventbridge-cloudevents-transformer-cdk-typescript/README.md new file mode 100644 index 0000000000..3ac5c0a5d0 --- /dev/null +++ b/eventbridge-cloudevents-transformer-cdk-typescript/README.md @@ -0,0 +1,147 @@ +# Transforming EventBridge Events to CloudEvents + +If your EventBridge target e.g., a Lambda function, expects a JSON-encoded [CloudEvent](https://cloudevents.io/) an +EventBridge [Input Transformer](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-transform-target-input.html) +can be used to perform a lossless conversion from the EventBridge event format to CloudEvents. + +This pattern demonstrates using an input transformation in an EventBridge rule on a custom event bus to convert an +EventBridge event to a *structured JSON-encoded* CloudEvent. + +Learn more about this pattern at Serverless Land Patterns: +https://serverlessland.com/patterns/eventbridge-cloudevents-transformer-cdk-typescript + +Important: this application uses various AWS services and there are costs associated with these services after the Free +Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any +AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already + have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls + and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) installed +* [Node and NPM](https://nodejs.org/en/download/) installed +* [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) (AWS CDK) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` + +1. Change directory to the pattern directory: + + ``` + cd serverless-patterns/eventbridge-cloudevents-transformer-cdk-typescript/src + ``` + +1. Install dependencies: + + ``` + npm install + ``` + +1. From the command line, configure AWS CDK (unless already done): + + ``` + # cdk bootstrap + cdk bootstrap 1111111111/us-east-1 + ``` + +1. From the command line, use AWS CDK to deploy the AWS resources for the pattern: + + ``` + cdk deploy + ``` + +## How it works + +This pattern demonstrates using an input transformation in an EventBridge rule on a custom event bus to convert an +EventBridge event to a *structured JSON-encoded* CloudEvent. A Lambda function, using the [CloudEvents SDK for +TypeScript](https://github.com/cloudevents/sdk-javascript), is configured as a target on the rule, using the SDK in the +handler to consume the incoming CloudEvent without performing manual deserialization from the EventBridge format. + +The example input transformer in this pattern uses the following EventBridge to CloudEvents field mappings +(customizable). For demo purposes, `account` and `region` are mapped to CloudEvent extension attributes. + +| EventBridge | CloudEvents | +|---------------|-------------| +| `id` | `id` | +| `source` | `source` | +| `detail-type` | `type` | +| `time` | `time` | +| `detail` | `data` | +| `account` | `account` | +| `region` | `region` | + +> **Note** +> If there is a stable, i.e., always present, JSON field in the EventBridge `detail` payload for a particular object, +> e.g., an order id, it can be easily mapped to the CloudEvents `subject` field, e.g. `subject: +> EventField.fromPath('$.detail.order_id')` (not shown in this example). + +### Example + +Given the following EventBridge event: + +```json +{ + "version": "0", + "id": "58c17c28-ce07-3b3d-a8c2-92830a2910de", + "account": "1234567890", + "time": "2023-07-03T13:18:31Z", + "region": "us-east-1", + "detail-type": "test.event", + "source": "test.source", + "resources": [], + "detail": { + "hello": "world" + } +} +``` + +The transformed event as received by the Lambda function is: + +```json +{ + "specversion": "1.0", + "id": "58c17c28-ce07-3b3d-a8c2-92830a2910de", + "time": "2023-07-03T13:18:31.000Z", + "type": "test.event", + "source": "test.source", + "data": { + "hello": "world" + }, + "region": "us-east-1", + "account": "1234567890" +} +``` + +## Testing + +Step-by-step instructions to understand the implementation for the pattern: + +1. Deploy the `EbLambdaCloudeventsStack` as described [above](#deployment-instructions) +2. Verify the custom event bus, rule, input transformer and Lambda function were created +3. In the AWS console, open the custom event bus and click on `Send Events` in the upper right (the rule used in this example will match any event from the same account) +4. Make sure the right event bus is selected +5. For `Event source` enter `test.source` +6. For `Detail type` enter `test.event` +7. For `Event detail` enter `{"hello":"world"}` +8. Check the CloudWatch logs for the function to see the resulting (converted) CloudEvent logged by the Lambda function +9. If you don't see an event, try sending it again or inspect the dead-letter queue + +## Cleanup + +Delete the stack: + +``` +cdk destroy +``` + +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/eventbridge-cloudevents-transformer-cdk-typescript/example-pattern.json b/eventbridge-cloudevents-transformer-cdk-typescript/example-pattern.json new file mode 100644 index 0000000000..d15c9b551c --- /dev/null +++ b/eventbridge-cloudevents-transformer-cdk-typescript/example-pattern.json @@ -0,0 +1,58 @@ +{ + "title": "EventBridge CloudEvents Input Transformer to Lambda", + "description": "This pattern uses an EventBridge input transfomer to convert an EventBridge event to a CloudEvent and send it to a Lambda function using the CloudEvents SDK for event handling.", + "language": "TypeScript", + "level": "200", + "framework": "CDK", + "introBox": { + "headline": "How it works", + "text": [ + "EventBridge events are converted to CloudEvents using an input transformer in a rule on a custom event bus. A Lambda function is configured as a target for that rule. The Lambda function uses the CloudEvents TypeScript SDK to deserialize and validate incoming CloudEvents." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/eventbridge-cloudevents-transformer-cdk-typescript", + "templateURL": "serverless-patterns/eventbridge-cloudevents-transformer-cdk-typescript", + "projectFolder": "eventbridge-cloudevents-transformer-cdk-typescript", + "templateFile": "src/lib/eb-lambda-cloudevents-stack.ts" + } + }, + "resources": { + "bullets": [ + { + "text": "EventBridge Input Transformation Documentation", + "link": "https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-transform-target-input.html" + }, + { + "text": "CloudEvents Specification", + "link": "https://cloudevents.io/" + }, + { + "text": "Cloudformation API for EventBridge", + "link": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/AWS_Events.html" + }, + { + "text": "Learn more about event-driven architecture", + "link": "https://serverlessland.com/event-driven-architecture" + } + ] + }, + "deploy": { + "text": ["cdk deploy"] + }, + "testing": { + "text": ["See the README in the GitHub repo for detailed testing instructions."] + }, + "cleanup": { + "text": ["Delete the stack: cdk destroy."] + }, + "authors": [ + { + "name": "Michael Gasch", + "image": "https://avatars.githubusercontent.com/u/15986659", + "bio": "Senior Product Manager Technical for Amazon EventBridge.", + "linkedin": "michael-gasch-10603298" + } + ] +} \ No newline at end of file diff --git a/cdk-s3-sqs-lambda-dynamodb/.gitignore b/eventbridge-cloudevents-transformer-cdk-typescript/src/.gitignore similarity index 92% rename from cdk-s3-sqs-lambda-dynamodb/.gitignore rename to eventbridge-cloudevents-transformer-cdk-typescript/src/.gitignore index 70a5db7c17..f60797b6a9 100644 --- a/cdk-s3-sqs-lambda-dynamodb/.gitignore +++ b/eventbridge-cloudevents-transformer-cdk-typescript/src/.gitignore @@ -2,7 +2,7 @@ !jest.config.js *.d.ts node_modules + # CDK asset staging directory .cdk.staging cdk.out -!./data/ \ No newline at end of file diff --git a/eventbridge-cloudevents-transformer-cdk-typescript/src/.npmignore b/eventbridge-cloudevents-transformer-cdk-typescript/src/.npmignore new file mode 100644 index 0000000000..c1d6d45dcf --- /dev/null +++ b/eventbridge-cloudevents-transformer-cdk-typescript/src/.npmignore @@ -0,0 +1,6 @@ +*.ts +!*.d.ts + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/cdk-s3-sqs-lambda-dynamodb/bin/my-cdk-project.ts b/eventbridge-cloudevents-transformer-cdk-typescript/src/bin/eb-lambda-cloudevents.ts similarity index 85% rename from cdk-s3-sqs-lambda-dynamodb/bin/my-cdk-project.ts rename to eventbridge-cloudevents-transformer-cdk-typescript/src/bin/eb-lambda-cloudevents.ts index 03ff075fb6..936364f5fc 100644 --- a/cdk-s3-sqs-lambda-dynamodb/bin/my-cdk-project.ts +++ b/eventbridge-cloudevents-transformer-cdk-typescript/src/bin/eb-lambda-cloudevents.ts @@ -1,10 +1,10 @@ #!/usr/bin/env node import 'source-map-support/register'; import * as cdk from 'aws-cdk-lib'; -import { MyCdkProjectStack } from '../lib/my-cdk-project-stack'; +import { EbLambdaCloudeventsStack } from '../lib/eb-lambda-cloudevents-stack'; const app = new cdk.App(); -new MyCdkProjectStack(app, 'MyCdkProjectStack', { +new EbLambdaCloudeventsStack(app, 'EbLambdaCloudeventsStack', { /* If you don't specify 'env', this stack will be environment-agnostic. * Account/Region-dependent features and context lookups will not work, * but a single synthesized template can be deployed anywhere. */ diff --git a/eventbridge-cloudevents-transformer-cdk-typescript/src/cdk.json b/eventbridge-cloudevents-transformer-cdk-typescript/src/cdk.json new file mode 100644 index 0000000000..e61a8554fb --- /dev/null +++ b/eventbridge-cloudevents-transformer-cdk-typescript/src/cdk.json @@ -0,0 +1,53 @@ +{ + "app": "npx ts-node --prefer-ts-exts bin/eb-lambda-cloudevents.ts", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "**/*.d.ts", + "**/*.js", + "tsconfig.json", + "package*.json", + "yarn.lock", + "node_modules", + "test" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-redshift:columnId": true, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true + } +} diff --git a/eventbridge-cloudevents-transformer-cdk-typescript/src/jest.config.js b/eventbridge-cloudevents-transformer-cdk-typescript/src/jest.config.js new file mode 100644 index 0000000000..08263b8954 --- /dev/null +++ b/eventbridge-cloudevents-transformer-cdk-typescript/src/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + testEnvironment: 'node', + roots: ['/test'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest' + } +}; diff --git a/eventbridge-cloudevents-transformer-cdk-typescript/src/lambda/index.ts b/eventbridge-cloudevents-transformer-cdk-typescript/src/lambda/index.ts new file mode 100644 index 0000000000..86ff15b9e6 --- /dev/null +++ b/eventbridge-cloudevents-transformer-cdk-typescript/src/lambda/index.ts @@ -0,0 +1,19 @@ +import { CloudEvent } from "cloudevents"; + +export const handler = async function (event: any) { + console.log("received event: %s", event); + + try { + const ce = new CloudEvent(event); + ce.validate(); // throws if invalid + console.log("received valid cloudevent: %s", ce) + } + catch (e) { + const msg = `invalid cloudevent: ${e}` + console.log(msg) + return { + statusCode: 400, + body: msg, + }; + } +}; diff --git a/eventbridge-cloudevents-transformer-cdk-typescript/src/lib/eb-lambda-cloudevents-stack.ts b/eventbridge-cloudevents-transformer-cdk-typescript/src/lib/eb-lambda-cloudevents-stack.ts new file mode 100644 index 0000000000..dc9780c773 --- /dev/null +++ b/eventbridge-cloudevents-transformer-cdk-typescript/src/lib/eb-lambda-cloudevents-stack.ts @@ -0,0 +1,68 @@ +import * as cdk from 'aws-cdk-lib'; +import { EventBus, EventField, Rule, RuleTargetInput } from 'aws-cdk-lib/aws-events'; +import { LambdaFunction } from 'aws-cdk-lib/aws-events-targets'; +import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs'; +import { RetentionDays } from 'aws-cdk-lib/aws-logs'; +import { Queue } from 'aws-cdk-lib/aws-sqs'; +import { Construct } from 'constructs'; +import { join } from 'path'; +import { Runtime } from 'aws-cdk-lib/aws-lambda'; + + +export class EbLambdaCloudeventsStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const dlq = new Queue(this, "dlq"); + const lambdaTS = new NodejsFunction(this, "ts-function", { + entry: join(__dirname, "..", "lambda", "index.ts"), + handler: "handler", + timeout: cdk.Duration.seconds(3), + reservedConcurrentExecutions: 1, + deadLetterQueue: dlq, + logRetention: RetentionDays.ONE_DAY, + runtime: Runtime.NODEJS_18_X, + }); + + + const bus = new EventBus(this, "bus"); + + const transformer = { + specversion: "1.0", + id: EventField.eventId, + source: EventField.source, + type: EventField.detailType, + time: EventField.time, + region: EventField.region, + account: EventField.account, + data: EventField.fromPath('$.detail') + } + + const targetLambdaTS = new LambdaFunction(lambdaTS, { + retryAttempts: 0, + deadLetterQueue: dlq, + event: RuleTargetInput.fromObject(transformer) + }); + + new Rule(this, "rule", { + eventBus: bus, + eventPattern: { + account: [cdk.Stack.of(this).account], + }, + targets: [targetLambdaTS], + }) + + new cdk.CfnOutput(this, "busArn", { + value: bus.eventBusArn, + }) + + new cdk.CfnOutput(this, "functionArn", { + value: lambdaTS.functionArn, + }) + + new cdk.CfnOutput(this, "dlqArn", { + value: dlq.queueArn, + }) + } +} + diff --git a/eventbridge-cloudevents-transformer-cdk-typescript/src/package.json b/eventbridge-cloudevents-transformer-cdk-typescript/src/package.json new file mode 100644 index 0000000000..07e22e75d1 --- /dev/null +++ b/eventbridge-cloudevents-transformer-cdk-typescript/src/package.json @@ -0,0 +1,28 @@ +{ + "name": "eb-lambda-cloudevents", + "version": "0.1.0", + "bin": { + "eb-lambda-cloudevents": "bin/eb-lambda-cloudevents.js" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "jest", + "cdk": "cdk" + }, + "devDependencies": { + "@types/jest": "^29.5.1", + "@types/node": "20.1.7", + "aws-cdk": "2.81.0", + "jest": "^29.5.0", + "ts-jest": "^29.1.0", + "ts-node": "^10.9.1", + "typescript": "~5.0.4" + }, + "dependencies": { + "aws-cdk-lib": "2.81.0", + "cloudevents": "^7.0.1", + "constructs": "^10.2.62", + "source-map-support": "^0.5.21" + } +} diff --git a/eventbridge-cloudevents-transformer-cdk-typescript/src/tsconfig.json b/eventbridge-cloudevents-transformer-cdk-typescript/src/tsconfig.json new file mode 100644 index 0000000000..aaa7dc510f --- /dev/null +++ b/eventbridge-cloudevents-transformer-cdk-typescript/src/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": [ + "es2020", + "dom" + ], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "./node_modules/@types" + ] + }, + "exclude": [ + "node_modules", + "cdk.out" + ] +} diff --git a/eventbridge-cloudwatch-dotnet-cdk/README.md b/eventbridge-cloudwatch-dotnet-cdk/README.md index 8b07439121..351d97a629 100644 --- a/eventbridge-cloudwatch-dotnet-cdk/README.md +++ b/eventbridge-cloudwatch-dotnet-cdk/README.md @@ -11,7 +11,7 @@ Important: this application uses various AWS services and there are costs associ * [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -* [.NET Core 3.1](https://dotnet.microsoft.com/en-us/download/dotnet/3.1) installed +* [.NET 6](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) installed * [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) (AWS CDK) installed ## Deployment Instructions diff --git a/eventbridge-cloudwatch-dotnet-cdk/example-pattern.json b/eventbridge-cloudwatch-dotnet-cdk/example-pattern.json index a1dce53eaa..2a3a21d4f0 100644 --- a/eventbridge-cloudwatch-dotnet-cdk/example-pattern.json +++ b/eventbridge-cloudwatch-dotnet-cdk/example-pattern.json @@ -37,7 +37,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-cloudwatch-dotnet-cdk/src/EventBridgeCloudWatchDotnetCdk/EventBridgeCloudWatchDotnetCdk.csproj b/eventbridge-cloudwatch-dotnet-cdk/src/EventBridgeCloudWatchDotnetCdk/EventBridgeCloudWatchDotnetCdk.csproj index 4c7d0101b7..e91a841aea 100644 --- a/eventbridge-cloudwatch-dotnet-cdk/src/EventBridgeCloudWatchDotnetCdk/EventBridgeCloudWatchDotnetCdk.csproj +++ b/eventbridge-cloudwatch-dotnet-cdk/src/EventBridgeCloudWatchDotnetCdk/EventBridgeCloudWatchDotnetCdk.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net6.0 Major diff --git a/eventbridge-firehose-cdk/example-pattern.json b/eventbridge-firehose-cdk/example-pattern.json index cff2fa8d40..04240910ef 100644 --- a/eventbridge-firehose-cdk/example-pattern.json +++ b/eventbridge-firehose-cdk/example-pattern.json @@ -39,7 +39,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-firehose-s3-cdk/README.md b/eventbridge-firehose-s3-cdk/README.md index b46dd9701c..e83af64c54 100644 --- a/eventbridge-firehose-s3-cdk/README.md +++ b/eventbridge-firehose-s3-cdk/README.md @@ -14,7 +14,7 @@ Important: this application uses various AWS services and there are costs associ - [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured - [AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/cli.html) installed and configured - [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -- [.Net Core 3.1 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/3.1) installed +- [.Net Core 3.1 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) installed ## Deployment Instructions diff --git a/eventbridge-firehose-s3-cdk/src/src.csproj b/eventbridge-firehose-s3-cdk/src/src.csproj index 5ff45379e1..17e8f9efa1 100644 --- a/eventbridge-firehose-s3-cdk/src/src.csproj +++ b/eventbridge-firehose-s3-cdk/src/src.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net6.0 enable enable latest diff --git a/eventbridge-global-endpoints-cdk/example-pattern.json b/eventbridge-global-endpoints-cdk/example-pattern.json index 9a8248803a..cd3140ae66 100644 --- a/eventbridge-global-endpoints-cdk/example-pattern.json +++ b/eventbridge-global-endpoints-cdk/example-pattern.json @@ -34,7 +34,7 @@ "text": ["cdk deploy --all"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy --all."] diff --git a/eventbridge-lambda-dotnet-cdk/README.md b/eventbridge-lambda-dotnet-cdk/README.md index db12f72333..22477f9e8b 100644 --- a/eventbridge-lambda-dotnet-cdk/README.md +++ b/eventbridge-lambda-dotnet-cdk/README.md @@ -11,7 +11,7 @@ Important: this application uses various AWS services and there are costs associ * [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -* [.NET Core 3.1](https://dotnet.microsoft.com/en-us/download/dotnet/3.1) installed +* [.NET 6](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) installed * [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) (AWS CDK) installed ## Deployment Instructions diff --git a/eventbridge-lambda-dotnet-cdk/example-pattern.json b/eventbridge-lambda-dotnet-cdk/example-pattern.json index c055702f15..668ac2dfdc 100644 --- a/eventbridge-lambda-dotnet-cdk/example-pattern.json +++ b/eventbridge-lambda-dotnet-cdk/example-pattern.json @@ -37,7 +37,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-lambda-dotnet-cdk/src/ConsumerLambda/ConsumerLambda.csproj b/eventbridge-lambda-dotnet-cdk/src/ConsumerLambda/ConsumerLambda.csproj index a84144f12b..a6ffb05897 100644 --- a/eventbridge-lambda-dotnet-cdk/src/ConsumerLambda/ConsumerLambda.csproj +++ b/eventbridge-lambda-dotnet-cdk/src/ConsumerLambda/ConsumerLambda.csproj @@ -1,6 +1,6 @@ - netcoreapp3.1 + net6.0 true Lambda diff --git a/eventbridge-lambda-dotnet-cdk/src/ConsumerLambda/aws-lambda-tools-defaults.json b/eventbridge-lambda-dotnet-cdk/src/ConsumerLambda/aws-lambda-tools-defaults.json index 4df0c57c16..ee57303ead 100644 --- a/eventbridge-lambda-dotnet-cdk/src/ConsumerLambda/aws-lambda-tools-defaults.json +++ b/eventbridge-lambda-dotnet-cdk/src/ConsumerLambda/aws-lambda-tools-defaults.json @@ -8,7 +8,7 @@ "profile": "", "region": "", "configuration": "Release", - "framework": "netcoreapp3.1", + "framework": "net6.0", "function-runtime": "dotnetcore3.1", "function-memory-size": 128, "function-timeout": 10, diff --git a/eventbridge-lambda-dotnet-cdk/src/EventBridgeLambdaDotnetCdk/EventBridgeLambdaDotnetCdk.csproj b/eventbridge-lambda-dotnet-cdk/src/EventBridgeLambdaDotnetCdk/EventBridgeLambdaDotnetCdk.csproj index 4c7d0101b7..e91a841aea 100644 --- a/eventbridge-lambda-dotnet-cdk/src/EventBridgeLambdaDotnetCdk/EventBridgeLambdaDotnetCdk.csproj +++ b/eventbridge-lambda-dotnet-cdk/src/EventBridgeLambdaDotnetCdk/EventBridgeLambdaDotnetCdk.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net6.0 Major diff --git a/eventbridge-lambda-dotnet-cdk/src/EventBridgeLambdaDotnetCdk/EventBridgeLambdaDotnetCdkStack.cs b/eventbridge-lambda-dotnet-cdk/src/EventBridgeLambdaDotnetCdk/EventBridgeLambdaDotnetCdkStack.cs index 3f622b60f2..b817c1e2f7 100644 --- a/eventbridge-lambda-dotnet-cdk/src/EventBridgeLambdaDotnetCdk/EventBridgeLambdaDotnetCdkStack.cs +++ b/eventbridge-lambda-dotnet-cdk/src/EventBridgeLambdaDotnetCdk/EventBridgeLambdaDotnetCdkStack.cs @@ -33,14 +33,18 @@ internal EventBridgeLambdaDotnetCdkStack(Construct scope, string id, IStackProps }); // Lambda Function Build Commands - var buildCommands = new[] + var buildOption = new BundlingOptions() { - "cd /asset-input", - "export DOTNET_CLI_HOME=\"/tmp/DOTNET_CLI_HOME\"", - "export PATH=\"$PATH:/tmp/DOTNET_CLI_HOME/.dotnet/tools\"", - "dotnet tool install -g Amazon.Lambda.Tools", - "dotnet lambda package -o output.zip", - "unzip -o -d /asset-output output.zip" + Image = Runtime.DOTNET_6.BundlingImage, + User = "root", + OutputType = BundlingOutput.ARCHIVED, + Command = new string[]{ + "/bin/sh", + "-c", + " dotnet tool install -g Amazon.Lambda.Tools"+ + " && dotnet build"+ + " && dotnet lambda package --output-package /asset-output/function.zip" + } }; // Lambda Function @@ -48,17 +52,10 @@ internal EventBridgeLambdaDotnetCdkStack(Construct scope, string id, IStackProps { MemorySize = 128, Timeout = Duration.Seconds(10), - Runtime = Runtime.DOTNET_CORE_3_1, + Runtime = Runtime.DOTNET_6, Code = Code.FromAsset("src/ConsumerLambda", new AssetOptions { - Bundling = new BundlingOptions - { - Image = Runtime.DOTNET_CORE_3_1.BundlingImage, - Command = new [] - { - "bash", "-c", string.Join(" && ", buildCommands) - } - } + Bundling = buildOption }), Handler = "ConsumerLambda::ConsumerLambda.Function::FunctionHandler" }); diff --git a/eventbridge-lambda-rust/example-pattern.json b/eventbridge-lambda-rust/example-pattern.json index d9ced54e67..de46af9e8a 100644 --- a/eventbridge-lambda-rust/example-pattern.json +++ b/eventbridge-lambda-rust/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-lambda-terraform/example-pattern.json b/eventbridge-lambda-terraform/example-pattern.json index 518c1b3c49..75daf66a34 100644 --- a/eventbridge-lambda-terraform/example-pattern.json +++ b/eventbridge-lambda-terraform/example-pattern.json @@ -37,7 +37,7 @@ }, "testing": { "text": [ - "See the README in the Github repo for detailed testing instructions." + "See the README in the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-pipes-ddb-stream-apigateway-iot-core-cdk/example-pattern.json b/eventbridge-pipes-ddb-stream-apigateway-iot-core-cdk/example-pattern.json index 0b903ece51..3287e262f6 100644 --- a/eventbridge-pipes-ddb-stream-apigateway-iot-core-cdk/example-pattern.json +++ b/eventbridge-pipes-ddb-stream-apigateway-iot-core-cdk/example-pattern.json @@ -51,7 +51,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy."] diff --git a/eventbridge-pipes-ddb-stream-lambda-sfn-enrich/example-pattern.json b/eventbridge-pipes-ddb-stream-lambda-sfn-enrich/example-pattern.json index 2c3a299674..0a5c18b47f 100644 --- a/eventbridge-pipes-ddb-stream-lambda-sfn-enrich/example-pattern.json +++ b/eventbridge-pipes-ddb-stream-lambda-sfn-enrich/example-pattern.json @@ -35,7 +35,7 @@ "sam deploy --guided" ] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": [ diff --git a/eventbridge-pipes-ddbstream-to-eventbridge-suffix-matching/README.md b/eventbridge-pipes-ddbstream-to-eventbridge-suffix-matching/README.md new file mode 100644 index 0000000000..23a131eca6 --- /dev/null +++ b/eventbridge-pipes-ddbstream-to-eventbridge-suffix-matching/README.md @@ -0,0 +1,82 @@ +# DynamoDB stream to EventBridge bus using EventBridge Pipes and suffix matching + +This pattern demonstrates sending DynamoDB Streams directly to Amazon EventBridge bus suffix filtering. + +Learn more about this pattern at Serverless Land Patterns:https://serverlessland.com/patterns/eventbridge-pipes-ddbstream-to-eventbridge-suffix-matching + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd eventbridge-pipes-ddbstream-to-eventbridge-suffix-matching + ``` +1. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: + ``` + sam deploy --guided + ``` +1. During the prompts: + * Enter a stack name: eg. eventbridge-pipes-ddbstream-to-eventbridge-suffix-matching + * Enter the desired AWS Region + * Enter Email ID for SNS to create a topic and subscription. + * Enter NationalTeam(This is configurable, you can create any parameter name).: eg. Argentina + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run `sam deploy --guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. + +## How it works + +When new items are added into the DynamoDB database, the EventBridge Pipe is triggered. Only events that match the rule will be forwarded onto an EventBridge bus. In this example these are images that end with the `.png` trigger the EventBridge event. + +## Testing + +Add an item to the DynamoDB stream that contains a `.png` avatarUrl. + +```bash +aws dynamodb put-item \ + --table-name Users \ + --item id={S="David"},avatarUrl={S="https://pbs.twimg.com/profile_images/1262283153563140096/DYRDqKg6_400x400.png"} +``` + +Event will be triggered. + +--- + +Add an item to the DynamoDB stream that contains a `.gif` avatarUrl. + +```bash +aws dynamodb put-item \ + --table-name Users \ + --item Name={S="David"},avatarUrl={S="https://pbs.twimg.com/profile_images/1262283153563140096/DYRDqKg6_400x400.gif"} +``` + +Event is not triggered. + +--- + + +## Cleanup + +1. Delete the stack + ```bash + sam delete + ``` + +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/eventbridge-pipes-ddbstream-to-eventbridge-suffix-matching/example-pattern.json b/eventbridge-pipes-ddbstream-to-eventbridge-suffix-matching/example-pattern.json new file mode 100644 index 0000000000..6e9756ae43 --- /dev/null +++ b/eventbridge-pipes-ddbstream-to-eventbridge-suffix-matching/example-pattern.json @@ -0,0 +1,55 @@ +{ + "title": "DynamoDB stream to EventBridge Bus using EventBridge Pipes using suffix matching", + "description": "Raise events from changes in DynamoDB using EventBridge Pipes and suffix filtering.", + "language": "", + "level": "200", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": ["EventBridge Pipe used to connect DynamoDB stream (new and old images) directly into an EventBridge bus with custom source and detail-type. Only items that match the suffix rule are forwarded on the bus"] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/eventbridge-pipes-ddbstream-to-eventbridge-suffix-matching", + "templateURL": "serverless-patterns/eventbridge-pipes-ddbstream-to-eventbridge-suffix-matching", + "projectFolder": "", + "templateFile": "template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "EventBridge Visuals", + "link": "https://serverlessland.com/serverless/visuals/eventbridge" + }, + { + "text": "Suffix filtering", + "link": "https://serverlessland.com/serverless/visuals/eventbridge/suffix-filtering" + }, + { + "text": "Cloudformation API for Pipes", + "link": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-pipes-pipe.html" + }, + { + "text": "Video explaining EventBridge Pipes", + "link": "https://www.youtube.com/watch?v=xXGXCOc3cBs&t=1037s" + } + ] + }, + "deploy": { + "text": ["Deploy the stack: sam deploy."] + }, + "testing": { + "text": ["See the README in the GitHub repo for detailed testing instructions."] + }, + "cleanup": { + "text": ["Delete the stack: sam delete."] + }, + "authors": [ + { + "name": "David Boyne", + "image": "../assets/images/resources/dboyne.png", + "bio": "Senior Developer Advocate at AWS focusing on EDA and Serverless." + } + ] +} diff --git a/eventbridge-pipes-ddbstream-to-eventbridge-suffix-matching/template.yaml b/eventbridge-pipes-ddbstream-to-eventbridge-suffix-matching/template.yaml new file mode 100644 index 0000000000..11d6c69273 --- /dev/null +++ b/eventbridge-pipes-ddbstream-to-eventbridge-suffix-matching/template.yaml @@ -0,0 +1,83 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: Template to connect DynamoDB Stream to EventBridge + +Resources: + + # DynamoDB Stream + DynamoDBUsersTable: + Type: AWS::DynamoDB::Table + Properties: + TableName: Users + AttributeDefinitions: + - AttributeName: id + AttributeType: S + KeySchema: + - AttributeName: id + KeyType: HASH + ProvisionedThroughput: + ReadCapacityUnits: 5 + WriteCapacityUnits: 5 + StreamSpecification: + ## Listen for KEYS_ONLY, NEW_IMAGE, OLD_IMAGE, or NEW_AND_OLD_IMAGES (https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_StreamSpecification.html) + StreamViewType: NEW_AND_OLD_IMAGES + + # Event Bus (Target) + ApplicationEventBus: + Type: AWS::Events::EventBus + Properties: + Name: UserBus + + PipeRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: + - pipes.amazonaws.com + Action: + - sts:AssumeRole + Policies: + - PolicyName: SourcePolicy + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - "dynamodb:DescribeStream" + - "dynamodb:GetRecords" + - "dynamodb:GetShardIterator" + - "dynamodb:ListStreams" + Resource: !GetAtt DynamoDBUsersTable.StreamArn + - PolicyName: TargetPolicy + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - 'events:PutEvents' + Resource: !GetAtt ApplicationEventBus.Arn + + # EventBridge Pipe + Pipe: + Type: AWS::Pipes::Pipe + Properties: + Name: ddb-to-eventbridge-suffix + Description: "Pipe to connect DDB stream to EventBridge event bus with suffix matching" + RoleArn: !GetAtt PipeRole.Arn + Source: !GetAtt DynamoDBUsersTable.StreamArn + SourceParameters: + FilterCriteria: + Filters: + - Pattern: '{"dynamodb": {"NewImage": {"avatarUrl": {"S": [{"suffix": ".png"}]}}}}' + DynamoDBStreamParameters: + StartingPosition: LATEST + BatchSize: 1 + Target: !GetAtt ApplicationEventBus.Arn + TargetParameters: + EventBridgeEventBusParameters: + DetailType: "UserDetailsChanged" + Source: "myapp.users" diff --git a/eventbridge-pipes-ddbstream-to-eventbridge/example-pattern.json b/eventbridge-pipes-ddbstream-to-eventbridge/example-pattern.json index 02dff7e17b..bd8e0438b6 100644 --- a/eventbridge-pipes-ddbstream-to-eventbridge/example-pattern.json +++ b/eventbridge-pipes-ddbstream-to-eventbridge/example-pattern.json @@ -32,7 +32,7 @@ "text": ["Deploy the stack: sam deploy."] }, "testing": { - "text": ["See the README in the Github repo for detailed testing instructions."] + "text": ["See the README in the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: sam delete."] diff --git a/eventbridge-pipes-ddbstream-with-filters-to-eventbridge/example-pattern.json b/eventbridge-pipes-ddbstream-with-filters-to-eventbridge/example-pattern.json index 46ba77816f..6654142a23 100644 --- a/eventbridge-pipes-ddbstream-with-filters-to-eventbridge/example-pattern.json +++ b/eventbridge-pipes-ddbstream-with-filters-to-eventbridge/example-pattern.json @@ -40,7 +40,7 @@ "text": ["sam deploy --guided"] }, "testing": { - "text": ["See the README in the Github repo for detailed testing instructions."] + "text": ["See the README in the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: sam delete."] diff --git a/eventbridge-pipes-ddbstream-with-filters-to-eventbridge/template.yaml b/eventbridge-pipes-ddbstream-with-filters-to-eventbridge/template.yaml index 94c4b19731..502e3c41b1 100644 --- a/eventbridge-pipes-ddbstream-with-filters-to-eventbridge/template.yaml +++ b/eventbridge-pipes-ddbstream-with-filters-to-eventbridge/template.yaml @@ -66,6 +66,14 @@ Resources: Action: - 'events:PutEvents' Resource: !GetAtt ApplicationEventBus.Arn + - PolicyName: !Sub ${AWS::StackName}-dlq-policy + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - "SQS:SendMessage" + Resource: !GetAtt PipeDLQueue.Arn # EventBridge Pipe to listen to all created items in DDB table OrderCreatedPipe: diff --git a/eventbridge-pipes-dynamodbstream-sns/example-pattern.json b/eventbridge-pipes-dynamodbstream-sns/example-pattern.json index 23fd1eecad..4a0709e836 100644 --- a/eventbridge-pipes-dynamodbstream-sns/example-pattern.json +++ b/eventbridge-pipes-dynamodbstream-sns/example-pattern.json @@ -35,7 +35,7 @@ "sam deploy --guided" ] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": [ diff --git a/eventbridge-pipes-dynamodbstream-to-sqs-cdk-python/example-pattern.json b/eventbridge-pipes-dynamodbstream-to-sqs-cdk-python/example-pattern.json index d41834d894..ced3e9bf15 100644 --- a/eventbridge-pipes-dynamodbstream-to-sqs-cdk-python/example-pattern.json +++ b/eventbridge-pipes-dynamodbstream-to-sqs-cdk-python/example-pattern.json @@ -47,7 +47,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the README in the Github repo for detailed testing instructions."] + "text": ["See the README in the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy."] diff --git a/eventbridge-pipes-kinesis-to-kinesis-with-filtering/example-pattern.json b/eventbridge-pipes-kinesis-to-kinesis-with-filtering/example-pattern.json index 9aa4168d17..5c2569e59a 100644 --- a/eventbridge-pipes-kinesis-to-kinesis-with-filtering/example-pattern.json +++ b/eventbridge-pipes-kinesis-to-kinesis-with-filtering/example-pattern.json @@ -40,7 +40,7 @@ "text": ["sam deploy --guided"] }, "testing": { - "text": ["See the README in the Github repo for detailed testing instructions."] + "text": ["See the README in the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: sam delete."] diff --git a/eventbridge-pipes-msk-iam-auth-to-lambda/example-pattern.json b/eventbridge-pipes-msk-iam-auth-to-lambda/example-pattern.json index 6e1ae77bae..abee6a8239 100644 --- a/eventbridge-pipes-msk-iam-auth-to-lambda/example-pattern.json +++ b/eventbridge-pipes-msk-iam-auth-to-lambda/example-pattern.json @@ -40,7 +40,7 @@ "text": ["sam deploy --guided"] }, "testing": { - "text": ["See the README in the Github repo for detailed testing instructions."] + "text": ["See the README in the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: sam delete."] diff --git a/eventbridge-pipes-msk-to-lambda/example-pattern.json b/eventbridge-pipes-msk-to-lambda/example-pattern.json index b1d641e3e9..87a86fb747 100644 --- a/eventbridge-pipes-msk-to-lambda/example-pattern.json +++ b/eventbridge-pipes-msk-to-lambda/example-pattern.json @@ -40,7 +40,7 @@ "text": ["sam deploy --guided"] }, "testing": { - "text": ["See the README in the Github repo for detailed testing instructions."] + "text": ["See the README in the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: sam delete."] diff --git a/eventbridge-pipes-splitter-pattern/example-pattern.json b/eventbridge-pipes-splitter-pattern/example-pattern.json index e1091c8153..46cd4a80fd 100644 --- a/eventbridge-pipes-splitter-pattern/example-pattern.json +++ b/eventbridge-pipes-splitter-pattern/example-pattern.json @@ -40,7 +40,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the README in the Github repo for detailed testing instructions."] + "text": ["See the README in the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy."] diff --git a/eventbridge-pipes-sqs-enrich-with-api-destination/example-pattern.json b/eventbridge-pipes-sqs-enrich-with-api-destination/example-pattern.json index c5d29ff35a..899fa019d1 100644 --- a/eventbridge-pipes-sqs-enrich-with-api-destination/example-pattern.json +++ b/eventbridge-pipes-sqs-enrich-with-api-destination/example-pattern.json @@ -45,7 +45,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-pipes-sqs-enrich-with-sfdc/example-pattern.json b/eventbridge-pipes-sqs-enrich-with-sfdc/example-pattern.json index 4d702b45f6..f505ec34d4 100644 --- a/eventbridge-pipes-sqs-enrich-with-sfdc/example-pattern.json +++ b/eventbridge-pipes-sqs-enrich-with-sfdc/example-pattern.json @@ -45,7 +45,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-pipes-sqs-lambda-api-destination/README.md b/eventbridge-pipes-sqs-lambda-api-destination/README.md index 683f9b5983..abc39f0866 100644 --- a/eventbridge-pipes-sqs-lambda-api-destination/README.md +++ b/eventbridge-pipes-sqs-lambda-api-destination/README.md @@ -1,4 +1,4 @@ -# Amazon SQS to Amazon API Gatewy using Amazon Eventbridge Pipes with enrichment +# Amazon SQS to Amazon API Gateway using Amazon Eventbridge Pipes with enrichment This pattern demonstrates how to use an EventBridge pipe to push and modify events before sending it to DynamoDB. This pattern is leveraging EventBridge pipe to first integrate 3 services together, simplifying the process by reducing the need for integration code. Here, SQS is the EventBridge source, Lambda to enrich the data, before pushing to the target API Destination that invokes an API Gateway. diff --git a/eventbridge-pipes-sqs-lambda-api-destination/example-pattern.json b/eventbridge-pipes-sqs-lambda-api-destination/example-pattern.json index 5e8e987a7e..a4efaad3d6 100644 --- a/eventbridge-pipes-sqs-lambda-api-destination/example-pattern.json +++ b/eventbridge-pipes-sqs-lambda-api-destination/example-pattern.json @@ -39,7 +39,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-pipes-sqs-to-aws-batch-with-filters/example-pattern.json b/eventbridge-pipes-sqs-to-aws-batch-with-filters/example-pattern.json index e2b998f138..32ec6d7fae 100644 --- a/eventbridge-pipes-sqs-to-aws-batch-with-filters/example-pattern.json +++ b/eventbridge-pipes-sqs-to-aws-batch-with-filters/example-pattern.json @@ -44,7 +44,7 @@ ] }, "testing": { - "text": ["See the README in the Github repo for detailed testing instructions."] + "text": ["See the README in the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: sam delete."] diff --git a/eventbridge-pipes-sqs-to-eventbridge-cdk-dotnet/example-pattern.json b/eventbridge-pipes-sqs-to-eventbridge-cdk-dotnet/example-pattern.json index 18000af341..1d40a17c04 100644 --- a/eventbridge-pipes-sqs-to-eventbridge-cdk-dotnet/example-pattern.json +++ b/eventbridge-pipes-sqs-to-eventbridge-cdk-dotnet/example-pattern.json @@ -40,7 +40,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the README in the Github repo for detailed testing instructions."] + "text": ["See the README in the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy."] diff --git a/eventbridge-pipes-sqs-to-eventbridge-cdk-python/example-pattern.json b/eventbridge-pipes-sqs-to-eventbridge-cdk-python/example-pattern.json index b8e875d72e..d2a7749199 100644 --- a/eventbridge-pipes-sqs-to-eventbridge-cdk-python/example-pattern.json +++ b/eventbridge-pipes-sqs-to-eventbridge-cdk-python/example-pattern.json @@ -44,7 +44,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the README in the Github repo for detailed testing instructions."] + "text": ["See the README in the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy."] diff --git a/eventbridge-pipes-sqs-to-eventbridge-cdk/example-pattern.json b/eventbridge-pipes-sqs-to-eventbridge-cdk/example-pattern.json index 98ab0253db..7e1acfc4e0 100644 --- a/eventbridge-pipes-sqs-to-eventbridge-cdk/example-pattern.json +++ b/eventbridge-pipes-sqs-to-eventbridge-cdk/example-pattern.json @@ -40,7 +40,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the README in the Github repo for detailed testing instructions."] + "text": ["See the README in the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy."] diff --git a/eventbridge-pipes-sqs-to-eventbridge-with-filters/example-pattern.json b/eventbridge-pipes-sqs-to-eventbridge-with-filters/example-pattern.json index 6fc7e6a652..dff37f3956 100644 --- a/eventbridge-pipes-sqs-to-eventbridge-with-filters/example-pattern.json +++ b/eventbridge-pipes-sqs-to-eventbridge-with-filters/example-pattern.json @@ -40,7 +40,7 @@ "text": ["sam deploy --guided"] }, "testing": { - "text": ["See the README in the Github repo for detailed testing instructions."] + "text": ["See the README in the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: sam delete."] diff --git a/eventbridge-pipes-sqs-to-lambda-with-stepfunction-enrichment/example-pattern.json b/eventbridge-pipes-sqs-to-lambda-with-stepfunction-enrichment/example-pattern.json index 04009aaae1..e125869045 100644 --- a/eventbridge-pipes-sqs-to-lambda-with-stepfunction-enrichment/example-pattern.json +++ b/eventbridge-pipes-sqs-to-lambda-with-stepfunction-enrichment/example-pattern.json @@ -40,7 +40,7 @@ "text": ["sam deploy --guided"] }, "testing": { - "text": ["See the README in the Github repo for detailed testing instructions."] + "text": ["See the README in the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: sam delete."] diff --git a/eventbridge-pipes-sqs-to-multiple-sqs/example-pattern.json b/eventbridge-pipes-sqs-to-multiple-sqs/example-pattern.json index 701acc9f2e..dc52f65d5c 100644 --- a/eventbridge-pipes-sqs-to-multiple-sqs/example-pattern.json +++ b/eventbridge-pipes-sqs-to-multiple-sqs/example-pattern.json @@ -50,7 +50,7 @@ }, "testing": { "text": [ - "See the README in the Github repo for detailed testing instructions." + "See the README in the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-pipes-sqs-to-sqs-with-lambda-enrichment-dotnet/example-pattern.json b/eventbridge-pipes-sqs-to-sqs-with-lambda-enrichment-dotnet/example-pattern.json index 41a9de9584..45c72163f9 100644 --- a/eventbridge-pipes-sqs-to-sqs-with-lambda-enrichment-dotnet/example-pattern.json +++ b/eventbridge-pipes-sqs-to-sqs-with-lambda-enrichment-dotnet/example-pattern.json @@ -40,7 +40,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the README in the Github repo for detailed testing instructions."] + "text": ["See the README in the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy."] diff --git a/eventbridge-pipes-sqs-to-step-functions-cdk-dotnet/example-pattern.json b/eventbridge-pipes-sqs-to-step-functions-cdk-dotnet/example-pattern.json index 11225eec01..486d726ee3 100644 --- a/eventbridge-pipes-sqs-to-step-functions-cdk-dotnet/example-pattern.json +++ b/eventbridge-pipes-sqs-to-step-functions-cdk-dotnet/example-pattern.json @@ -40,7 +40,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the README in the Github repo for detailed testing instructions."] + "text": ["See the README in the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy."] diff --git a/eventbridge-pipes-sqs-to-stepfunctions-cdk-python/example-pattern.json b/eventbridge-pipes-sqs-to-stepfunctions-cdk-python/example-pattern.json index 36fbb4d598..4af1a792a8 100644 --- a/eventbridge-pipes-sqs-to-stepfunctions-cdk-python/example-pattern.json +++ b/eventbridge-pipes-sqs-to-stepfunctions-cdk-python/example-pattern.json @@ -49,7 +49,7 @@ }, "testing": { "text": [ - "See the README in the Github repo for detailed testing instructions." + "See the README in the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-pipes-sqs-to-stepfunctions/example-pattern.json b/eventbridge-pipes-sqs-to-stepfunctions/example-pattern.json index 84f40aa509..d797687d5d 100644 --- a/eventbridge-pipes-sqs-to-stepfunctions/example-pattern.json +++ b/eventbridge-pipes-sqs-to-stepfunctions/example-pattern.json @@ -45,7 +45,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-ecs-cdk-go/example-pattern.json b/eventbridge-schedule-ecs-cdk-go/example-pattern.json index fe407638f4..be80741865 100644 --- a/eventbridge-schedule-ecs-cdk-go/example-pattern.json +++ b/eventbridge-schedule-ecs-cdk-go/example-pattern.json @@ -45,7 +45,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-ecs-cdk-java/example-pattern.json b/eventbridge-schedule-ecs-cdk-java/example-pattern.json index 5fe51d8fed..42a7ef9a53 100644 --- a/eventbridge-schedule-ecs-cdk-java/example-pattern.json +++ b/eventbridge-schedule-ecs-cdk-java/example-pattern.json @@ -45,7 +45,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-ecs-terraform/example-pattern.json b/eventbridge-schedule-ecs-terraform/example-pattern.json index 7ffdef6c7c..b1206a76cc 100644 --- a/eventbridge-schedule-ecs-terraform/example-pattern.json +++ b/eventbridge-schedule-ecs-terraform/example-pattern.json @@ -36,12 +36,12 @@ }, "deploy": { "text": [ - "See the Github repo for detailed deployment instructions." + "See the GitHub repo for detailed deployment instructions." ] }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-lambda-to-dynamodb/README.md b/eventbridge-schedule-lambda-to-dynamodb/README.md index c7b746a6f9..8f12cd68a5 100644 --- a/eventbridge-schedule-lambda-to-dynamodb/README.md +++ b/eventbridge-schedule-lambda-to-dynamodb/README.md @@ -20,7 +20,7 @@ Learn more about this pattern at Serverless Land Patterns: https://serverlesslan * [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -* [.NET 6](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) installed +* [.NET 7](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) installed * [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) (AWS CDK) installed ## Deployment Instructions diff --git a/eventbridge-schedule-lambda-to-dynamodb/example-pattern.json b/eventbridge-schedule-lambda-to-dynamodb/example-pattern.json index b0e20e1700..530a73d098 100644 --- a/eventbridge-schedule-lambda-to-dynamodb/example-pattern.json +++ b/eventbridge-schedule-lambda-to-dynamodb/example-pattern.json @@ -26,7 +26,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-remove-one-time-schedules/example-pattern.json b/eventbridge-schedule-remove-one-time-schedules/example-pattern.json index 9397ce99a6..123c3af1d0 100644 --- a/eventbridge-schedule-remove-one-time-schedules/example-pattern.json +++ b/eventbridge-schedule-remove-one-time-schedules/example-pattern.json @@ -32,7 +32,7 @@ "text": ["sam build && sam deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: sam delete."] diff --git a/eventbridge-schedule-ssm-cdk-go/example-pattern.json b/eventbridge-schedule-ssm-cdk-go/example-pattern.json index 074a1f364b..02a2fe9d6b 100644 --- a/eventbridge-schedule-ssm-cdk-go/example-pattern.json +++ b/eventbridge-schedule-ssm-cdk-go/example-pattern.json @@ -47,7 +47,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-ssm-cdk-java/example-pattern.json b/eventbridge-schedule-ssm-cdk-java/example-pattern.json index fac36ac305..94c2a565fa 100644 --- a/eventbridge-schedule-ssm-cdk-java/example-pattern.json +++ b/eventbridge-schedule-ssm-cdk-java/example-pattern.json @@ -47,7 +47,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-to-batch-terraform/.gitignore b/eventbridge-schedule-to-batch-terraform/.gitignore new file mode 100644 index 0000000000..8555bc1c89 --- /dev/null +++ b/eventbridge-schedule-to-batch-terraform/.gitignore @@ -0,0 +1,7 @@ +# terraform + +/.terraform +/terraform.lock.hcl +*.tfstate* +*.tfstate.backup +*.tfvars diff --git a/eventbridge-schedule-to-batch-terraform/README.md b/eventbridge-schedule-to-batch-terraform/README.md new file mode 100644 index 0000000000..4b366812ca --- /dev/null +++ b/eventbridge-schedule-to-batch-terraform/README.md @@ -0,0 +1,54 @@ +# Amazon EventBridge Scheduler to AWS Batch + +This pattern will create an [EventBridge Scheduler](https://docs.aws.amazon.com/scheduler/latest/UserGuide/getting-started.html) to submit an [AWS Batch](https://docs.aws.amazon.com/batch/latest/userguide/Batch_GetStarted.html) job from a job definition every 5 minutes. The pattern is deployed using Terraform to create the required VPC, Batch and EventBridge Scheduler resources. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/eventbridge-schedule-to-batch-terraform + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [Terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/aws-get-started) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd eventbridge-schedule-to-batch-terraform + ``` +1. From the command line, initialize Terraform: + ``` + terraform init + ``` +1. From the commend line, apply the configuration in the main.tf file and follow the prompts: + ``` + terraform apply + ``` + + +## How it works + +An Amazon EventBridge Schedule is created that submits an AWS Batch job from a job definition every 5 minutes. The Terraform stack creates a VPC, Batch compute environment, jobs and job queues, and EventBridge Scheduler that invokes the submitJob API to run the AWS Batch job. + +## Testing + +1. After deployment, view the schedule created in the Amazon EventBridge console under Scheduler>Schedules. +2. From the AWS Batch console, navigate to the Jobs section. The schedule will trigger a new job every 5 minutes, which will be visable in the Jobs section. You can also view the job status from the Dashboard section of the AWS Batch console in the Job queue overview. + +## Cleanup + +1. Delete all created resources and follow prompts: + ``` + terraform destroy + ``` +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/eventbridge-schedule-to-batch-terraform/example-pattern.json b/eventbridge-schedule-to-batch-terraform/example-pattern.json new file mode 100644 index 0000000000..a8999772e5 --- /dev/null +++ b/eventbridge-schedule-to-batch-terraform/example-pattern.json @@ -0,0 +1,56 @@ +{ + "title": "Amazon EventBridge Scheduler to AWS Batch", + "description": "Schedule AWS Batch jobs using Amazon EventBridge Scheduler", + "level": "200", + "framework": "Terraform", + "introBox": { + "headline": "How it works", + "text": [ + "This sample project demonstrates how to use Amazon EventBridge Scheduler to schedule batch compute jobs with AWS Batch. This pattern leverages universal targets with EventBridge Scheduler to talk directly to Batch, which uses only JSON_based, structured language to define the implementation.", + "This pattern deploys one VPC, EventBridge Scheduler Schedule, one Batch compute environment, Batch Job and Queue." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/eventbridge-schedule-to-batch-terraform", + "templateURL": "serverless-patterns/eventbridge-schedule-to-batch-terraform", + "projectFolder": "eventbridge-schedule-to-batch-terraform", + "templateFile": "eventbridge-schedule-to-batch-terraform/main.tf" + } + }, + "resources": { + "bullets": [ + { + "text": "Amazon EventBridge Scheduler", + "link": "https://docs.aws.amazon.com/eventbridge/latest/userguide/scheduler.html" + }, + { + "text": "AWS Batch", + "link": "https://docs.aws.amazon.com/batch/latest/userguide/what-is-batch.html" + } + ] + }, + "deploy": { + "text": [ + "terraform apply" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: terraform destroy." + ] + }, + "authors": [ + { + "name": "Ian Lodge", + "image": "https://avatars.githubusercontent.com/u/135351711?v=4", + "bio": "Ian is a Solutions Architect at Amazon Web Services based in the US.", + "linkedin": "https://www.linkedin.com/in/ian-lodge" + } + ] +} diff --git a/eventbridge-schedule-to-batch-terraform/main.tf b/eventbridge-schedule-to-batch-terraform/main.tf new file mode 100644 index 0000000000..9bc0f887e5 --- /dev/null +++ b/eventbridge-schedule-to-batch-terraform/main.tf @@ -0,0 +1,346 @@ +# eventbridge-schedule-to-batch-terraform + +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~>4.0" + } + } +} + +provider "aws" { + region = var.region +} + +locals { + project_name = "batch-job" +} + +resource "random_string" "random" { + length = 16 + special = false + upper = false +} + +# This section creates the Batch job and any IAM resources required for the job to execute +resource "aws_iam_role" "batch_job_execution_role" { + name = "StepFunctions-BatchJobManagementRole-${random_string.random.result}" + assume_role_policy = data.aws_iam_policy_document.assume_role_policy.json +} + +data "aws_iam_policy_document" "assume_role_policy" { + statement { + actions = ["sts:AssumeRole"] + + principals { + type = "Service" + identifiers = ["ecs-tasks.amazonaws.com"] + } + } +} + +resource "aws_iam_role_policy_attachment" "batch_job_execution_policy" { + role = aws_iam_role.batch_job_execution_role.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" +} + +resource "aws_batch_job_definition" "batch_job" { + name = "StepFunctions-BatchJobManagement-${random_string.random.result}" + type = "container" + container_properties = jsonencode({ + command = ["ls", "-la"], + image = "busybox" + + resourceRequirements = [ + { + type = "VCPU" + value = "1" + }, + { + type = "MEMORY" + value = "512" + } + ] + + volumes = [ + { + host = { + sourcePath = "/tmp" + } + name = "tmp" + } + ] + + environment = [ + { + name = "VARNAME" + value = "VARVAL" + } + ] + + mountPoints = [ + { + sourceVolume = "tmp" + containerPath = "/tmp" + readOnly = false + } + ] + + ulimits = [ + { + hardLimit = 1024 + name = "nofile" + softLimit = 1024 + } + ] + executionRoleArn = aws_iam_role.batch_job_execution_role.arn + }) +} + +data "aws_iam_policy_document" "ec2_assume_role" { + statement { + effect = "Allow" + + principals { + type = "Service" + identifiers = ["ec2.amazonaws.com"] + } + + actions = ["sts:AssumeRole"] + } +} + +resource "aws_iam_role" "ecs_instance_role" { + name = "ecs_instance_role_${random_string.random.result}" + assume_role_policy = data.aws_iam_policy_document.ec2_assume_role.json +} + +resource "aws_iam_role_policy_attachment" "ecs_instance_role" { + role = aws_iam_role.ecs_instance_role.name + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" +} + +resource "aws_iam_instance_profile" "ecs_instance_role" { + name = "ecs_instance_role_${random_string.random.result}" + role = aws_iam_role.ecs_instance_role.name +} + +data "aws_iam_policy_document" "batch_assume_role" { + statement { + effect = "Allow" + + principals { + type = "Service" + identifiers = ["batch.amazonaws.com"] + } + + actions = ["sts:AssumeRole"] + } +} + +# This section creates a VPC and related resources for the AWS Batch environment + +# VPC to hold all Batch resources +resource "aws_vpc" "vpc" { + cidr_block = var.vpc_cidr + +} + +# Create an Internet Gateway +resource "aws_internet_gateway" "gw" { + vpc_id = aws_vpc.vpc.id +} + +# Public Route Table +resource "aws_route_table" "public_route" { + vpc_id = aws_vpc.vpc.id + route { + + cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.gw.id + } + +} + +#Create a single subnet which allows public IP's +resource "aws_subnet" "subnet" { + vpc_id = aws_vpc.vpc.id + cidr_block = var.subnet_cidr + map_public_ip_on_launch = true +} + +# Associate the route table with the subnet +resource "aws_route_table_association" "subnet_rt" { + subnet_id = aws_subnet.subnet.id + route_table_id = aws_route_table.public_route.id +} + +#Create a security group for the batch job +resource "aws_security_group" "batch_sg" { + name = "${local.project_name}-sg" + description = "Security group for Batch environment" + vpc_id = aws_vpc.vpc.id + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + + } +} + +# Create the Role and Policies for the Batch environment +resource "aws_iam_role" "batch_role" { + name = "${local.project_name}-batch-svc-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "batch.amazonaws.com" + } + }, + ] + }) + managed_policy_arns = ["arn:aws:iam::aws:policy/service-role/AWSBatchServiceRole"] +} + +#Create the ECS instance role +resource "aws_iam_role" "batch_ecs_role" { + name = "${local.project_name}-batch-ecs-role" + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "ec2.amazonaws.com" + } + }, + ] + }) + managed_policy_arns = ["arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"] +} + +# Create the ECS instance profile +resource "aws_iam_instance_profile" "ecs_profile" { + name = "${local.project_name}-batch-profile" + role = aws_iam_role.batch_ecs_role.name +} + +# Create the Batch compute environment +resource "aws_batch_compute_environment" "batch_compute_env" { + compute_environment_name = "BatchComputeEnvironment-${random_string.random.result}" + type = "MANAGED" + service_role = aws_iam_role.batch_role.arn + compute_resources { + max_vcpus = 64 + min_vcpus = 0 + desired_vcpus = 2 + type = "EC2" + instance_role = aws_iam_instance_profile.ecs_profile.arn + security_group_ids = [ + aws_security_group.batch_sg.id + ] + instance_type = ["optimal"] + subnets = [ + aws_subnet.subnet.id + ] + } + depends_on = [ + aws_iam_role.batch_ecs_role, + aws_subnet.subnet, + aws_security_group.batch_sg, + aws_iam_role.batch_role + ] +} + +# Create the Batch job queue +resource "aws_batch_job_queue" "batch_job_queue" { + name = "Scheduler-Batch-Queue-${random_string.random.result}" + state = "ENABLED" + priority = 1 + compute_environments = [ + aws_batch_compute_environment.batch_compute_env.arn + ] +} + +# This section creates schedules using Amazon EventBridge Scheduler, as well as the required IAM roles to interact with Batch + +resource "aws_scheduler_schedule" "batch-schedule" { + name = "batch-submit-job-schedule" + + flexible_time_window { + mode = "OFF" + } + + schedule_expression = "rate(5 minutes)" + schedule_expression_timezone = "US/Eastern" # Default is UTC + description = "submitJob Batch event" + + target { + arn = "arn:aws:scheduler:::aws-sdk:batch:submitJob" + role_arn = aws_iam_role.scheduler-batch-role.arn + + input = jsonencode({ + "JobName": "scheduled-job", + "JobDefinition": "${aws_batch_job_definition.batch_job.arn}", + "JobQueue": "${aws_batch_job_queue.batch_job_queue.arn}" + }) + } +} + +resource "aws_iam_policy" "scheduler_batch_policy" { + name = "scheduler_batch_policy" + + policy = jsonencode({ + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "batch:SubmitJob", + "batch:DescribeJobs", + "batch:TerminateJob" + ], + "Resource": "*", + "Effect": "Allow" + }, + { + "Action": [ + "events:PutTargets", + "events:PutRule", + "events:DescribeRule" + ], + "Resource": [ + "*" + ], + "Effect": "Allow" + } + ] + } + ) +} + +resource "aws_iam_role" "scheduler-batch-role" { + name = "scheduler-batch-role" + managed_policy_arns = [aws_iam_policy.scheduler_batch_policy.arn] + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Sid = "" + Principal = { + Service = "scheduler.amazonaws.com" + } + }, + ] + }) +} + diff --git a/eventbridge-schedule-to-batch-terraform/variables.tf b/eventbridge-schedule-to-batch-terraform/variables.tf new file mode 100644 index 0000000000..a95f30cfce --- /dev/null +++ b/eventbridge-schedule-to-batch-terraform/variables.tf @@ -0,0 +1,23 @@ +variable "region" { + type=string + description = "AWS Region where deploying resources" + default = "us-east-1" +} + +variable "aws_profile_name" { + type=string + description = "AWS CLI credentials profile name" + default="default" +} + +variable "vpc_cidr" { + type=string + description = "CIDR block for Batch VPC" + default = "10.0.0.0/16" +} + +variable "subnet_cidr" { + type=string + description = "CIDR block for Batch subnet" + default = "10.0.0.0/24" +} \ No newline at end of file diff --git a/eventbridge-schedule-to-cloudwatch-terraform/.gitignore b/eventbridge-schedule-to-cloudwatch-terraform/.gitignore new file mode 100644 index 0000000000..8555bc1c89 --- /dev/null +++ b/eventbridge-schedule-to-cloudwatch-terraform/.gitignore @@ -0,0 +1,7 @@ +# terraform + +/.terraform +/terraform.lock.hcl +*.tfstate* +*.tfstate.backup +*.tfvars diff --git a/eventbridge-schedule-to-cloudwatch-terraform/README.md b/eventbridge-schedule-to-cloudwatch-terraform/README.md new file mode 100644 index 0000000000..ad48b5d45b --- /dev/null +++ b/eventbridge-schedule-to-cloudwatch-terraform/README.md @@ -0,0 +1,54 @@ +# Amazon EventBridge Scheduler to Amazon CloudWatch + +This pattern will create an [EventBridge Scheduler](https://docs.aws.amazon.com/scheduler/latest/UserGuide/getting-started.html) to publish [custom metrics to Amazon Cloudwatch](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/publishingMetrics.html). The pattern is deployed using Terraform to create the EventBridge Scheduler and IAM resources required for Scheduler to interact with CloudWatch. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/eventbridge-schedule-to-cloudwatch-terraform. + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [Terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/aws-get-started) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd eventbridge-schedule-to-cloudwatch-terraform + ``` +1. From the command line, initialize Terraform: + ``` + terraform init + ``` +1. From the commend line, apply the configuration in the main.tf file and follow the prompts: + ``` + terraform apply + ``` + + +## How it works + +An Amazon EventBridge Schedule is used to publish custom metrics to Amazon CloudWatch every minute. The Terraform stack creates an EventBridge Schedule and the required IAM resource for Scheduler to interact with CloudWatch. + +## Testing + +1. After deployment, view the schedule created in the Amazon EventBridge console under Scheduler>Schedules. +2. From the Amazon CloudWatch console, navigate to the Metrics dashboard and view the CustomMetrics namespace which contains the metrics published by the schedule. You can view additional metrics published each minute in the same CustomMetrics namespace. + +## Cleanup + +1. Delete all created resources and follow prompts: + ``` + terraform destroy + ``` +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 \ No newline at end of file diff --git a/eventbridge-schedule-to-cloudwatch-terraform/example-pattern.json b/eventbridge-schedule-to-cloudwatch-terraform/example-pattern.json new file mode 100644 index 0000000000..dd10219bc7 --- /dev/null +++ b/eventbridge-schedule-to-cloudwatch-terraform/example-pattern.json @@ -0,0 +1,58 @@ +{ + "title": "Amazon EventBridge Scheduler to Amazon CloudWatch", + "description": "Use EventBridge Scheduler to insert custom metric data into CloudWatch on a schedule", + "language" : "", + "level": "200", + "framework": "Terraform", + "introBox": { + "headline": "How it works", + "text": [ + "This sample project demonstrates how to use Amazon EventBridge Scheduler to publish your own metrics to CloudWatch on a schedule. This pattern leverages templated targets with EventBridge Scheduler to talk directly to CloudWatch using the PutEvents API operation.", + "This pattern deploys an EventBridge Schedule and the required IAM resource for Scheduler to interact with CloudWatch." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/eventbridge-schedule-to-cloudwatch-terraform", + "templateURL": "serverless-patterns/eventbridge-schedule-to-cloudwatch-terraform", + "projectFolder": "eventbridge-schedule-to-cloudwatch-terraform", + "templateFile": "eventbridge-schedule-to-cloudwatch-terraform/main.tf" + } + }, + "resources": { + "bullets": [ + { + "text": "Amazon EventBridge Scheduler", + "link": "https://docs.aws.amazon.com/eventbridge/latest/userguide/scheduler.html" + }, + { + "text": "Publish custom metrics to Amazon CloudWatch", + "link": "https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/publishingMetrics.html" + } + ] + }, + "deploy": { + "text": [ + "terraform apply" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: terraform destroy." + ] + }, + "authors": [ + { + "name": "Ian Lodge", + "image": "https://avatars.githubusercontent.com/u/135351711?v=4", + "bio": "Ian is a Solutions Architect at Amazon Web Services based in the US.", + "linkedin": "https://www.linkedin.com/in/ian-lodge" + } + ] + } + \ No newline at end of file diff --git a/eventbridge-schedule-to-cloudwatch-terraform/main.tf b/eventbridge-schedule-to-cloudwatch-terraform/main.tf new file mode 100644 index 0000000000..678d81ec7d --- /dev/null +++ b/eventbridge-schedule-to-cloudwatch-terraform/main.tf @@ -0,0 +1,95 @@ +# This template uses EventBridge Scheduler to insert custom metric data into CloudWatch on a schedule. + +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.64.0" + } + } + + required_version = ">= 0.14.9" +} + +provider "aws" { + profile = "default" + region = "us-east-1" +} + +data "aws_caller_identity" "current" {} + + + +# This section creates cron schedules using Amazon EventBridge Scheduler, as well as the required IAM roles to interact with CloudWatch + +resource "aws_scheduler_schedule" "cloudwatch-schedule" { + name = "cloudwatch-schedule" + + flexible_time_window { + mode = "OFF" + } + + schedule_expression = "rate(1 minutes)" + schedule_expression_timezone = "US/Eastern" # Default is UTC + description = "Start putMetric event" + + target { + arn = "arn:aws:scheduler:::aws-sdk:cloudwatch:putMetricData" + role_arn = aws_iam_role.scheduler-cloudwatch-role.arn + + input = jsonencode( + { + "MetricData": [ + { + "MetricName": "CustomSchedulerData", + "Dimensions": [ + { "Name": "Type", "Value": "1" } + ], + "Unit": "Count", + "Value": 1 + } + ], + "Namespace": "MyData" + } + ) + } +} + + +resource "aws_iam_policy" "scheduler_cloudwatch_policy" { + name = "scheduler_cloudwatch_policy" + + policy = jsonencode( + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "cloudwatch:PutMetricData" + ], + "Resource": "*" + } + ] + } + ) +} + +resource "aws_iam_role" "scheduler-cloudwatch-role" { + name = "scheduler-cloudwatch-role" + managed_policy_arns = [aws_iam_policy.scheduler_cloudwatch_policy.arn] + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Sid = "" + Principal = { + Service = "scheduler.amazonaws.com" + } + }, + ] + }) +} \ No newline at end of file diff --git a/eventbridge-schedule-to-ec2-cdk/example-pattern.json b/eventbridge-schedule-to-ec2-cdk/example-pattern.json index 88ca7d561d..d7aaefe002 100644 --- a/eventbridge-schedule-to-ec2-cdk/example-pattern.json +++ b/eventbridge-schedule-to-ec2-cdk/example-pattern.json @@ -32,7 +32,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy."] diff --git a/eventbridge-schedule-to-ec2-terraform/README.md b/eventbridge-schedule-to-ec2-terraform/README.md new file mode 100644 index 0000000000..d12c5d8509 --- /dev/null +++ b/eventbridge-schedule-to-ec2-terraform/README.md @@ -0,0 +1,55 @@ +# EventBridge Scheduler to start and stop EC2 instances +This pattern will create two EventBridge schedules that will start and stop a given array of instance-ids. You can control the start/stop time and timezone of you chosing. This example will start instances at 08:00 and stop them at 17:00 in the US/Eastern timezone. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/eventbridge-schedule-to-ec2-terraform. + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [Terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/aws-get-started) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd eventbridge-schedule-to-ec2-terraform + ``` +1. From the command line, initialize Terraform: + ``` + terraform init + ``` +1. From the commend line, apply the configuration in the main.tf file and follow the prompts: + ``` + terraform apply + ``` + + +## How it works + +An Amazon EventBridge Schedule is used to start and stop an EC2 instance. The Terraform stack creates a VPC, EC2 instance and EventBridge Scheduler that invokes the startInstance and stopInstance API on a schedule. + +## Testing + +1. After deployment, view the schedule created in the Amazon EventBridge console under Scheduler>Schedules. +2. View the `ec2-start-schedule`. Navigate to the *Target* tab and note the `Payload` value which is in the format: `{"InstanceIds":["i-006c2e9f4e706bf48"]}` +3. Navigate to the EC2 console and find the EC2 instance from the `Payload` value. Check the instance is powered on during 08:00 and 17:00 in the US/Eastern timezone. + +## Cleanup + +1. Delete all created resources and follow prompts: + ``` + terraform destroy + ``` +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/eventbridge-schedule-to-ec2-terraform/example-pattern.json b/eventbridge-schedule-to-ec2-terraform/example-pattern.json new file mode 100644 index 0000000000..98cb51b8c7 --- /dev/null +++ b/eventbridge-schedule-to-ec2-terraform/example-pattern.json @@ -0,0 +1,54 @@ +{ + "title": "EventBridge Scheduler to start and stop EC2 instances Monday to Friday", + "description": "Simple pattern that starts and stops given EC2 instances based on time of day, timezone and days of week", + "level": "300", + "framework": "Terraform", + "introBox": { + "headline": "How it works", + "text": ["Creates a schedule that turns on instances at 08:00 and turns them off at 17:00 Monday to Friday using the US/Eastern timezone."] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/eventbridge-schedule-to-ec2-terrafrom", + "templateURL": "serverless-patterns/eventbridge-schedule-to-ec2-terraform", + "projectFolder": "eventbridge-schedule-to-ec2-terraform", + "templateFile": "main.tf" + } + }, + "resources": { + "bullets": [ + { + "text": "Introducing Amazon EventBridge Scheduler", + "link": "https://aws.amazon.com/blogs/compute/introducing-amazon-eventbridge-scheduler/" + }, + { + "text": "Amazon EventBridge Scheduler Docs", + "link": "https://docs.aws.amazon.com/scheduler/latest/UserGuide/what-is-scheduler.html" + } + ] + }, + "deploy": { + "text": [ + "terraform apply" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: terraform destroy." + ] + }, + "authors": [ + { + "name": "Ian Lodge", + "image": "https://avatars.githubusercontent.com/u/135351711?v=4", + "bio": "Ian is a Solutions Architect at Amazon Web Services based in the US.", + "linkedin": "https://www.linkedin.com/in/ian-lodge" + } + ] + } + diff --git a/eventbridge-schedule-to-ec2-terraform/main.tf b/eventbridge-schedule-to-ec2-terraform/main.tf new file mode 100644 index 0000000000..563edfd0ac --- /dev/null +++ b/eventbridge-schedule-to-ec2-terraform/main.tf @@ -0,0 +1,147 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.64.0" + } + } +} + +provider "aws" { + region = var.region +} + +locals { + project_name = "tf-test" +} + + +# This section creates VPC resources for the Amazon EC2 environment + +resource "aws_vpc" "vpc" { + cidr_block = var.vpc_cidr +} + +resource "aws_subnet" "subnet" { + vpc_id = aws_vpc.vpc.id + cidr_block = var.subnet_cidr + tags = { + Name = "private-subnet-1" + } +} + +# This section creates an Amazon EC2 instance using the latest Amazon Linux AMI + +data "aws_ami" "amazon-linux-2" { + most_recent = true + + filter { + name = "owner-alias" + values = ["amazon"] + } + + filter { + name = "name" + values = ["amzn2-ami-hvm*"] + } +} + +resource "aws_instance" "test-ec2" { + ami = "${data.aws_ami.amazon-linux-2.id}" + instance_type = "t3.micro" + subnet_id = aws_subnet.subnet.id + tags = { + Name = "tf-test-ec2" + } +} + +# This section creates cron schedules using Amazon EventBridge Scheduler, as well as the required IAM roles to interact with EC2 + +resource "aws_scheduler_schedule" "ec2-start-schedule" { + name = "ec2-start-schedule" + + flexible_time_window { + mode = "OFF" + } + + schedule_expression = "cron(0 8 ? * MON-FRI *)" # Scheduled startInstances at 8am EST Mon-Fri + schedule_expression_timezone = "US/Eastern" # Default is UTC + description = "Start instances event" + + target { + arn = "arn:aws:scheduler:::aws-sdk:ec2:startInstances" + role_arn = aws_iam_role.scheduler-ec2-role.arn + + input = jsonencode({ + "InstanceIds": [ + "${aws_instance.test-ec2.id}" + ] + }) + } +} + +resource "aws_scheduler_schedule" "ec2-stop-schedule" { + name = "ec2-stop-schedule" + + flexible_time_window { + mode = "OFF" + } + + schedule_expression = "cron(0 17 ? * MON-FRI *)" # Scheduled stopinstances at 5pm EST Mon-Fri + schedule_expression_timezone = "US/Eastern" # Default is UTC + description = "Stop instances event" + + target { + arn = "arn:aws:scheduler:::aws-sdk:ec2:stopInstances" + role_arn = aws_iam_role.scheduler-ec2-role.arn + + input = jsonencode({ + "InstanceIds": [ + "${aws_instance.test-ec2.id}" + ] + }) + } +} + +resource "aws_iam_policy" "scheduler_ec2_policy" { + name = "scheduler_ec2_policy" + + policy = jsonencode( + { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "VisualEditor0", + "Effect": "Allow", + "Action": [ + "ec2:StartInstances", + "ec2:StopInstances" + ], + "Resource": [ + "${aws_instance.test-ec2.arn}:*", + "${aws_instance.test-ec2.arn}" + ], + } + ] + } + ) +} + +resource "aws_iam_role" "scheduler-ec2-role" { + name = "scheduler-ec2-role" + managed_policy_arns = [aws_iam_policy.scheduler_ec2_policy.arn] + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Sid = "" + Principal = { + Service = "scheduler.amazonaws.com" + } + }, + ] + }) +} diff --git a/eventbridge-schedule-to-ec2-terraform/variables.tf b/eventbridge-schedule-to-ec2-terraform/variables.tf new file mode 100644 index 0000000000..832228321f --- /dev/null +++ b/eventbridge-schedule-to-ec2-terraform/variables.tf @@ -0,0 +1,23 @@ +variable "region" { + type=string + description = "AWS Region where deploying resources" + default = "us-east-1" +} + +variable "aws_profile_name" { + type=string + description = "AWS CLI credentials profile name" + default="default" +} + +variable "vpc_cidr" { + type=string + description = "CIDR block for VPC" + default = "10.0.0.0/16" +} + +variable "subnet_cidr" { + type=string + description = "CIDR block for the subnet" + default = "10.0.0.0/24" +} diff --git a/eventbridge-schedule-to-eventbridge-cdk-python/example-pattern.json b/eventbridge-schedule-to-eventbridge-cdk-python/example-pattern.json index 33d837056d..23b7af835c 100644 --- a/eventbridge-schedule-to-eventbridge-cdk-python/example-pattern.json +++ b/eventbridge-schedule-to-eventbridge-cdk-python/example-pattern.json @@ -36,7 +36,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the README in the Github repo for detailed testing instructions."] + "text": ["See the README in the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy."] diff --git a/eventbridge-schedule-to-eventbridge-cdk/example-pattern.json b/eventbridge-schedule-to-eventbridge-cdk/example-pattern.json index d7a0e166ec..1072505f9b 100644 --- a/eventbridge-schedule-to-eventbridge-cdk/example-pattern.json +++ b/eventbridge-schedule-to-eventbridge-cdk/example-pattern.json @@ -32,7 +32,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy."] diff --git a/eventbridge-schedule-to-eventbridge-terraform/README.md b/eventbridge-schedule-to-eventbridge-terraform/README.md new file mode 100644 index 0000000000..505ac7f441 --- /dev/null +++ b/eventbridge-schedule-to-eventbridge-terraform/README.md @@ -0,0 +1,53 @@ +# Amazon EventBridge Scheduler to Amazon EventBridge + +This pattern will create an [EventBridge Scheduler](https://docs.aws.amazon.com/scheduler/latest/UserGuide/getting-started.html) to publish an event to EventBridge every minute using templated targets. The pattern is deployed using Terraform to create the EventBridge Scheduler, EventBridge bus and rules as well as the IAM resources required for Scheduler to interact with EventBridge and CloudWatch. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/eventbridge-schedule-to-eventbridge-terraform. + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [Terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/aws-get-started) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd eventbridge-schedule-to-eventbridge-terraform + ``` +1. From the command line, initialize Terraform: + ``` + terraform init + ``` +1. From the commend line, apply the configuration in the main.tf file and follow the prompts: + ``` + terraform apply + ``` + +## How it works + +An Amazon EventBridge Schedule is used to publish an EventBridge event using templated targets. The Terraform stack creates an EventBridge Schedule to invoke the PutEvents operation in EventBridge to publish an event to a custom event bus. The event is then matched to an EventBridge rule, with a CloudWatch Log Group set as the rule's target. + +## Testing + +1. After deployment, view the schedule created in the Amazon EventBridge console under Scheduler>Schedules. +2. From the Amazon EventBridge console, navigate to the Rules dashboard and select the "scheduler-event-bus" to view the Rule. From the Targets menu on the "schedule-rule", navigate to the CloudWatch log group configured as the target to the rule. A Log stream is created each minute, you can view the payload passed by Scheduler in the event details. + +## Cleanup + +1. Delete all created resources and follow prompts: + ``` + terraform destroy + ``` +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/eventbridge-schedule-to-eventbridge-terraform/example-pattern.json b/eventbridge-schedule-to-eventbridge-terraform/example-pattern.json new file mode 100644 index 0000000000..9f75f8a7e9 --- /dev/null +++ b/eventbridge-schedule-to-eventbridge-terraform/example-pattern.json @@ -0,0 +1,58 @@ +{ + "title": "Amazon EventBridge Scheduler to Amazon EventBridge", + "description": "Use EventBridge Scheduler to send custom events to EventBridge on a schedule", + "language": "", + "level": "300", + "framework": "Terraform", + "introBox": { + "headline": "How it works", + "text": [ + "This sample project shows how to use Amazon EventBridge Scheduler to send custom events to Amazon EventBridge on a schedule. This pattern uses templated targets with EventBridge Scheduler to talk directly to EventBridge using the PutEvents API operation.", + "The pattern is deployed using Terraform to create the EventBridge Scheduler, EventBridge bus and rules, as well as the IAM resources required for Scheduler to interact with EventBridge and CloudWatch." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/eventbridge-schedule-to-eventbridge-terraform", + "templateURL": "serverless-patterns/eventbridge-schedule-to-eventbridge-terraform", + "projectFolder": "eventbridge-schedule-to-eventbridge-terraform", + "templateFile": main.tf" + } + }, + "resources": { + "bullets": [ + { + "text": "Amazon EventBridge Scheduler", + "link": "https://docs.aws.amazon.com/eventbridge/latest/userguide/scheduler.html" + }, + { + "text": "Send custom events to Amazon EventBridge", + "link": "https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_PutEvents.html" + } + ] + }, + "deploy": { + "text": [ + "terraform apply" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: terraform destroy." + ] + }, + "authors": [ + { + "name": "Ian Lodge", + "image": "https://avatars.githubusercontent.com/u/135351711?v=4", + "bio": "Ian is a Solutions Architect at Amazon Web Services based in the US.", + "linkedin": "https://www.linkedin.com/in/ian-lodge" + } + ] + } + diff --git a/eventbridge-schedule-to-eventbridge-terraform/main.tf b/eventbridge-schedule-to-eventbridge-terraform/main.tf new file mode 100644 index 0000000000..d239b5db04 --- /dev/null +++ b/eventbridge-schedule-to-eventbridge-terraform/main.tf @@ -0,0 +1,178 @@ +# This template uses publishes an EventBridge event every minute using Amazon EventBridge Scheduler. + +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.64.0" + } + } + + required_version = ">= 0.14.9" +} + +provider "aws" { + profile = "default" + region = "us-east-1" +} + + +# Create custom event bus + +resource "aws_cloudwatch_event_bus" "scheduler-custom-event-bus" { + name = "scheduler-event-bus" +} + +#Configuring dead-letter queues: https://docs.aws.amazon.com/scheduler/latest/UserGuide/configuring-schedule-dlq.html +# Use cmd for troubleshooting "aws sqs get-queue-attributes --queue-url your-dlq-url --attribute-names QueueArn" + +resource "aws_sqs_queue" "scheduler-dlq" { + name = "scheduler-dlq" +} + +# This section configures an EventBridge Rule that uses a CloudWatch Log Group as a target + +resource "aws_cloudwatch_event_rule" "rule" { + name = "schedule-rule" + event_bus_name = aws_cloudwatch_event_bus.scheduler-custom-event-bus.name + + event_pattern = jsonencode({ + detail-type = ["message"] + }) +} + +# Create CloudWatch Log Group + +resource "aws_cloudwatch_log_group" "example" { + name = "/aws/events/schedulerApplication" + retention_in_days = 1 +} + +data "aws_iam_policy_document" "example_log_policy" { + statement { + effect = "Allow" + actions = [ + "logs:CreateLogStream" + ] + + resources = [ + "${aws_cloudwatch_log_group.example.arn}:*" + ] + + principals { + type = "Service" + identifiers = [ + "events.amazonaws.com", + "delivery.logs.amazonaws.com" + ] + } + } + statement { + effect = "Allow" + actions = [ + "logs:PutLogEvents" + ] + + resources = [ + "${aws_cloudwatch_log_group.example.arn}:*:*" + ] + + principals { + type = "Service" + identifiers = [ + "events.amazonaws.com", + "delivery.logs.amazonaws.com" + ] + } + + condition { + test = "ArnEquals" + values = [aws_cloudwatch_event_rule.rule.arn] + variable = "aws:SourceArn" + } + } +} + +resource "aws_cloudwatch_log_resource_policy" "example" { + policy_document = data.aws_iam_policy_document.example_log_policy.json + policy_name = "schedulerApplication-log-publishing-policy" +} + +resource "aws_cloudwatch_event_target" "example" { + rule = aws_cloudwatch_event_rule.rule.name + arn = aws_cloudwatch_log_group.example.arn + event_bus_name = aws_cloudwatch_event_bus.scheduler-custom-event-bus.name +} + + +# Create EventBridge Schedule + +resource "aws_scheduler_schedule" "eventbridge-schedule" { + name = "eventbridge-schedule" + + flexible_time_window { + mode = "OFF" + } + + schedule_expression = "rate(1 minutes)" # + schedule_expression_timezone = "US/Eastern" # Default is UTC + description = "Rate schedule to EventBridge custom bus" + + target { + arn = aws_cloudwatch_event_bus.scheduler-custom-event-bus.arn + role_arn = aws_iam_role.scheduler-role.arn + + dead_letter_config { + arn = aws_sqs_queue.scheduler-dlq.arn + } + + eventbridge_parameters { + detail_type = "message" + source = "scheduledEvents" + } + + input = jsonencode( + {"msg": "Hello from EventBridge Scheduler!"} + ) + } +} + +resource "aws_iam_policy" "scheduler_policy" { + name = "scheduler_policy" + + policy = jsonencode( + { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "VisualEditor0", + "Effect": "Allow", + "Action": [ + "events:putEvents", + "sqs:SendMessage" + ], + "Resource": "*" + } + ] + } + ) +} + +resource "aws_iam_role" "scheduler-role" { + name = "scheduler-role" + managed_policy_arns = [aws_iam_policy.scheduler_policy.arn] + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Sid = "" + Principal = { + Service = "scheduler.amazonaws.com" + } + }, + ] + }) +} diff --git a/eventbridge-schedule-to-lambda-cdk-go/example-pattern.json b/eventbridge-schedule-to-lambda-cdk-go/example-pattern.json index a80667d7bd..45d94656a7 100644 --- a/eventbridge-schedule-to-lambda-cdk-go/example-pattern.json +++ b/eventbridge-schedule-to-lambda-cdk-go/example-pattern.json @@ -32,12 +32,12 @@ }, "deploy": { "text": [ - "See the Github repo for detailed deployment instructions." + "See the GitHub repo for detailed deployment instructions." ] }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-to-lambda-cdk-java/example-pattern.json b/eventbridge-schedule-to-lambda-cdk-java/example-pattern.json index 266d6039fc..142331a084 100644 --- a/eventbridge-schedule-to-lambda-cdk-java/example-pattern.json +++ b/eventbridge-schedule-to-lambda-cdk-java/example-pattern.json @@ -34,7 +34,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-to-lambda-cdk/example-pattern.json b/eventbridge-schedule-to-lambda-cdk/example-pattern.json index 73eeded753..0f72df44ba 100644 --- a/eventbridge-schedule-to-lambda-cdk/example-pattern.json +++ b/eventbridge-schedule-to-lambda-cdk/example-pattern.json @@ -32,7 +32,7 @@ "text": ["sam deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: sam delete."] diff --git a/eventbridge-schedule-to-lambda-sam-go/example-pattern.json b/eventbridge-schedule-to-lambda-sam-go/example-pattern.json index 549ea00abe..5450dc5876 100644 --- a/eventbridge-schedule-to-lambda-sam-go/example-pattern.json +++ b/eventbridge-schedule-to-lambda-sam-go/example-pattern.json @@ -32,12 +32,12 @@ }, "deploy": { "text": [ - "See the Github repo for detailed deployment instructions." + "See the GitHub repo for detailed deployment instructions." ] }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-to-lambda-sam-java/example-pattern.json b/eventbridge-schedule-to-lambda-sam-java/example-pattern.json index 0935571bf9..f651293992 100644 --- a/eventbridge-schedule-to-lambda-sam-java/example-pattern.json +++ b/eventbridge-schedule-to-lambda-sam-java/example-pattern.json @@ -34,7 +34,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-to-lambda-terraform-python/example-pattern.json b/eventbridge-schedule-to-lambda-terraform-python/example-pattern.json index 1c6e3ca254..c8bc1b9fe1 100644 --- a/eventbridge-schedule-to-lambda-terraform-python/example-pattern.json +++ b/eventbridge-schedule-to-lambda-terraform-python/example-pattern.json @@ -32,12 +32,12 @@ }, "deploy": { "text": [ - "See the Github repo for detailed deployment instructions." + "See the GitHub repo for detailed deployment instructions." ] }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-to-lambda-terraform-typescript/example-pattern.json b/eventbridge-schedule-to-lambda-terraform-typescript/example-pattern.json index bd22d3294a..58585da5df 100644 --- a/eventbridge-schedule-to-lambda-terraform-typescript/example-pattern.json +++ b/eventbridge-schedule-to-lambda-terraform-typescript/example-pattern.json @@ -32,12 +32,12 @@ }, "deploy": { "text": [ - "See the Github repo for detailed deployment instructions." + "See the GitHub repo for detailed deployment instructions." ] }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-to-lambda/example-pattern.json b/eventbridge-schedule-to-lambda/example-pattern.json index cac5211b55..e57646dee4 100644 --- a/eventbridge-schedule-to-lambda/example-pattern.json +++ b/eventbridge-schedule-to-lambda/example-pattern.json @@ -32,7 +32,7 @@ "text": ["sam deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: sam delete."] diff --git a/eventbridge-schedule-to-sns-cdk-go/example-pattern.json b/eventbridge-schedule-to-sns-cdk-go/example-pattern.json index fbd7aa715d..afda135d42 100644 --- a/eventbridge-schedule-to-sns-cdk-go/example-pattern.json +++ b/eventbridge-schedule-to-sns-cdk-go/example-pattern.json @@ -32,12 +32,12 @@ }, "deploy": { "text": [ - "See the Github repo for detailed deployment instructions." + "See the GitHub repo for detailed deployment instructions." ] }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-to-sns-cdk-python/example-pattern.json b/eventbridge-schedule-to-sns-cdk-python/example-pattern.json index 9b263a2c08..b8d7e152e7 100644 --- a/eventbridge-schedule-to-sns-cdk-python/example-pattern.json +++ b/eventbridge-schedule-to-sns-cdk-python/example-pattern.json @@ -32,12 +32,12 @@ }, "deploy": { "text": [ - "See the Github repo for detailed deployment instructions." + "See the GitHub repo for detailed deployment instructions." ] }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-to-sns-cdk-typescript/example-pattern.json b/eventbridge-schedule-to-sns-cdk-typescript/example-pattern.json index f4affd0602..f1f7a4807d 100644 --- a/eventbridge-schedule-to-sns-cdk-typescript/example-pattern.json +++ b/eventbridge-schedule-to-sns-cdk-typescript/example-pattern.json @@ -32,12 +32,12 @@ }, "deploy": { "text": [ - "See the Github repo for detailed deployment instructions." + "See the GitHub repo for detailed deployment instructions." ] }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-to-sns-publish-cdk-java/eventbridge-schedule-to-sns-publish-cdk-java.json b/eventbridge-schedule-to-sns-publish-cdk-java/eventbridge-schedule-to-sns-publish-cdk-java.json index aa6148afe5..3af978136d 100644 --- a/eventbridge-schedule-to-sns-publish-cdk-java/eventbridge-schedule-to-sns-publish-cdk-java.json +++ b/eventbridge-schedule-to-sns-publish-cdk-java/eventbridge-schedule-to-sns-publish-cdk-java.json @@ -45,7 +45,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-to-sns-publish-cdk-java/example-pattern.json b/eventbridge-schedule-to-sns-publish-cdk-java/example-pattern.json index aa6148afe5..3af978136d 100644 --- a/eventbridge-schedule-to-sns-publish-cdk-java/example-pattern.json +++ b/eventbridge-schedule-to-sns-publish-cdk-java/example-pattern.json @@ -45,7 +45,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-to-sqs-cdk-go/example-pattern.json b/eventbridge-schedule-to-sqs-cdk-go/example-pattern.json index a9523eb4c0..464d9b170d 100644 --- a/eventbridge-schedule-to-sqs-cdk-go/example-pattern.json +++ b/eventbridge-schedule-to-sqs-cdk-go/example-pattern.json @@ -32,12 +32,12 @@ }, "deploy": { "text": [ - "See the Github repo for detailed deployment instructions." + "See the GitHub repo for detailed deployment instructions." ] }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-to-sqs-cdk-python/example-pattern.json b/eventbridge-schedule-to-sqs-cdk-python/example-pattern.json index 26c144e15a..f2bf1e82b0 100644 --- a/eventbridge-schedule-to-sqs-cdk-python/example-pattern.json +++ b/eventbridge-schedule-to-sqs-cdk-python/example-pattern.json @@ -37,7 +37,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-to-sqs-cdk-typescript/example-pattern.json b/eventbridge-schedule-to-sqs-cdk-typescript/example-pattern.json index 7ec7d63f00..53ff7cc91d 100644 --- a/eventbridge-schedule-to-sqs-cdk-typescript/example-pattern.json +++ b/eventbridge-schedule-to-sqs-cdk-typescript/example-pattern.json @@ -37,7 +37,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-to-sqs-sam/example-pattern.json b/eventbridge-schedule-to-sqs-sam/example-pattern.json index e66649c80a..f56ec60a01 100644 --- a/eventbridge-schedule-to-sqs-sam/example-pattern.json +++ b/eventbridge-schedule-to-sqs-sam/example-pattern.json @@ -31,12 +31,12 @@ }, "deploy": { "text": [ - "See the Github repo for detailed deployment instructions." + "See the GitHub repo for detailed deployment instructions." ] }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-to-sqs-terraform/example-pattern.json b/eventbridge-schedule-to-sqs-terraform/example-pattern.json index 6e79f30810..95263a5b32 100644 --- a/eventbridge-schedule-to-sqs-terraform/example-pattern.json +++ b/eventbridge-schedule-to-sqs-terraform/example-pattern.json @@ -37,7 +37,7 @@ }, "testing": { "text": [ - "See the README in the Github repo for detailed instructions." + "See the README in the GitHub repo for detailed instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-to-ssm-sam/example-pattern.json b/eventbridge-schedule-to-ssm-sam/example-pattern.json index 47962953f9..888e7aa578 100644 --- a/eventbridge-schedule-to-ssm-sam/example-pattern.json +++ b/eventbridge-schedule-to-ssm-sam/example-pattern.json @@ -43,12 +43,12 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { "text": [ - "See the Github repo for detailed clean-up instructions." + "See the GitHub repo for detailed clean-up instructions." ] }, "authors": [ diff --git a/eventbridge-schedule-to-step-function-cdk-go/example-pattern.json b/eventbridge-schedule-to-step-function-cdk-go/example-pattern.json index ffb32bb481..79c92b666c 100644 --- a/eventbridge-schedule-to-step-function-cdk-go/example-pattern.json +++ b/eventbridge-schedule-to-step-function-cdk-go/example-pattern.json @@ -45,7 +45,7 @@ }, "testing": { "text": [ - "See the README in the Github repo for detailed testing instructions." + "See the README in the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-to-step-function-cdk-python/example_pattern.json b/eventbridge-schedule-to-step-function-cdk-python/example_pattern.json index 16571f5c0d..d2599e16be 100644 --- a/eventbridge-schedule-to-step-function-cdk-python/example_pattern.json +++ b/eventbridge-schedule-to-step-function-cdk-python/example_pattern.json @@ -41,7 +41,7 @@ }, "testing": { "text": [ - "See the README in the Github repo for detailed testing instructions." + "See the README in the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-schedule-to-step-function-terraform/README.md b/eventbridge-schedule-to-step-function-terraform/README.md new file mode 100644 index 0000000000..60b440c7ec --- /dev/null +++ b/eventbridge-schedule-to-step-function-terraform/README.md @@ -0,0 +1,64 @@ +# EventBridge Scheduler to Step Functions + +This pattern uses EventBridge Scheduler to start the execution of a Step Functions State Machine every minute. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/eventbridge-schedule-to-step-function-terraform + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [Terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/aws-get-started) installed +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd eventbridge-schedule-to-step-function-terraform + ``` +1. From the command line, initialize terraform to to downloads and installs the providers defined in the configuration: + ``` + terraform init + ``` +1. From the command line, apply the configuration in the main.tf file: + ``` + terraform apply + ``` +1. During the prompts: + * Enter yes +1. Note the outputs from the deployment process. These contain the resource names and/or ARNs which are used for testing. + +## How it works + +This Terraform template creates a Step Functions State Machine with a simple "Pass" state that gets executed every minute. + +## Testing + +After running `terraform apply`, go to the Step Functions console. Find your Step Functions State Machine "my-state-machine", and under the "Executions" section you should find the successful executions. + +## Cleanup + +1. Change directory to the pattern directory: + ``` + cd eventbridge-schedule-to-step-function-terraform + ``` +2. Delete all created resources by terraform + ```bash + terraform destroy + ``` +3. During the prompts: + * Enter yes +4. Confirm all created resources has been deleted + ```bash + terraform show + ``` +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/eventbridge-schedule-to-step-function-terraform/example-pattern.json b/eventbridge-schedule-to-step-function-terraform/example-pattern.json new file mode 100644 index 0000000000..0150a34238 --- /dev/null +++ b/eventbridge-schedule-to-step-function-terraform/example-pattern.json @@ -0,0 +1,55 @@ +{ + "title": "Amazon EventBridge Scheduler to AWS Step Functions", + "description": "Create an EventBridge Scheduler that executes a Step Functions State Machine", + "language": "Python", + "level": "200", + "framework": "Terraform", + "introBox": { + "headline": "How it works", + "text": [ + "This template deploys an EventBridge Scheduler that triggers the execution of a Step Functions State Machine every minute" + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/eventbridge-schedule-to-step-function-terraform", + "templateURL": "serverless-patterns/eventbridge-schedule-to-step-function-terraform", + "projectFolder": "eventbridge-schedule-to-step-function-terraform", + "templateFile": "eventbridge-schedule-to-step-function-terraform/main.tf" + } + }, + "resources": { + "bullets": [{ + "text": "Introducing Amazon EventBridge Scheduler", + "link": "https://aws.amazon.com/blogs/compute/introducing-amazon-eventbridge-scheduler/" + }, + { + "text": "Use Amazon EventBridge to Build Decoupled, Event-Driven Architectures", + "link": "https://serverlessland.com/learn/eventbridge" + } + ] + }, + "deploy": { + "text": [ + "terraform init", + "terraform apply" + ] + }, + "testing": { + "text": [ + "See the README in the GitHub repo for detailed instructions." + ] + }, + "cleanup": { + "text": [ + "terraform destroy", + "terraform show" + ] + }, + "authors": [{ + "name": "Francesco Vergona", + "image": "https://avatars.githubusercontent.com/f-verg", + "bio": "Solutions Architect, AWS", + "linkedin": "https://www.linkedin.com/in/francesco-vergona/" + }] +} diff --git a/eventbridge-schedule-to-step-function-terraform/main.tf b/eventbridge-schedule-to-step-function-terraform/main.tf new file mode 100644 index 0000000000..ca6a64cf32 --- /dev/null +++ b/eventbridge-schedule-to-step-function-terraform/main.tf @@ -0,0 +1,130 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 4.22" + } + } + required_version = ">= 0.14.9" +} + +provider "aws" { + profile = "default" + region = "us-east-1" +} + +data "aws_caller_identity" "current" {} + +# Create new IAM Policy and Role for EventBridge Scheduler +resource "aws_iam_policy" "eb_access_policy" { + name = "eb-access-policy" + description = "Policy for EventBridge Scheduler to trigger Step Functions" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = [ + "states:StartExecution" + ], + Effect = "Allow" + Resource = "${aws_sfn_state_machine.sfn_state_machine.arn}" + } + ] + }) +} + +resource "aws_iam_role" "eventbridge_scheduler_iam_role" { + name_prefix = "eb-scheduler-role-" + managed_policy_arns = [aws_iam_policy.eb_access_policy.arn] + path = "/" + assume_role_policy = < Exe - netcoreapp3.1 + net6.0 Major EventBridgeSNSDotnetCdk diff --git a/eventbridge-sns-java-cdk/example-pattern.json b/eventbridge-sns-java-cdk/example-pattern.json index b1719c1f55..dcf164f005 100644 --- a/eventbridge-sns-java-cdk/example-pattern.json +++ b/eventbridge-sns-java-cdk/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-sns-lambda/.gitignore b/eventbridge-sns-lambda/.gitignore new file mode 100644 index 0000000000..4808264dbf --- /dev/null +++ b/eventbridge-sns-lambda/.gitignore @@ -0,0 +1,244 @@ + +# Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Ruby plugin and RubyMine +/.rakeTasks + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +### PyCharm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +.pytest_cache/ +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule.* + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Build folder + +*/build/* + +# End of https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode \ No newline at end of file diff --git a/eventbridge-sns-lambda/Readme.md b/eventbridge-sns-lambda/Readme.md new file mode 100644 index 0000000000..b3f02e54dc --- /dev/null +++ b/eventbridge-sns-lambda/Readme.md @@ -0,0 +1,67 @@ +# Integration of Amazon EventBridge with Amazon SNS and Amazon Lambda + +The SAM template deploys a EventBridge rule with target as SNS topic which has a Lambda function as subscription and the IAM permissions required to run the application. Whenever the EventBridge rule gets triggered, the Lambda function is invoked by the SNS topic. The AWS SAM template deploys the resources and the IAM permissions required to run the application. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/eventbridge-sns-lambda/. + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed + +## Deployment Instructions + + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` + +2. Change directory to the pattern directory: + ``` + cd eventbridge-sns-lambda + ``` +3. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yaml file: + ``` + sam deploy --guided + ``` +4. During the prompts: + + * Enter a stack name + * Enter the desired AWS Region + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run sam deploy -guided mode once and saved arguments to a configuration file (samconfig.toml), you can use sam deploy in future to use these defaults. + +5. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. + + +## Testing + +Create a new test EC2 instance or modify(start/stop) an existing test EC2 instance to trigger the Eventbridge rule. + + +Check the CloudWatch logs of the Lambda function to see the SNS event JSON. + + +## Cleanup + + +1. Delete the stack + ``` + sam delete + ``` +2. Confirm the stack has been deleted + ``` + aws cloudformation list-stacks —query "StackSummaries[?contains(StackName,'STACK_NAME')].StackStatus" + ``` + +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/eventbridge-sns-lambda/example-pattern.json b/eventbridge-sns-lambda/example-pattern.json new file mode 100644 index 0000000000..65e97b8e93 --- /dev/null +++ b/eventbridge-sns-lambda/example-pattern.json @@ -0,0 +1,54 @@ +{ + "title": "Amazon EventBridge to SNS to Lambda Function", + "description": "Using SNS to forward EventBridge rule trigger to AWS Lambda Function", + "language": "Python", + "level": "200", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This sample project demonstrates how to create a SNS Topic can be used as target for an EventBridge rule and then invokes a Lambda function.", + "This pattern deploys a EventBridge rule with target as SNS topic which has a Lambda function as subscription" + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/eventbridge-sns-lambda", + "templateURL": "serverless-patterns/eventbridge-sns-lambda", + "projectFolder": "eventbridge-sns-lambda", + "templateFile": "eventbridge-sns-lambda/template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Creating Amazon EventBridge rules that react to events", + "link": "https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-create-rule.html" + } + ] + }, + "deploy": { + "text": [ + "sam deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "name": "Bhavyesh", + "image": "https://drive.google.com/file/d/1oe_B5KT6iPJgOnS9RzVAlQNVr1GY3N0s/view?usp=sharing", + "bio": "Bhavyesh is a Serverless Cloud Engineer at Amazon Web Services and is based in India. Bhavyesh is a SME of serverless and messaging domains", + "linkedin": "https://www.linkedin.com/in/vyasbhavyesh/" + } + ] + } + diff --git a/eventbridge-sns-lambda/src/app.py b/eventbridge-sns-lambda/src/app.py new file mode 100644 index 0000000000..ca25a01b42 --- /dev/null +++ b/eventbridge-sns-lambda/src/app.py @@ -0,0 +1,20 @@ +import json +import logging +# import requests + + +def lambda_handler(event, context): + + logger = logging.getLogger(__name__) + logger.setLevel(logging.INFO) + + logger.info("Recieved the SNS event") + logger.info(json.dumps(event)) + + + return { + "statusCode": 200, + "body": json.dumps({ + "message": "hello world", + }), + } diff --git a/eventbridge-sns-lambda/template.yaml b/eventbridge-sns-lambda/template.yaml new file mode 100644 index 0000000000..1750d70193 --- /dev/null +++ b/eventbridge-sns-lambda/template.yaml @@ -0,0 +1,59 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 + +Description: > + Eventbridge-sns-lambda + + Sample SAM Template for + +Globals: + Function: + Timeout: 3 + MemorySize: 128 + +Resources: + SNSTopic: + Type: AWS::SNS::Topic + Properties: + DisplayName: SNS-Lambda-Topic + + SNSTopicPolicy: + Type: AWS::SNS::TopicPolicy + Properties: + Topics: + - Ref: SNSTopic + PolicyDocument: + Version: "2012-10-17" + Statement: + - Sid: AllowEventBridgeToPublish + Effect: Allow + Principal: + Service: events.amazonaws.com + Action: sns:Publish + Resource: !Ref SNSTopic + + LambdaFunction: + Type: AWS::Serverless::Function #https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html + Properties: + FunctionName: LambdaFunction + Runtime: python3.8 + Handler: app.lambda_handler + CodeUri: ./src + Events: + SNSTOpicEvent: + Type: SNS + Properties: + Topic: !Ref SNSTopic + # More details about EventBridge https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-eventbridgerule.html + EventBridgeRule: + Type: AWS::Events::Rule + Properties: + Description: EventBridge Rule + EventPattern: + source: + - aws.ec2 + detail-type: + - EC2 Instance State-change Notification + Targets: + - Arn: !Ref SNSTopic + Id: SNS-Topic diff --git a/eventbridge-sqs-cdk-typescript/eventbridge-sqs-cdk-typescript.json b/eventbridge-sqs-cdk-typescript/eventbridge-sqs-cdk-typescript.json index f28ae7d72f..ff4f40fad4 100644 --- a/eventbridge-sqs-cdk-typescript/eventbridge-sqs-cdk-typescript.json +++ b/eventbridge-sqs-cdk-typescript/eventbridge-sqs-cdk-typescript.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-sqs-dotnet-cdk/README.md b/eventbridge-sqs-dotnet-cdk/README.md index 63403cccb8..6c205c9e79 100644 --- a/eventbridge-sqs-dotnet-cdk/README.md +++ b/eventbridge-sqs-dotnet-cdk/README.md @@ -11,7 +11,7 @@ Important: this application uses various AWS services and there are costs associ * [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -* [.NET Core 3.1](https://dotnet.microsoft.com/en-us/download/dotnet/3.1) installed +* [.NET 6](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) installed * [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) (AWS CDK) installed ## Deployment Instructions diff --git a/eventbridge-sqs-dotnet-cdk/example-pattern.json b/eventbridge-sqs-dotnet-cdk/example-pattern.json index a0cf171f5c..c433b569f0 100644 --- a/eventbridge-sqs-dotnet-cdk/example-pattern.json +++ b/eventbridge-sqs-dotnet-cdk/example-pattern.json @@ -36,7 +36,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-sqs-dotnet-cdk/src/EventBridgeSqsDotnetCdk/EventBridgeSqsDotnetCdk.csproj b/eventbridge-sqs-dotnet-cdk/src/EventBridgeSqsDotnetCdk/EventBridgeSqsDotnetCdk.csproj index 46a928208c..f7861800ac 100644 --- a/eventbridge-sqs-dotnet-cdk/src/EventBridgeSqsDotnetCdk/EventBridgeSqsDotnetCdk.csproj +++ b/eventbridge-sqs-dotnet-cdk/src/EventBridgeSqsDotnetCdk/EventBridgeSqsDotnetCdk.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net6.0 Major EventBridgeSqsDotnetCdk diff --git a/eventbridge-sqs-ecs-cdk/example-pattern.json b/eventbridge-sqs-ecs-cdk/example-pattern.json index 8470a2c3d3..b147ee8918 100644 --- a/eventbridge-sqs-ecs-cdk/example-pattern.json +++ b/eventbridge-sqs-ecs-cdk/example-pattern.json @@ -39,7 +39,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-sqs-sls/example-pattern.json b/eventbridge-sqs-sls/example-pattern.json index 590243f816..41ea91f062 100644 --- a/eventbridge-sqs-sls/example-pattern.json +++ b/eventbridge-sqs-sls/example-pattern.json @@ -47,7 +47,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-webhooks/1-stripe/example-pattern.json b/eventbridge-webhooks/1-stripe/example-pattern.json index fb49576f03..4c923d4df4 100644 --- a/eventbridge-webhooks/1-stripe/example-pattern.json +++ b/eventbridge-webhooks/1-stripe/example-pattern.json @@ -35,7 +35,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-webhooks/2-github/example-pattern.json b/eventbridge-webhooks/2-github/example-pattern.json index a055b47a21..608b22a574 100644 --- a/eventbridge-webhooks/2-github/example-pattern.json +++ b/eventbridge-webhooks/2-github/example-pattern.json @@ -7,7 +7,7 @@ "introBox": { "headline": "How it works", "text": [ - "This stack deploys an inbound webhook that subscribes to events from Github and receives them on an Amazon EventBridge event bus for further processing.", + "This stack deploys an inbound webhook that subscribes to events from GitHub and receives them on an Amazon EventBridge event bus for further processing.", "EventBridge is a serverless event bus that enables you to build scalable event-driven applications by routing events between your own applications, third-party SaaS applications, and other AWS services.", "This pattern deploys one Lambda function with a fURL, one Secrets Manager secret, and a CloudWatch alarm." ] @@ -35,7 +35,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/eventbridge-webhooks/2-github/template.yaml b/eventbridge-webhooks/2-github/template.yaml index 09d849dcc1..199359367c 100644 --- a/eventbridge-webhooks/2-github/template.yaml +++ b/eventbridge-webhooks/2-github/template.yaml @@ -6,9 +6,9 @@ Description: > Amazon EventBridge Inbound webhooks using lambda fURLs CFNs Template. Parameters: - GithubWebhookSecret: + GitHubWebhookSecret: Type: String - Description: Github webhook secret + Description: GitHub webhook secret NoEcho: true AllowedPattern: ".+" @@ -28,7 +28,7 @@ Resources: Properties: Name: !Sub WebhookSecret-${AWS::StackName} Description: Secrets Manager for storing Webhook Secret - SecretString: !Ref GithubWebhookSecret + SecretString: !Ref GitHubWebhookSecret LambdaInvocationsAlarm: Type: AWS::CloudWatch::Alarm diff --git a/eventbridge-webhooks/3-twilio/example-pattern.json b/eventbridge-webhooks/3-twilio/example-pattern.json index 1db30ec210..258683b3ac 100644 --- a/eventbridge-webhooks/3-twilio/example-pattern.json +++ b/eventbridge-webhooks/3-twilio/example-pattern.json @@ -34,7 +34,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/fifo-sns-sqs-lambda-firehose-s3/fifo-sns-sqs-lambda-firehose-s3-pattern.json b/fifo-sns-sqs-lambda-firehose-s3/fifo-sns-sqs-lambda-firehose-s3-pattern.json index 1c0cb407ab..8037f1322e 100644 --- a/fifo-sns-sqs-lambda-firehose-s3/fifo-sns-sqs-lambda-firehose-s3-pattern.json +++ b/fifo-sns-sqs-lambda-firehose-s3/fifo-sns-sqs-lambda-firehose-s3-pattern.json @@ -59,7 +59,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/firehose-transformation-cdk/example-pattern.json b/firehose-transformation-cdk/example-pattern.json index cce8f3f990..c3755aa9cf 100644 --- a/firehose-transformation-cdk/example-pattern.json +++ b/firehose-transformation-cdk/example-pattern.json @@ -39,7 +39,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/firehose-transformation-sam/example-pattern.json b/firehose-transformation-sam/example-pattern.json index a700796b3c..b0724c082f 100644 --- a/firehose-transformation-sam/example-pattern.json +++ b/firehose-transformation-sam/example-pattern.json @@ -39,7 +39,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/glue-eventbridge-lambda-cdk/example-pattern.json b/glue-eventbridge-lambda-cdk/example-pattern.json index 7c184d4297..72e9f3133d 100644 --- a/glue-eventbridge-lambda-cdk/example-pattern.json +++ b/glue-eventbridge-lambda-cdk/example-pattern.json @@ -42,7 +42,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/healthlake-change-data-capture/example-pattern.json b/healthlake-change-data-capture/example-pattern.json index 4517085973..89d5e8d253 100644 --- a/healthlake-change-data-capture/example-pattern.json +++ b/healthlake-change-data-capture/example-pattern.json @@ -42,7 +42,7 @@ "text": ["make deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["make destroy"] diff --git a/healthlake-change-data-capture/src/go.mod b/healthlake-change-data-capture/src/go.mod index e170801cb7..8eeaaf1893 100644 --- a/healthlake-change-data-capture/src/go.mod +++ b/healthlake-change-data-capture/src/go.mod @@ -35,7 +35,7 @@ require ( github.com/aws/aws-xray-sdk-go v1.8.0 // indirect github.com/aws/smithy-go v1.13.5 // indirect github.com/cenkalti/backoff/v4 v4.2.0 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/dgraph-io/ristretto v0.1.0 // indirect github.com/dustin/go-humanize v1.0.0 // indirect github.com/golang/glog v1.0.0 // indirect @@ -58,8 +58,8 @@ require ( golang.org/x/text v0.7.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f // indirect - google.golang.org/grpc v1.45.0 // indirect - google.golang.org/protobuf v1.28.0 // indirect + google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect + google.golang.org/grpc v1.53.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect inet.af/netaddr v0.0.0-20220617031823-097006376321 // indirect ) diff --git a/healthlake-change-data-capture/src/go.sum b/healthlake-change-data-capture/src/go.sum index 87d9a52d2a..2dc66c0665 100644 --- a/healthlake-change-data-capture/src/go.sum +++ b/healthlake-change-data-capture/src/go.sum @@ -1,6 +1,3 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1MRDJM= github.com/DataDog/datadog-agent/pkg/obfuscate v0.41.1 h1:AHZu7lzfW6amjOLkbjioAxT+pKiiwD6KdkR0VfT3pMw= github.com/DataDog/datadog-agent/pkg/obfuscate v0.41.1/go.mod h1:DNHeRExTGWQoMgmOgcDtNENOEHN/tYJIicmAUgW1nXk= @@ -21,7 +18,6 @@ github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VM github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/aws/aws-lambda-go v1.39.1 h1:UcuX9O3JqhQyP/rxPJEpTUUSehzqkNpwKKRFa9N+ozk= github.com/aws/aws-lambda-go v1.39.1/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8qK17ewzbQMM= github.com/aws/aws-sdk-go v1.44.229 h1:lku0ZSHRzj/qtFVM//QE8VjV6kvJ6CFijDZSsjNaD9A= @@ -61,20 +57,12 @@ github.com/bix-digital/golang-fhir-models/fhir-models v0.0.0-20220829092912-860e github.com/bix-digital/golang-fhir-models/fhir-models v0.0.0-20220829092912-860eb0b78446/go.mod h1:aI/UKXSpuJewIqokv/VuEpaWJppuzi0G1tppgaIZlaQ= github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -86,53 +74,37 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUn github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/flynn/go-docopt v0.0.0-20140912013429-f6dd2ebbb31e/go.mod h1:HyVoz1Mz5Co8TFO8EupIdlcpwShBmY98dkT2xeHkvEI= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= @@ -165,8 +137,6 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/secure-systems-lab/go-securesystemslib v0.3.1/go.mod h1:o8hhjkbNl2gOamKUA/eNW3xUrntHT9L4W89W1nfj43U= github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE= github.com/secure-systems-lab/go-securesystemslib v0.4.0/go.mod h1:FGBZgq2tXWICsxWQW1msNf49F0Pf2Op5Htayx335Qbs= @@ -202,7 +172,6 @@ github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7Fw github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE= @@ -217,24 +186,14 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= @@ -246,16 +205,11 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -298,10 +252,6 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -313,36 +263,21 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f h1:izedQ6yVIc5mZsRuXzmSreCOlzI0lCU1HpG8yEdMiKw= -google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/DataDog/dd-trace-go.v1 v1.48.0 h1:AZhmo9zstDWWD7qG7g+2W7x4X7FYuGJcwRIIEjsLiEY= gopkg.in/DataDog/dd-trace-go.v1 v1.48.0/go.mod h1:z+Zm99hL8zep83JLkTbknOxSMJnNvAggmL6mnUtDhWE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -351,7 +286,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -360,7 +294,5 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= inet.af/netaddr v0.0.0-20220617031823-097006376321 h1:B4dC8ySKTQXasnjDTMsoCMf1sQG4WsMej0WXaHxunmU= inet.af/netaddr v0.0.0-20220617031823-097006376321/go.mod h1:OIezDfdzOgFhuw4HuWapWq2e9l0H9tK4F1j+ETRtF3k= diff --git a/inspector-lambda/example-pattern.json b/inspector-lambda/example-pattern.json index 62108c231d..0ed4cad13c 100644 --- a/inspector-lambda/example-pattern.json +++ b/inspector-lambda/example-pattern.json @@ -40,7 +40,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/iot-dynamodb/example-pattern.json b/iot-dynamodb/example-pattern.json index 1adb028229..616bb86261 100644 --- a/iot-dynamodb/example-pattern.json +++ b/iot-dynamodb/example-pattern.json @@ -48,7 +48,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/iot-dynamodbv2-ttl-cdk/example-pattern.json b/iot-dynamodbv2-ttl-cdk/example-pattern.json index af45c90336..fa59a2466e 100644 --- a/iot-dynamodbv2-ttl-cdk/example-pattern.json +++ b/iot-dynamodbv2-ttl-cdk/example-pattern.json @@ -35,7 +35,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy."] diff --git a/iot-firehose-s3-cdk/example-pattern.json b/iot-firehose-s3-cdk/example-pattern.json index bf6b74cf0a..74a2b952d9 100644 --- a/iot-firehose-s3-cdk/example-pattern.json +++ b/iot-firehose-s3-cdk/example-pattern.json @@ -38,7 +38,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy."] diff --git a/iot-kinesis-lambda-cdk/iot-kinesis-lambda-cdk-pattern.json b/iot-kinesis-lambda-cdk/iot-kinesis-lambda-cdk-pattern.json index f49bfbadf1..d6ede484bc 100644 --- a/iot-kinesis-lambda-cdk/iot-kinesis-lambda-cdk-pattern.json +++ b/iot-kinesis-lambda-cdk/iot-kinesis-lambda-cdk-pattern.json @@ -46,7 +46,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/iot-s3-lambda-cdk/iot-s3-lamdba-cdk-pattern.json b/iot-s3-lambda-cdk/iot-s3-lamdba-cdk-pattern.json index db5b566604..a27126ce7a 100644 --- a/iot-s3-lambda-cdk/iot-s3-lamdba-cdk-pattern.json +++ b/iot-s3-lambda-cdk/iot-s3-lamdba-cdk-pattern.json @@ -46,7 +46,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/iot-sns-sqs-cdk/iot-sns-sqs-cdk-pattern.json b/iot-sns-sqs-cdk/iot-sns-sqs-cdk-pattern.json index e26e0d365e..caefbe06e0 100644 --- a/iot-sns-sqs-cdk/iot-sns-sqs-cdk-pattern.json +++ b/iot-sns-sqs-cdk/iot-sns-sqs-cdk-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/iot-sns-sqs-sam/README.md b/iot-sns-sqs-sam/README.md new file mode 100644 index 0000000000..b575cd9f28 --- /dev/null +++ b/iot-sns-sqs-sam/README.md @@ -0,0 +1,78 @@ +# AWS IoT Core to Amazon SNS to Amazon SQS + +This pattern contains a sample AWS SAM stack to create an IoT Rule with a SNS action, a SNS Topic and a SQS standard Queue subscribed to the SNS topic. + +When a message is published to the IoT topic defined in the IoT Rule, this message will be published to the SNS topic and finally the message will be delivered to the SQS queue subscribed to the SNS topic + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/iot-sns-sqs-sam + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) + + +## Deployment Instructions + +1. Clone the project to your local working directory + + ```sh + git clone https://github.com/aws-samples/serverless-patterns/ + ``` + +2. Change the working directory to this pattern's directory + + ```sh + cd iot-sns-sqs-sam + ``` + +1. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: + ``` + sam deploy --guided + ``` +1. During the prompts: + * Enter a stack name + * Enter the desired AWS Region + * Enter Secret Name. To use default, click Enter. + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run `sam deploy --guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +## How it works + +When a message is published to the IoT topic defined in the IoT Rule, this message will be published to the SNS topic and finally the message will be delivered to the SQS queue subscribed to the SNS topic + + +## Testing + +Log into the AWS Console, browse to AWS IoT Core: + +1. In the AWS IoT Core Console, in the `Test` section (left-side pane), select the `MQTT test client`. + +2. Then under `Publish to a topic`, in the Topic filter field enter this: `device/data`, change the default Message payload to `{"default": "Hello from AWS IoT console"}`. Then click the `Publish` button. + +3. In the AWS Console navigate to the SQS service. Select the SQS queue generated by the CFN stack (the name is in the format `iot-sns-sqs-cdk-IotSnsSqsCfnQueueXXXXXXX-XXXXX`) + +4. In the `Receive messages` section, click on `Poll for messages`. This should display a new message. Click on the message ID and inspect the `Body` attributes which should contain the following: +```json +{ + ... + "message": "Hello from AWS IoT console" + ... +} +``` + +## Cleanup + +1. Delete the stack + ```bash + sam delete + ``` + +---- +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 \ No newline at end of file diff --git a/iot-sns-sqs-sam/example-pattern.json b/iot-sns-sqs-sam/example-pattern.json new file mode 100644 index 0000000000..fbf7eaa108 --- /dev/null +++ b/iot-sns-sqs-sam/example-pattern.json @@ -0,0 +1,58 @@ +{ + "title": "Amazon IoT Core to Amazon SNS to Amazon SQS", + "description": "Create an IoT rule with an SNS action that delivers messages to an SQS queue.", + "language": "", + "level": "200", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern contains a sample AWS SAM stack to create an IoT rule with an SNS action, an SNS topic and an SQS standard queue subscribed to the SNS topic.", + "When a message is published to the IoT topic defined in the IoT rule, this message will be published to the SNS topic and then the message will be delivered to the SQS queue subscribed to the SNS topic." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/iot-sns-sqs-sam", + "templateURL": "serverless-patterns/iot-sns-sqs-sam", + "projectFolder": "iot-sns-sqs-sam", + "templateFile": "template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "IoT rule Amazon SNS action", + "link": "https://docs.aws.amazon.com/iot/latest/developerguide/sns-rule-action.html" + }, + { + "text": "Subscribing an Amazon SQS queue to an Amazon SNS topic", + "link": "https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-configure-subscribe-queue-sns-topic.html" + } + ] + }, + "deploy": { + "text": [ + "sam deploy --guided --capabilities CAPABILITY_NAMED_IAM" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "name": "Makendran G", + "image": "https://drive.google.com/file/d/1mUObnbmn52UWL-Zn39EpgpneiBNv3LCN/view?usp=sharing", + "bio": "Cloud Support Engineer @ AWS", + "linkedin": "https://www.linkedin.com/in/makendran", + "twitter": "@MakendranG" + } + ] +} \ No newline at end of file diff --git a/iot-sns-sqs-sam/template.yaml b/iot-sns-sqs-sam/template.yaml new file mode 100644 index 0000000000..c7c816207c --- /dev/null +++ b/iot-sns-sqs-sam/template.yaml @@ -0,0 +1,68 @@ +AWSTemplateFormatVersion: 2010-09-09 +Resources: + IotSnsSqsCfnQueue: + Type: 'AWS::SQS::Queue' + Properties: + QueueName: IotSnsSqsCfnQueue + VisibilityTimeout: 300 + IotSnsSqsCfnTopic: + Type: 'AWS::SNS::Topic' + Properties: + TopicName: IotSnsSqsCfnTopic + IotSnsSqsCfnSubscription: + Type: 'AWS::SNS::Subscription' + Properties: + TopicArn: !Ref IotSnsSqsCfnTopic + Protocol: sqs + Endpoint: !GetAtt IotSnsSqsCfnQueue.Arn + ExampleQueuePolicy: + Type: 'AWS::SQS::QueuePolicy' + Properties: + Queues: + - !Ref IotSnsSqsCfnQueue + PolicyDocument: + Version: 2012-10-17 + Statement: + - Sid: AllowSNSMessages + Effect: Allow + Principal: + AWS: '*' + Action: 'SQS:SendMessage' + Resource: !GetAtt IotSnsSqsCfnQueue.Arn + Condition: + ArnEquals: + 'aws:SourceArn': !Ref IotSnsSqsCfnTopic + IotSnsSqsCfnRule: + Type: 'AWS::IoT::TopicRule' + Properties: + RuleName: IotSnsSqsCfnRule + TopicRulePayload: + Sql: SELECT * FROM 'device/data' + AwsIotSqlVersion: 2016-03-23 + Actions: + - Sns: + TargetArn: !Ref IotSnsSqsCfnTopic + RoleArn: !GetAtt Role.Arn + Role: + Type: 'AWS::IAM::Role' + Properties: + RoleName: myrole + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: iot.amazonaws.com + Action: 'sts:AssumeRole' + IamPolicyForLambda: + Type: 'AWS::IAM::Policy' + Properties: + PolicyName: mypolicy + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: 'sns:Publish' + Resource: !Ref IotSnsSqsCfnTopic + Roles: + - !Ref Role diff --git a/iot-sns-sqs-terraform/README.md b/iot-sns-sqs-terraform/README.md new file mode 100644 index 0000000000..7a1b089292 --- /dev/null +++ b/iot-sns-sqs-terraform/README.md @@ -0,0 +1,86 @@ +# AWS IoT Core to Amazon SNS to Amazon SQS + +This pattern contains a sample Terraform template to create an IoT Rule with a SNS action, a SNS Topic and a SQS standard Queue subscribed to the SNS topic. + +When a message is published to the IoT topic defined in the IoT Rule, this message will be published to the SNS topic and finally the message will be delivered to the SQS queue subscribed to the SNS topic + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/iot-sns-sqs-terraform + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [Terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/aws-get-started) installed + + +## Deployment Instructions + +1. Clone the project to your local working directory + + ```sh + git clone https://github.com/aws-samples/serverless-patterns/ + ``` + +2. Change the working directory to this pattern's directory + + ```sh + cd serverless-patterns/iot-sns-sqs-terraform + ``` + +1. From the command line, initialize terraform to to downloads and installs the providers defined in the configuration: + ``` + terraform init + ``` +1. From the command line, apply the configuration in the main.tf file: + ``` + terraform apply + ``` +1. During the prompts: + + - Enter yes + +## How it works + +When a message is published to the IoT topic defined in the IoT Rule, this message will be published to the SNS topic and finally the message will be delivered to the SQS queue subscribed to the SNS topic + + +## Testing + +Log into the AWS Console, browse to AWS IoT Core: + +1. In the AWS IoT Core Console, in the `Test` section (left-side pane), select the `MQTT test client`. + +2. Then under `Publish to a topic`, in the Topic filter field enter this: `device/data`, change the default Message payload to `{"default": "Hello from AWS IoT console"}`. Then click the `Publish` button. + +3. In the AWS Console navigate to the SQS service. Select the SQS queue generated by the CFN stack (the name is in the format `iot-sns-sqs-tf-IotSnsSqsCfnQueueXXXXXXX-XXXXX`) + +4. In the `Receive messages` section, click on `Poll for messages`. This should display a new message. Click on the message ID and inspect the `Body` attributes which should contain the following: +```json +{ + ... + "message": "Hello from AWS IoT console" + ... +} +``` + +## Cleanup + +1. Delete all created resources by terraform + ```bash + terraform destroy + ``` +1. During the prompts: + * Enter yes +1. Confirm all created resources has been deleted + ```bash + terraform show + ``` + + +---- +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 \ No newline at end of file diff --git a/iot-sns-sqs-terraform/example-pattern.json b/iot-sns-sqs-terraform/example-pattern.json new file mode 100644 index 0000000000..1f0acce1ce --- /dev/null +++ b/iot-sns-sqs-terraform/example-pattern.json @@ -0,0 +1,58 @@ +{ + "title": "AWS IoT Core to Amazon SNS to Amazon SQS", + "description": "Create a IoT Rule with a SNS action that delivers messages to a SQS queue.", + "language": "", + "level": "200", + "framework": "Terraform", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern contains a sample AWS terraform template to create an IoT Rule with a SNS action, a SNS Topic and a SQS standard Queue subscribed to the SNS topic.", + "When a message is published to the IoT topic defined in the IoT Rule, this message will be published to the SNS topic and then the message will be delivered to the SQS queue subscribed to the SNS topic." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/iot-sns-sqs-terraform", + "templateURL": "serverless-patterns/iot-sns-sqs-terraform", + "projectFolder": "iot-sns-sqs-terraform", + "templateFile": "main.tf" + } + }, + "resources": { + "bullets": [ + { + "text": "IoT Rule SNS action", + "link": "https://docs.aws.amazon.com/iot/latest/developerguide/sns-rule-action.html" + }, + { + "text": "Subscribing a SQS queue to a SNS topic", + "link": "https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-configure-subscribe-queue-sns-topic.html" + } + ] + }, + "deploy": { + "text": [ + "terraform init", + "terraform apply" + ] + }, + "testing": { + "text": ["See the GitHub repo for detailed testing instructions."] + }, + "cleanup": { + "text": [ + "terraform destroy", + "terraform show" + ] + }, + "authors": [ + { + "name": "Makendran G", + "image": "https://drive.google.com/file/d/1mUObnbmn52UWL-Zn39EpgpneiBNv3LCN/view?usp=sharing", + "bio": "Cloud Support Engineer @ AWS", + "linkedin": "https://www.linkedin.com/in/makendran", + "twitter": "@MakendranG" + } + ] +} \ No newline at end of file diff --git a/iot-sns-sqs-terraform/main.tf b/iot-sns-sqs-terraform/main.tf new file mode 100644 index 0000000000..a05886b587 --- /dev/null +++ b/iot-sns-sqs-terraform/main.tf @@ -0,0 +1,87 @@ +resource "aws_sqs_queue" "iot_sns_sqs_tf_queue" { + name = "IotSnsSqstfQueue" + visibility_timeout_seconds = 300 + } + + +resource "aws_sns_topic" "iot_sns_sqs_tf_topic" { + name = "IotSnsSqstfTopic" + } + +resource "aws_sns_topic_subscription" "iot_sns_sqs_tf_subscription" { + topic_arn = aws_sns_topic.iot_sns_sqs_tf_topic.arn + protocol = "sqs" + endpoint = aws_sqs_queue.iot_sns_sqs_tf_queue.arn + } + + + resource "aws_sqs_queue_policy" "example_policy" { + queue_url = aws_sqs_queue.iot_sns_sqs_tf_queue.id + + policy = jsonencode({ + "Version" : "2012-10-17", + "Id" : "ExampleQueuePolicy", + "Statement" : [ + { + "Sid" : "AllowSNSMessages", + "Effect" : "Allow", + "Principal" : { "AWS" : "*" }, + "Action" : "SQS:SendMessage", + "Resource" : aws_sqs_queue.iot_sns_sqs_tf_queue.arn, + "Condition" : { + "ArnEquals" : { + "aws:SourceArn" : aws_sns_topic.iot_sns_sqs_tf_topic.arn + } + } + } + ] + }) + } + +resource "aws_iot_topic_rule" "iot_sns_sqs_tf_rule" { + name = "IotSnsSqstfRule" + sql = "SELECT * FROM 'device/data'" + sql_version = "2016-03-23" + enabled = true + + sns { + message_format = "RAW" + role_arn = aws_iam_role.role.arn + target_arn = aws_sns_topic.iot_sns_sqs_tf_topic.arn + } + } + + +data "aws_iam_policy_document" "assume_role" { + statement { + effect = "Allow" + + principals { + type = "Service" + identifiers = ["iot.amazonaws.com"] + } + + actions = ["sts:AssumeRole"] + } + } + +resource "aws_iam_role" "role" { + name = "myrole" + assume_role_policy = data.aws_iam_policy_document.assume_role.json + } + + +data "aws_iam_policy_document" "iam_policy_for_lambda" { + statement { + effect = "Allow" + actions = ["sns:Publish"] + resources = [aws_sns_topic.iot_sns_sqs_tf_topic.arn] + } + } + +resource "aws_iam_role_policy" "iam_policy_for_lambda" { + name = "mypolicy" + role = aws_iam_role.role.id + policy = data.aws_iam_policy_document.iam_policy_for_lambda.json + } + \ No newline at end of file diff --git a/iot-sqs-lambda-cdk/iot-sqs-lambda-cdk-pattern.json b/iot-sqs-lambda-cdk/iot-sqs-lambda-cdk-pattern.json index 55b1fb63b0..70d91e1f9e 100644 --- a/iot-sqs-lambda-cdk/iot-sqs-lambda-cdk-pattern.json +++ b/iot-sqs-lambda-cdk/iot-sqs-lambda-cdk-pattern.json @@ -46,7 +46,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/iot-timestream-cdk/example-pattern.json b/iot-timestream-cdk/example-pattern.json index efc950aaa9..fdd9210413 100644 --- a/iot-timestream-cdk/example-pattern.json +++ b/iot-timestream-cdk/example-pattern.json @@ -35,7 +35,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy."] diff --git a/kinesis-data-stream-firehose-apigw-sam/README.md b/kinesis-data-stream-firehose-apigw-sam/README.md new file mode 100644 index 0000000000..012d31b3f0 --- /dev/null +++ b/kinesis-data-stream-firehose-apigw-sam/README.md @@ -0,0 +1,56 @@ +# Amazon Kinesis Data Streams to Amazon API Gateway via Amazon Kinesis Data Firehose + +This sample project demonstrates how to send data received by Kinesis Data Streams to a HTTP endpoint with authorization (here API Gateway REST API) using Kinesis Data Firehose. + +Learn more about this pattern at [Serverless Land Patterns](https://serverlessland.com/patterns/kinesis-data-stream-firehose-apigw-sam). + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd kinesis-data-stream-firehose-apigw-sam + ``` +1. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: + ``` + sam deploy --guided + ``` +1. During the prompts: + * Enter a stack name + * Enter the desired AWS Region + * Enter Secret Name. To use default, click Enter. + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run `sam deploy --guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +## Testing + +* The AWS SAM output specifies the Kinesis Data Streams name value for `MyKinesisStream`. +* Replace `` in the following command with the copied name of the Kinesis Data Streams in above step to get the test command. + ``` + aws kinesis put-record --stream-name --partition-key 001 --data $(echo -n "{\"hello\" : \"world\"}" | base64) + ``` +* Run the above command a few times and check the logs of the test Lambda function to see the records put to Kinesis Data Streams. + +## AWS Documentation +- [Stream data to an HTTP endpoint with Amazon Kinesis Data Firehose](https://aws.amazon.com/blogs/big-data/stream-data-to-an-http-endpoint-with-amazon-kinesis-data-firehose/) +- [Kinesis Data Firehose HTTP Endpoint Destination](https://docs.aws.amazon.com/firehose/latest/dev/create-destination.html#create-destination-http) + +## Cleanup + +1. Delete the stack + ```bash + sam delete + ``` diff --git a/kinesis-data-stream-firehose-apigw-sam/example-pattern.json b/kinesis-data-stream-firehose-apigw-sam/example-pattern.json new file mode 100644 index 0000000000..4cc3dc4a12 --- /dev/null +++ b/kinesis-data-stream-firehose-apigw-sam/example-pattern.json @@ -0,0 +1,56 @@ +{ + "title": "Amazon Kinesis Data Streams to Amazon API Gateway via Amazon Kinesis Data Firehose", + "description": "POST data received by Kinesis Data Streams to an HTTP endpoint securely using Kinesis Data Firehose", + "level": "200", + "language": "Python", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This sample project demonstrates how to send data received by Kinesis Data Streams to an HTTP endpoint with authorization (here API Gateway REST API) using Kinesis Data Firehose." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/kinesis-data-stream-firehose-apigw-sam", + "templateURL": "serverless-patterns/kinesis-data-stream-firehose-apigw-sam", + "projectFolder": "kinesis-data-stream-firehose-apigw-sam", + "templateFile": "template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Stream data to an HTTP endpoint with Amazon Kinesis Data Firehose", + "link": "https://aws.amazon.com/blogs/big-data/stream-data-to-an-http-endpoint-with-amazon-kinesis-data-firehose/" + }, + { + "text": "Kinesis Data Firehose HTTP Endpoint Destination", + "link": "https://docs.aws.amazon.com/firehose/latest/dev/create-destination.html#create-destination-http" + } + ] + }, + "deploy": { + "text": [ + "sam deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "name": "Udit Parikh", + "image": "https://drive.google.com/file/d/1SfEjQtLkPaOHC7JlfugR4OaUD0yNjHu6/view?usp=drivesdk", + "bio": "Udit is passionate about serverless & event-driven architectures.", + "linkedin": "parikhudit" + } + ] +} diff --git a/kinesis-data-stream-firehose-apigw-sam/src/lambda_authorizer.py b/kinesis-data-stream-firehose-apigw-sam/src/lambda_authorizer.py new file mode 100644 index 0000000000..354c696c29 --- /dev/null +++ b/kinesis-data-stream-firehose-apigw-sam/src/lambda_authorizer.py @@ -0,0 +1,51 @@ +import json +import boto3 +import os + +secretsmanager_client = boto3.client('secretsmanager') + +def lambda_handler(event, context): + + # print(event) + access_key = event['authorizationToken'] + methodArn = event['methodArn'] + secret_name = os.environ['SECRET_NAME'] + # print('Secret Name: ' + secret_name) + # print('Access Key: ' + access_key) + + try: + response = secretsmanager_client.get_secret_value( + SecretId=secret_name + ) + expected_key = response['SecretString'] + # print('Expected key: ' + expected_key) + except Exception: + return build_response(False, methodArn) + + if access_key != expected_key: + return build_response(False, methodArn) + + return build_response(True, methodArn) + +def build_response(allowRequest, methodArn): + + output = { + "principalId": "yyyyyyyy", + "policyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": "execute-api:Invoke", + "Effect": "Deny", + "Resource": "arn:aws:execute-api:*" + } + ] + } + } + + if allowRequest is True: + output["policyDocument"]["Statement"][0]["Effect"] = "Allow" + + output["policyDocument"]["Statement"][0]["Resource"] = methodArn + + return output \ No newline at end of file diff --git a/kinesis-data-stream-firehose-apigw-sam/src/lambda_function.py b/kinesis-data-stream-firehose-apigw-sam/src/lambda_function.py new file mode 100644 index 0000000000..45cdf747ff --- /dev/null +++ b/kinesis-data-stream-firehose-apigw-sam/src/lambda_function.py @@ -0,0 +1,20 @@ +import json +import time + +def lambda_handler(event, context): + print(event) + + body = { + "requestId": event["headers"]["X-Amz-Firehose-Request-Id"], + "timestamp": int(time.time() * 1000) + } + + output = { + "statusCode": 200, + "body": json.dumps(body), + "headers": { + "Content-Type": "application/json" + } + } + print(output) + return output \ No newline at end of file diff --git a/kinesis-data-stream-firehose-apigw-sam/template.yaml b/kinesis-data-stream-firehose-apigw-sam/template.yaml new file mode 100644 index 0000000000..a936a4ff3a --- /dev/null +++ b/kinesis-data-stream-firehose-apigw-sam/template.yaml @@ -0,0 +1,150 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: 'AWS::Serverless-2016-10-31' +Description: SAM application to POST data to API Gateway from Kinesis Data Streams via Kinesis Firehose + +Parameters: + SecretName: + Type: String + Default: KinesisFirehoseHttpEndpointAccessKey + +Resources: + + MySecret: + Type: "AWS::SecretsManager::Secret" + Properties: + Name: "KinesisFirehoseHttpEndpointAccessKey" + Description: "Secret with dynamically generated secret password." + GenerateSecretString: + PasswordLength: 30 + ExcludeCharacters: '"@/\' + + MyApiGateway: + Type: AWS::Serverless::Api + Properties: + OpenApiVersion: 3.0.1 + StageName: dev + Auth: + DefaultAuthorizer: MyLambdaAuthorizer + Authorizers: + MyLambdaAuthorizer: + FunctionArn: !GetAtt LambdaAuthorizerFunction.Arn + Identity: + Header: "X-Amz-Firehose-Access-Key" + + LambdaAuthorizerFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: src/ + Handler: lambda_authorizer.lambda_handler + Runtime: python3.10 + Policies: + - AWSSecretsManagerGetSecretValuePolicy: + SecretArn: !Ref MySecret + Environment: + Variables: + SECRET_NAME: !Ref SecretName + + TestLambdaFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: src/ + Handler: lambda_function.lambda_handler + Runtime: python3.10 + Events: + Post: + Type: Api + Properties: + Path: / + Method: POST + RestApiId: !Ref MyApiGateway + + MyKinesisStream: + Type: AWS::Kinesis::Stream + Properties: + ShardCount: 2 + + MyKinesisFirehoseRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: firehose.amazonaws.com + Action: sts:AssumeRole + Policies: + - PolicyName: KinesisFirehosePolicy + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - "firehose:CreateDeliveryStream" + - "firehose:DeleteDeliveryStream" + - "firehose:DescribeDeliveryStream" + - "firehose:PutRecord" + - "firehose:PutRecords" + - "firehose:PutRecordBatch" + - "firehose:UpdateDestination" + Resource: + - !GetAtt MyKinesisStream.Arn + - Effect: Allow + Action: + - "kinesis:GetRecords" + - "kinesis:GetShardIterator" + - "kinesis:DescribeStream" + - "kinesis:DescribeStreamSummary" + - "kinesis:DescribeStreamConsumer" + - "kinesis:SubscribeToShard" + - "kinesis:RegisterStreamConsumer" + Resource: + - "*" + - Effect: Allow + Action: + - "s3:GetObject" + - "s3:ListBucket" + - "s3:GetObjectVersion" + - "s3:PutObject" + - "s3:PutObjectAcl" + Resource: + - !Sub arn:aws:s3:::${MyS3Bucket}/* + + MyS3Bucket: + Type: AWS::S3::Bucket + + KinesisFirehoseDeliveryStream: + Type: AWS::KinesisFirehose::DeliveryStream + Properties: + DeliveryStreamName: "MyDeliveryStream" + DeliveryStreamType: "KinesisStreamAsSource" + KinesisStreamSourceConfiguration: + KinesisStreamARN: !GetAtt MyKinesisStream.Arn + RoleARN: !GetAtt MyKinesisFirehoseRole.Arn + HttpEndpointDestinationConfiguration: + EndpointConfiguration: + Url: !Sub "https://${MyApiGateway}.execute-api.${AWS::Region}.amazonaws.com/dev/" + AccessKey: '{{resolve:secretsmanager:KinesisFirehoseHttpEndpointAccessKey:SecretString}}' + RoleARN: !GetAtt MyKinesisFirehoseRole.Arn + BufferingHints: + IntervalInSeconds: 60 + SizeInMBs: 1 + S3Configuration: + RoleARN: !GetAtt MyKinesisFirehoseRole.Arn + BucketARN: !GetAtt MyS3Bucket.Arn + BufferingHints: + IntervalInSeconds: 60 + SizeInMBs: 1 + ErrorOutputPrefix: "error/" + CompressionFormat: "UNCOMPRESSED" + Prefix: "raw/" + +Outputs: + ApiEndpoint: + Description: API Gateway endpoint URL + Value: + Fn::Sub: "https://${MyApiGateway}.execute-api.${AWS::Region}.amazonaws.com/dev/" + MyKinesisStream: + Description: Kinesis Data Stream name + Value: !Ref MyKinesisStream + \ No newline at end of file diff --git a/kinesis-video-rekognition-lambda/example-pattern.json b/kinesis-video-rekognition-lambda/example-pattern.json index e15c8b81a6..dfaa66384b 100644 --- a/kinesis-video-rekognition-lambda/example-pattern.json +++ b/kinesis-video-rekognition-lambda/example-pattern.json @@ -47,7 +47,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-athena-sam/example-pattern.json b/lambda-athena-sam/example-pattern.json index 6bcd6708a8..0cc14ba2de 100644 --- a/lambda-athena-sam/example-pattern.json +++ b/lambda-athena-sam/example-pattern.json @@ -41,7 +41,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-comprehend/example-pattern.json b/lambda-comprehend/example-pattern.json index dce72a60e0..04dc6c3520 100644 --- a/lambda-comprehend/example-pattern.json +++ b/lambda-comprehend/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-destinations-cdk/example-pattern.json b/lambda-destinations-cdk/example-pattern.json index 37e2a8e9ed..e6af786bda 100644 --- a/lambda-destinations-cdk/example-pattern.json +++ b/lambda-destinations-cdk/example-pattern.json @@ -34,7 +34,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy."] diff --git a/lambda-destinations-sam/example-pattern.json b/lambda-destinations-sam/example-pattern.json index 1d47c45fb4..7bc222b7f1 100644 --- a/lambda-destinations-sam/example-pattern.json +++ b/lambda-destinations-sam/example-pattern.json @@ -34,7 +34,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-dynamodb-rust/example-pattern.json b/lambda-dynamodb-rust/example-pattern.json index 736c36fe4a..9aad5ce674 100644 --- a/lambda-dynamodb-rust/example-pattern.json +++ b/lambda-dynamodb-rust/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-esm-rabbitmq-filters-sam/dependencies/python/idna-3.4.dist-info/METADATA b/lambda-esm-rabbitmq-filters-sam/dependencies/python/idna-3.4.dist-info/METADATA index 07f6193b0f..5a8e519bfa 100644 --- a/lambda-esm-rabbitmq-filters-sam/dependencies/python/idna-3.4.dist-info/METADATA +++ b/lambda-esm-rabbitmq-filters-sam/dependencies/python/idna-3.4.dist-info/METADATA @@ -237,6 +237,6 @@ Additional Notes out across the domain industry due to associated security risks. For now, applications that wish need to support these non-compliant labels may wish to consider trying the encode/decode operation in this library - first, and then falling back to using `encodings.idna`. See `the Github + first, and then falling back to using `encodings.idna`. See `the GitHub project `_ for more discussion. diff --git a/lambda-eventbridge-java/example-pattern.json b/lambda-eventbridge-java/example-pattern.json index e87a15a72c..f1b54a8592 100644 --- a/lambda-eventbridge-java/example-pattern.json +++ b/lambda-eventbridge-java/example-pattern.json @@ -35,7 +35,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-eventbridge-rust/example-pattern.json b/lambda-eventbridge-rust/example-pattern.json index d345417617..d0efe6097d 100644 --- a/lambda-eventbridge-rust/example-pattern.json +++ b/lambda-eventbridge-rust/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-eventbridge-sls/example-pattern.json b/lambda-eventbridge-sls/example-pattern.json index 263fe7f2db..f5c7b6328c 100644 --- a/lambda-eventbridge-sls/example-pattern.json +++ b/lambda-eventbridge-sls/example-pattern.json @@ -34,7 +34,7 @@ "text": ["serverless deploy --verbose"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": [ diff --git a/lambda-eventbridge-terraform/example-pattern.json b/lambda-eventbridge-terraform/example-pattern.json index 553d7a31dc..1aa1050b7e 100644 --- a/lambda-eventbridge-terraform/example-pattern.json +++ b/lambda-eventbridge-terraform/example-pattern.json @@ -37,7 +37,7 @@ }, "testing": { "text": [ - "See the README in the Github repo for detailed testing instructions." + "See the README in the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-extension-parameter-store-cdk/example-pattern.json b/lambda-extension-parameter-store-cdk/example-pattern.json index b580413800..ce673b1bc2 100644 --- a/lambda-extension-parameter-store-cdk/example-pattern.json +++ b/lambda-extension-parameter-store-cdk/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-function-url-cdk/example-pattern.json b/lambda-function-url-cdk/example-pattern.json index b9641a33f3..1bc903785f 100644 --- a/lambda-function-url-cdk/example-pattern.json +++ b/lambda-function-url-cdk/example-pattern.json @@ -32,7 +32,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-function-url-sls/example-pattern.json b/lambda-function-url-sls/example-pattern.json index 7879376c2e..f35df5e726 100644 --- a/lambda-function-url-sls/example-pattern.json +++ b/lambda-function-url-sls/example-pattern.json @@ -44,7 +44,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-function-url/example-pattern.json b/lambda-function-url/example-pattern.json index 339d654d9a..c30ef545c5 100644 --- a/lambda-function-url/example-pattern.json +++ b/lambda-function-url/example-pattern.json @@ -37,7 +37,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-functionurl-dynamodb-sam-go/example-pattern.json b/lambda-functionurl-dynamodb-sam-go/example-pattern.json index 775785cb32..53d76d1ff0 100644 --- a/lambda-functionurl-dynamodb-sam-go/example-pattern.json +++ b/lambda-functionurl-dynamodb-sam-go/example-pattern.json @@ -23,7 +23,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-glue-s3/example-pattern.json b/lambda-glue-s3/example-pattern.json index fa2cab3e2d..740a0a11dc 100644 --- a/lambda-glue-s3/example-pattern.json +++ b/lambda-glue-s3/example-pattern.json @@ -35,7 +35,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-inspector-scans/example-pattern.json b/lambda-inspector-scans/example-pattern.json index 477b750406..8aebd934e8 100644 --- a/lambda-inspector-scans/example-pattern.json +++ b/lambda-inspector-scans/example-pattern.json @@ -1,7 +1,7 @@ { "title": "Event driven vulnerability scanning with EventBridge and Lambda", "description": "This pattern listens to Amazon Inspector events and triggers downstream consumers", - "language": "", + "language": "YAMl", "level": "200", "framework": "SAM", "introBox": { @@ -28,7 +28,7 @@ "text": ["sam deploy --guided"] }, "testing": { - "text": ["See the README in the Github repo for detailed testing instructions."] + "text": ["See the README in the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: sam delete."] diff --git a/lambda-iot-sam/example-pattern.json b/lambda-iot-sam/example-pattern.json index 4ad92c54c8..e40bb3fe53 100644 --- a/lambda-iot-sam/example-pattern.json +++ b/lambda-iot-sam/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-jbang/example-pattern.json b/lambda-jbang/example-pattern.json index 2baea8a18b..30141e4fda 100644 --- a/lambda-jbang/example-pattern.json +++ b/lambda-jbang/example-pattern.json @@ -37,7 +37,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-kinesis-lambda-cdk/lambda-kinesis-lambda-cdk-pattern.json b/lambda-kinesis-lambda-cdk/lambda-kinesis-lambda-cdk-pattern.json index 5710099de2..dbc30827a0 100644 --- a/lambda-kinesis-lambda-cdk/lambda-kinesis-lambda-cdk-pattern.json +++ b/lambda-kinesis-lambda-cdk/lambda-kinesis-lambda-cdk-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-kinesis-lambda/example-pattern.json b/lambda-kinesis-lambda/example-pattern.json index 555469d31a..125d2626fa 100644 --- a/lambda-kinesis-lambda/example-pattern.json +++ b/lambda-kinesis-lambda/example-pattern.json @@ -39,7 +39,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-layer-terraform/lambda-layer-terraform.json b/lambda-layer-terraform/lambda-layer-terraform.json index 5e06935575..ba6af00e3a 100644 --- a/lambda-layer-terraform/lambda-layer-terraform.json +++ b/lambda-layer-terraform/lambda-layer-terraform.json @@ -50,7 +50,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-layer/lambda-layer-sam.json b/lambda-layer/lambda-layer-sam.json index b9f96ee487..4955fefa79 100644 --- a/lambda-layer/lambda-layer-sam.json +++ b/lambda-layer/lambda-layer-sam.json @@ -42,7 +42,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-powershell-runtime-sam/example-pattern.json b/lambda-powershell-runtime-sam/example-pattern.json index a7f46f9dc0..f4f1edebe6 100644 --- a/lambda-powershell-runtime-sam/example-pattern.json +++ b/lambda-powershell-runtime-sam/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-powertools-secretsmanager-cdk/example-pattern.json b/lambda-powertools-secretsmanager-cdk/example-pattern.json index db2c3076e6..61dd484688 100644 --- a/lambda-powertools-secretsmanager-cdk/example-pattern.json +++ b/lambda-powertools-secretsmanager-cdk/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-rekognition-rust/example-pattern.json b/lambda-rekognition-rust/example-pattern.json index 70dc803d5e..f9547e1d84 100644 --- a/lambda-rekognition-rust/example-pattern.json +++ b/lambda-rekognition-rust/example-pattern.json @@ -35,7 +35,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-s3-async-lambda/example-pattern.json b/lambda-s3-async-lambda/example-pattern.json index 589c2123d0..10fc591bec 100644 --- a/lambda-s3-async-lambda/example-pattern.json +++ b/lambda-s3-async-lambda/example-pattern.json @@ -41,7 +41,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-s3-polly-cdk/example-pattern.json b/lambda-s3-polly-cdk/example-pattern.json index 003ea1035a..84e22e911b 100644 --- a/lambda-s3-polly-cdk/example-pattern.json +++ b/lambda-s3-polly-cdk/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-s3-rust/example-pattern.json b/lambda-s3-rust/example-pattern.json index 2855c6d18a..a48cd062c4 100644 --- a/lambda-s3-rust/example-pattern.json +++ b/lambda-s3-rust/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-s3-translate-cdk/README.md b/lambda-s3-translate-cdk/README.md new file mode 100644 index 0000000000..0352dfbf2d --- /dev/null +++ b/lambda-s3-translate-cdk/README.md @@ -0,0 +1,36 @@ +# S3 Triggered Lambda Function that starts an Amazon Translate Job and stores result into another S3 bucket +This repo contains serverless patterns showing how to setup a Lambda with an S3 *object created* trigger that starts a basic, Amazon Translate Job. The result is placed into another S3 bucket. + +![Demo Project Solution Architecture Diagram](architecture.png) + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* AWS Account +* AWS CLI already configured with Administrator permission +* [NodeJS 14.x installed](https://nodejs.org/en/download/) +* CDK v2 installed: See Getting Started With the AWS CDK +* Python CDK required libraries: (install with pip install -r requirements.txt) +* Clone this repo! + +## Deployment Instructions + +1. Within your CDK Python module directory(where all your cdk stacks are located) create a constructs folder and within the constructs folder, create an assets folder. +2. Place the `lambda_s3_translate_cdk.py` file in the constructs folder you created and then place the lambda_function.py file in the assets folder you created. +3. In your terminal run `CDK Deploy` for the specified stack that uses this construct + +### Removing the resources + +1. run `cdk destroy ` for the specified stack that used this construct + +``` +git clone https://github.com/aws-samples/serverless-patterns/lambda-s3-translate-cdk +``` + +Each subdirectory contains additional installation and usage instructions. + +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. +---- + diff --git a/lambda-s3-translate-cdk/app.py b/lambda-s3-translate-cdk/app.py new file mode 100644 index 0000000000..e6e9a2d0e3 --- /dev/null +++ b/lambda-s3-translate-cdk/app.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +import os + +import aws_cdk as cdk + +from lambda_s3_translate_cdk.lambda_s3_translate_cdk_stack import LambdaS3TranslateCdkStack + + +app = cdk.App() +LambdaS3TranslateCdkStack(app, "LambdaS3TranslateCdkStack", + # If you don't specify 'env', this stack will be environment-agnostic. + # Account/Region-dependent features and context lookups will not work, + # but a single synthesized template can be deployed anywhere. + + # Uncomment the next line to specialize this stack for the AWS Account + # and Region that are implied by the current CLI configuration. + + #env=cdk.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')), + + # Uncomment the next line if you know exactly what Account and Region you + # want to deploy the stack to. */ + + #env=cdk.Environment(account='123456789012', region='us-east-1'), + + # For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html + ) + +app.synth() diff --git a/lambda-s3-translate-cdk/architecture.png b/lambda-s3-translate-cdk/architecture.png new file mode 100644 index 0000000000..1a5c92df29 Binary files /dev/null and b/lambda-s3-translate-cdk/architecture.png differ diff --git a/lambda-s3-translate-cdk/cdk.json b/lambda-s3-translate-cdk/cdk.json new file mode 100644 index 0000000000..223fa01199 --- /dev/null +++ b/lambda-s3-translate-cdk/cdk.json @@ -0,0 +1,48 @@ +{ + "app": "python3 app.py", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "requirements*.txt", + "source.bat", + "**/__init__.py", + "python/__pycache__", + "tests" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-redshift:columnId": true + } +} diff --git a/lambda-s3-translate-cdk/example-pattern.json b/lambda-s3-translate-cdk/example-pattern.json new file mode 100644 index 0000000000..6986b9d173 --- /dev/null +++ b/lambda-s3-translate-cdk/example-pattern.json @@ -0,0 +1,58 @@ +{ + "title": "S3 to Lambda to Translate", + "description": "Create a Lambda with an S3 object created trigger that starts an Amazon Translate Task and puts the results in anoter S3 Bucket", + "language": "Python", + "level": "200", + "framework": "CDK", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern contains a sample AWS Cloud Development Kit (AWS CDK) template that deploys a Lambda Function with an S3 object created trigger to start an Amazon Translate Task and place the results in another S3 bucket.", + "This pattern deploys one Lambda Function and two S3 Buckets." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/lambda-s3-translate-cdk", + "templateURL": "serverless-patterns/lambda-s3-translate-cdk", + "projectFolder": "pattern", + "templateFile": "lambda-s3-translate-cdk.py" + } + }, + "resources": { + "bullets": [ + { + "text": "Using an Amazon S3 trigger to invoke a Lambda function", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/with-s3-example.html" + }, + { + "text": "Guidelines and quotas - Amazon Translate", + "link": "https://docs.aws.amazon.com/translate/latest/dg/what-is-limits.html" + } + ] + }, + "deploy": { + "text": [ + "cdk deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: cdk delete." + ] + }, + "authors": [ + { + "name": "Kranthi Kiran A", + "image": "https://media.licdn.com/dms/image/C5603AQGMzL2iVESvpA/profile-displayphoto-shrink_400_400/0/1662286284377?e=1691020800&v=beta&t=p7litfMASeNBNU-Xeb7bpl5WD8KoWrlVd3Azl3_q1bg", + "bio": "Kranthi is an Associate Cloud Developer with Amazon Web Services", + "linkedin": "akkiran003" + } + ] + } + diff --git a/lambda-s3-translate-cdk/pattern/assets/lambda_function.py b/lambda-s3-translate-cdk/pattern/assets/lambda_function.py new file mode 100644 index 0000000000..b0188c4c3f --- /dev/null +++ b/lambda-s3-translate-cdk/pattern/assets/lambda_function.py @@ -0,0 +1,43 @@ +import json +import boto3 +import os +import io +from tempfile import gettempdir +from contextlib import closing + +# Get the s3 client +s3 = boto3.client("s3") +# Create a client to use Polly +translate_client = boto3.client("translate") + +output_bucket = os.environ['DESTINATION_BUCKET'] + +def lambda_handler(event, context): + + print(event) + + # Get the s3 bucket name + bucket_name = event["Records"][0]["s3"]["bucket"]["name"] + # Get the file name + file_name = event["Records"][0]["s3"]["object"]["key"] + # Get the file object + file_obj = s3.get_object(Bucket=bucket_name, Key=file_name) + # Read the file + file_content = file_obj["Body"].read().decode("utf-8") + print(file_content) + # Call the Polly API to convert the text to speech + response = translate_client.translate_text( + Text=file_content, + SourceLanguageCode='auto', + TargetLanguageCode='te' + ) + print(response) + # Get the audio file + # Get the translated text from the response + response_text = io.BytesIO(response['TranslatedText'].encode('utf-8')) + txt_file_name = file_name.split(".")[0] + ".txt" + s3.put_object(Key=txt_file_name, Body=response_text, Bucket=output_bucket) + return { + 'statusCode': 200, + 'body': json.dumps(response_text) + } diff --git a/lambda-s3-translate-cdk/pattern/lambda_s3_translate_cdk_stack.py b/lambda-s3-translate-cdk/pattern/lambda_s3_translate_cdk_stack.py new file mode 100644 index 0000000000..de6271b90b --- /dev/null +++ b/lambda-s3-translate-cdk/pattern/lambda_s3_translate_cdk_stack.py @@ -0,0 +1,64 @@ +from aws_cdk import ( + # Duration, + Stack, + aws_s3 as s3, + aws_lambda as lambda_, + Duration, + aws_iam as iam, + aws_lambda_event_sources as eventsources +) +from constructs import Construct + +class LambdaS3TranslateCdkStack(Stack): + + def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: + super().__init__(scope, construct_id, **kwargs) + + #Input Bucket + self.input_bucket = s3.Bucket(self, "input-bucket", + versioned=True, + encryption=s3.BucketEncryption.S3_MANAGED, + block_public_access=s3.BlockPublicAccess.BLOCK_ALL, + enforce_ssl=True) + + # Destination Bucket + self.destination_bucket = s3.Bucket(self, 'destination-bucket', + versioned=True, + encryption=s3.BucketEncryption.S3_MANAGED, + block_public_access=s3.BlockPublicAccess.BLOCK_ALL, + enforce_ssl=True) + + python_lambda_kwargs = { + 'handler': 'lambda_function.lambda_handler', + 'runtime': lambda_.Runtime.PYTHON_3_9, + 'timeout': Duration.minutes(2) + } + + # Trigger Transcription Lambda + trigger_translate_lambda = lambda_.Function(self, "file-upload-trigger", **python_lambda_kwargs, + code=lambda_.Code.from_asset( + './pattern/assets'), + function_name="start-translate", + initial_policy=[ + iam.PolicyStatement( + actions=['translate:TranslateText', + 'comprehend:DetectDominantLanguage' + ], + resources=['*']), + iam.PolicyStatement(actions=['s3:PutObject'], + resources=[ + self.destination_bucket.bucket_arn+'/*'] + ), + iam.PolicyStatement(actions=['s3:GetObject', + 's3:ListBucket'], + resources=[ + self.input_bucket.bucket_arn, + self.input_bucket.bucket_arn+'/*' + ] + ) + ] + ) + + # Add Trigger and Environment Variable + trigger_translate_lambda.add_event_source(eventsources.S3EventSource(self.input_bucket, events=[s3.EventType.OBJECT_CREATED])) + trigger_translate_lambda.add_environment('DESTINATION_BUCKET', self.destination_bucket.bucket_name) diff --git a/lambda-s3-translate-cdk/requirements.txt b/lambda-s3-translate-cdk/requirements.txt new file mode 100644 index 0000000000..eb336b1c40 --- /dev/null +++ b/lambda-s3-translate-cdk/requirements.txt @@ -0,0 +1,2 @@ +aws-cdk-lib==2.70.0 +constructs>=10.0.0,<11.0.0 diff --git a/lambda-secrets-manager-extension-python/example-pattern.json b/lambda-secrets-manager-extension-python/example-pattern.json index 93a8ade45b..15b6372315 100644 --- a/lambda-secrets-manager-extension-python/example-pattern.json +++ b/lambda-secrets-manager-extension-python/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-secretsmanager-java-sam/GetSecretFunction/pom.xml b/lambda-secretsmanager-java-sam/GetSecretFunction/pom.xml new file mode 100644 index 0000000000..649dc9a8a5 --- /dev/null +++ b/lambda-secretsmanager-java-sam/GetSecretFunction/pom.xml @@ -0,0 +1,64 @@ + + 4.0.0 + com.example + GetSecretFunction + 1.0 + jar + Lambda Function reading secret value from secrets manager. + + 2.20.45 + 11 + 11 + + + + + + software.amazon.awssdk + bom + ${aws.java.sdk.version} + pom + import + + + + + + + com.amazonaws + aws-lambda-java-core + 1.2.2 + + + software.amazon.awssdk + secretsmanager + 2.20.26 + + + com.amazonaws.secretsmanager + aws-secretsmanager-caching-java + 1.0.2 + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + + + package + + shade + + + + + + + \ No newline at end of file diff --git a/lambda-secretsmanager-java-sam/GetSecretFunction/src/main/java/com/example/App.java b/lambda-secretsmanager-java-sam/GetSecretFunction/src/main/java/com/example/App.java new file mode 100644 index 0000000000..5e1ffd4675 --- /dev/null +++ b/lambda-secretsmanager-java-sam/GetSecretFunction/src/main/java/com/example/App.java @@ -0,0 +1,39 @@ +package com.example; + +import com.amazonaws.services.lambda.runtime.Context; +import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider; +import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient; +import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest; +import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse; +import software.amazon.awssdk.services.secretsmanager.model.SecretsManagerException; + +import java.util.Map; + +//import com.amazonaws.services.lambda.runtime.Context; +//import com.amazonaws.services.lambda.runtime.RequestHandler; +//import com.amazonaws.services.lambda.runtime.LambdaLogger; +// +//import com.amazonaws.secretsmanager.caching.SecretCache; + +public class App{ + + String secretName = System.getenv("SECRET_NAME"); + SecretsManagerClient secretsClient = SecretsManagerClient.builder().build(); + + public String handleRequest(final Map event, final Context context) { + String secret = null; + try { + GetSecretValueRequest valueRequest = GetSecretValueRequest.builder() + .secretId(secretName) + .build(); + + GetSecretValueResponse valueResponse = secretsClient.getSecretValue(valueRequest); + secret = valueResponse.secretString(); + } catch (SecretsManagerException e) { + System.err.println(e.awsErrorDetails().errorMessage()); + System.exit(1); + } + return secret; + } + +} \ No newline at end of file diff --git a/lambda-secretsmanager-java-sam/README.md b/lambda-secretsmanager-java-sam/README.md new file mode 100644 index 0000000000..52b3f00652 --- /dev/null +++ b/lambda-secretsmanager-java-sam/README.md @@ -0,0 +1,68 @@ +# Lambda Secrets Manager using Java SDK + +This pattern is a Java port of the original [pattern](https://serverlessland.com/patterns/lambda-secretsmanager-node-sdkv3-sam). + +Learn more about this pattern at Serverless Land Patterns: TODO: Pattern Link here after publish + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd lambda-secretsmanager-java-sam + ``` +1. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: + ``` + sam deploy --guided + ``` +1. During the prompts: + * Enter a stack name + * Enter the desired AWS Region + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run `sam deploy --guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. + +## How it works + +* A secret with a randomly generated value is stored in Secrets Manager. +* A Lambda function which uses the AWS SDK for Java reads the secret in the init phase, outside the handler. +* The function returns the value of the secret. + +## Testing + +Run the following Lambda CLI invoke command to invoke the function. Edit the {GetSecretFunction} placeholder with the ARN of the deployed Lambda function. This is provided in the stack outputs. +View the secret in the function output which is stored in `response.json`. + +```bash +aws lambda invoke --function-name {GetSecretFunction} --cli-binary-format raw-in-base64-out response.json +cat response.json +``` + +## Cleanup + +1. Delete the stack + ```bash + sam delete --stack-name STACK_NAME + ``` +1. Confirm the stack has been deleted + ```bash + aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'STACK_NAME')].StackStatus" + ``` +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 \ No newline at end of file diff --git a/lambda-secretsmanager-java-sam/example-pattern.json b/lambda-secretsmanager-java-sam/example-pattern.json new file mode 100644 index 0000000000..5e1cc023b2 --- /dev/null +++ b/lambda-secretsmanager-java-sam/example-pattern.json @@ -0,0 +1,56 @@ +{ + "title": "Lambda Secrets Manager using Java SDK", + "description": "Lambda function retrieving a secret from Secrets Manager using Java SDK.", + "language": "Java", + "level": "200", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern stores a secret in Secrets Manager. A Lambda function uses AWS SDK for Java to read the secret in the init phase, outside the handler." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/lambda-secretsmanager-java-sam", + "templateURL": "serverless-patterns/lambda-secretsmanager-java-sam", + "projectFolder": "lambda-secretsmanager-java-sam", + "templateFile": "lambda-secretsmanager-java-sam/template.yaml" + } + }, + "resources": { + "bullets": [{ + "text": "Retrieve AWS Secrets Manager secrets in Java applications", + "link": "https://docs.aws.amazon.com/secretsmanager/latest/userguide/retrieving-secrets_cache-java.html" + }] + }, + "build": { + "text": [ + "sam build" + ] + }, + "deploy": { + "text": [ + "sam deploy --guided" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "name": "Paras Jain", + "image": "https://avatars.githubusercontent.com/u/583119?v=4", + "bio": "Paras is a Technical Account Manager with AWS based out of Herndon, Virginia, USA. He is a member of Serverless Technical Field community", + "linkedin": "parasjain01", + "twitter": "parasjain01" + } + ] +} diff --git a/lambda-secretsmanager-java-sam/template.yaml b/lambda-secretsmanager-java-sam/template.yaml new file mode 100644 index 0000000000..236d7261ee --- /dev/null +++ b/lambda-secretsmanager-java-sam/template.yaml @@ -0,0 +1,65 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 + +Description: Lambda function retrieving a secret from Secrets Manager using AWS SDK for Java + +########################################################################## +# Parameters & Globals # +########################################################################## +Globals: + Function: + Tracing: Active + Tags: + Application: lambda-secretsmanager-java-sam +Parameters: + SecretName: + Description: Secret Name + Type: String + Default: MySecret + AllowedPattern: ^[a-zA-Z0-9_.-/]*$ +Resources: + ########################################################################## + # Lambda functions # + ########################################################################## + GetSecretFunction: + Type: AWS::Serverless::Function + Properties: + FunctionName: GetSecret + CodeUri: GetSecretFunction + Handler: com.example.App::handleRequest + Runtime: java11 + Architectures: + - x86_64 + Timeout: 30 + MemorySize: 512 + Environment: + Variables: + SECRET_NAME: !Ref SecretName + Policies: + - AWSSecretsManagerGetSecretValuePolicy: + SecretArn: !Ref MySecret + ########################################################################## + # Secret # + ########################################################################## + MySecret: + Type: 'AWS::SecretsManager::Secret' + Properties: + Name: !Ref SecretName + Description: "Secret with dynamically generated secret password." + GenerateSecretString: + PasswordLength: 30 + ExcludeCharacters: '"@/\' + +########################################################################## +# OUTPUTS # +########################################################################## +Outputs: + MySecret: + Value: !Ref MySecret + Description: MySecret ARN + MySecretName: + Value: !Ref SecretName + Description: SecretName + GetSecretFunction: + Value: !Ref GetSecretFunction + Description: GetSecretFunction Lambda Function diff --git a/lambda-secretsmanager-node-sdkv3-sam/example-pattern.json b/lambda-secretsmanager-node-sdkv3-sam/example-pattern.json index 595d27e9e0..5c94b7adbf 100644 --- a/lambda-secretsmanager-node-sdkv3-sam/example-pattern.json +++ b/lambda-secretsmanager-node-sdkv3-sam/example-pattern.json @@ -31,7 +31,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-sfn-rust/example-pattern.json b/lambda-sfn-rust/example-pattern.json index ee194ccb61..8b0fe791c6 100644 --- a/lambda-sfn-rust/example-pattern.json +++ b/lambda-sfn-rust/example-pattern.json @@ -35,7 +35,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-sns-filters-lambda/example-pattern.json b/lambda-sns-filters-lambda/example-pattern.json index e0a04da5c9..1d929bfe0a 100644 --- a/lambda-sns-filters-lambda/example-pattern.json +++ b/lambda-sns-filters-lambda/example-pattern.json @@ -34,7 +34,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk delete."] diff --git a/lambda-sns-lambda-cdk/example-pattern.json b/lambda-sns-lambda-cdk/example-pattern.json index b09e2d0632..f84e534250 100644 --- a/lambda-sns-lambda-cdk/example-pattern.json +++ b/lambda-sns-lambda-cdk/example-pattern.json @@ -30,7 +30,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk delete."] diff --git a/lambda-sns-sls/example-pattern.json b/lambda-sns-sls/example-pattern.json index 02b3b08629..99c7db0412 100644 --- a/lambda-sns-sls/example-pattern.json +++ b/lambda-sns-sls/example-pattern.json @@ -23,7 +23,7 @@ "text": ["serverless deploy --verbose"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": [ diff --git a/lambda-sns-terraform/example-pattern.json b/lambda-sns-terraform/example-pattern.json index 7226ac575c..d9bee5607a 100644 --- a/lambda-sns-terraform/example-pattern.json +++ b/lambda-sns-terraform/example-pattern.json @@ -37,7 +37,7 @@ }, "testing": { "text": [ - "See the README in the Github repo for detailed testing instructions." + "See the README in the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-sqs-java/example-pattern.json b/lambda-sqs-java/example-pattern.json index bf258ea98a..7d07cc36ba 100644 --- a/lambda-sqs-java/example-pattern.json +++ b/lambda-sqs-java/example-pattern.json @@ -34,7 +34,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-sqs-rust-sam/example-pattern.json b/lambda-sqs-rust-sam/example-pattern.json index 137e370a5c..3583a5df0f 100644 --- a/lambda-sqs-rust-sam/example-pattern.json +++ b/lambda-sqs-rust-sam/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-sqs-terraform/example-pattern.json b/lambda-sqs-terraform/example-pattern.json index eea3ce69a6..5f038aa493 100644 --- a/lambda-sqs-terraform/example-pattern.json +++ b/lambda-sqs-terraform/example-pattern.json @@ -37,7 +37,7 @@ }, "testing": { "text": [ - "See the README in the Github repo for detailed testing instructions." + "See the README in the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-ssm-parameter-java/example-pattern.json b/lambda-ssm-parameter-java/example-pattern.json index 72c6d7a233..b97fec4fae 100644 --- a/lambda-ssm-parameter-java/example-pattern.json +++ b/lambda-ssm-parameter-java/example-pattern.json @@ -36,7 +36,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-ssm-parameter-terraform/example-pattern.json b/lambda-ssm-parameter-terraform/example-pattern.json index 9706b9e959..fad8ceb3b6 100644 --- a/lambda-ssm-parameter-terraform/example-pattern.json +++ b/lambda-ssm-parameter-terraform/example-pattern.json @@ -1,7 +1,7 @@ { "title": "Lambda to SSM Parameter Store", "description": "Creates an AWS Lambda function and an AWS Systems Manager Parameter Store parameter", - "language": "", + "language": "Node.js", "level": "100", "framework": "Terraform", "introBox": { @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the README in the Github repo for detailed testing instructions." + "See the README in the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-ssm-sls/example-pattern.json b/lambda-ssm-sls/example-pattern.json index e47e2beddb..2ba2cd6674 100644 --- a/lambda-ssm-sls/example-pattern.json +++ b/lambda-ssm-sls/example-pattern.json @@ -20,7 +20,7 @@ "text": ["serverless deploy --verbose"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": [ diff --git a/lambda-streaming-large-sam/example-pattern.json b/lambda-streaming-large-sam/example-pattern.json index 6c74d1dcff..6d87feda39 100644 --- a/lambda-streaming-large-sam/example-pattern.json +++ b/lambda-streaming-large-sam/example-pattern.json @@ -34,7 +34,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-streaming-sdk-sam/example-pattern.json b/lambda-streaming-sdk-sam/example-pattern.json index 643023ee32..67cb606a45 100644 --- a/lambda-streaming-sdk-sam/example-pattern.json +++ b/lambda-streaming-sdk-sam/example-pattern.json @@ -34,7 +34,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-streaming-ttfb-pipeline-sam/example-pattern.json b/lambda-streaming-ttfb-pipeline-sam/example-pattern.json index 3b9e262280..892613a082 100644 --- a/lambda-streaming-ttfb-pipeline-sam/example-pattern.json +++ b/lambda-streaming-ttfb-pipeline-sam/example-pattern.json @@ -34,7 +34,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-streaming-ttfb-write-sam/example-pattern.json b/lambda-streaming-ttfb-write-sam/example-pattern.json index 81460be8a0..29dcc8fc93 100644 --- a/lambda-streaming-ttfb-write-sam/example-pattern.json +++ b/lambda-streaming-ttfb-write-sam/example-pattern.json @@ -34,7 +34,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-vpc-endpoints-secrets-manager-cdk-dotnet/cdk/src/Cdk/Cdk.csproj b/lambda-vpc-endpoints-secrets-manager-cdk-dotnet/cdk/src/Cdk/Cdk.csproj index 7677b7d8c8..ae92f16ab3 100644 --- a/lambda-vpc-endpoints-secrets-manager-cdk-dotnet/cdk/src/Cdk/Cdk.csproj +++ b/lambda-vpc-endpoints-secrets-manager-cdk-dotnet/cdk/src/Cdk/Cdk.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net6.0 Major diff --git a/lambda-vpc-endpoints-secrets-manager-cdk-dotnet/example-pattern.json b/lambda-vpc-endpoints-secrets-manager-cdk-dotnet/example-pattern.json index 99c41d0c69..454bbf7294 100644 --- a/lambda-vpc-endpoints-secrets-manager-cdk-dotnet/example-pattern.json +++ b/lambda-vpc-endpoints-secrets-manager-cdk-dotnet/example-pattern.json @@ -26,7 +26,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-vpc-interface-endpoints-secrets-manager/example-pattern.json b/lambda-vpc-interface-endpoints-secrets-manager/example-pattern.json index 34df86dda4..13ecea7889 100644 --- a/lambda-vpc-interface-endpoints-secrets-manager/example-pattern.json +++ b/lambda-vpc-interface-endpoints-secrets-manager/example-pattern.json @@ -42,7 +42,7 @@ }, "testing": { "text": [ - "See README.md file in the Github repo for detailed testing instructions." + "See README.md file in the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-vpc-secrets-sls/example-pattern.json b/lambda-vpc-secrets-sls/example-pattern.json index 7e6aabf1f6..8868f8fef2 100644 --- a/lambda-vpc-secrets-sls/example-pattern.json +++ b/lambda-vpc-secrets-sls/example-pattern.json @@ -49,7 +49,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/lambda-web-adapter-cdk/example-pattern.json b/lambda-web-adapter-cdk/example-pattern.json index 222efaf0fe..4156f97190 100644 --- a/lambda-web-adapter-cdk/example-pattern.json +++ b/lambda-web-adapter-cdk/example-pattern.json @@ -23,7 +23,7 @@ "resources": { "bullets": [ { - "text": "Lambda Web Adapter Github project", + "text": "Lambda Web Adapter GitHub project", "link": "https://github.com/awslabs/aws-lambda-web-adapter" }, { @@ -36,7 +36,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy."] diff --git a/msk-cfn-sasl-lambda/create-cluster-cfn/README.md b/msk-cfn-sasl-lambda/create-cluster-cfn/README.md index 3318adeacb..6b554685d5 100644 --- a/msk-cfn-sasl-lambda/create-cluster-cfn/README.md +++ b/msk-cfn-sasl-lambda/create-cluster-cfn/README.md @@ -31,13 +31,17 @@ The MSK cluster is set up to work with all three authentication mechanisms - TLS - Download Apache Kafka. ``` - wget https://downloads.apache.org/kafka/3.3.2/kafka_2.13-3.3.2.tgz + wget https://downloads.apache.org/kafka/3.4.1/kafka_2.13-3.4.1.tgz ``` +If this version (2.13-3.4.1) is not available anymore, please download latest Apache Kafka version that is MSK supports from https://downloads.apache.org/kafka/. + +Review this page to get a list of Apache Kafka versions supported by MSK - https://docs.aws.amazon.com/msk/latest/developerguide/supported-kafka-versions.html + - Unzip the package you just downloaded ``` - tar -xzf kafka_2.13-3.3.2.tgz + tar -xzf kafka_2.13-3.4.1.tgz ``` @@ -45,14 +49,14 @@ The MSK cluster is set up to work with all three authentication mechanisms - TLS ## IAM -- Go to the kafka_2.13-3.3.2/libs/ directory, then run the following command to download the Amazon MSK IAM JAR file. +- Go to the kafka_2.13-3.4.1/libs/ directory, then run the following command to download the Amazon MSK IAM JAR file. ``` - cd kafka_2.13-3.3.2/libs/ + cd kafka_2.13-3.4.1/libs/ wget https://github.com/aws/aws-msk-iam-auth/releases/download/v1.1.6/aws-msk-iam-auth-1.1.6-all.jar ``` -- Under kafka_2.13-3.3.2/bin directory create a new file with name `client.properties` and add following content in that file - +- Under kafka_2.13-3.4.1/bin directory create a new file with name `client.properties` and add following content in that file - ``` security.protocol=SASL_SSL @@ -85,10 +89,10 @@ export AWS_SECRET_ACCESS_KEY= - Get the value of BootstrapBrokerStringSaslIam, and pick the first url from that, for example - b-3...c9.kafka.us-east-1.amazonaws.com:9098 -- Open terminal and cd to kafka_2.13-3.3.2/bin directory +- Open terminal and cd to kafka_2.13-3.4.1/bin directory ``` - cd ~/environment/kafka_2.13-3.3.2/bin + cd ~/environment/kafka_2.13-3.4.1/bin ``` - Run this command to create a Kafka topic @@ -170,7 +174,7 @@ Here's the documentation based on which following instructions are created - htt - If you get a prompt like this - `Install reply anyway? [no]:`, enter yes, and press enter. -- Open kafka_2.13-3.3.2/bin/client.properties, and comment out existing list by adding # in front of each line. +- Open kafka_2.13-3.4.1/bin/client.properties, and comment out existing list by adding # in front of each line. - Add following lines after existing lines. @@ -182,7 +186,7 @@ ssl.keystore.password=storepass ssl.key.password=storepass ``` -- Run following command to create a test topic from kafka_2.13-3.3.2/bin directory in terminal. Use the bootstrap server for TLS (BootstrapBrokerStringTls) from the list obtained in IAM section. - +- Run following command to create a test topic from kafka_2.13-3.4.1/bin directory in terminal. Use the bootstrap server for TLS (BootstrapBrokerStringTls) from the list obtained in IAM section. - ``` ./kafka-topics.sh --create --bootstrap-server b-1.mskstackcluster.97lslp.c9.kafka.us-east-1.amazonaws.com:9094 --command-config client.properties --replication-factor 3 --partitions 1 --topic MSKTutorialTopicTLS @@ -239,7 +243,7 @@ ssl.truststore.location=/kafka.client.truststore.jks - Run following command to create a topic. Replace `BootstrapBrokerStringSaslScram` with the value that you retrieved in the previous step. ``` - cd ~/environment/kafka_2.13-3.3.2/ + cd ~/environment/kafka_2.13-3.4.1/ ./bin/kafka-topics.sh --create --bootstrap-server BootstrapBrokerStringSaslScram --replication-factor 3 --partitions 1 --topic DemoSASLTopic --command-config client_sasl.properties ``` diff --git a/msk-cfn-sasl-lambda/example-pattern.json b/msk-cfn-sasl-lambda/example-pattern.json index cd69d4bd0d..8163f979db 100644 --- a/msk-cfn-sasl-lambda/example-pattern.json +++ b/msk-cfn-sasl-lambda/example-pattern.json @@ -46,7 +46,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/msk-lambda-iam-python-sam/.gitignore b/msk-lambda-iam-python-sam/.gitignore new file mode 100644 index 0000000000..1502ca89fd --- /dev/null +++ b/msk-lambda-iam-python-sam/.gitignore @@ -0,0 +1,28 @@ +node_modules +npm-debug.log +package-lock.json +package +*out.yml +out.json +bucket-name.txt +target +build +.gradle +*.zip +bin +obj +Gemfile.lock +lib +__pycache__ +*.pyc +.classpath +.factorypath +.project +.settings/* +.aws +.sam +.aws-sam +samconfig.toml +__init__.py +.DS_Store +.project diff --git a/msk-lambda-iam-python-sam/HandlerKafka/app.py b/msk-lambda-iam-python-sam/HandlerKafka/app.py new file mode 100644 index 0000000000..3f3013b532 --- /dev/null +++ b/msk-lambda-iam-python-sam/HandlerKafka/app.py @@ -0,0 +1,51 @@ +# Lambda Runtime delivers a batch of messages to the lambda function +# Each batch of messages has two fields EventSource and EventSourceARN +# Each batch of messages also has a field called Records +# The Records is a map with multiple keys and values +# Each key is a combination of the Topic Name and the Partition Number +# One batch of messages can contain messages from multiple partitions + +import json +import base64 + +def lambda_handler(event, context): + # Now logging the contents of the batch of Kafka messages delivered to the Lambda function + print("Received an event: " + str(event)) + print("Event Source: " + event['eventSource']) + print("Event Source ARN: " + event['eventSourceArn']) + print("Bootstrap Servers: " + event['bootstrapServers']) + print("Records: " + str(event['records'])) + # Defining a variable to keep track of the number of the message in the batch of messages + i=1 + # Now looping through the map for each key (combination of topic and partition) + for record in event['records']: + print("Current Record: " + str(event['records'][record])) + # Now looping through the kafka messages within a particular key + for messages in event['records'][record]: + print("********************") + print("Now printing details of record number: " + str(i)) + print("Topic: " + str(messages['topic'])) + print("Partition: " + str(messages['partition'])) + print("Offset: " + str(messages['offset'])) + print("Topic: " + str(messages['topic'])) + print("Timestamp: " + str(messages['timestamp'])) + print("TimestampType: " + str(messages['timestampType'])) + # each kafka message has a key and a value that are base64 encoded + if None is not messages.get('key'): + b64decodedKey=base64.b64decode(messages['key']) + decodedKey=b64decodedKey.decode('ascii') + else: + decodedKey="null" + if None is not messages.get('value'): + b64decodedValue=base64.b64decode(messages['value']) + decodedValue=b64decodedValue.decode('ascii') + else: + decodedValue="null" + print("Key = " + str(decodedKey)) + print("Value = " + str(decodedValue)) + print("Now finished printing details of record number: " + str(i)) + print("********************") + i=i+1 + return { + 'statusCode': 200, + } diff --git a/msk-lambda-iam-python-sam/README.md b/msk-lambda-iam-python-sam/README.md new file mode 100644 index 0000000000..0e4cd2a290 --- /dev/null +++ b/msk-lambda-iam-python-sam/README.md @@ -0,0 +1,177 @@ +# Python AWS Lambda Kafka consumer with IAM auth, using AWS SAM + +This pattern is an example of a Lambda function that consumes messages from an Amazon Managed Streaming for Kafka (Amazon MSK) topic, where the MSK Cluster has been configured to use IAM authentication. This pattern assumes you already have an MSK cluster with a topic configured, if you need a sample pattern to deploy an MSK cluster either in Provisioned or Serverless modes please see the [msk-cfn-sasl-lambda pattern](https://serverlessland.com/patterns/msk-cfn-sasl-lambda). + +This project contains source code and supporting files for a serverless application that you can deploy with the AWS Serverless Application Model (AWS SAM) CLI. It includes the following files and folders. + +- HandlerKafka - Code for the application's Lambda function. +- events - Invocation events that you can use to invoke the function. +- template.yaml - An AWS SAM template that defines the application's AWS resources. + +The application creates a Lambda function that listens to Kafka messages on a topic of an MSK Cluster. These resources are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed +* Create MSK cluster and topic that will be used for testing. It is important to create the topic before deploying the Lambda function, otherwise the event source mapping will stay disabled. + +## Deploy the sample application + +The AWS SAM CLI is a serverless tool for building and testing Lambda applications. It uses Docker to locally test your functions in an Amazon Linux environment that resembles the Lambda execution environment. It can also emulate your application's build environment and API. + +To use the AWS SAM CLI, you need the following tools. + +* AWS SAM CLI - [Install the AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +* Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community) + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns.git + ``` +1. Change directory to the pattern directory: + ``` + cd msk-lambda-iam-python-sam + ``` + +1. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: + ``` + sam deploy --guided + ``` + +1. During the prompts: +* **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. +* **AWS Region**: The AWS region you want to deploy your app to. +* **Parameter MSKClusterName**: The name of the MSKCluster, eg. msk-test-cluster + +* **Parameter MSKClusterId**: The unique ID of the MSKCluster, eg. a4e132c8-6ad0-4334-a313-123456789012-s2 +* **Parameter MSKTopic**: The Kafka topic on which the lambda function will listen on +* **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. +* **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. +* **Disable rollback**: Defaults to No and it preserves the state of previously provisioned resources when an operation fails +* **Save arguments to configuration file**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. +* **SAM configuration file [samconfig.toml]**: Name of the configuration file to store configuration information locally +* **SAM configuration environment [default]**: Environment for storing deployment information locally + +You should get a message "Successfully created/updated stack - in " if all goes well. + +Once you have run `sam deploy --guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +## How it works + +This pattern creates a Lambda function along with a Lambda Event Source Mapping(ESM) resource. This maps a Kafka topic on an MSK Cluster as a trigger to a Lambda function. The ESM takes care of polling the Kafka topic and then invokes the Lambda function with a batch of messages. + +## Test the sample application + +Once the Lambda function is deployed, send some Kafka messages to the topic that you configured in the Lambda function trigger. + +Either send at least 10 messages or wait for 300 seconds (check the values of BatchSize: 10 and MaximumBatchingWindowInSeconds: 300 in the template.yaml file) + +Then check Amazon CloudWatch logs and you should see messages in the CloudWatch Log Group with the name of the deployed Lambda function. + +The Lambda code parses the Kafka messages and outputs the fields in the Kafka messages to CloudWatch logs. + +A single Lambda function receives a batch of messages. The messages are received as a map with each key being a combination of the topic and the partition, as a single batch can receive messages from multiple partitions. + +Each key has a list of messages. Each Kafka message has the following properties - `Topic`, `Partition`, `Offset`, `TimeStamp`, `TimeStampType`, `Key`, and `Value`. + +The `Key` and `Value` are base64 encoded and have to be decoded. A message can also have a list of headers, each header having a key and a value. + +The code in this example prints out the fields in the Kafka message and also decrypts the key and the value and logs them to CloudWatch logs. + + +### Local development + +**You can invoke the function locally using `sam local`** + +```bash +sam local invoke --event=events/event.json +``` + +You should see a response similar to the below + +`START RequestId: 5c10310a-abf9-416e-b017-697d2c3ba097 Version: $LATEST +Received an event: {'eventSource': 'aws:kafka', 'eventSourceArn': 'arn:aws:kafka:us-west-2:123456789012:cluster/MSKWorkshopCluster/a93759a9-c9d0-4952-984c-492c6bfa2be8-13', 'bootstrapServers': 'b-1.mskworkshopcluster.z9kc4f.c13.kafka.us-west-2.amazonaws.com:9098,b-3.mskworkshopcluster.z9kc4f.c13.kafka.us-west-2.amazonaws.com:9098,b-2.mskworkshopcluster.z9kc4f.c13.kafka.us-west-2.amazonaws.com:9098', 'records': {'myTopic-0': [{'topic': 'myTopic', 'partition': 0, 'offset': 383, 'timestamp': 1678484822068, 'timestampType': 'CREATE_TIME', 'value': 'bTE=', 'headers': []}, {'topic': 'myTopic', 'partition': 0, 'offset': 384, 'timestamp': 1678484823448, 'timestampType': 'CREATE_TIME', 'value': 'bTI=', 'headers': []}, {'topic': 'myTopic', 'partition': 0, 'offset': 385, 'timestamp': 1678484824763, 'timestampType': 'CREATE_TIME', 'value': 'bTM=', 'headers': []}, {'topic': 'myTopic', 'partition': 0, 'offset': 386, 'timestamp': 1678484825902, 'timestampType': 'CREATE_TIME', 'value': 'bTQ=', 'headers': []}, {'topic': 'myTopic', 'partition': 0, 'offset': 387, 'timestamp': 1678484827810, 'timestampType': 'CREATE_TIME', 'value': 'bTU=', 'headers': []}]}} +Event Source: aws:kafka +Event Source ARN: arn:aws:kafka:us-west-2:123456789012:cluster/MSKWorkshopCluster/a93759a9-c9d0-4952-984c-492c6bfa2be8-13 +Bootstrap Servers: b-1.mskworkshopcluster.z9kc4f.c13.kafka.us-west-2.amazonaws.com:9098,b-3.mskworkshopcluster.z9kc4f.c13.kafka.us-west-2.amazonaws.com:9098,b-2.mskworkshopcluster.z9kc4f.c13.kafka.us-west-2.amazonaws.com:9098 +Records: {'myTopic-0': [{'topic': 'myTopic', 'partition': 0, 'offset': 383, 'timestamp': 1678484822068, 'timestampType': 'CREATE_TIME', 'value': 'bTE=', 'headers': []}, {'topic': 'myTopic', 'partition': 0, 'offset': 384, 'timestamp': 1678484823448, 'timestampType': 'CREATE_TIME', 'value': 'bTI=', 'headers': []}, {'topic': 'myTopic', 'partition': 0, 'offset': 385, 'timestamp': 1678484824763, 'timestampType': 'CREATE_TIME', 'value': 'bTM=', 'headers': []}, {'topic': 'myTopic', 'partition': 0, 'offset': 386, 'timestamp': 1678484825902, 'timestampType': 'CREATE_TIME', 'value': 'bTQ=', 'headers': []}, {'topic': 'myTopic', 'partition': 0, 'offset': 387, 'timestamp': 1678484827810, 'timestampType': 'CREATE_TIME', 'value': 'bTU=', 'headers': []}]} +Current Record: [{'topic': 'myTopic', 'partition': 0, 'offset': 383, 'timestamp': 1678484822068, 'timestampType': 'CREATE_TIME', 'value': 'bTE=', 'headers': []}, {'topic': 'myTopic', 'partition': 0, 'offset': 384, 'timestamp': 1678484823448, 'timestampType': 'CREATE_TIME', 'value': 'bTI=', 'headers': []}, {'topic': 'myTopic', 'partition': 0, 'offset': 385, 'timestamp': 1678484824763, 'timestampType': 'CREATE_TIME', 'value': 'bTM=', 'headers': []}, {'topic': 'myTopic', 'partition': 0, 'offset': 386, 'timestamp': 1678484825902, 'timestampType': 'CREATE_TIME', 'value': 'bTQ=', 'headers': []}, {'topic': 'myTopic', 'partition': 0, 'offset': 387, 'timestamp': 1678484827810, 'timestampType': 'CREATE_TIME', 'value': 'bTU=', 'headers': []}]` +******************** +Now printing details of record number: 1 +Topic: myTopic +Partition: 0 +Offset: 383 +Topic: myTopic +Timestamp: 1678484822068 +TimestampType: CREATE_TIME +Key = null +Value = m1 +Now finished printing details of record number: 1 +******************** +******************** +Now printing details of record number: 2 +Topic: myTopic +Partition: 0 +Offset: 384 +Topic: myTopic +Timestamp: 1678484823448 +TimestampType: CREATE_TIME +Key = null +Value = m2 +Now finished printing details of record number: 2 +******************** +******************** +Now printing details of record number: 3 +Topic: myTopic +Partition: 0 +Offset: 385 +Topic: myTopic +Timestamp: 1678484824763 +TimestampType: CREATE_TIME +Key = null +Value = m3 +Now finished printing details of record number: 3 +******************** +******************** +Now printing details of record number: 4 +Topic: myTopic +Partition: 0 +Offset: 386 +Topic: myTopic +Timestamp: 1678484825902 +TimestampType: CREATE_TIME +Key = null +Value = m4 +Now finished printing details of record number: 4 +******************** +******************** +Now printing details of record number: 5 +Topic: myTopic +Partition: 0 +Offset: 387 +Topic: myTopic +Timestamp: 1678484827810 +TimestampType: CREATE_TIME +Key = null +Value = m5 +Now finished printing details of record number: 5 +******************** +END RequestId: 5c10310a-abf9-416e-b017-697d2c3ba097 +REPORT RequestId: 5c10310a-abf9-416e-b017-697d2c3ba097 Init Duration: 6.68 ms Duration: 1502.83 ms Billed Duration: 1503 ms Memory Size: 128 MB Max Memory Used: 128 MB + +## Cleanup + +1. Delete the stack + ```bash + sam delete + ``` + +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/msk-lambda-iam-python-sam/events/event.json b/msk-lambda-iam-python-sam/events/event.json new file mode 100644 index 0000000000..d76b4b0152 --- /dev/null +++ b/msk-lambda-iam-python-sam/events/event.json @@ -0,0 +1,54 @@ +{ + "eventSource": "aws:kafka", + "eventSourceArn": "arn:aws:kafka:us-west-2:123456789012:cluster/MSKWorkshopCluster/a93759a9-c9d0-4952-984c-492c6bfa2be8-13", + "bootstrapServers": "b-1.mskworkshopcluster.z9kc4f.c13.kafka.us-west-2.amazonaws.com:9098,b-3.mskworkshopcluster.z9kc4f.c13.kafka.us-west-2.amazonaws.com:9098,b-2.mskworkshopcluster.z9kc4f.c13.kafka.us-west-2.amazonaws.com:9098", + "records": { + "myTopic-0": [ + { + "topic": "myTopic", + "partition": 0, + "offset": 383, + "timestamp": 1678484822068, + "timestampType": "CREATE_TIME", + "value": "bTE=", + "headers": [] + }, + { + "topic": "myTopic", + "partition": 0, + "offset": 384, + "timestamp": 1678484823448, + "timestampType": "CREATE_TIME", + "value": "bTI=", + "headers": [] + }, + { + "topic": "myTopic", + "partition": 0, + "offset": 385, + "timestamp": 1678484824763, + "timestampType": "CREATE_TIME", + "value": "bTM=", + "headers": [] + }, + { + "topic": "myTopic", + "partition": 0, + "offset": 386, + "timestamp": 1678484825902, + "timestampType": "CREATE_TIME", + "value": "bTQ=", + "headers": [] + }, + { + "topic": "myTopic", + "partition": 0, + "offset": 387, + "timestamp": 1678484827810, + "timestampType": "CREATE_TIME", + "value": "bTU=", + "headers": [] + } + ] + } +} \ No newline at end of file diff --git a/msk-lambda-iam-python-sam/example-pattern.json b/msk-lambda-iam-python-sam/example-pattern.json new file mode 100644 index 0000000000..4bff6743a9 --- /dev/null +++ b/msk-lambda-iam-python-sam/example-pattern.json @@ -0,0 +1,89 @@ +{ + "title": "AWS Lambda function subscribed to an Amazon MSK topic using IAM auth", + "description": "Creates a Lambda function that uses an Amazon MSK topic as an event source with IAM authentication.", + "language": "Python", + "level": "200", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This pattern provides a Lambda function along with an Event Source Mapping to a Kafka topic.", + "It requires that you already have an Amazon Managed Streaming for Kafka(Amazon MSK) cluster setup with a topic created. If you don't already have an MSK cluster ", + "you can use the example in this pattern https://serverlessland.com/patterns/msk-cfn-sasl-lambda (linked in the resources) to deploy a cluster.", + "This pattern works with either a Provisioned or Serverless MSK cluster as long as the cluster is configured to use IAM authentication. ", + "For detailed deployment instructions instructions see the README " + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/msk-lambda-python-sam", + "templateURL": "serverless-patterns/msk-lambda-python-sam", + "projectFolder": "msk-lambda-python-sam", + "templateFile": "template.yml" + } + }, + "resources": { + "bullets": [ + { + "text": "Amazon MSK Cluster pattern", + "link": "https://serverlessland.com/patterns/msk-cfn-sasl-lambda" + }, + { + "text": "Using AWS Lambda with Amazon MSK", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/with-msk.html" + }, + { + "text": "AWS CloudFormation Provisioned MSK cluster reference", + "link": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-msk-cluster.html" + }, + { + "text": "AWS CloudFormation Serverless MSK cluster reference", + "link": "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-msk-serverlesscluster.html" + } + ] + }, + "deploy": { + "text": [ + "sam deploy --guided" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the template: sam delete." + ] + }, + "authors": [ + { + "name": "Vaibhav Jain", + "bio": "AWS - Sr. Application Architect", + "image": "https://media.licdn.com/dms/image/C4E03AQEqzZWHGT4dBQ/profile-displayphoto-shrink_800_800/0/1580165399872?e=1687392000&v=beta&t=zdxENLnqCpqCz9i1Uf5Yx4YXlR9EYvgxP8N5UTsy6J8", + "linkedin": "https://www.linkedin.com/in/vaibhavjainv/" + }, + { + "name": "Paveen Allam", + "bio": "Senior Solutions Architect", + "image": "https://www.fintail.me/images/pa.jpg", + "linkedin": "https://www.linkedin.com/in/pallam/" + }, + { + "name": "Suraj Tripathi", + "bio": "AWS - AppDev Cloud Consultant", + "linkedin": "https://www.linkedin.com/in/suraj-tripathi-01b49a140/" + }, + { + "name": "Adam Wagner", + "bio": "AWS - Principal Serverless Solutions Architect", + "linkedin": "https://www.linkedin.com/in/adam-wagner-4bb412/" + }, + { + "name": "Indranil Banerjee", + "bio": "AWS - Senior Solutions Architect", + "linkedin": "https://www.linkedin.com/in/indranil-banerjee-b00a261/" + } + ] +} diff --git a/msk-lambda-iam-python-sam/template.yaml b/msk-lambda-iam-python-sam/template.yaml new file mode 100644 index 0000000000..37b55e0473 --- /dev/null +++ b/msk-lambda-iam-python-sam/template.yaml @@ -0,0 +1,82 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + simple_kafka_consumer_sam + + Sample SAM Template for simple_kafka_consumer_sam + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Timeout: 15 + +Resources: + LambdaMSKConsumerPythonFunction: + Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + Properties: + CodeUri: HandlerKafka/ + Handler: app.lambda_handler + Runtime: python3.9 + Architectures: + - x86_64 + Events: + MSKEvent: + Type: MSK + Properties: + StartingPosition: LATEST + BatchSize: 10 + MaximumBatchingWindowInSeconds: 300 + Stream: !Join [ '', ["arn:", "aws:", "kafka:", !Ref "AWS::Region" , ":" ,!Ref "AWS::AccountId", ":", "cluster/", !Ref MSKClusterName, "/" , !Ref MSKClusterId] ] + Topics: + - !Ref MSKTopic + Policies: + - Statement: + - Sid: KafkaClusterPermissionsPolicy + Effect: Allow + Action: + - kafka-cluster:Connect + - kafka-cluster:DescribeGroup + - kafka-cluster:DescribeCluster + - kafka-cluster:AlterCluster + - kafka-cluster:AlterClusterDynamicConfiguration + - kafka-cluster:WriteDataIdempotently + - kafka-cluster:AlterGroup + - kafka-cluster:DescribeTopic + - kafka-cluster:ReadData + - kafka-cluster:DescribeClusterDynamicConfiguration + Resource: + - !Join ['', ["arn:", "aws:", "kafka:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":", "cluster/", !Ref MSKClusterName, "/" , !Ref MSKClusterId]] + - !Join ['', ["arn:", "aws:", "kafka:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":", "topic/", !Ref MSKClusterName, "/" , !Ref MSKClusterId, "/*"]] + - !Join ['', ["arn:", "aws:", "kafka:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":", "group/", !Ref MSKClusterName, "/" , !Ref MSKClusterId, "/*"]] + + - Sid: KafkaPermissionsPolicy + Effect: Allow + Action: + - kafka:DescribeClusterV2 + - kafka:GetBootstrapBrokers + Resource: '*' + + - Sid: EC2PermissionsPolicy + Effect: Allow + Action: + - ec2:DescribeSecurityGroups + - ec2:DescribeSubnets + - ec2:DescribeVpcs + - ec2:CreateNetworkInterface + - ec2:DescribeNetworkInterfaces + - ec2:DeleteNetworkInterface + Resource: '*' +Parameters: + MSKClusterName: + Type: String + Description: Enter the name of the MSK Cluster + MSKClusterId: + Type: String + Description: Enter the ID of the MSK Cluster + MSKTopic: + Type: String + Description: Enter the name of the MSK Topic +Outputs: + LambdaMSKConsumerPythonFunction: + Description: "Topic Consumer Lambda Function ARN" + Value: !GetAtt LambdaMSKConsumerPythonFunction.Arn \ No newline at end of file diff --git a/normalizer-pattern-cdk/example-pattern.json b/normalizer-pattern-cdk/example-pattern.json index a9b8c853e7..bfb7dfdd38 100644 --- a/normalizer-pattern-cdk/example-pattern.json +++ b/normalizer-pattern-cdk/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/public-alb-private-api-terraform/example-pattern.json b/public-alb-private-api-terraform/example-pattern.json index f1aa29bd6e..0f63fb266b 100644 --- a/public-alb-private-api-terraform/example-pattern.json +++ b/public-alb-private-api-terraform/example-pattern.json @@ -39,7 +39,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/public-nlb-or-alb-private-api-cfn/example-pattern.json b/public-nlb-or-alb-private-api-cfn/example-pattern.json index 94608f902b..5f01048526 100644 --- a/public-nlb-or-alb-private-api-cfn/example-pattern.json +++ b/public-nlb-or-alb-private-api-cfn/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/rds-sns-event-notification-terraform/README.md b/rds-sns-event-notification-terraform/README.md new file mode 100644 index 0000000000..78c9eef555 --- /dev/null +++ b/rds-sns-event-notification-terraform/README.md @@ -0,0 +1,74 @@ +# Amazon RDS to Amazon SNS + +RDS Event Subscriptions allow users to configure notifications for RDS Events (provided through an SNS topic). This template configures an event subscription for failure, low storage, and availability event categories for RDS Instances. + +Learn more about this pattern at Serverless Land Patterns:https://serverlessland.com/patterns/rds-sns-event-notification-terraform + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +- [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +- [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +- [Terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/aws-get-started) installed +- [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed +- [Create an RDS Instance and copy Name of RDS Instance somewhere in notes. You will need it during template deployment](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_CreateDBInstance.html#USER_CreateDBInstance.Creating) + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd terraform-rds-sns-event-notification + ``` +1. From the command line, initialize terraform to to downloads and installs the providers defined in the configuration: + ``` + terraform init + ``` +1. From the command line, apply the configuration in the main.tf file: + ``` + terraform apply + ``` +1. During the prompts: + + - Enter the desired AWS Account + - Provide name of RDS Instance you created during Deployment instructions + - Provide your email address to receive notification from Amazon SNS + - Enter yes + +1. Note the outputs from the deployment process. These contain the resource names and/or ARNs which are used for testing. + +## How it works + +RDS Event Subscriptions allow users to configure notifications for RDS Events (provided through an SNS topic). This template configures an event subscription for failure, low storage, and availability event categories for RDS Instances. + +## Testing + +Once the terraform deployment is successful, first thing to do is to confirm the Email subscription. You will receive an email to confirm it. Then go to RDS console. Select the RDS Instance you have created. Stop the Instance and Restart it again. You will receive a notification related to it on your Email Address. Moving forward, you will receive failure, low storage, and availability events that happen on your RDS Instance. + +## Cleanup + +1. Change directory to the pattern directory: + ``` + cd terraform-rds-sns-event-notification + ``` +1. Delete all created resources by terraform + ```bash + terraform destroy + ``` +1. During the prompts: + * Enter yes +1. Confirm all created resources has been deleted + ```bash + terraform show + ``` + +--- + +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/rds-sns-event-notification-terraform/example-pattern.json b/rds-sns-event-notification-terraform/example-pattern.json new file mode 100644 index 0000000000..cc83ac9d80 --- /dev/null +++ b/rds-sns-event-notification-terraform/example-pattern.json @@ -0,0 +1,57 @@ +{ + "title": "Amazon RDS instance event notification to Amazon SNS", + "description": "Amazon RDS event subscriptions allow users to configure notifications for RDS events (provided through an SNS topic).", + "language": "YAML", + "level": "200", + "framework": "Terraform", + "introBox": { + "headline": "How it works", + "text": [ + "This template configures an event subscription for failure, low storage, and availability event categories for RDS Instances." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/rds-sns-event-notification-terraform", + "templateURL": "serverless-patterns/rds-sns-event-notification-terraform", + "projectFolder": "rds-sns-event-notification-terraform", + "templateFile": "main.tf" + } + }, + "resources": { + "bullets": [ + { + "text": "Working with Amazon RDS event notification", + "link": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_Events.html" + }, + { + "text": "Creating an Amazon RDS DB instance", + "link": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_CreateDBInstance.html#USER_CreateDBInstance.Creating" + } + ] + }, + "deploy": { + "text": [ + "terraform init", + "terraform apply" + ] + }, + "testing": { + "text": ["See the GitHub repo for detailed testing instructions."] + }, + "cleanup": { + "text": [ + "Delete the stack: terraform destroy.", + "Confirm the stack has been deleted: terraform show." + ] + }, + "authors": [ + { + "name": "Makendran G", + "image": "https://drive.google.com/file/d/1mUObnbmn52UWL-Zn39EpgpneiBNv3LCN/view?usp=sharing", + "bio": "Cloud Support Engineer @ AWS", + "linkedin": "https://www.linkedin.com/in/makendran", + "twitter": "@MakendranG" + } + ] +} diff --git a/rds-sns-event-notification-terraform/main.tf b/rds-sns-event-notification-terraform/main.tf new file mode 100644 index 0000000000..6a52a2eee2 --- /dev/null +++ b/rds-sns-event-notification-terraform/main.tf @@ -0,0 +1,84 @@ +provider "aws" { + region = "us-east-1" +} + +variable "rds_instance_name" { + description = "Provide name of your existing RDS Instance for which you want to receive event notifications" +} + +variable "sns_endpoint" { + description = "Provide your email address to receive notification from SNS" +} + +variable "aws_account_id" { + description = "Your AWS Account ID" +} + +resource "aws_sns_topic" "sns_for_rds_event_subscription" { + name = "rds-subscription-topic" +} + +resource "aws_sns_topic_subscription" "sns_subscription" { + topic_arn = aws_sns_topic.sns_for_rds_event_subscription.arn + protocol = "email" + endpoint = var.sns_endpoint +} + +resource "aws_sns_topic_policy" "sns_topic_policy" { + arn = aws_sns_topic.sns_for_rds_event_subscription.arn + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Sid = "__default_statement_ID" + Effect = "Allow" + Principal = { AWS = var.aws_account_id } + Action = [ + "SNS:GetTopicAttributes", + "SNS:SetTopicAttributes", + "SNS:AddPermission", + "SNS:RemovePermission", + "SNS:DeleteTopic", + "SNS:Subscribe", + "SNS:ListSubscriptionsByTopic", + "SNS:Publish", + "SNS:Receive", + ] + Resource = aws_sns_topic.sns_for_rds_event_subscription.arn + Condition = { + StringEquals = { + "AWS:SourceOwner" = var.aws_account_id + } + } + }, + { + Sid = "TrustRDSToPublishEventsToMyTopic" + Effect = "Allow" + Principal = { Service = "events.rds.amazonaws.com" } + Action = "sns:Publish" + Resource = aws_sns_topic.sns_for_rds_event_subscription.arn + }, + ] + }) +} + +resource "aws_db_event_subscription" "rds_event_subscription" { + name = "RDS-Event-Subscription" + sns_topic = aws_sns_topic.sns_for_rds_event_subscription.arn + source_ids = [var.rds_instance_name] + source_type = "db-instance" + event_categories = [ + "failure", + "low storage", + "availability", + ] +} + +output "sns_topic_name" { + value = aws_sns_topic.sns_for_rds_event_subscription.name + description = "SNS topic name" +} + +output "rds_instance_name" { + value = var.rds_instance_name +} diff --git a/rds-sns-event-notification/example-pattern.json b/rds-sns-event-notification/example-pattern.json index 194b6e420b..1bb37384d3 100644 --- a/rds-sns-event-notification/example-pattern.json +++ b/rds-sns-event-notification/example-pattern.json @@ -34,7 +34,7 @@ "text": ["sam deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: sam delete."] diff --git a/route53-alb-fargate-cdk-dotnet/README.md b/route53-alb-fargate-cdk-dotnet/README.md index 775d5b8cb9..0aecde4c28 100644 --- a/route53-alb-fargate-cdk-dotnet/README.md +++ b/route53-alb-fargate-cdk-dotnet/README.md @@ -16,7 +16,7 @@ Learn more about this pattern at Serverless Land Patterns: https://serverlesslan * [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. * [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -* [.NET 6](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) installed +* [.NET 7](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) installed * [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) (AWS CDK) installed * [**Domain name**](https://en.wikipedia.org/wiki/Domain_name) - This is required to expose the API with a custom domain name diff --git a/route53-alb-fargate-cdk-dotnet/example-pattern.json b/route53-alb-fargate-cdk-dotnet/example-pattern.json index 2900ea9b66..5bb5a441f3 100644 --- a/route53-alb-fargate-cdk-dotnet/example-pattern.json +++ b/route53-alb-fargate-cdk-dotnet/example-pattern.json @@ -24,17 +24,17 @@ }, "deploy": { "text": [ - "See the Github repo for detailed deployment instructions." + "See the GitHub repo for detailed deployment instructions." ] }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { "text": [ - "See the Github repo for detailed cleanup instructions." + "See the GitHub repo for detailed cleanup instructions." ] }, "authors": [ diff --git a/s3-eventbridge-direct-java/example-pattern.json b/s3-eventbridge-direct-java/example-pattern.json index 91ade0e1a7..5097a11bc3 100644 --- a/s3-eventbridge-direct-java/example-pattern.json +++ b/s3-eventbridge-direct-java/example-pattern.json @@ -36,7 +36,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/s3-eventbridge-ecs/example-pattern.json b/s3-eventbridge-ecs/example-pattern.json index 88e653f8a9..b30dc94eff 100644 --- a/s3-eventbridge-ecs/example-pattern.json +++ b/s3-eventbridge-ecs/example-pattern.json @@ -37,12 +37,12 @@ }, "deploy": { "text": [ - "See the Github repo for detailed deployment and testing instructions." + "See the GitHub repo for detailed deployment and testing instructions." ] }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/s3-eventbridge-lambda-cdk/example-pattern.json b/s3-eventbridge-lambda-cdk/example-pattern.json index 8e9104b043..c209542e1f 100644 --- a/s3-eventbridge-lambda-cdk/example-pattern.json +++ b/s3-eventbridge-lambda-cdk/example-pattern.json @@ -40,7 +40,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/s3-eventbridge-rust-sam/example-pattern.json b/s3-eventbridge-rust-sam/example-pattern.json index 0e5da1fda8..3000a9d3db 100644 --- a/s3-eventbridge-rust-sam/example-pattern.json +++ b/s3-eventbridge-rust-sam/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/s3-eventbridge-sfn-cdk/example-pattern.json b/s3-eventbridge-sfn-cdk/example-pattern.json index 2ee61c15c6..eb33db22ef 100644 --- a/s3-eventbridge-sfn-cdk/example-pattern.json +++ b/s3-eventbridge-sfn-cdk/example-pattern.json @@ -36,7 +36,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk delete."] diff --git a/s3-eventbridge-sfn/example-pattern.json b/s3-eventbridge-sfn/example-pattern.json index ed25fef5af..34db1dcc26 100644 --- a/s3-eventbridge-sfn/example-pattern.json +++ b/s3-eventbridge-sfn/example-pattern.json @@ -40,7 +40,7 @@ "text": ["sam deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: sam delete."] diff --git a/s3-eventbridge-sns-sam/README.md b/s3-eventbridge-sns-sam/README.md new file mode 100644 index 0000000000..687085d346 --- /dev/null +++ b/s3-eventbridge-sns-sam/README.md @@ -0,0 +1,65 @@ +# S3 to EventBridge to SNS + +Publish events directly from S3 to EventBridge and send notifications to SNS when an object is created. This template creates an S3 bucket that publishes events to Amazon EventBridge. When an object is uploaded to the bucket, the EventBridge is triggered and a SNS notification is sent. + +Learn more about this pattern at Serverless Land Patterns:https://serverlessland.com/patterns/s3-eventbridge-sns-sam + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +- [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +- [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +- [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd s3-eventbridge-sam + ``` +1. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: + ``` + sam deploy --guided --capabilities CAPABILITY_NAMED_IAM + ``` +1. You can also use AWS CloudFormation console and paste the template.yml file in the designer and deploy it by passing the below required parameters. + + - Enter a stack name + +## How it works + +This template creates an S3 bucket that publishes events to Amazon EventBridge, allows you to upload objects to that bucket, and will send you notifications from EventBridge to SNS when an object is created in that bucket. + +## Testing + +1. Subscribe your email address to the SNS topic: + ```bash + aws sns subscribe --topic-arn ENTER_YOUR_TOPIC_ARN --protocol email --notification-endpoint ENTER_YOUR_EMAIL_ADDRESS + ``` +1. Click the confirmation link delivered to your email to verify the endpoint. + +1. Upload an object to the S3 bucket created by the deployment. You can also use the below command to upload a file: + ```bash + aws s3 cp README.md s3://ENTER_YOUR_S3_BUCKET_NAME + ``` +1. The notification message is delivered to your email address. + +## Cleanup + +1. Delete all files from the S3 bucket + +1. Delete the stack + ```bash + sam delete + ``` + +--- + +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/s3-eventbridge-sns-sam/example-pattern.json b/s3-eventbridge-sns-sam/example-pattern.json new file mode 100644 index 0000000000..28949eb33a --- /dev/null +++ b/s3-eventbridge-sns-sam/example-pattern.json @@ -0,0 +1,55 @@ +{ + "title": "Amazon S3 to Amazon EventBridge to Amazon SNS", + "description": "Publish events directly from S3 to EventBridge and send notifications to SNS when an object is created. This template creates an S3 bucket that publishes events to Amazon EventBridge. When an object is uploaded to the bucket, the EventBridge is triggered and a SNS notification is sent.", + "language": "", + "level": "200", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This template creates an S3 bucket that publishes events to Amazon EventBridge, allows you to upload objects to that bucket, and will send you notifications from EventBridge to SNS when an object is created in that bucket." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/s3-eventbridge-sam", + "templateURL": "serverless-patterns/s3-eventbridge-sam", + "projectFolder": "s3-eventbridge-sam", + "templateFile": "template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Use Amazon EventBridge to Build Decoupled, Event-Driven Architectures", + "link": "https://serverlessland.com/learn/eventbridge" + }, + { + "text": "Use Amazon S3 Event Notifications with Amazon EventBridge", + "link": "https://aws.amazon.com/blogs/aws/new-use-amazon-s3-event-notifications-with-amazon-eventbridge/" + }, + { + "text": "Reducing custom code by using advanced rules in Amazon EventBridge", + "link": "https://aws.amazon.com/blogs/compute/reducing-custom-code-by-using-advanced-rules-in-amazon-eventbridge/" + } + ] + }, + "deploy": { + "text": ["sam deploy --guided"] + }, + "testing": { + "text": ["See the GitHub repo for detailed testing instructions."] + }, + "cleanup": { + "text": ["Delete the stack: sam delete"] + }, + "authors": [ + { + "name": "Makendran G", + "image": "https://drive.google.com/file/d/1mUObnbmn52UWL-Zn39EpgpneiBNv3LCN/view?usp=sharing", + "bio": "Cloud Support Engineer @ AWS", + "linkedin": "https://www.linkedin.com/in/makendran", + "twitter": "@MakendranG" + } + ] +} \ No newline at end of file diff --git a/s3-eventbridge-sns-sam/template.yaml b/s3-eventbridge-sns-sam/template.yaml new file mode 100644 index 0000000000..f9db56f97b --- /dev/null +++ b/s3-eventbridge-sns-sam/template.yaml @@ -0,0 +1,57 @@ +AWSTemplateFormatVersion: 2010-09-09 +Resources: + MyS3Bucket: + Type: 'AWS::S3::Bucket' + Properties: + BucketName: !Sub 'serverlessland-${AWS::StackName}' + NotificationConfiguration: + EventBridgeConfiguration: + EventBridgeEnabled: 'true' + MySNSTopic: + Type: 'AWS::SNS::Topic' + Properties: + TopicName: MySNSTopic + MyEventRule: + Type: 'AWS::Events::Rule' + Properties: + Description: !Sub 'Object create events on bucket s3://${MyS3Bucket}' + EventPattern: + source: + - aws.s3 + detail-type: + - Object Created + detail: + bucket: + name: + - !Ref MyS3Bucket + State: ENABLED + Targets: + - Arn: !Sub 'arn:aws:sns:${AWS::Region}:${AWS::AccountId}:MySNSTopic' + Id: MySNSTopicTarget + MySNSTopicPolicy: + Type: 'AWS::SNS::TopicPolicy' + Properties: + PolicyDocument: + Version: 2012-10-17 + Statement: + - Sid: AWSEventsPermission + Effect: Allow + Principal: + Service: events.amazonaws.com + Action: 'sns:Publish' + Resource: !Ref MySNSTopic + Condition: + ArnEquals: + 'aws:SourceArn': !GetAtt MyEventRule.Arn + Topics: + - !Ref MySNSTopic +Outputs: + S3Bucket: + Value: !Ref MyS3Bucket + Description: The S3 Bucket + SNSTopicARN: + Value: !Ref MySNSTopic + Description: The SNS Topic ARN + EventBridgeRule: + Value: !Ref MyEventRule + Description: The EventBridge Rule Name diff --git a/s3-lambda-dotnet/.gitignore b/s3-lambda-dotnet/.gitignore new file mode 100644 index 0000000000..4507fc7d24 --- /dev/null +++ b/s3-lambda-dotnet/.gitignore @@ -0,0 +1,402 @@ +# Created by https://www.toptal.com/developers/gitignore/api/csharp +# Edit at https://www.toptal.com/developers/gitignore?templates=csharp + +### Csharp ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml + +# End of https://www.toptal.com/developers/gitignore/api/csharp \ No newline at end of file diff --git a/s3-lambda-dotnet/ImageResize.sln b/s3-lambda-dotnet/ImageResize.sln new file mode 100644 index 0000000000..ffcd6c86c5 --- /dev/null +++ b/s3-lambda-dotnet/ImageResize.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33815.320 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageResize", "ImageResize\ImageResize.csproj", "{FADD6CE5-EDF7-4BFE-B8F5-E84CD788D6DE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FADD6CE5-EDF7-4BFE-B8F5-E84CD788D6DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FADD6CE5-EDF7-4BFE-B8F5-E84CD788D6DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FADD6CE5-EDF7-4BFE-B8F5-E84CD788D6DE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FADD6CE5-EDF7-4BFE-B8F5-E84CD788D6DE}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3B666636-4024-4B31-80A6-CB25C208D7F9} + EndGlobalSection +EndGlobal diff --git a/s3-lambda-dotnet/ImageResize/Function.cs b/s3-lambda-dotnet/ImageResize/Function.cs new file mode 100644 index 0000000000..41dbf4305f --- /dev/null +++ b/s3-lambda-dotnet/ImageResize/Function.cs @@ -0,0 +1,127 @@ +using System.IO; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.PixelFormats; +using Amazon.S3.Model; +using SixLabors.ImageSharp.Formats.Jpeg; +using Amazon.Lambda.Core; +using Amazon.Lambda.S3Events; +using Amazon.S3; +using Amazon.S3.Util; + +// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class. +[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] + +namespace ImageResize; + +public class Function +{ + IAmazonS3 S3Client { get; set; } + + /// + /// Default constructor. This constructor is used by Lambda to construct the instance. When invoked in a Lambda environment + /// the AWS credentials will come from the IAM role associated with the function and the AWS region will be set to the + /// region the Lambda function is executed in. + /// + public Function() + { + S3Client = new AmazonS3Client(); + } + + /// + /// Constructs an instance with a preconfigured S3 client. This can be used for testing outside of the Lambda environment. + /// + /// + public Function(IAmazonS3 s3Client) + { + this.S3Client = s3Client; + } + + public async Task FunctionHandler(S3Event evnt, ILambdaContext context) + { + string[] fileExtentions = new string[] { ".jpg", ".jpeg" }; + var s3Event = evnt.Records?[0].S3; + if (s3Event == null) + { + return null; + } + + try + { + foreach (var record in evnt.Records) + { + LambdaLogger.Log("----> File: " + record.S3.Object.Key); + if (!fileExtentions.Contains(Path.GetExtension(record.S3.Object.Key).ToLower())) + { + LambdaLogger.Log("File Extension is not supported - " + s3Event.Object.Key); + continue; + } + + string suffix = Path.GetExtension(record.S3.Object.Key).ToLower(); + Stream imageStream = new MemoryStream(); + using (var objectResponse = await S3Client.GetObjectAsync(record.S3.Bucket.Name, record.S3.Object.Key)) + { + using (Stream responseStream = objectResponse.ResponseStream) + { + using (var image = Image.Load(responseStream)) + { + // Create B&W thumbnail + image.Mutate(ctx => ctx.Grayscale().Resize(200, 200)); + image.Save(imageStream, new JpegEncoder()); + imageStream.Seek(0, SeekOrigin.Begin); + } + } + } + + // Creating a new S3 ObjectKey for the thumbnails + string thumbnailObjectKey = null; + int endSlash = record.S3.Object.Key.ToLower().LastIndexOf("/"); + if (endSlash > 0) + { + string S3ObjectName = record.S3.Object.Key.ToLower().Substring(endSlash + 1); + int beginSlash = 0; + if (endSlash > 0) + { + beginSlash = record.S3.Object.Key.ToLower().Substring(0, endSlash - 1).LastIndexOf("/"); + if (beginSlash > 0) + { + thumbnailObjectKey = + record.S3.Object.Key.ToLower().Substring(0, beginSlash) + + "thumbnails/" + + S3ObjectName; + } + else + { + thumbnailObjectKey = "thumbnails/" + S3ObjectName; + } + } + } + else + { + thumbnailObjectKey = "thumbnails/" + record.S3.Object.Key.ToLower(); + } + + LambdaLogger.Log("----> Thumbnail file Key: " + thumbnailObjectKey); + + await S3Client.PutObjectAsync(new PutObjectRequest + { + BucketName = record.S3.Bucket.Name, + Key = thumbnailObjectKey, + InputStream = imageStream + }); + } + + LambdaLogger.Log("Processed " + evnt.Records.Count.ToString()); + + return null; + } + catch (Exception e) + { + context.Logger.LogLine($"Error getting object {s3Event.Object.Key} from bucket {s3Event.Bucket.Name}"); + context.Logger.LogLine($"Make sure they exist and your bucket is in the same region as this function"); + context.Logger.LogLine(e.Message); + context.Logger.LogLine(e.StackTrace); + throw; + } + } +} \ No newline at end of file diff --git a/s3-lambda-dotnet/ImageResize/ImageResize.csproj b/s3-lambda-dotnet/ImageResize/ImageResize.csproj new file mode 100644 index 0000000000..77eb3532d0 --- /dev/null +++ b/s3-lambda-dotnet/ImageResize/ImageResize.csproj @@ -0,0 +1,20 @@ + + + net6.0 + enable + enable + true + Lambda + + true + + true + + + + + + + + + \ No newline at end of file diff --git a/s3-lambda-dotnet/ImageResize/Properties/launchSettings.json b/s3-lambda-dotnet/ImageResize/Properties/launchSettings.json new file mode 100644 index 0000000000..e6144efbb2 --- /dev/null +++ b/s3-lambda-dotnet/ImageResize/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "Mock Lambda Test Tool": { + "commandName": "Executable", + "commandLineArgs": "--port 5050", + "workingDirectory": ".\\bin\\$(Configuration)\\net6.0", + "executablePath": "%USERPROFILE%\\.dotnet\\tools\\dotnet-lambda-test-tool-6.0.exe" + } + } +} \ No newline at end of file diff --git a/s3-lambda-dotnet/ImageResize/aws-lambda-tools-defaults.json b/s3-lambda-dotnet/ImageResize/aws-lambda-tools-defaults.json new file mode 100644 index 0000000000..6ce1ffef1f --- /dev/null +++ b/s3-lambda-dotnet/ImageResize/aws-lambda-tools-defaults.json @@ -0,0 +1,26 @@ + +{ + "Information" : [ + "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", + "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", + "dotnet lambda help", + "All the command line options for the Lambda command can be specified in this file." + ], + "profile" : "default", + "region" : "us-east-1", + "configuration" : "Release", + "function-runtime" : "dotnet6", + "function-memory-size" : 256, + "function-timeout" : 30, + "function-handler" : "ImageResize::ImageResize.Function::FunctionHandler", + "framework" : "net6.0", + "function-name" : "ImageResize", + "package-type" : "Zip", + "function-role" : "arn:aws:iam::595982400875:role/ImageResizeLambdaRole", + "function-architecture" : "x86_64", + "function-subnets" : "", + "function-security-groups" : "", + "tracing-mode" : "PassThrough", + "environment-variables" : "", + "image-tag" : "" +} \ No newline at end of file diff --git a/s3-lambda-dotnet/README.md b/s3-lambda-dotnet/README.md new file mode 100644 index 0000000000..5c23b3cf6d --- /dev/null +++ b/s3-lambda-dotnet/README.md @@ -0,0 +1,77 @@ +# AWS Amazon S3 to AWS Lambda - Create a Lambda function that resizes images uploaded to S3 + +The SAM template deploys a .NET 6 Lambda function, an S3 bucket and the IAM resources required to run the application. A Lambda function consumes ObjectCreated events from an Amazon S3 bucket. The function code checks the uploaded file is an image and creates a thumbnail version of the image in the same bucket. + +Learn more about this pattern at Serverless Land Patterns: [https://serverlessland.com/patterns/s3-lambda-dotnet](https://serverlessland.com/patterns/s3-lambda-dotnet) + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed +* [.Net 6.0](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) +* [Docker](https://docs.docker.com/get-docker/) installed and running + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd s3-lambda-dotnet + ``` +1. From the command line, use AWS SAM to build and deploy the AWS resources for the pattern as specified in the template.yml file: + ``` + sam build + sam deploy --guided + ``` +1. During the prompts: + * Enter a stack name + * Enter the desired AWS Region + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run `sam deploy -guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. + +## How it works + +* Use the AWS CLI upload an image to S3 +* If the object is a .jpeg in the /images folder, the code creates a thumbnail and saves it to the target bucket in a new folder, /thumbnails. +* The code assumes that the destination bucket exists and is watching a folder you need to create called "images". + +============================================== + +## Testing + +Run the following S3 CLI command to upload an image to the S3 bucket. Note, you must edit the {SourceBucketName} placeholder with the name of the S3 Bucket. This is provided in the stack outputs. + +```bash +aws s3 cp './images/example.jpeg' s3://{BucketName}/images/example.jpeg +``` + +Run the following command to check that a new thumbnails folder has been created with a new version of the image. + +```bash +aws s3 ls s3://{BucketName}/thumbnails +``` + +## Cleanup + +1. Delete the stack + ```bash + aws cloudformation delete-stack --stack-name STACK_NAME + ``` +1. Confirm the stack has been deleted + ```bash + aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'STACK_NAME')].StackStatus" + ``` +---- +Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/s3-lambda-dotnet/example-pattern.json b/s3-lambda-dotnet/example-pattern.json new file mode 100644 index 0000000000..d49c79423d --- /dev/null +++ b/s3-lambda-dotnet/example-pattern.json @@ -0,0 +1,62 @@ +{ + "title": "Amazon S3 to AWS Lambda with .NET", + "description": "Automate the creation of thumbnail images from new images on Amazon S3 leveraging Lambda and .NET", + "language": ".NET", + "level": "200", + "framework": "SAM", + "services": { + "from": "s3", + "to": "lambda" + }, + "introBox": { + "headline": "How it works", + "text": [ + "This pattern deploys an Amazon S3 bucket for image hosting with an AWS Lambda function that gets invoked when new images are added to the bucket. The Lambda function uses .NET 6 to create a thumbnail of the image and save the thumbnail back to the same S3 bucket." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/s3-lambda-dotnet", + "templateURL": "serverless-patterns/s3-lambda-dotnet", + "projectFolder": "s3-lambda-dotnet", + "templateFile": "s3-lambda-dotnet/template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Using an Amazon S3 trigger to invoke a Lambda function", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/with-s3-example.html" + }, + { + "text": "Building Lambda functions with C#", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/lambda-csharp.html" + } + ] + }, + "deploy": { + "text": [ + "sam build", + "sam deploy --guided" + ] + }, + "testing": { + "text": [ + "See the GitHub repo readme for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Empty the S3 bucket", + "sam delete --stack-name STACK_NAME" + ] + }, + "authors": [ + { + "name": "Garrett Johnson", + "image": "https://grream-rando-7-19-26-bucket.s3.amazonaws.com/images/IMG_1549.jpg", + "bio": "Garrett is a Sr. Solutions Architect at AWS based in Utah.", + "linkedin": "/garrett-johnson-30998247" + } + ] +} diff --git a/s3-lambda-dotnet/images/example.jpeg b/s3-lambda-dotnet/images/example.jpeg new file mode 100644 index 0000000000..031e7e132d Binary files /dev/null and b/s3-lambda-dotnet/images/example.jpeg differ diff --git a/s3-lambda-dotnet/template.yaml b/s3-lambda-dotnet/template.yaml new file mode 100644 index 0000000000..e720e19f35 --- /dev/null +++ b/s3-lambda-dotnet/template.yaml @@ -0,0 +1,47 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: > + .NET Image resizing service + +# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst +Globals: + Function: + Tracing: PassThrough + Timeout: 10 + Runtime: dotnet6 + +Resources: + ## S3 bucket + SourceBucket: + Type: AWS::S3::Bucket + Properties: + BucketName: !Sub "imageresizer-${AWS::AccountId}" + + ResizerFunction: + Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction + Properties: + CodeUri: ImageResize/ + Handler: ImageResize::ImageResize.Function::FunctionHandler + MemorySize: 2048 + Events: + FileUpload: + Type: S3 + Properties: + Bucket: !Ref SourceBucket + Events: s3:ObjectCreated:* + Filter: + S3Key: + Rules: + - Name: suffix + Value: '.jpeg' + Policies: + - S3FullAccessPolicy: + BucketName: !Sub "imageresizer-${AWS::AccountId}/*" + +Outputs: + SourceBucketName: + Value: !Ref SourceBucket + Description: S3 Bucket for object storage + FunctionArn: + Value: !Ref ResizerFunction + Description: ResizerFunction function Arn \ No newline at end of file diff --git a/s3-lambda-mediaconvert/example-pattern.json b/s3-lambda-mediaconvert/example-pattern.json index d713e0faba..bf0552c827 100644 --- a/s3-lambda-mediaconvert/example-pattern.json +++ b/s3-lambda-mediaconvert/example-pattern.json @@ -40,13 +40,13 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { "text": [ "Delete the stack: sam delete.", - "See the Github repo for detailed cleanup instructions." + "See the GitHub repo for detailed cleanup instructions." ] }, "authors": [ diff --git a/s3-lambda-polly-sam/example-pattern.json b/s3-lambda-polly-sam/example-pattern.json index 8b11c99325..9c053336e2 100644 --- a/s3-lambda-polly-sam/example-pattern.json +++ b/s3-lambda-polly-sam/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/s3-lambda-rekognition-cdk/example-pattern.json b/s3-lambda-rekognition-cdk/example-pattern.json index d7c65e9981..5592c92988 100644 --- a/s3-lambda-rekognition-cdk/example-pattern.json +++ b/s3-lambda-rekognition-cdk/example-pattern.json @@ -39,7 +39,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/s3-lambda-rekognition-ddb-cdk/.gitignore b/s3-lambda-rekognition-ddb-cdk/.gitignore new file mode 100644 index 0000000000..f60797b6a9 --- /dev/null +++ b/s3-lambda-rekognition-ddb-cdk/.gitignore @@ -0,0 +1,8 @@ +*.js +!jest.config.js +*.d.ts +node_modules + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/s3-lambda-rekognition-ddb-cdk/.npmignore b/s3-lambda-rekognition-ddb-cdk/.npmignore new file mode 100644 index 0000000000..c1d6d45dcf --- /dev/null +++ b/s3-lambda-rekognition-ddb-cdk/.npmignore @@ -0,0 +1,6 @@ +*.ts +!*.d.ts + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/s3-lambda-rekognition-ddb-cdk/README.md b/s3-lambda-rekognition-ddb-cdk/README.md new file mode 100644 index 0000000000..92483daded --- /dev/null +++ b/s3-lambda-rekognition-ddb-cdk/README.md @@ -0,0 +1,99 @@ +# Detect labels in pictures using Amazon S3, AWS Lambda, Amazon Rekognition, and Amazon DynamoDB with Amazon Cloud Development Kit (AWS CDK) + +This pattern shows how to detect labels using an event driven architecture. + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/ + +# Requirements + +- [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +- [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +- [Node and NPM](https://nodejs.org/en/download/) installed +- [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) (AWS CDK) installed + +## Deployment + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +2. Change directory to the pattern directory: + ``` + cd s3-lambda-rekognition-ddb-cdk + ``` +3. Run below command to install required dependencies: + + ``` + npm install + ``` + +4. Run the command below to bootstrap your account. CDK needs it to deploy + + ``` + cdk bootstrap + ``` + +5. From the command line, run: + ``` + cdk deploy --all + ``` + +The CDK stack outputs the S3 bucket and DynamoDB table names which you can use for testing. + +## Testing + +1. Have an image that Rekognition can generate a labekl. For example a lion from [Wikipedia](https://en.wikipedia.org/wiki/Lion). +2. Run the following command to upload the image to an S3 bucket. Replace `` with your image file name. + Replace `` with the name of the S3 bucket from the CDK outputs. + +``` + aws s3 cp s3://<3BucketName> +``` + +For example: `aws s3 cp ./Lion.jpg s3://s3lambdarekognitionstack-imagesbucketd8e2a22e-123456789012` + +The Lambda function triggers on the S3 object upload, calls Rekognition to get the image labels, and writes the labels to DynamoDB. + +1. Retrieve the Rekognition label output from the DynamoDB table. Replace `` with the name of the DynamoDB table from the CDK outputs. + + ``` + aws dynamodb scan --table-name + ``` + +For example: `aws dynamodb scan --table-name S3LambdaRekognitionStack-rekognitiontable70ADE0DF-1234567890123` + +3. You should see the Rekognition label output from DynamoDB displayed. + +For example: + +``` +{ + "Items": [ + { + "file_name": { + "S": "Lion.jpg" + }, + "labels": { + "S": "{\"Labels\": [{\"Name\": \"Animal\", \"Confidence\": 99.99979400634766, \"Instances\": [], \"Parents\": [], \"Aliases\": [], \"Categories\": [{\"Name\": \"Animals and Pets\"}]}, {\"Name\": \"Lion\", \"Confidence\": 99.99979400634766, \"Instances\": [{\"BoundingBox\": {\"Width\": 0.650372326374054, \"Height\": 0.6257815361022949, \"Left\": 0.23760296404361725, \"Top\": 0.1613311469554901}, \"Confidence\": 99.9444808959961}], \"Parents\": [{\"Name\": \"Animal\"}, {\"Name\": \"Mammal\"}, {\"Name\": \"Wildlife\"}], \"Aliases\": [], \"Categories\": [{\"Name\": \"Animals and Pets\"}]}, {\"Name\": \"Mammal\", \"Confidence\": 99.99979400634766, \"Instances\": [], \"Parents\": [{\"Name\": \"Animal\"}], \"Aliases\": [], \"Categories\": [{\"Name\": \"Animals and Pets\"}]}, {\"Name\": \"Wildlife\", \"Confidence\": 99.99979400634766, \"Instances\": [], \"Parents\": [{\"Name\": \"Animal\"}], \"Aliases\": [], \"Categories\": [{\"Name\": \"Animals and Pets\"}]}], \"LabelModelVersion\": \"3.0\", \"ResponseMetadata\": {\"RequestId\": \"08092028-bb58-49a7-bd5d-07208646e203\", \"HTTPStatusCode\": 200, \"HTTPHeaders\": {\"x-amzn-requestid\": \"08092028-bb58-49a7-bd5d-07208646e203\", \"content-type\": \"application/x-amz-json-1.1\", \"content-length\": \"812\", \"date\": \"Fri, 04 Aug 2023 17:01:31 GMT\"}, \"RetryAttempts\": 0}}" + } + } + ], + "Count": 1, + "ScannedCount": 1, + "ConsumedCapacity": null +} +``` + +## Cleanup + +1. To delete the stack, run: + ```bash + cdk destroy --all + ``` + +--- + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the AWS Pricing page for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## This project was developed with the assistance of Amazon CodeWhisperer diff --git a/s3-lambda-rekognition-ddb-cdk/bin/s3-lambda-rekognition.ts b/s3-lambda-rekognition-ddb-cdk/bin/s3-lambda-rekognition.ts new file mode 100644 index 0000000000..09e1bf8e15 --- /dev/null +++ b/s3-lambda-rekognition-ddb-cdk/bin/s3-lambda-rekognition.ts @@ -0,0 +1,6 @@ +#!/usr/bin/env node +import * as cdk from 'aws-cdk-lib'; +import { S3LambdaRekognitionStack } from '../lib/s3-lambda-rekognition-stack'; + +const app = new cdk.App(); +new S3LambdaRekognitionStack(app, 'S3LambdaRekognitionStack'); diff --git a/s3-lambda-rekognition-ddb-cdk/cdk.json b/s3-lambda-rekognition-ddb-cdk/cdk.json new file mode 100644 index 0000000000..a952dff115 --- /dev/null +++ b/s3-lambda-rekognition-ddb-cdk/cdk.json @@ -0,0 +1,44 @@ +{ + "app": "npx ts-node --prefer-ts-exts bin/s3-lambda-rekognition.ts", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "**/*.d.ts", + "**/*.js", + "tsconfig.json", + "package*.json", + "yarn.lock", + "node_modules", + "test" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false + } +} diff --git a/s3-lambda-rekognition-ddb-cdk/example-pattern.json b/s3-lambda-rekognition-ddb-cdk/example-pattern.json new file mode 100644 index 0000000000..fe0c78550e --- /dev/null +++ b/s3-lambda-rekognition-ddb-cdk/example-pattern.json @@ -0,0 +1,62 @@ +{ + "title": "Amazon S3 triggering AWS Lambda using Amazon Rekognition and writing Amazon DynamoDB", + "description": "Detect labels in pictures using Amazon S3, AWS Lambda, Amazon Rekognition, and Amazon DynamoDB with Amazon Cloud Development Kit (AWS CDK)", + "language": "TypeScript", + "level": "200", + "framework": "CDK", + "introBox": { + "headline": "How it works", + "text": [ + "This sample project demonstrates how to deliver an event-driven mechanism to update DynamoDB for labels in images uploaded to S3.", + "Implemented in CDK." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/s3-lambda-rekognition-ddb-cdk", + "templateURL": "serverless-patterns/s3-lambda-rekognition-ddb-cdk", + "projectFolder": "s3-lambda-rekognition-ddb-cdk/lib", + "templateFile": "s3-lambda-rekognition-stack.ts" + } + }, + "resources": { + "bullets": [ + { + "text": "Using AWS Lambda with Amazon S3", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/with-s3.html" + }, + { + "text": "Amazon Rekognition", + "link": "https://docs.aws.amazon.com/rekognition/latest/dg/what-is.html" + }, + { + "text": "Using AWS Lambda with Amazon DynamoDB", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html" + } + + ] + }, + "deploy": { + "text": [ + "cdk deploy --all" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: cdk destroy --all." + ] + }, + "authors": [ + { + "name": "Chris Peter Francis", + "image": "https://media.licdn.com/dms/image/D5603AQGSCw1U075kjQ/profile-displayphoto-shrink_800_800/0/1679337668977?e=2147483647&v=beta&t=18EMAaL8Qrm0hpxVVR525WEImFw1cyHrASNg3iYw6k4", + "bio": "Chris is a Solution Architect for AWS Energy. Serverless advocate and enthusiast.", + "linkedin": "chrispf" + } + ] +} \ No newline at end of file diff --git a/s3-lambda-rekognition-ddb-cdk/jest.config.js b/s3-lambda-rekognition-ddb-cdk/jest.config.js new file mode 100644 index 0000000000..08263b8954 --- /dev/null +++ b/s3-lambda-rekognition-ddb-cdk/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + testEnvironment: 'node', + roots: ['/test'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest' + } +}; diff --git a/s3-lambda-rekognition-ddb-cdk/lib/rekognitionLambda/lambda_function.py b/s3-lambda-rekognition-ddb-cdk/lib/rekognitionLambda/lambda_function.py new file mode 100644 index 0000000000..e569e9ddca --- /dev/null +++ b/s3-lambda-rekognition-ddb-cdk/lib/rekognitionLambda/lambda_function.py @@ -0,0 +1,26 @@ +# lambda handler +import boto3 +import json +import os + + + +def callRekognition(bucket_name,file_name): + rekognition_client = boto3.client('rekognition') + response = rekognition_client.detect_labels(Image={'S3Object':{'Bucket':bucket_name,'Name':file_name}}) + return response + + +def writeDynamoDB(file_name, labels): + dynamodb_client = boto3.client('dynamodb') + return dynamodb_client.put_item(TableName=os.environ['TABLE_NAME'], Item={'file_name': {'S':file_name}, 'labels': {'S':json.dumps(labels)}}) + + +def lambda_handler(event, context): + # S3 event + s3_event = event['Records'][0]['s3'] + bucket_name = s3_event['bucket']['name'] + file_name = s3_event['object']['key'] + response = callRekognition(bucket_name,file_name) + writeDynamoDB(file_name, response) + diff --git a/s3-lambda-rekognition-ddb-cdk/lib/s3-lambda-rekognition-stack.ts b/s3-lambda-rekognition-ddb-cdk/lib/s3-lambda-rekognition-stack.ts new file mode 100644 index 0000000000..00a404ca76 --- /dev/null +++ b/s3-lambda-rekognition-ddb-cdk/lib/s3-lambda-rekognition-stack.ts @@ -0,0 +1,68 @@ +import { Duration, Stack, StackProps, aws_lambda } from 'aws-cdk-lib'; +import * as s3 from 'aws-cdk-lib/aws-s3'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import * as path from 'path'; +import * as eventsources from 'aws-cdk-lib/aws-lambda-event-sources'; +import * as ddb from 'aws-cdk-lib/aws-dynamodb'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import { CfnOutput } from 'aws-cdk-lib'; +import { Construct } from 'constructs'; + +export class S3LambdaRekognitionStack extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + //create new S3 bucket + const bucket = new s3.Bucket(this, 'images-bucket', { + }) + + + + const dynamoTable = new ddb.Table(this, 'rekognition-table', {partitionKey: {name: 'file_name', type: ddb.AttributeType.STRING}}) + + + + const lambdaFunction = new lambda.Function(this, 'lambdaFunction', { + runtime: lambda.Runtime.PYTHON_3_9, + code: lambda.Code.fromAsset(path.join(__dirname,'rekognitionLambda/')), + handler: 'lambda_function.lambda_handler', + environment: { + 'TABLE_NAME': dynamoTable.tableName + } + +}) + +bucket.grantRead(lambdaFunction) +dynamoTable.grantReadWriteData(lambdaFunction) + +// Grant Lambda function to call Rekognition detect labels +lambdaFunction.role?.attachInlinePolicy( + new iam.Policy(this, 'rekognitionDetectLabelsPolicy', { + statements: [ + new iam.PolicyStatement({ + actions: ['rekognition:detectLabels'], + resources: ['*'], + }) + ], + }), +); + + + // add S3 event to lambda function + lambdaFunction.addEventSource(new eventsources.S3EventSource(bucket, { + events: [s3.EventType.OBJECT_CREATED], + })) + + +new CfnOutput(this, 'bucketName', { + value: bucket.bucketName, + description: 'The name of the s3 bucket created.', +}); + +new CfnOutput(this, 'DynamoDBTable', { + value: dynamoTable.tableName, + description: 'The name of the DynamoDB table created.', + }); + + +} +} diff --git a/s3-lambda-rekognition-ddb-cdk/package.json b/s3-lambda-rekognition-ddb-cdk/package.json new file mode 100644 index 0000000000..e8b8e957d6 --- /dev/null +++ b/s3-lambda-rekognition-ddb-cdk/package.json @@ -0,0 +1,27 @@ +{ + "name": "s3-lambda-rekognition", + "version": "0.1.0", + "bin": { + "s3-lambda-rekognition": "bin/s3-lambda-rekognition.js" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "jest", + "cdk": "cdk" + }, + "devDependencies": { + "@types/jest": "^29.4.0", + "@types/node": "18.11.18", + "aws-cdk": "2.63.0", + "jest": "^29.4.1", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.1", + "typescript": "~4.9.4" + }, + "dependencies": { + "aws-cdk-lib": "2.80.0", + "constructs": "^10.0.0", + "util": "^0.12.5" + } +} diff --git a/s3-lambda-rekognition-ddb-cdk/test/s3-lambda-rekognition.test.ts b/s3-lambda-rekognition-ddb-cdk/test/s3-lambda-rekognition.test.ts new file mode 100644 index 0000000000..f3b910e42f --- /dev/null +++ b/s3-lambda-rekognition-ddb-cdk/test/s3-lambda-rekognition.test.ts @@ -0,0 +1,17 @@ +import * as cdk from 'aws-cdk-lib'; +import { Template, Match } from 'aws-cdk-lib/assertions'; +import * as S3LambdaRekognition from '../lib/s3-lambda-rekognition-stack'; + +test('SQS Queue and SNS Topic Created', () => { + const app = new cdk.App(); + // WHEN + const stack = new S3LambdaRekognition.S3LambdaRekognitionStack(app, 'MyTestStack'); + // THEN + + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::SQS::Queue', { + VisibilityTimeout: 300 + }); + template.resourceCountIs('AWS::SNS::Topic', 1); +}); diff --git a/s3-lambda-rekognition-ddb-cdk/tsconfig.json b/s3-lambda-rekognition-ddb-cdk/tsconfig.json new file mode 100644 index 0000000000..fc44377a1e --- /dev/null +++ b/s3-lambda-rekognition-ddb-cdk/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": [ + "es2020" + ], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "./node_modules/@types" + ] + }, + "exclude": [ + "node_modules", + "cdk.out" + ] +} diff --git a/s3-lambda-rekognition-iot-cdk/.gitignore b/s3-lambda-rekognition-iot-cdk/.gitignore new file mode 100644 index 0000000000..f60797b6a9 --- /dev/null +++ b/s3-lambda-rekognition-iot-cdk/.gitignore @@ -0,0 +1,8 @@ +*.js +!jest.config.js +*.d.ts +node_modules + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/s3-lambda-rekognition-iot-cdk/.npmignore b/s3-lambda-rekognition-iot-cdk/.npmignore new file mode 100644 index 0000000000..c1d6d45dcf --- /dev/null +++ b/s3-lambda-rekognition-iot-cdk/.npmignore @@ -0,0 +1,6 @@ +*.ts +!*.d.ts + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/s3-lambda-rekognition-iot-cdk/README.md b/s3-lambda-rekognition-iot-cdk/README.md new file mode 100644 index 0000000000..ffae336f95 --- /dev/null +++ b/s3-lambda-rekognition-iot-cdk/README.md @@ -0,0 +1,76 @@ +# Use event-driven architecture (EDA) to promote worker safety using Amazon S3, AWS Lambda, Amazon Rekognition, and AWS IoT Core + +This pattern demonstrates how to detect if individuals are wearing the correct personal protective equipment (PPE) and sends an alert via IoT Core. This pattern is compatible with the published [AWS IoT Puppy Park workshop](https://catalog.workshops.aws/iot-puppy-park). + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/ + +# Requirements + +- [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +- [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +- [Node and NPM](https://nodejs.org/en/download/) installed +- [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) (AWS CDK) installed + +## Deployment + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +2. Change directory to the pattern directory: + ``` + cd s3-lambda-rekognition-iot-cdk + ``` +3. Run below command to install required dependancies: + + ``` + npm install + ``` + +4. Run the command below to bootstrap your account. CDK needs it to deploy + + ``` + cdk bootstrap + ``` + +5. From the command line, run: + ``` + cdk deploy --all + ``` + +## Testing + +1. In the AWS console, go to the AWS IoT Core service. IN the left sidebar, choose the MQTT test client under Test. Subscribe to the topic that the Lambda function will publish to:`Bittle1/sub`. You are now subscribed to MQTT topic. + +1. Run the command below to upload an image with individuals wearing PPE (or not) to the S3 bucket created in the stack. The bucket name should be: +`ppeiotviolationsstack-imagesbucketXXXX-XXXX`. Ex: "Worker.jpg". + + ``` + aws s3 cp Worker.jpg s3://[S3BucketName]/Worker.jpg + ``` + +2. You should see the message ("khi") in IoT core under the topic ("Bittle1/sub") if there is a violation. If this was set up with the [IoT Puppy Park workshop](https://github.com/novekm/iot-puppy-park), the bittle should perform the 'hi' action. + +## (Optional) Configuration +You can set environment variables for the Lambda function for the following in +`s3-lambda-rekognition-iot-cdk/lib/s3-lambda-rekognition-iot-stack.ts`: + +1. Change the IoT Core Topic +2. Change the IoT Core Message +3. Change the required PPE for alerts +4. Set the Rekognition confidence level + + +## Cleanup + +1. To delete the stack, run: + ```bash + cdk destroy --all + ``` + +--- + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the AWS Pricing page for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +# This project was developed with the assistance of AWS CodeWhisperer diff --git a/s3-lambda-rekognition-iot-cdk/bin/s3-lambda-rekognition-iot.ts b/s3-lambda-rekognition-iot-cdk/bin/s3-lambda-rekognition-iot.ts new file mode 100644 index 0000000000..fa6add727d --- /dev/null +++ b/s3-lambda-rekognition-iot-cdk/bin/s3-lambda-rekognition-iot.ts @@ -0,0 +1,6 @@ +#!/usr/bin/env node +import * as cdk from 'aws-cdk-lib'; +import { PPEIoTViolations } from '../lib/s3-lambda-rekognition-iot-stack'; + +const app = new cdk.App(); +new PPEIoTViolations(app, 'PPEIoTViolationsStack'); diff --git a/s3-lambda-rekognition-iot-cdk/cdk.json b/s3-lambda-rekognition-iot-cdk/cdk.json new file mode 100644 index 0000000000..e9ea642e2c --- /dev/null +++ b/s3-lambda-rekognition-iot-cdk/cdk.json @@ -0,0 +1,44 @@ +{ + "app": "npx ts-node --prefer-ts-exts bin/s3-lambda-rekognition-iot.ts", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "**/*.d.ts", + "**/*.js", + "tsconfig.json", + "package*.json", + "yarn.lock", + "node_modules", + "test" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false + } +} diff --git a/s3-lambda-rekognition-iot-cdk/example-pattern.json b/s3-lambda-rekognition-iot-cdk/example-pattern.json new file mode 100644 index 0000000000..662ccfea63 --- /dev/null +++ b/s3-lambda-rekognition-iot-cdk/example-pattern.json @@ -0,0 +1,62 @@ +{ + "title": "Amazon S3, AWS Lambda, Amazon Rekognition and IoT Core to detect PPE", + "description": "Send a IoT Core message when a worker is not in PPE (Personal Protective Equipment) compliance.", + "language": "TypeScript", + "level": "200", + "framework": "CDK", + "introBox": { + "headline": "How it works", + "text": [ + "This sample project demonstrates how to deliver an event-driven mechanism to send an IoT core message for images uploaded to S3.", + "Implemented in CDK." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/s3-lambda-rekognition-iot-cdk", + "templateURL": "serverless-patterns/s3-lambda-rekognition-iot-cdk", + "projectFolder": "s3-lambda-rekognition-iot-cdk", + "templateFile": "s3-lambda-rekognition-iot-stack.ts" + } + }, + "resources": { + "bullets": [ + { + "text": "Using AWS Lambda with Amazon S3", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/with-s3.html" + }, + { + "text": "Rekognition PPE API", + "link": "https://docs.aws.amazon.com/rekognition/latest/dg/ppe-detection.html" + }, + { + "text": "Using Lambda with IoT Core Data Plane", + "link": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iot-data.html" + } + + ] + }, + "deploy": { + "text": [ + "cdk deploy --all" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: cdk destroy --all." + ] + }, + "authors": [ + { + "name": "Chris Peter Francis", + "image": "https://media.licdn.com/dms/image/D5603AQGSCw1U075kjQ/profile-displayphoto-shrink_800_800/0/1679337668977?e=2147483647&v=beta&t=18EMAaL8Qrm0hpxVVR525WEImFw1cyHrASNg3iYw6k4", + "bio": "Chris is a Solution Architect for AWS Energy. Serverless advocate and enthusiast.", + "linkedin": "chrispf" + } + ] +} diff --git a/s3-lambda-rekognition-iot-cdk/jest.config.js b/s3-lambda-rekognition-iot-cdk/jest.config.js new file mode 100644 index 0000000000..08263b8954 --- /dev/null +++ b/s3-lambda-rekognition-iot-cdk/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + testEnvironment: 'node', + roots: ['/test'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest' + } +}; diff --git a/s3-lambda-rekognition-iot-cdk/lib/ppeDetectionFunction/lambda_function.py b/s3-lambda-rekognition-iot-cdk/lib/ppeDetectionFunction/lambda_function.py new file mode 100644 index 0000000000..6d82922800 --- /dev/null +++ b/s3-lambda-rekognition-iot-cdk/lib/ppeDetectionFunction/lambda_function.py @@ -0,0 +1,44 @@ +# lambda handler +import boto3 +import json +import os + + + +def callRekognition(bucket_name,file_name): + rekog_client = boto3.client('rekognition') + response = rekog_client.detect_protective_equipment( + Image={ + 'S3Object': { + 'Bucket': bucket_name, + 'Name': file_name, + } + }, + SummarizationAttributes={ + 'MinConfidence': float(os.environ['MIN_CONFIDENCE']), + 'RequiredEquipmentTypes': json.loads(os.environ['REQUIRED_PPE']) + } +) + + + return response + + +# publish a message to the IoT Topic +def writeIoT(): + iot_client = boto3.client('iot-data') + iot_client.publish(topic = os.environ['TOPIC'],payload=json.dumps({"message":os.environ['MESSAGE']})) + +# Check if anyone in the frame is not wearing the right PPE +def checkViolations(response): + return response["Summary"]["PersonsWithoutRequiredEquipment"] + +def lambda_handler(event, context): + # S3 event + s3_event = event['Records'][0]['s3'] + bucket_name = s3_event['bucket']['name'] + file_name = s3_event['object']['key'] + response = callRekognition(bucket_name,file_name) + if(checkViolations(response)): + writeIoT() + diff --git a/s3-lambda-rekognition-iot-cdk/lib/s3-lambda-rekognition-iot-stack.ts b/s3-lambda-rekognition-iot-cdk/lib/s3-lambda-rekognition-iot-stack.ts new file mode 100644 index 0000000000..bd8e694167 --- /dev/null +++ b/s3-lambda-rekognition-iot-cdk/lib/s3-lambda-rekognition-iot-stack.ts @@ -0,0 +1,61 @@ +import {Stack, StackProps, } from 'aws-cdk-lib'; +import * as s3 from 'aws-cdk-lib/aws-s3'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import * as path from 'path'; +import * as eventsources from 'aws-cdk-lib/aws-lambda-event-sources'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import { CfnOutput } from 'aws-cdk-lib'; +import { Construct } from 'constructs'; + + +export class PPEIoTViolations extends Stack { + constructor(scope: Construct, id: string, props?: StackProps) { + super(scope, id, props); + //create new S3 bucket + const bucket = new s3.Bucket(this, 'images-bucket', { + }) + + + const lambdaFunction = new lambda.Function(this, 'lambdaFunction', { + runtime: lambda.Runtime.PYTHON_3_9, + code: lambda.Code.fromAsset(path.join(__dirname,'ppeDetectionFunction/')), + handler: 'lambda_function.lambda_handler', + environment: { + 'TOPIC': 'Bittle1/sub', + 'MESSAGE': 'khi', + 'MIN_CONFIDENCE': '80', + 'REQUIRED_PPE': '["HAND_COVER","FACE_COVER"]' + } + +}) + +bucket.grantRead(lambdaFunction) + +// Grant Lambda function to call Rekognition detect labels +lambdaFunction.role?.attachInlinePolicy( + new iam.Policy(this, 'rekognitionDetectLabelsPolicy', { + statements: [ + new iam.PolicyStatement({ + actions: ['rekognition:DetectProtectiveEquipment','iot:Publish'], + resources: ['*'], + }) + ], + }), +); + + + // add S3 event to lambda function + lambdaFunction.addEventSource(new eventsources.S3EventSource(bucket, { + events: [s3.EventType.OBJECT_CREATED], + })) + + // output S3 bucket name + + new CfnOutput(this, 'bucketName', { + value: bucket.bucketName, + description: 'The name of the s3 bucket created.', + }); + + +} +} diff --git a/s3-lambda-rekognition-iot-cdk/package.json b/s3-lambda-rekognition-iot-cdk/package.json new file mode 100644 index 0000000000..9f6880a1a1 --- /dev/null +++ b/s3-lambda-rekognition-iot-cdk/package.json @@ -0,0 +1,26 @@ +{ + "name": "s3-lambda-rekognition-iot", + "version": "0.1.0", + "bin": { + "s3-lambda-rekognition": "bin/s3-lambda-rekognition-iot.js" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "jest", + "cdk": "cdk" + }, + "devDependencies": { + "@types/jest": "^29.4.0", + "@types/node": "18.11.18", + "jest": "^29.4.1", + "ts-jest": "^29.0.5", + "aws-cdk": "2.63.0", + "ts-node": "^10.9.1", + "typescript": "~4.9.4" + }, + "dependencies": { + "aws-cdk-lib": "2.63.0", + "constructs": "^10.0.0" + } +} diff --git a/s3-lambda-rekognition-iot-cdk/test/s3-lambda-rekognition.test.ts b/s3-lambda-rekognition-iot-cdk/test/s3-lambda-rekognition.test.ts new file mode 100644 index 0000000000..6de3273955 --- /dev/null +++ b/s3-lambda-rekognition-iot-cdk/test/s3-lambda-rekognition.test.ts @@ -0,0 +1,17 @@ +import * as cdk from 'aws-cdk-lib'; +import { Template, Match } from 'aws-cdk-lib/assertions'; +import * as S3LambdaRekognition from '../lib/s3-lambda-rekognition-iot-stack'; + +test('SQS Queue and SNS Topic Created', () => { + const app = new cdk.App(); + // WHEN + const stack = new S3LambdaRekognition.S3LambdaRekognitionStack(app, 'MyTestStack'); + // THEN + + const template = Template.fromStack(stack); + + template.hasResourceProperties('AWS::SQS::Queue', { + VisibilityTimeout: 300 + }); + template.resourceCountIs('AWS::SNS::Topic', 1); +}); diff --git a/s3-lambda-rekognition-iot-cdk/tsconfig.json b/s3-lambda-rekognition-iot-cdk/tsconfig.json new file mode 100644 index 0000000000..42cdedfc81 --- /dev/null +++ b/s3-lambda-rekognition-iot-cdk/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": [ + "es2020" + ], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "node_modules/@types" + ] + }, + "exclude": [ + "node_modules", + "cdk.out" + ] +} diff --git a/s3-lambda-ses-terraform/example-pattern.json b/s3-lambda-ses-terraform/example-pattern.json index 1966206b2f..a7a2f79d19 100644 --- a/s3-lambda-ses-terraform/example-pattern.json +++ b/s3-lambda-ses-terraform/example-pattern.json @@ -43,7 +43,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/s3-lambda-transcribe-cdk/example-pattern.json b/s3-lambda-transcribe-cdk/example-pattern.json index 8f3d1899a9..b0d41b3b7e 100644 --- a/s3-lambda-transcribe-cdk/example-pattern.json +++ b/s3-lambda-transcribe-cdk/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/s3-s3-replication-cdk/example-pattern.json b/s3-s3-replication-cdk/example-pattern.json index 00315d7758..b2b92ca9e5 100644 --- a/s3-s3-replication-cdk/example-pattern.json +++ b/s3-s3-replication-cdk/example-pattern.json @@ -34,12 +34,12 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { "text": [ - "See the Github repo for deleting instructions." + "See the GitHub repo for deleting instructions." ] }, "authors": [ diff --git a/s3-sns-terraform/s3-sns-terraform.json b/s3-sns-terraform/s3-sns-terraform.json index d442fb8b63..08b6e2e95a 100644 --- a/s3-sns-terraform/s3-sns-terraform.json +++ b/s3-sns-terraform/s3-sns-terraform.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/s3-sqs-dotnet-cdk/README.md b/s3-sqs-dotnet-cdk/README.md index 73feec462d..68d1529262 100644 --- a/s3-sqs-dotnet-cdk/README.md +++ b/s3-sqs-dotnet-cdk/README.md @@ -13,7 +13,7 @@ Important: this application uses various AWS services and there are costs associ * [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) * [Node and NPM](https://nodejs.org/en/download/) installed * [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) (AWS CDK) installed -* [.NET](https://dotnet.microsoft.com/en-us/download/dotnet/3.1) (.NET Core 3.1) installed +* [.NET](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) (.NET Core 3.1) installed ## Deployment Instructions diff --git a/s3-sqs-dotnet-cdk/src/S3SqsDotnetCdk/S3SqsDotnetCdk.csproj b/s3-sqs-dotnet-cdk/src/S3SqsDotnetCdk/S3SqsDotnetCdk.csproj index d27496a4f0..1bf19ac983 100644 --- a/s3-sqs-dotnet-cdk/src/S3SqsDotnetCdk/S3SqsDotnetCdk.csproj +++ b/s3-sqs-dotnet-cdk/src/S3SqsDotnetCdk/S3SqsDotnetCdk.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net6.0 Major diff --git a/s3-trigger-lambda-glue-workflow-terraform/example-pattern.json b/s3-trigger-lambda-glue-workflow-terraform/example-pattern.json index 119ae9fa02..56a0b5e1bf 100644 --- a/s3-trigger-lambda-glue-workflow-terraform/example-pattern.json +++ b/s3-trigger-lambda-glue-workflow-terraform/example-pattern.json @@ -42,7 +42,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/s3-upload-presignedurl-api-cdk-ts/example-pattern.json b/s3-upload-presignedurl-api-cdk-ts/example-pattern.json index e674ae3f74..7d49608aed 100644 --- a/s3-upload-presignedurl-api-cdk-ts/example-pattern.json +++ b/s3-upload-presignedurl-api-cdk-ts/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/sam-webapp-cognito/example-pattern.json b/sam-webapp-cognito/example-pattern.json index b528c7a781..2ecaf935d1 100644 --- a/sam-webapp-cognito/example-pattern.json +++ b/sam-webapp-cognito/example-pattern.json @@ -34,7 +34,7 @@ "text": ["sam deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: sam delete."] diff --git a/scripts/pattern-schema.json b/scripts/pattern-schema.json index ed3c20c3d0..f5877da4ea 100644 --- a/scripts/pattern-schema.json +++ b/scripts/pattern-schema.json @@ -6,7 +6,7 @@ "description": { "type": "string" }, "language": { "type": "string", - "enum": ["TypeScript", "Node.js", "Python", "Java", "Go", "Rust", ".NET", "OpenAPI", "YAML"] + "enum": ["TypeScript", "Node.js", "Python", "Java", "Go", "Rust", ".NET", "OpenAPI", "YAML", ""] }, "framework": { "type": "string", diff --git a/sfn-callback-pattern-cdk-dotnet/example-pattern.json b/sfn-callback-pattern-cdk-dotnet/example-pattern.json index d6accf9a6a..26446f4744 100644 --- a/sfn-callback-pattern-cdk-dotnet/example-pattern.json +++ b/sfn-callback-pattern-cdk-dotnet/example-pattern.json @@ -40,7 +40,7 @@ }, "testing": { "text": [ - "See the Github repo Readme.md for detailed testing instructions." + "See the GitHub repo Readme.md for detailed testing instructions." ] }, "cleanup": { diff --git a/sfn-ecs-sam/example-pattern.json b/sfn-ecs-sam/example-pattern.json index 337bcea32f..c6e1e99697 100644 --- a/sfn-ecs-sam/example-pattern.json +++ b/sfn-ecs-sam/example-pattern.json @@ -42,7 +42,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/sfn-lambda-function-crossaccount-terraform/example-pattern.json b/sfn-lambda-function-crossaccount-terraform/example-pattern.json index 1dc1d07187..9888396d6e 100644 --- a/sfn-lambda-function-crossaccount-terraform/example-pattern.json +++ b/sfn-lambda-function-crossaccount-terraform/example-pattern.json @@ -39,7 +39,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/sfn-lambda-function-crossaccount/example-pattern.json b/sfn-lambda-function-crossaccount/example-pattern.json index 5ef8138e92..214eadae27 100644 --- a/sfn-lambda-function-crossaccount/example-pattern.json +++ b/sfn-lambda-function-crossaccount/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/sfn-lambda-lambda/example-pattern.json b/sfn-lambda-lambda/example-pattern.json index 84422f85c0..874f3865b1 100644 --- a/sfn-lambda-lambda/example-pattern.json +++ b/sfn-lambda-lambda/example-pattern.json @@ -47,7 +47,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/sfn-to-sfn-cdk/example-pattern.json b/sfn-to-sfn-cdk/example-pattern.json index 79cbd8256e..13410e54e0 100644 --- a/sfn-to-sfn-cdk/example-pattern.json +++ b/sfn-to-sfn-cdk/example-pattern.json @@ -42,7 +42,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/sns-firehose-opensearch/example-pattern.json b/sns-firehose-opensearch/example-pattern.json index 59b5140073..ca182d865c 100644 --- a/sns-firehose-opensearch/example-pattern.json +++ b/sns-firehose-opensearch/example-pattern.json @@ -37,7 +37,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/sns-firehose/example-pattern.json b/sns-firehose/example-pattern.json index b7024b2c85..f23e7e49f5 100644 --- a/sns-firehose/example-pattern.json +++ b/sns-firehose/example-pattern.json @@ -39,7 +39,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/sns-lambda-slack-integration-SAM/.gitignore b/sns-lambda-slack-integration-SAM/.gitignore new file mode 100644 index 0000000000..4808264dbf --- /dev/null +++ b/sns-lambda-slack-integration-SAM/.gitignore @@ -0,0 +1,244 @@ + +# Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### OSX ### +*.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Ruby plugin and RubyMine +/.rakeTasks + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +### PyCharm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +.pytest_cache/ +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule.* + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Build folder + +*/build/* + +# End of https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode \ No newline at end of file diff --git a/sns-lambda-slack-integration-SAM/README.md b/sns-lambda-slack-integration-SAM/README.md new file mode 100644 index 0000000000..2131bc1ae1 --- /dev/null +++ b/sns-lambda-slack-integration-SAM/README.md @@ -0,0 +1,145 @@ +# sns-slack-integration + +This project contains source code and supporting files for a serverless application that you can deploy with the SAM CLI. It includes the following files and folders. + +Please follow the steps in order to integrate SNS-Lambda-Slack integration: + +1. Create a slack channel +2. Add workflow and copy the webhook URL to the lambda function. +3. Publish SNS topic with subject and message body which will be published in the slack channel + +Kindly watch the video for slack integration and webhook URL : + +https://www.youtube.com/watch?v=PsIwZmrUxYM + +Possible error: + +1. While sam build: The lambda function file not found : solution: please create manually a folder over there. + +- hello_world - Code for the application's Lambda function where you need to paste the webhook url. +- events - Invocation events that you can use to invoke the function. +- tests - Unit tests for the application code. +- template.yaml - A template that defines the application's AWS resources. + +The application uses several AWS resources, including Lambda functions and an API Gateway API. These resources are defined in the `template.yaml` file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. + +If you prefer to use an integrated development environment (IDE) to build and test your application, you can use the AWS Toolkit. +The AWS Toolkit is an open source plug-in for popular IDEs that uses the SAM CLI to build and deploy serverless applications on AWS. The AWS Toolkit also adds a simplified step-through debugging experience for Lambda function code. See the following links to get started. + +- [CLion](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +- [GoLand](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +- [IntelliJ](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +- [WebStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +- [Rider](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +- [PhpStorm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +- [PyCharm](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +- [RubyMine](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +- [DataGrip](https://docs.aws.amazon.com/toolkit-for-jetbrains/latest/userguide/welcome.html) +- [VS Code](https://docs.aws.amazon.com/toolkit-for-vscode/latest/userguide/welcome.html) +- [Visual Studio](https://docs.aws.amazon.com/toolkit-for-visual-studio/latest/user-guide/welcome.html) + +## Deploy the sample application + +The Serverless Application Model Command Line Interface (SAM CLI) is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API. + +To use the SAM CLI, you need the following tools. + +- SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +- [Python 3 installed](https://www.python.org/downloads/) +- Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community) + +To build and deploy your application for the first time, run the following in your shell: + +```bash +sam build --use-container +sam deploy --guided +``` + +The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: + +- **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. +- **AWS Region**: The AWS region you want to deploy your app to. +- **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. +- **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. +- **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. + +You can find your API Gateway Endpoint URL in the output values displayed after deployment. + +## Use the SAM CLI to build and test locally + +Build your application with the `sam build --use-container` command. + +```bash +sns-slack-integration$ sam build --use-container +``` + +The SAM CLI installs dependencies defined in `hello_world/requirements.txt`, creates a deployment package, and saves it in the `.aws-sam/build` folder. + +Test a single function by invoking it directly with a test event. An event is a JSON document that represents the input that the function receives from the event source. Test events are included in the `events` folder in this project. + +Run functions locally and invoke them with the `sam local invoke` command. + +```bash +sns-slack-integration$ sam local invoke HelloWorldFunction --event events/event.json +``` + +The SAM CLI can also emulate your application's API. Use the `sam local start-api` to run the API locally on port 3000. + +```bash +sns-slack-integration$ sam local start-api +sns-slack-integration$ curl http://localhost:3000/ +``` + +The SAM CLI reads the application template to determine the API's routes and the functions that they invoke. The `Events` property on each function's definition includes the route and method for each path. + +```yaml +Events: + HelloWorld: + Type: Api + Properties: + Path: /hello + Method: get +``` + +## Add a resource to your application + +The application template uses AWS Serverless Application Model (AWS SAM) to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources such as functions, triggers, and APIs. For resources not included in [the SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use standard [AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html) resource types. + +## Fetch, tail, and filter Lambda function logs + +To simplify troubleshooting, SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs generated by your deployed Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug. + +`NOTE`: This command works for all AWS Lambda functions; not just the ones you deploy using SAM. + +```bash +sns-slack-integration$ sam logs -n HelloWorldFunction --stack-name "sns-slack-integration" --tail +``` + +You can find more information and examples about filtering Lambda function logs in the [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html). + +## Tests + +Tests are defined in the `tests` folder in this project. Use PIP to install the test dependencies and run tests. + +```bash +sns-slack-integration$ pip install -r tests/requirements.txt --user +# unit test +sns-slack-integration$ python -m pytest tests/unit -v +# integration test, requiring deploying the stack first. +# Create the env variable AWS_SAM_STACK_NAME with the name of the stack we are testing +sns-slack-integration$ AWS_SAM_STACK_NAME="sns-slack-integration" python -m pytest tests/integration -v +``` + +## Cleanup + +To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following: + +```bash +sam delete --stack-name "sns-slack-integration" +``` + +## Resources + +See the [AWS SAM developer guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) for an introduction to SAM specification, the SAM CLI, and serverless application concepts. + +Next, you can use AWS Serverless Application Repository to deploy ready to use Apps that go beyond hello world samples and learn how authors developed their applications: [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) diff --git a/aurora-serverless-s3-ingestion/cdk/tests/__init__.py b/sns-lambda-slack-integration-SAM/__init__.py similarity index 100% rename from aurora-serverless-s3-ingestion/cdk/tests/__init__.py rename to sns-lambda-slack-integration-SAM/__init__.py diff --git a/sns-lambda-slack-integration-SAM/events/event.json b/sns-lambda-slack-integration-SAM/events/event.json new file mode 100644 index 0000000000..a6197dea6c --- /dev/null +++ b/sns-lambda-slack-integration-SAM/events/event.json @@ -0,0 +1,62 @@ +{ + "body": "{\"message\": \"hello world\"}", + "resource": "/hello", + "path": "/hello", + "httpMethod": "GET", + "isBase64Encoded": false, + "queryStringParameters": { + "foo": "bar" + }, + "pathParameters": { + "proxy": "/path/to/resource" + }, + "stageVariables": { + "baz": "qux" + }, + "headers": { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Accept-Encoding": "gzip, deflate, sdch", + "Accept-Language": "en-US,en;q=0.8", + "Cache-Control": "max-age=0", + "CloudFront-Forwarded-Proto": "https", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-Mobile-Viewer": "false", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Tablet-Viewer": "false", + "CloudFront-Viewer-Country": "US", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Custom User Agent String", + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "requestContext": { + "accountId": "123456789012", + "resourceId": "123456", + "stage": "prod", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "requestTime": "09/Apr/2015:12:34:56 +0000", + "requestTimeEpoch": 1428582896000, + "identity": { + "cognitoIdentityPoolId": null, + "accountId": null, + "cognitoIdentityId": null, + "caller": null, + "accessKey": null, + "sourceIp": "127.0.0.1", + "cognitoAuthenticationType": null, + "cognitoAuthenticationProvider": null, + "userArn": null, + "userAgent": "Custom User Agent String", + "user": null + }, + "path": "/prod/hello", + "resourcePath": "/hello", + "httpMethod": "POST", + "apiId": "1234567890", + "protocol": "HTTP/1.1" + } +} diff --git a/sns-lambda-slack-integration-SAM/example-pattern.json b/sns-lambda-slack-integration-SAM/example-pattern.json new file mode 100644 index 0000000000..a0ef79a3d8 --- /dev/null +++ b/sns-lambda-slack-integration-SAM/example-pattern.json @@ -0,0 +1,57 @@ +{ + "title": "SNS to Lambda to Slack Integration via SAM", + "description": "Creating a SNS topic to publish to Slack channel via AWS Lambda", + "language": "Python", + "level": "200", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "This is a sample project where the message will be published from SNS topic to aws lambda which triggers notification to slack channel." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/sns-lambda-slack-integration-SAM", + "templateURL": "serverless-patterns/sns-lambda-slack-integration-SAM", + "projectFolder": "sns-lambda-slack-integration-SAM", + "templateFile": "sns-lambda-slack-integration-SAM/template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "AWS SNS to AWS Lambda", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/with-sns-example.html" + }, + { + "text": "AWS Lambda with Slack integration", + "link": "https://serverlessrepo.aws.amazon.com/applications/us-east-1/289559741701/lambda-to-slack" + } + ] + }, + "deploy": { + "text": [ + "sam deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: cdk delete." + ] + }, + "authors": [ + { + "name": "Ibraheem Khaleel", + "image": "", + "bio": "AWS Cloud Support Engineer", + "linkedin": "", + "twitter": "" + } + ] +} diff --git a/aurora-serverless-s3-ingestion/cdk/tests/unit/__init__.py b/sns-lambda-slack-integration-SAM/hello_world/__init__.py similarity index 100% rename from aurora-serverless-s3-ingestion/cdk/tests/unit/__init__.py rename to sns-lambda-slack-integration-SAM/hello_world/__init__.py diff --git a/sns-lambda-slack-integration-SAM/hello_world/app.py b/sns-lambda-slack-integration-SAM/hello_world/app.py new file mode 100644 index 0000000000..0b5602467f --- /dev/null +++ b/sns-lambda-slack-integration-SAM/hello_world/app.py @@ -0,0 +1,30 @@ +import json +import urllib3 +http = urllib3.PoolManager() + + +def lambda_handler(event, context): + print(event) + + # Enter slack URL below. + url = 'https://hooks.slack.com/workflows/{your webhook url}' + msg = { + 'channel': '#sns-topic-slack-integration', + 'Message': event['Records'][0]['Sns']['Message'], + # 'Message': 'SAM - Hello', + # 'Subject' : 'SAM ', + 'Subject' : event['Records'][0]['Sns']['Subject'], + 'icon_emoji': '', + } + + encoded_msg = json.dumps(msg).encode('utf-8') + resp = http.request('POST', url, body=encoded_msg) + print(resp) + print({ + # "message": event["Records"][0]["Sns"]["Message"], + "message" : 'Hello', + "status_code": 'status', + "response": 'data', + "status_code": resp.status, + "response": resp.data, + }) \ No newline at end of file diff --git a/sns-lambda-slack-integration-SAM/hello_world/requirements.txt b/sns-lambda-slack-integration-SAM/hello_world/requirements.txt new file mode 100644 index 0000000000..663bd1f6a2 --- /dev/null +++ b/sns-lambda-slack-integration-SAM/hello_world/requirements.txt @@ -0,0 +1 @@ +requests \ No newline at end of file diff --git a/sns-lambda-slack-integration-SAM/template.yaml b/sns-lambda-slack-integration-SAM/template.yaml new file mode 100644 index 0000000000..1eddd28a72 --- /dev/null +++ b/sns-lambda-slack-integration-SAM/template.yaml @@ -0,0 +1,41 @@ +AWSTemplateFormatVersion: "2010-09-09" +Transform: AWS::Serverless-2016-10-31 +Description: Serverless patterns - SNS to Lambda + +Resources: + # Define the SNS topic + MySnsTopic: + Type: AWS::SNS::Topic + Properties: + Subscription: + - Protocol: lambda + Endpoint: !GetAtt LambdaFn.Arn + + # Provide permission for SNS to invoke the Lambda function + TopicConsumerFunction1Permission: + Type: "AWS::Lambda::Permission" + Properties: + Action: "lambda:InvokeFunction" + FunctionName: !Ref LambdaFn + Principal: sns.amazonaws.com + + # Define the consumer Lambda function + LambdaFn: + Type: AWS::Serverless::Function + Properties: + CodeUri: hello_world/ + Handler: app.handler + Runtime: python3.10 + +Outputs: + TopicConsumerFunction1Name: + Description: LambdaFn function name + Value: !Ref LambdaFn + + SNStopicName: + Description: SNS topic name + Value: !GetAtt MySnsTopic.TopicName + + SNStopicARN: + Description: SNS topic ARN + Value: !Ref MySnsTopic diff --git a/aurora-serverless-s3-ingestion/cdk/reqs.txt b/sns-lambda-slack-integration-SAM/tests/__init__.py similarity index 100% rename from aurora-serverless-s3-ingestion/cdk/reqs.txt rename to sns-lambda-slack-integration-SAM/tests/__init__.py diff --git a/sns-lambda-slack-integration-SAM/tests/integration/__init__.py b/sns-lambda-slack-integration-SAM/tests/integration/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sns-lambda-slack-integration-SAM/tests/integration/test_api_gateway.py b/sns-lambda-slack-integration-SAM/tests/integration/test_api_gateway.py new file mode 100644 index 0000000000..b96e803380 --- /dev/null +++ b/sns-lambda-slack-integration-SAM/tests/integration/test_api_gateway.py @@ -0,0 +1,45 @@ +import os + +import boto3 +import pytest +import requests + +""" +Make sure env variable AWS_SAM_STACK_NAME exists with the name of the stack we are going to test. +""" + + +class TestApiGateway: + + @pytest.fixture() + def api_gateway_url(self): + """ Get the API Gateway URL from Cloudformation Stack outputs """ + stack_name = os.environ.get("AWS_SAM_STACK_NAME") + + if stack_name is None: + raise ValueError('Please set the AWS_SAM_STACK_NAME environment variable to the name of your stack') + + client = boto3.client("cloudformation") + + try: + response = client.describe_stacks(StackName=stack_name) + except Exception as e: + raise Exception( + f"Cannot find stack {stack_name} \n" f'Please make sure a stack with the name "{stack_name}" exists' + ) from e + + stacks = response["Stacks"] + stack_outputs = stacks[0]["Outputs"] + api_outputs = [output for output in stack_outputs if output["OutputKey"] == "HelloWorldApi"] + + if not api_outputs: + raise KeyError(f"HelloWorldAPI not found in stack {stack_name}") + + return api_outputs[0]["OutputValue"] # Extract url from stack outputs + + def test_api_gateway(self, api_gateway_url): + """ Call the API Gateway endpoint and check the response """ + response = requests.get(api_gateway_url) + + assert response.status_code == 200 + assert response.json() == {"message": "hello world"} diff --git a/sns-lambda-slack-integration-SAM/tests/requirements.txt b/sns-lambda-slack-integration-SAM/tests/requirements.txt new file mode 100644 index 0000000000..b9cf27ab27 --- /dev/null +++ b/sns-lambda-slack-integration-SAM/tests/requirements.txt @@ -0,0 +1,3 @@ +pytest +boto3 +requests diff --git a/sns-lambda-slack-integration-SAM/tests/unit/__init__.py b/sns-lambda-slack-integration-SAM/tests/unit/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/sns-lambda-slack-integration-SAM/tests/unit/test_handler.py b/sns-lambda-slack-integration-SAM/tests/unit/test_handler.py new file mode 100644 index 0000000000..d98ce5745f --- /dev/null +++ b/sns-lambda-slack-integration-SAM/tests/unit/test_handler.py @@ -0,0 +1,72 @@ +import json + +import pytest + +from hello_world import app + + +@pytest.fixture() +def apigw_event(): + """ Generates API GW Event""" + + return { + "body": '{ "test": "body"}', + "resource": "/{proxy+}", + "requestContext": { + "resourceId": "123456", + "apiId": "1234567890", + "resourcePath": "/{proxy+}", + "httpMethod": "POST", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "accountId": "123456789012", + "identity": { + "apiKey": "", + "userArn": "", + "cognitoAuthenticationType": "", + "caller": "", + "userAgent": "Custom User Agent String", + "user": "", + "cognitoIdentityPoolId": "", + "cognitoIdentityId": "", + "cognitoAuthenticationProvider": "", + "sourceIp": "127.0.0.1", + "accountId": "", + }, + "stage": "prod", + }, + "queryStringParameters": {"foo": "bar"}, + "headers": { + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "Accept-Language": "en-US,en;q=0.8", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Mobile-Viewer": "false", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "CloudFront-Viewer-Country": "US", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Upgrade-Insecure-Requests": "1", + "X-Forwarded-Port": "443", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "X-Forwarded-Proto": "https", + "X-Amz-Cf-Id": "aaaaaaaaaae3VYQb9jd-nvCd-de396Uhbp027Y2JvkCPNLmGJHqlaA==", + "CloudFront-Is-Tablet-Viewer": "false", + "Cache-Control": "max-age=0", + "User-Agent": "Custom User Agent String", + "CloudFront-Forwarded-Proto": "https", + "Accept-Encoding": "gzip, deflate, sdch", + }, + "pathParameters": {"proxy": "/examplepath"}, + "httpMethod": "POST", + "stageVariables": {"baz": "qux"}, + "path": "/examplepath", + } + + +def test_lambda_handler(apigw_event): + + ret = app.lambda_handler(apigw_event, "") + data = json.loads(ret["body"]) + + assert ret["statusCode"] == 200 + assert "message" in ret["body"] + assert data["message"] == "hello world" diff --git a/sns-sqs-fanout-pattern-cdk-dotnet/example-pattern.json b/sns-sqs-fanout-pattern-cdk-dotnet/example-pattern.json index 569d531775..6e71bfd8f1 100644 --- a/sns-sqs-fanout-pattern-cdk-dotnet/example-pattern.json +++ b/sns-sqs-fanout-pattern-cdk-dotnet/example-pattern.json @@ -27,7 +27,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/sns-sqs-lambda-cdk/example-pattern.json b/sns-sqs-lambda-cdk/example-pattern.json index 9b23b94881..f656cf63b2 100644 --- a/sns-sqs-lambda-cdk/example-pattern.json +++ b/sns-sqs-lambda-cdk/example-pattern.json @@ -30,7 +30,7 @@ "text": ["cdk deploy"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk delete."] diff --git a/sns-sqs-lambda-rust-sam/example-pattern.json b/sns-sqs-lambda-rust-sam/example-pattern.json index d672539feb..f4143d3108 100644 --- a/sns-sqs-lambda-rust-sam/example-pattern.json +++ b/sns-sqs-lambda-rust-sam/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/sns-sqs-message-content-router-cdk/README.md b/sns-sqs-message-content-router-cdk/README.md new file mode 100644 index 0000000000..e2644d4f2f --- /dev/null +++ b/sns-sqs-message-content-router-cdk/README.md @@ -0,0 +1,124 @@ +# Amazon SNS to Amazon SQS by filtering content (Message-content router pattern) + +This pattern creates an SNS topic and various SQS destination queues. Messages received by the SNS topic are routed to various SQS queues according to routing logic. The pattern is an implementation of the Integration pattern: "Content-Based Message route" available here: [https://www.enterpriseintegrationpatterns.com/patterns/messaging/ContentBasedRouter.html](https://www.enterpriseintegrationpatterns.com/patterns/messaging/ContentBasedRouter.html) + +Learn more about this pattern at Serverless Land Patterns: [https://serverlessland.com/patterns/sns-sqs-message-content-router-cdk](https://serverlessland.com/patterns/sns-sqs-message-content-router-cdk) + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +* [AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html) (AWS SAMCDK) installed + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +1. Change directory to the pattern directory: + ``` + cd sns-sqs-message-content-router-cdk/src + ``` +1. From the command line, use AWS CDK to deploy the Stack: + ``` + cdk deploy + ``` +1. Note the outputs from the CDK deployment process. These contain the resource names and/or ARNs which are used for testing. + +## How it works + +The CDK Stack pick-up a configuration from `stackconfiguration.json` stored inside the `src` folder. This files contains a JSON structure that describe the routing logic. The stack will also create a DeadQueue letter for each Queue. + +The parameter `IsAttribute` specifies to CDK if the filter is of type attribute or content. + +```json +{ + "Sns": { + "TopicName": "content-router" + }, + "Sqs": [ + { + "QueueName": "country-usa", + "IsAttribute": true, + "Filter": { + "FilterName": "country", + "FilterValues": [ + "united states of america", + "usa" + ] + } + }, + { + "QueueName": "country-germany", + "IsAttribute": true, + "Filter": { + "FilterName": "country", + "FilterValues": [ + "germany", + "de" + ] + } + }, + { + "QueueName": "language-english", + "IsAttribute": false, + "Filter": { + "FilterName": "language", + "FilterValues": [ + "english", + "en" + ] + } + } + ] +} +``` + +## Testing + +In order to test the solution, you need to login into your AWS Console and generate a couple of Amazon SNS messages that mimics the filters provided in `stackconfiguration.js`. + +**Scenario-1 Attribute based** + +Create an SNS message with the following information: + +``` +Message Body: +{ + "Message":"Test Message to be received by Queue3 only" +} +Message Attribute: +Type - String; Name - country; Value - usa +``` + +**Scenario-2 Content based** + +Create an SNS message with the following information: + +``` +Message body based filter. Copy Json to the message body and add attibutes given below. This message should be received by Queue1 and Queue3 +Message Body: +{ + "Message":"Test Message to be received by Queue2 only", + "Filter":{ + "Language": ["en"] + } +} +``` + +Then head to the Amazon SQS queues, pull the messages and verify that the messages have been routed to the correct queue. + +## Cleanup + +1. Delete the stack + ```bash + cdk destroy + ``` +---- +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 \ No newline at end of file diff --git a/sns-sqs-message-content-router-cdk/example-pattern.json b/sns-sqs-message-content-router-cdk/example-pattern.json new file mode 100644 index 0000000000..c43f192d75 --- /dev/null +++ b/sns-sqs-message-content-router-cdk/example-pattern.json @@ -0,0 +1,56 @@ +{ + "title": "Content based router with SNS and SQS filters", + "description": "Create an SNS topic and multiple SQS queues with content filtered based on configurable criteria.", + "language": "TypeScript", + "level": "200", + "framework": "CDK", + "introBox": { + "headline": "How it works", + "text": [ + "This CDK project demonstrates how to create a Message Content-based routing pattern using Amazon SNS and Amazon SQS.", + "The number of Queues and the Filters are dynamically defined inside the stackconfiguration.json file. The solution can be adapted for any specific requirement.", + "This architecture is an implementation of the Integration pattern: Content-Based Message Router explained in the Integration Patterns book.", + "The content can be filtered by attributes or by message body which grants full flexibility." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/sns-sqs-message-content-router-cdk", + "templateURL": "serverless-patterns/sns-sqs-message-content-router-cdk", + "projectFolder": "sns-sqs-message-content-router-cdk", + "templateFile": "sns-sqs-message-content-router-cdk/src/lib/src-stack.ts" + } + }, + "resources": { + "bullets": [ + { + "text": "Amazon SNS subscription filter policies", + "link": "https://docs.aws.amazon.com/sns/latest/dg/sns-subscription-filter-policies.html" + } + ] + }, + "deploy": { + "text": [ + "cdk deploy" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: cdk destroy." + ] + }, + "authors": [ + { + "name": "Raffaele Garofalo (Raf)", + "image": "https://avatars.githubusercontent.com/raffaeu", + "bio": "Raffaele Garofalo (Raf) is a Senior Solutions Architect at AWS and member of the AWS Serverless TFC (Technical Field Communities).", + "linkedin": "raffaeu", + "twitter": "raffaeu" + } + ] +} diff --git a/sns-sqs-message-content-router-cdk/src/.gitignore b/sns-sqs-message-content-router-cdk/src/.gitignore new file mode 100644 index 0000000000..f60797b6a9 --- /dev/null +++ b/sns-sqs-message-content-router-cdk/src/.gitignore @@ -0,0 +1,8 @@ +*.js +!jest.config.js +*.d.ts +node_modules + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/sns-sqs-message-content-router-cdk/src/.npmignore b/sns-sqs-message-content-router-cdk/src/.npmignore new file mode 100644 index 0000000000..c1d6d45dcf --- /dev/null +++ b/sns-sqs-message-content-router-cdk/src/.npmignore @@ -0,0 +1,6 @@ +*.ts +!*.d.ts + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/sns-sqs-message-content-router-cdk/src/bin/src.ts b/sns-sqs-message-content-router-cdk/src/bin/src.ts new file mode 100644 index 0000000000..438a452e64 --- /dev/null +++ b/sns-sqs-message-content-router-cdk/src/bin/src.ts @@ -0,0 +1,9 @@ +#!/usr/bin/env node +import 'source-map-support/register'; +import * as cdk from 'aws-cdk-lib'; +import { SrcStack } from '../lib/src-stack'; + +const app = new cdk.App(); +new SrcStack(app, 'cdk-stack-message-router', { + +}); \ No newline at end of file diff --git a/cdk-s3-sqs-lambda-dynamodb/cdk.json b/sns-sqs-message-content-router-cdk/src/cdk.json similarity index 67% rename from cdk-s3-sqs-lambda-dynamodb/cdk.json rename to sns-sqs-message-content-router-cdk/src/cdk.json index 7193f6c32c..36b0ca2ee4 100644 --- a/cdk-s3-sqs-lambda-dynamodb/cdk.json +++ b/sns-sqs-message-content-router-cdk/src/cdk.json @@ -1,5 +1,5 @@ { - "app": "npx ts-node --prefer-ts-exts bin/my-cdk-project.ts", + "app": "npx ts-node --prefer-ts-exts bin/src.ts", "watch": { "include": [ "**" @@ -17,26 +17,28 @@ ] }, "context": { - "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, - "@aws-cdk/core:stackRelativeExports": true, - "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, - "@aws-cdk/aws-lambda:recognizeVersionProps": true, "@aws-cdk/aws-lambda:recognizeLayerVersion": true, - "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, - "@aws-cdk/core:checkSecretUsage": true, - "@aws-cdk/aws-iam:minimizePolicies": true, "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, "@aws-cdk/core:validateSnapshotRemovalPolicy": true, "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, "@aws-cdk/core:enablePartitionLiterals": true, - "@aws-cdk/core:target-partitions": [ - "aws", - "aws-cn" - ] + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false } } diff --git a/sns-sqs-message-content-router-cdk/src/jest.config.js b/sns-sqs-message-content-router-cdk/src/jest.config.js new file mode 100644 index 0000000000..08263b8954 --- /dev/null +++ b/sns-sqs-message-content-router-cdk/src/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + testEnvironment: 'node', + roots: ['/test'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest' + } +}; diff --git a/sns-sqs-message-content-router-cdk/src/lib/src-stack.ts b/sns-sqs-message-content-router-cdk/src/lib/src-stack.ts new file mode 100644 index 0000000000..3e1b9bdbaf --- /dev/null +++ b/sns-sqs-message-content-router-cdk/src/lib/src-stack.ts @@ -0,0 +1,64 @@ +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as configuration from '../stackconfiguration.json'; +import { AllowListReceiptFilter } from 'aws-cdk-lib/aws-ses'; + +export class SrcStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + // parameters + const account = cdk.Stack.of(this).account; + const stackName = cdk.Stack.of(this).stackName; + const snsName = `${account}-${stackName}-topic-${configuration.Sns.TopicName}`; + + // sns topic + const sns = new cdk.aws_sns.Topic(this, 'cdk-sns-topic', { + displayName: snsName, + topicName: snsName + }); + + // create all required sqs + for (let q = 0; q < configuration.Sqs.length; q++) { + const queueConfig = configuration.Sqs[q]; + // dead queue + const sqsDead = new cdk.aws_sqs.Queue(this, `cdk-sqs-dead-${q + 1}`, { + queueName: `${account}-${stackName}-dead-${queueConfig.QueueName}`, + removalPolicy: cdk.RemovalPolicy.DESTROY + }); + // queue + const sqs = new cdk.aws_sqs.Queue(this, `cdk-sqs-queue-${q + 1}`, { + queueName: `${account}-${stackName}-queue-${queueConfig.QueueName}`, + removalPolicy: cdk.RemovalPolicy.DESTROY, + deadLetterQueue: { + maxReceiveCount: 3, + queue: sqsDead + } + }); + if (queueConfig.IsAttribute) { + // subscription attribute + sns.addSubscription(new cdk.aws_sns_subscriptions.SqsSubscription(sqs, { + filterPolicy: { + [queueConfig.Filter.FilterName]: cdk.aws_sns.SubscriptionFilter.stringFilter({ + allowlist: queueConfig.Filter.FilterValues + }) + }, + rawMessageDelivery: true + })); + } else { + // subscription content + sns.addSubscription(new cdk.aws_sns_subscriptions.SqsSubscription(sqs, { + filterPolicyWithMessageBody: { + filter: cdk.aws_sns.FilterOrPolicy.policy({ + [queueConfig.Filter.FilterName]: cdk.aws_sns.FilterOrPolicy.filter(cdk.aws_sns.SubscriptionFilter.stringFilter({ + allowlist: queueConfig.Filter.FilterValues + })) + }) + + }, + rawMessageDelivery: true + })); + } + } + } +} diff --git a/sns-sqs-message-content-router-cdk/src/package.json b/sns-sqs-message-content-router-cdk/src/package.json new file mode 100644 index 0000000000..97165b1547 --- /dev/null +++ b/sns-sqs-message-content-router-cdk/src/package.json @@ -0,0 +1,27 @@ +{ + "name": "src", + "version": "0.1.0", + "bin": { + "src": "bin/src.js" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "jest", + "cdk": "cdk" + }, + "devDependencies": { + "@types/jest": "^29.2.5", + "@types/node": "18.11.18", + "aws-cdk": "2.61.0", + "jest": "^29.3.1", + "ts-jest": "^29.0.3", + "ts-node": "^10.9.1", + "typescript": "~4.9.4" + }, + "dependencies": { + "aws-cdk-lib": "^2.84.0", + "constructs": "^10.0.0", + "source-map-support": "^0.5.21" + } +} diff --git a/sns-sqs-message-content-router-cdk/src/stackconfiguration.json b/sns-sqs-message-content-router-cdk/src/stackconfiguration.json new file mode 100644 index 0000000000..2706c5e23a --- /dev/null +++ b/sns-sqs-message-content-router-cdk/src/stackconfiguration.json @@ -0,0 +1,40 @@ +{ + "Sns": { + "TopicName": "content-router" + }, + "Sqs": [ + { + "QueueName": "country-usa", + "IsAttribute": true, + "Filter": { + "FilterName": "country", + "FilterValues": [ + "united states of america", + "usa" + ] + } + }, + { + "QueueName": "country-germany", + "IsAttribute": true, + "Filter": { + "FilterName": "country", + "FilterValues": [ + "germany", + "de" + ] + } + }, + { + "QueueName": "language-english", + "IsAttribute": false, + "Filter": { + "FilterName": "language", + "FilterValues": [ + "english", + "en" + ] + } + } + ] +} \ No newline at end of file diff --git a/cdk-s3-sqs-lambda-dynamodb/test/my-cdk-project.test.ts b/sns-sqs-message-content-router-cdk/src/test/src.test.ts similarity index 68% rename from cdk-s3-sqs-lambda-dynamodb/test/my-cdk-project.test.ts rename to sns-sqs-message-content-router-cdk/src/test/src.test.ts index 7b55b9e723..7d90b8da54 100644 --- a/cdk-s3-sqs-lambda-dynamodb/test/my-cdk-project.test.ts +++ b/sns-sqs-message-content-router-cdk/src/test/src.test.ts @@ -1,13 +1,13 @@ // import * as cdk from 'aws-cdk-lib'; // import { Template } from 'aws-cdk-lib/assertions'; -// import * as MyCdkProject from '../lib/my-cdk-project-stack'; +// import * as Src from '../lib/src-stack'; // example test. To run these tests, uncomment this file along with the -// example resource in lib/my-cdk-project-stack.ts +// example resource in lib/src-stack.ts test('SQS Queue Created', () => { // const app = new cdk.App(); // // WHEN -// const stack = new MyCdkProject.MyCdkProjectStack(app, 'MyTestStack'); +// const stack = new Src.SrcStack(app, 'MyTestStack'); // // THEN // const template = Template.fromStack(stack); diff --git a/sns-sqs-message-content-router-cdk/src/tsconfig.json b/sns-sqs-message-content-router-cdk/src/tsconfig.json new file mode 100644 index 0000000000..307ee521c8 --- /dev/null +++ b/sns-sqs-message-content-router-cdk/src/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": [ + "es2020" + ], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "./node_modules/@types" + ], + "resolveJsonModule": true, + }, + "exclude": [ + "node_modules", + "cdk.out" + ] +} diff --git a/sqs-fargate-cdk-python/example-pattern.json b/sqs-fargate-cdk-python/example-pattern.json index ca7e451d53..ad020e6711 100644 --- a/sqs-fargate-cdk-python/example-pattern.json +++ b/sqs-fargate-cdk-python/example-pattern.json @@ -46,7 +46,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/sqs-lambda-rust-sam/example-pattern.json b/sqs-lambda-rust-sam/example-pattern.json index 6655620924..0afd3958c7 100644 --- a/sqs-lambda-rust-sam/example-pattern.json +++ b/sqs-lambda-rust-sam/example-pattern.json @@ -34,7 +34,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/sqs-lambda-terraform-python/sqs-lambda-terraform-python.json b/sqs-lambda-terraform-python/sqs-lambda-terraform-python.json index f8b581285e..fd56730cf9 100644 --- a/sqs-lambda-terraform-python/sqs-lambda-terraform-python.json +++ b/sqs-lambda-terraform-python/sqs-lambda-terraform-python.json @@ -35,7 +35,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/sqs-stepfunctions-pipes-pattern/README.md b/sqs-stepfunctions-pipes-pattern/README.md new file mode 100644 index 0000000000..2e294ebd7f --- /dev/null +++ b/sqs-stepfunctions-pipes-pattern/README.md @@ -0,0 +1,74 @@ +# Exponential Backoff in SQS Processing with Step Functions and EventBridge Pipes + +This pattern demonstrates a seamless integration between an SQS queue and AWS Step Functions using Amazon EventBridge Pipes. It enables direct routing of messages from an SQS queue into a Step Functions state machine, which orchestrates the processing flow and manages AWS Lambda invocation errors. When Lambda encounters an error, the erroneous message is directed to an SQS Dead Letter Queue (DLQ) for further inspection or reprocessing. + +Learn more about this pattern at Serverless Land Patterns: [Add the live URL here](#). + +**Important**: This application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources. +* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured. +* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git). +* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed. + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ```bash + git clone https://github.com/aws-samples/serverless-patterns + ``` + +2. Change directory to the pattern directory: + ```bash + cd serverless-patterns/sqs-stepfunctions-pipes-pattern + ``` + +3. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file: + ```bash + sam deploy --guided + ``` + +4. During the prompts: + * Enter a stack name. + * Enter the desired AWS Region. + * Allow SAM CLI to create IAM roles with the required permissions. + + Once you have run `sam deploy --guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +5. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing. + +## How it works + +Messages sent to the SQS queue are routed through EventBridge Pipes to a Step Functions state machine. The state machine manages message processing and invokes a Lambda function, which is intentionally set with a reserved concurrency of 0 to simulate throttling. When errors, such as throttling, occur in the Lambda function, the state machine employs an exponential backoff retry strategy that starts with a 5-second delay, doubling this delay after each subsequent retry, for up to 6 maximum retry attempts. If all retry attempts are exhausted, the message is directed to a Dead Letter Queue (DLQ) for further analysis. + +## Testing + +1. Retrieve the Queue URL from the outputs of the SAM deployment: + ```bash + aws cloudformation describe-stacks --stack-name YOUR_STACK_NAME --query "Stacks[0].Outputs[?OutputKey=='MyQueueUrl'].OutputValue" --output text + ``` + Replace `YOUR_STACK_NAME` with the name you provided for your stack during deployment. + +2. Send a test message to the SQS queue using the AWS CLI: + ```bash + aws sqs send-message --queue-url [RETRIEVED_QUEUE_URL] --message-body "Your test message content here" + ``` + Replace `[RETRIEVED_QUEUE_URL]` with the URL you obtained in the previous step. + +3. Monitor the Step Functions state machine to observe the execution. +4. If the Lambda function is simulated to fail, check the DLQ for the erroneous message. + +## Cleanup + +1. To delete the deployed resources, use: + ```bash + sam delete --stack-name YOUR_STACK_NAME + ``` + Replace `YOUR_STACK_NAME` with the name you provided for your stack during deployment. + +--- +Copyright 2023 [Amazon.com, Inc. or its affiliates](http://Amazon.com). All Rights Reserved. + +SPDX-License-Identifier: MIT-0 \ No newline at end of file diff --git a/sqs-stepfunctions-pipes-pattern/example-pattern.json b/sqs-stepfunctions-pipes-pattern/example-pattern.json new file mode 100644 index 0000000000..d02f206938 --- /dev/null +++ b/sqs-stepfunctions-pipes-pattern/example-pattern.json @@ -0,0 +1,65 @@ +{ + "title": "Exponential Backoff in SQS Processing with Step Functions and EventBridge Pipes", + "description": "This pattern demonstrates a seamless integration between an SQS queue and AWS Step Functions using Amazon EventBridge Pipes. It enables direct routing of messages from an SQS queue into a Step Functions state machine, which orchestrates the processing flow and manages AWS Lambda invocation errors. When Lambda encounters an error, the erroneous message is directed to an SQS Dead Letter Queue (DLQ) for further inspection or reprocessing.", + "language": "Python", + "level": "200", + "framework": "SAM", + "introBox": { + "headline": "How it works", + "text": [ + "Messages sent to the SQS queue are routed through EventBridge Pipes to a Step Functions state machine. The state machine manages message processing and invokes a Lambda function, which is intentionally set with a reserved concurrency of 0 to simulate throttling.", + "When errors, such as throttling, occur in the Lambda function, the state machine employs an exponential backoff retry strategy that starts with a 5-second delay, doubling this delay after each subsequent retry, for up to 6 maximum retry attempts. If all retry attempts are exhausted, the message is directed to a Dead Letter Queue (DLQ) for further analysis." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns", + "templateURL": "serverless-patterns/sqs-stepfunctions-pipes-pattern", + "projectFolder": "sqs-stepfunctions-pipes-pattern", + "templateFile": "template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Amazon Simple Queue Service (SQS)", + "link": "https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html" + }, + { + "text": "AWS Step Functions", + "link": "https://aws.amazon.com/step-functions/" + }, + { + "text": "AWS Lambda", + "link": "https://docs.aws.amazon.com/lambda/latest/dg/welcome.html" + }, + { + "text": "Amazon EventBridge Pipes", + "link": "https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-pipes.html" + } + ] + }, + "deploy": { + "text": [ + "sam deploy --guided" + ] + }, + "testing": { + "text": [ + "Retrieve the Queue URL from the outputs of the SAM deployment and send a test message using AWS CLI. Monitor the Step Functions state machine to observe the execution. If the Lambda function fails, check the DLQ for the erroneous message." + ] + }, + "cleanup": { + "text": [ + "To delete the deployed resources, use: sam delete --stack-name YOUR_STACK_NAME. Replace YOUR_STACK_NAME with the name you provided for your stack during deployment." + ] + }, + "authors": [ + { + "name": "Mohammed Atiq", + "image": "https://i.postimg.cc/N0804zKg/photo.jpg", + "bio": "Solutions Architect at AWS and Serverless enthusiast.", + "linkedin": "https://www.linkedin.com/in/mohammed-a-2980151b7" + } + ] +} \ No newline at end of file diff --git a/sqs-stepfunctions-pipes-pattern/lambda/lambda_function.py b/sqs-stepfunctions-pipes-pattern/lambda/lambda_function.py new file mode 100644 index 0000000000..76eda3b9ae --- /dev/null +++ b/sqs-stepfunctions-pipes-pattern/lambda/lambda_function.py @@ -0,0 +1,8 @@ +import json + +def lambda_handler(event, context): + # TODO implement + return { + 'statusCode': 200, + 'body': json.dumps('Hello from Lambda!') + } \ No newline at end of file diff --git a/sqs-stepfunctions-pipes-pattern/statemachine/StateMachine.asl.json b/sqs-stepfunctions-pipes-pattern/statemachine/StateMachine.asl.json new file mode 100644 index 0000000000..3a6e738e5c --- /dev/null +++ b/sqs-stepfunctions-pipes-pattern/statemachine/StateMachine.asl.json @@ -0,0 +1,45 @@ +{ + "Comment": "A state machine that processes an SQS message and sends it to DLQ if processing fails", + "StartAt": "SaveOriginalMessage", + "States": { + "SaveOriginalMessage": { + "Type": "Pass", + "Parameters": { + "originalInput.$": "$[0]" + }, + "Next": "ProcessMessage" + }, + "ProcessMessage": { + "Type": "Task", + "Resource": "${MyLambdaFunctionArn}", + "Parameters": { + "input.$": "$.originalInput.body" + }, + "End": true, + "Retry": [ + { + "ErrorEquals": ["Lambda.ServiceException", "Lambda.AWSLambdaException", "Lambda.SdkClientException", "Lambda.TooManyRequestsException"], + "IntervalSeconds": 5, + "MaxAttempts": 6, + "BackoffRate": 2 + } + ], + "Catch": [ + { + "ErrorEquals": ["States.ALL"], + "ResultPath": "$.errorInfo", + "Next": "MoveMessageToDLQ" + } + ] + }, + "MoveMessageToDLQ": { + "Type": "Task", + "Resource": "arn:aws:states:::sqs:sendMessage", + "Parameters": { + "QueueUrl": "${DeadLetterQueueUrl}", + "MessageBody.$": "$.originalInput.body" + }, + "End": true + } + } +} \ No newline at end of file diff --git a/sqs-stepfunctions-pipes-pattern/template.yaml b/sqs-stepfunctions-pipes-pattern/template.yaml new file mode 100644 index 0000000000..1577ef79e2 --- /dev/null +++ b/sqs-stepfunctions-pipes-pattern/template.yaml @@ -0,0 +1,114 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: A SAM template that integrates SQS with Step Functions through EventBridge Pipes. Messages are fetched from SQS and processed by a Step Function workflow. The template also creates a Lambda function and a DLQ for error handling. + +Resources: + MyQueue: + Type: AWS::SQS::Queue + + DeadLetterQueue: + Type: AWS::SQS::Queue + + MyLambdaFunction: + Type: AWS::Serverless::Function + Properties: + CodeUri: lambda/ + Handler: lambda_function.lambda_handler + Runtime: python3.11 + ReservedConcurrentExecutions: 0 + Timeout: 600 + + StatesExecutionRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: + - states.amazonaws.com + Action: + - sts:AssumeRole + Policies: + - PolicyName: StatesExecutionPolicy + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - lambda:InvokeFunction + Resource: !GetAtt MyLambdaFunction.Arn + - Effect: Allow + Action: + - sqs:SendMessage + Resource: !GetAtt DeadLetterQueue.Arn + + MyStateMachine: + Type: AWS::Serverless::StateMachine + Properties: + DefinitionUri: statemachine/StateMachine.asl.json + Role: !GetAtt StatesExecutionRole.Arn + DefinitionSubstitutions: + MyLambdaFunctionArn: !GetAtt MyLambdaFunction.Arn + DeadLetterQueueUrl: !Ref DeadLetterQueue + + PipeRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: + - pipes.amazonaws.com + Action: + - sts:AssumeRole + Policies: + - PolicyName: !Sub ${AWS::StackName}-source-policy + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - 'sqs:ReceiveMessage' + - 'sqs:DeleteMessage' + - 'sqs:GetQueueAttributes' + Resource: !GetAtt MyQueue.Arn + - PolicyName: !Sub ${AWS::StackName}-target-policy + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - 'states:StartExecution' + Resource: + - !GetAtt MyStateMachine.Arn + Pipe: + Type: AWS::Pipes::Pipe + Properties: + Name: !Sub ${AWS::StackName}-pipe + RoleArn: !GetAtt PipeRole.Arn + Source: !GetAtt MyQueue.Arn + SourceParameters: + SqsQueueParameters: + BatchSize: 1 + Target: !GetAtt MyStateMachine.Arn + TargetParameters: + StepFunctionStateMachineParameters: + InvocationType: 'FIRE_AND_FORGET' + +Outputs: + MyQueueUrl: + Description: "URL of the newly created SQS queue" + Value: !Ref MyQueue + DeadLetterQueueUrl: + Description: "URL of the DLQ" + Value: !Ref DeadLetterQueue + MyLambdaFunction: + Description: "ARN of the Lambda function" + Value: !GetAtt MyLambdaFunction.Arn + MyStateMachine: + Description: "ARN of the State Machine" + Value: !Ref MyStateMachine diff --git a/step-function-sync-rest-api/example-pattern.json b/step-function-sync-rest-api/example-pattern.json index ad96534ab3..cba2615346 100644 --- a/step-function-sync-rest-api/example-pattern.json +++ b/step-function-sync-rest-api/example-pattern.json @@ -33,7 +33,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/step-functions-athena-glue-sam/example-pattern.json b/step-functions-athena-glue-sam/example-pattern.json index 08dfc2b2bb..5c636864cd 100644 --- a/step-functions-athena-glue-sam/example-pattern.json +++ b/step-functions-athena-glue-sam/example-pattern.json @@ -39,7 +39,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/stepfunction-polly-s3-cdk/example-pattern.json b/stepfunction-polly-s3-cdk/example-pattern.json index 22a06ae696..01d8259ce0 100644 --- a/stepfunction-polly-s3-cdk/example-pattern.json +++ b/stepfunction-polly-s3-cdk/example-pattern.json @@ -42,7 +42,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/stepfunction-ses-cdk/example-pattern.json b/stepfunction-ses-cdk/example-pattern.json index bc3f523ea6..9dab7c83ca 100644 --- a/stepfunction-ses-cdk/example-pattern.json +++ b/stepfunction-ses-cdk/example-pattern.json @@ -38,7 +38,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/stepfunctions-eventbridge-schedule-sam-python/example-pattern.json b/stepfunctions-eventbridge-schedule-sam-python/example-pattern.json index a8b8193f1b..7cc73005be 100644 --- a/stepfunctions-eventbridge-schedule-sam-python/example-pattern.json +++ b/stepfunctions-eventbridge-schedule-sam-python/example-pattern.json @@ -37,7 +37,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/systems-manager-automation-to-stepfunctions/example-pattern.json b/systems-manager-automation-to-stepfunctions/example-pattern.json index 340b3a2b7f..0000374b83 100644 --- a/systems-manager-automation-to-stepfunctions/example-pattern.json +++ b/systems-manager-automation-to-stepfunctions/example-pattern.json @@ -40,7 +40,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/terraform-apigw-http-lambda/example-pattern.json b/terraform-apigw-http-lambda/example-pattern.json index 4cb5c90254..a8f8f57e80 100644 --- a/terraform-apigw-http-lambda/example-pattern.json +++ b/terraform-apigw-http-lambda/example-pattern.json @@ -59,7 +59,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/terraform-cloudtrail-lambda-slack/example-pattern.json b/terraform-cloudtrail-lambda-slack/example-pattern.json index 4ac1044f63..b54f4ef72a 100644 --- a/terraform-cloudtrail-lambda-slack/example-pattern.json +++ b/terraform-cloudtrail-lambda-slack/example-pattern.json @@ -43,7 +43,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed execution instructions." + "See the GitHub repo for detailed execution instructions." ] }, "cleanup": { diff --git a/terraform-dynamodb-streams-lambda/example-pattern.json b/terraform-dynamodb-streams-lambda/example-pattern.json index 8f69b22a54..183d6a75eb 100644 --- a/terraform-dynamodb-streams-lambda/example-pattern.json +++ b/terraform-dynamodb-streams-lambda/example-pattern.json @@ -61,7 +61,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/terraform-eventbridge-scheduled-lambda/example-pattern.json b/terraform-eventbridge-scheduled-lambda/example-pattern.json index 6a9b17238d..30ee238ec7 100644 --- a/terraform-eventbridge-scheduled-lambda/example-pattern.json +++ b/terraform-eventbridge-scheduled-lambda/example-pattern.json @@ -65,7 +65,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/terraform-lambda-layer/example-pattern.json b/terraform-lambda-layer/example-pattern.json index 60ed0999a8..049e02a59d 100644 --- a/terraform-lambda-layer/example-pattern.json +++ b/terraform-lambda-layer/example-pattern.json @@ -55,7 +55,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/terraform-lambda-sfn/example-pattern.json b/terraform-lambda-sfn/example-pattern.json index 968f9a4368..a3a982f47e 100644 --- a/terraform-lambda-sfn/example-pattern.json +++ b/terraform-lambda-sfn/example-pattern.json @@ -54,7 +54,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/terraform-s3-lambda/example-pattern.json b/terraform-s3-lambda/example-pattern.json index 68b244c2c6..89885be0a9 100644 --- a/terraform-s3-lambda/example-pattern.json +++ b/terraform-s3-lambda/example-pattern.json @@ -66,7 +66,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/terraform-s3-object-lambda/example-pattern.json b/terraform-s3-object-lambda/example-pattern.json index a2d2136aed..f5b4f3124f 100644 --- a/terraform-s3-object-lambda/example-pattern.json +++ b/terraform-s3-object-lambda/example-pattern.json @@ -62,7 +62,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/terraform-sqs-lambda/example-pattern.json b/terraform-sqs-lambda/example-pattern.json index b7f19cea02..2211fb64a2 100644 --- a/terraform-sqs-lambda/example-pattern.json +++ b/terraform-sqs-lambda/example-pattern.json @@ -61,7 +61,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/waf-apigw-rest/example-pattern.json b/waf-apigw-rest/example-pattern.json index 253a3814d4..31a84aa55a 100644 --- a/waf-apigw-rest/example-pattern.json +++ b/waf-apigw-rest/example-pattern.json @@ -37,7 +37,7 @@ }, "testing": { "text": [ - "See the Github repo for detailed testing instructions." + "See the GitHub repo for detailed testing instructions." ] }, "cleanup": { diff --git a/waf-appsync-cdk/waf-appsync-cdk-pattern.json b/waf-appsync-cdk/waf-appsync-cdk-pattern.json index af7c6978df..a99099429f 100644 --- a/waf-appsync-cdk/waf-appsync-cdk-pattern.json +++ b/waf-appsync-cdk/waf-appsync-cdk-pattern.json @@ -43,7 +43,7 @@ "text": ["cdk deploy --all"] }, "testing": { - "text": ["See the Github repo for detailed testing instructions."] + "text": ["See the GitHub repo for detailed testing instructions."] }, "cleanup": { "text": ["Delete the stack: cdk destroy."]