From 81d3b8b8ed9eabd4f2adbdc63d607975990c62ea Mon Sep 17 00:00:00 2001 From: Daniel <3842788+dscpinheiro@users.noreply.github.com> Date: Fri, 16 Oct 2020 12:59:22 +0000 Subject: [PATCH 1/3] feat(aws-apigateway-sagemakerendpoint): New aws-apigateway-sagemakerendpoint pattern implementation (#87) * feat(aws-apigateway-sagemakerendpoint): New aws-apigateway-sagemakerendpoint pattern implementation * Update comment not to exceed line limit * Update construct to allow usage of existing role for API Gateway * Remove duplicated property * Add comment to clarify request validation --- .../.eslintignore | 4 + .../.gitignore | 15 + .../.npmignore | 18 + .../README.md | 97 +++ .../architecture.png | Bin 0 -> 43379 bytes .../lib/index.ts | 176 +++++ .../package.json | 81 ++ .../apigateway-sagemakerendpoint.test.js.snap | 716 ++++++++++++++++++ .../test/apigateway-sagemakerendpoint.test.ts | 143 ++++ ...-sagemakerendpoint-overwrite.expected.json | 358 +++++++++ ....apigateway-sagemakerendpoint-overwrite.ts | 73 ++ .../test/integ.no-overwrite.expected.json | 347 +++++++++ .../test/integ.no-overwrite.ts | 45 ++ 13 files changed, 2073 insertions(+) create mode 100644 source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/.eslintignore create mode 100644 source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/.gitignore create mode 100644 source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/.npmignore create mode 100644 source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/README.md create mode 100644 source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/architecture.png create mode 100644 source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/lib/index.ts create mode 100644 source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/package.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/test/__snapshots__/apigateway-sagemakerendpoint.test.js.snap create mode 100644 source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/test/apigateway-sagemakerendpoint.test.ts create mode 100644 source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/test/integ.apigateway-sagemakerendpoint-overwrite.expected.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/test/integ.apigateway-sagemakerendpoint-overwrite.ts create mode 100644 source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/test/integ.no-overwrite.expected.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/test/integ.no-overwrite.ts diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/.eslintignore b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/.eslintignore new file mode 100644 index 000000000..910cb0513 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/.eslintignore @@ -0,0 +1,4 @@ +lib/*.js +test/*.js +*.d.ts +coverage \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/.gitignore b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/.gitignore new file mode 100644 index 000000000..6773cabd2 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/.gitignore @@ -0,0 +1,15 @@ +lib/*.js +test/*.js +*.js.map +*.d.ts +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +.nycrc +.LAST_PACKAGE +*.snk \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/.npmignore b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/.npmignore new file mode 100644 index 000000000..a1a9fdd01 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/.npmignore @@ -0,0 +1,18 @@ +# Exclude typescript source and config +*.ts +tsconfig.json +coverage +.nyc_output +*.tgz +*.snk +*.tsbuildinfo + +# Include javascript files and typescript declarations +!*.js +!*.d.ts + +# Exclude jsii outdir +dist + +# Include .jsii +!.jsii \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/README.md b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/README.md new file mode 100644 index 000000000..394a64a52 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/README.md @@ -0,0 +1,97 @@ +# aws-apigateway-sagemakerendpoint module + + +--- + +![Stability: Experimental](https://img.shields.io/badge/stability-Experimental-important.svg?style=for-the-badge) + +> All classes are under active development and subject to non-backward compatible changes or removal in any +> future version. These are not subject to the [Semantic Versioning](https://semver.org/) model. +> This means that while you may use them, you may need to update your source code when upgrading to a newer version of this package. + +--- + + +| **Reference Documentation**:| https://docs.aws.amazon.com/solutions/latest/constructs/| +|:-------------|:-------------| +
+ +| **Language** | **Package** | +|:-------------|-----------------| +|![Python Logo](https://docs.aws.amazon.com/cdk/api/latest/img/python32.png) Python|`aws_solutions_constructs.aws_apigateway_sagemakerendpoint`| +|![Typescript Logo](https://docs.aws.amazon.com/cdk/api/latest/img/typescript32.png) Typescript|`@aws-solutions-constructs/aws-apigateway-sagemakerendpoint`| +|![Java Logo](https://docs.aws.amazon.com/cdk/api/latest/img/java32.png) Java|`software.amazon.awsconstructs.services.apigatewaysagemakerendpoint`| + +## Overview + +This AWS Solutions Construct implements an Amazon API Gateway connected to an Amazon SageMaker endpoint pattern. + +Here is a minimal deployable pattern definition in Typescript: + +``` javascript +import { ApiGatewayToSageMakerEndpoint, ApiGatewayToSageMakerEndpointProps } from '@aws-solutions-constructs/aws-apigateway-sagemakerendpoint'; + +new ApiGatewayToSageMakerEndpoint(this, 'test-apigw-sagemakerendpoint', { + endpointName: 'my-endpoint', + resourcePath: '{my_param}', + requestMappingTemplate: 'my-request-vtl-template' +}); +``` + +## Initializer + +``` text +new ApiGatewayToSageMakerEndpoint(scope: Construct, id: string, props: ApiGatewayToSageMakerEndpointProps); +``` + +_Parameters_ + +* scope [`Construct`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.Construct.html) +* id `string` +* props [`ApiGatewayToSageMakerEndpointProps`](#pattern-construct-props) + +## Pattern Construct Props + +| **Name** | **Type** | **Description** | +|:-------------|:----------------|-----------------| +|apiGatewayProps?|[`api.RestApiProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-apigateway.RestApiProps.html)|Optional user-provided props to override the default props for the API Gateway.| +|apiGatewayExecutionRole?|[`iam.Role`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-iam.Role.html)|IAM Role used by API Gateway to invoke the SageMaker endpoint. If not specified, a default role is created with access to `endpointName`.| +|endpointName|`string`|Name of the deployed SageMaker inference endpoint.| +|resourceName?|`string`|Optional resource name where the GET method will be available.| +|resourcePath|`string`|Resource path for the GET method. The variable defined here can be referenced in `requestMappingTemplate`.| +|requestMappingTemplate|`string`|Mapping template to convert GET requests received on the REST API to POST requests expected by the SageMaker endpoint.| +|responseMappingTemplate?|`string`|Optional mapping template to convert responses received from the SageMaker endpoint.| + +## Pattern Properties + +| **Name** | **Type** | **Description** | +|:-------------|:----------------|-----------------| +|apiGateway|[`api.RestApi`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-apigateway.RestApi.html)|Returns an instance of the API Gateway REST API created by the pattern.| +|apiGatewayRole|[`iam.Role`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-iam.Role.html)|Returns an instance of the iam.Role created by the construct for API Gateway.| +|apiGatewayCloudWatchRole|[`iam.Role`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-iam.Role.html)|Returns an instance of the iam.Role created by the construct for API Gateway for CloudWatch access.| +|apiGatewayLogGroup|[`logs.LogGroup`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-logs.LogGroup.html)|Returns an instance of the LogGroup created by the construct for API Gateway access logging to CloudWatch.| + +## Sample API Usage + +> **Note**: Each SageMaker endpoint is unique, and the response from the API will depend on the deployed model. The example given below assumes the sample from [this blog post](https://aws.amazon.com/blogs/machine-learning/creating-a-machine-learning-powered-rest-api-with-amazon-api-gateway-mapping-templates-and-amazon-sagemaker/). For a reference on how that'd be implemented, please refer to [integ.apigateway-sagemakerendpoint-overwrite.ts](test/integ.apigateway-sagemakerendpoint-overwrite.ts). + +| **Method** | **Request Path** | **Query String** | **SageMaker Action** | **Description** | +|:-------------|:----------------|-----------------|-----------------|-----------------| +|GET|`/predicted-ratings/321`| `items=101,131,162` |`sagemaker:InvokeEndpoint`|Retrieves the predictions for a specific user and items.| + +## Default settings +Out of the box implementation of the Construct without any override will set the following defaults: + +### Amazon API Gateway +* Deploy an edge-optimized API endpoint +* Enable CloudWatch logging for API Gateway +* Configure least privilege access IAM role for API Gateway +* Set the default authorizationType for all API methods to IAM +* Enable X-Ray Tracing +* Validate request parameters before passing data to SageMaker + +## Architecture +![Architecture Diagram](architecture.png) + +*** +© Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/architecture.png b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..def2a1970744ca91d82e76b79bfdb2ea90ff1c1a GIT binary patch literal 43379 zcmeFZby!qU_b7~nfRr@S9V%TzNW;+0z#z@QFfil*LnDH8hlr$#Akrb7qO^b_ozjhT z!yUl)y}$cB-}mSJ?>-MRbI#uTti4z6wbovT2yIOu!2{|CXlQ5zD#{AFXlVBmP@f{4 z`=}P$?{8UAKlc#2KsmIMe%duOG;AMtMI(1dFIyPg28~Tn{_cuRfX~hu;m#(gz$PF7 zakE6ioO!IB-CTGq9W8yFolw_~s1DR9M`sAk7G?vv8!5miz{kyx`V-I>;A0b%6%ayw z35)TF3JDqCjkko_IQ;{w;p_v0!!6kal!STsP@p(9EUjTq?#>81HbFVmy^52&4IK50 zYDRr)>7l-iP#=CibABOn5iZoFtcwfW#?Zz}4d#x5BEl!k!_UJnjOyT2Q`Xl|XA_V| zUBh7xHmDzb3DhH$-LW$zIt|K_I>|UFBp@pSx4CTiqQm_#ba$<+2 z@kSPWo{HjaeiKaED!U7GvrW?Jt~Ot-r}RflkzY4rXqoncQnWLnqL zqxx|=;CxGLFO%O|iBOXtJ1oI>ubTrQeYvLfJ?xF;gob0Q^5d`seJ2#H$w%IV_R_fz z%GNzQp3 <{c8#@rn?XZW1zq7wi{$RH {q7v&42;lD!8|R3e0&-ZXdr`79Ae9Q}(Qy~}^&`E1V+7ctG=( Yw~dUr fDVvg8AbA3^nPxo#QdOa2A)Lfb0 z*z-p#R8_mIeN3ee%y16eEbqiO{A$lfr)W7xr_7D9a>Cnk;nuK{ %i9& ztey=xUn1bl3xdZ8kMNmza0qaRAh=nv#N!mPsDHHuW3P-dl5meQH=j@MP%H6rb-z}i z&&0+UbxS0*r0a}+l(f<=9QC!;gSZ6Cl{oNt8fiE}@4IP Y>-hP-AV13 !ncRSdYuev$1sVM#GWHeY}3pmvb4}c8*RkXdlfv0My)F2_X63@K3_fm@L zf~hKU$~DZAz#_sXF&=N*{bj0&TSPSPhU>|nFbAeamt^?~#qI^iHj`T0XFT<*%!yB2 z{?Ir`dd~VEcRzCZ4We3QHS=B~k5LA=S>%v )W!{dSnQy!1$B*zD_bg6e=b5{i zkp3~2dr*7MiTsMok=sst(*#(Nlv2VHx22{{Sl+c<6uyoV5uIF6eHK=TO!*^@yK+%S zl}&L_=Ve+u_~TVzwA-E=9*gw(_&}9XJY-I@jCQ+-1b0-Y)@z`Q)0@)7C1^ynR^(;F zdwqtfM;{NVX7pudia56Qhu$VoSqa+okQF~_!_8;i;(yuQ7PL4-`0PM?n0UyojwMb~ zpl124!}5Mzhr*Q!Wl++u7=j{f%JCr$4DQ{zALw}PEWAQD7PZWTN&BGmW#{-Wy@d?) z$Gp6vn%29Isqa4&acc|GN<};lC&F_RDys@|AoT0EX?=08-p0$)kAf{uPlJ|;d-adP z%;#R>$sE~WiVmOhwc2!b7ReHtT~BMx=WJjXrTb70dL!l0Phk#fcQv6#x|oqO5l>cX4x zf6Lq6-qK;Bs %putqH53A7&!#q65k}XTT^eU&v`Kp z;~(ATxUpp!GnBE#5`ISCIyBArD2JwsbUle(1IE$ccIUI%V5273a^YE?%QWV=n&as3 zzY}zE0n|Ds{%p>8A7jkFJ=$VqHo7fIC{w>#Z;TcOkO4V+9AE3k@F79uK41(BHNC(q z)bt`!bhQ3Lw%K({Qc5h2_%3C5sFm3~K+WRlCH{*uWOmD9?xqLFrR>2?58-W(>H{EC zgk!W42}wB-sx>zJ29kIo_y1?>@zimd!Gd5-N(%Yeu3i&Yzxy)q7U7%^Ob@d=9X|+) z)6Mdf78VX5BzAvikC|V4IbGE@Hum`(Zl+1c-fbXs_kt>72g7aN5#YH!ygWZNrT9$Y zaR+7kVfb2+XRrv>UG>c_svQ5|&=1>)UKWG8yVJ({XEz^xp`#-S(_8yNfH)m`*ObHgrz2@Bd=6gq*ZHFipO{``77 z(FDN_lkYq7-M)R0vw(UVAa@~q!Orq_qODLAsQ-uV{}t{|tkE*K%>`_I6T7SEDcfY! zPLwzD{7fo*TTF}ckl8V 15t=HWC-b+3)Vv?@MmQ;HmY^%P1Y1%K99)Qeq{Ck^}zC;!h5@obIJUNy+PS zQj+1jO5QgQIiKY9R2OYbS)L2AEvXjeetQ}wEvQrmeHmLP- pXYq9;d*VfN_#(1yC(G{(3kl99Vr4e<$>jjj>gXJFaNTe$!IO-A6P~(Wqt(c6 zn7#C07OjG^Gmm{uO}3cDA=zA0i(jr%c648{u2UMYzPJ2AohfsSD@HOw9;^v=-!s@k z#kjclj!0LfdFuyb$UZHUT!(Xgl}b2^<$#cd@?cW$1e%4rOZLBsxOc{YgUH$cHC1`- zDv%3U;y8JI?5`t{pu{Ofhj|fPl}^_RB-d88x2H%H5ZxMQ(&og*>ZjGv4JfFU#P|9n zc{J0~O2egkP!Z# rOFB7NIzEQXT4;`{b&=Pbi$y@ zr5>Aav$=#d8 WVeM^IMlezTzyD_Wl{BuhMe-Un?oz-eOu@ zJFz_u^9#|J!1r9Lr}n1sNMF{(Oli}#!T6!qY7)=nFJE#g^M0HWcu5@Y3GUnlkZ04q zpbHnuP04yPAFKG;nXUH3s##n$p=aPTU4x7aPS=P7BvyV^x&dvdtd|+^mxWV`G+|R> zV1@WWg4Ch(_j1cp9a!Qg=Tk3*Cfr7V<0hsgV=!}4wu@2hF)Ev%>ru5_`(6h)7m?f{ zV!CCA7SpE``|xjPvmCSL_cL PzZ4@jB?EmsVB=tb2_8Ps;SdNip>zuoc`&mTF&z%3qlmq$^e2w4;Y5aZ7I&l78 zf@dU8c`*zf_oh5a=;8vg^*yLiD&;46LX@Y|R-H>%6n)Lb*n3j#HkE?# CS8oC i#QI4)4L6A&qJ &`< zOMGvAaG6xk2AY{la|HUdBae9r_QA3Km?{&H%#cgog~II!$jsFHD x2EM|rnS~h-WwyAVf+TO1v8&c>L zenZ>2um}#>8y-X{-y?KdjociwR>HO_c&8*v1+s$Z*$(-S*auU;GPw-6MxEiqXk>t) z>lPNAVqL(A){ Ia#^?7yF2tx3=7FB4I}NBKs=*f=SSbtfa{Ctx4}ePa4+^sww^FQJYBNUo z_c*fQe^;opw?2O^@*6cRy4pe(S^nnaSQ;iluMvyx^d=t$?Wq|)|K3wQ)3HwPGv+pw z|Bl5rmWO7!VNN8G*A^9e2w-~Y WWBBrvS5bKnIWap(~9TsSlA;RANhej__uP zhG#PA`^9&`qve6kmyOd`f6_n6+jo9lD!krUrczaHnmLAV&M)fO+Dk7Gzo3pz$v$RQ zu =HBr^*4cafXi!%FE`#k26DRt=iKfUwyg$Bd?_!vE;A!nND3+5EWsVT-p^ z%JO+>?OVFaHj$B!jg@p&M|stgpB*NpXS_PLX-;|0U>^)p48{^@;IOcwrnHn4PdSxJ z{>r=$n+qbAM^7a@3QZjsu7cq|W-hk|8W^mQeg~nZr|fh(F}*MSV&K}E{r4PozceMx z#L3nPZK3`j14bt2d_vc6)8p2C2B*P~KXh^J!j!1h6YOPtANYiYa*U?|eN+avL9Tz> zK;92CfVN#}lK{uJDM#CmAam!T(Cv>|^NFnn7i~1}jh|#+#b%ZtHzAExQh7!ImM@i9 zjTUv-5MREyupf7&38}vguNvvyxm=Z>r%Oj|cbh&7*%iGdeHCt ^FRj^CTvn!OrVe)2u4;KifyJZAn!lAi98so=Mxuc3SS4!oWv zFYj$eUxQx9r2L1On)cD@uV={JsQUFY@rLb8%B|U*7Ap^9=Qd?blOx*3=APrCPw&GU zqdOVfeFH#Ri?F$u-~1j>=hQQo^2PX3j9vi=rNNNi^E}i!C{M$A#K@_q$4}gk9;vq4 z&mZRJ36jMpA8cA0gwelD=)5N|qiF#;zQ7aNS$8Oge;_F8jo|2tP@lHuJ)jg&aSHMM z uXPA)r0{*$Y$y83zNvwy{K1t)lEhp!S>7-JWT9DkG<*4W+Z8drZHVPFxePr zhI$jQDW6q5!LL<&rnEdK#QXaP7x8 ;2g8!C&3gh!2r zb%5aS+JN3Yx5BZ&5Y&roal=!1P`7D<5kOWyglmz*5*4bwc%h{PQB{AHtW+_}HxyWM zt?SLo_hVn!|0Cv(i@}o3QA|vAW$?&&pZG3$9gar~!BG>oQx#FU6H`Fr+^^^tyj^+% zHqFnxi!xoV4@LZ9VuUJIjm?52N^QsM24 @M}1Jzk7hbP?aPT*N*mChix_@>xbfHz2BvB;TVE5L*>zk+EK9J_@w=I z!|bvdvPn3Wu4e3rvS$D )H2$nI|zkh< pU!;8bHp$9t z$|BpZCN1}yqIM!u>rAPrK+;f|Q=V5QAgOO(>w3iDGQ1^~6-t8^S?v=~$$8X&_z}{a zmL5dwhoZ{T7zaI;3$<4kq8!HSR{6!q_iAx`ff!kE!-uTI9&y#TNfz6|AJ<)27t%G1 z8Dq1~{=Ln;8Y7dVkFv^Pd*TTnIFo&cTg9-wtor^3ZQiy2f;0I% a CijKPXSEjBeldtmaX+z6 zMU!!)qBGE{XWGX*th#*bn^rkuxbcoB1t??9<4<7C1JhQYd M{q=vC$ZT7k6%lTDd zWKub7m_{a*!}peBsd?ZboAcMpa8tl^XQi?Wkl&OWu|Pn+K{^L4)D@`L^d`KhLW-wJ z6Q%#8P2=a?u^9fNZHHQAS-FrLJ#(sE^!&s2%J|Rh2;5cG6u?C~o$+A?qUsWo(4?pG zCRxEeb?RgE7zOg>l+=$ABe7>IGIr(+`|%^U*lw$>s}T|(PTXgW4Qz*%SGV6jb&tI` zcH~0kFG>5eDlYfCq8^F(JAeEf`jwGPG88FiU5-E2s&Kom0-eVrk7-({{?kvrBXC`u ze>Cz?K6Mo;Bb5*#ue^*)NtNt6sv!b9nhGkTrF>>FBuC?iV9pE*X(8XKU9LN`R5#Ll zy7*z!fa$AQ=luaAlWVmAPbX5sHIu#fvK;OvoBjLqB}Vi#2>Q!Pr;70~LLdMH@~`}X zUh6tc+Rs;zdR|_4(O3L!*z6~+|Ho{+tsF=VrmU{R)&KQxzlb%EdX%IC@{$Bu-~K@w zby0mn`p+DsNGH)J3gqWYBBEE#v*#^9etsk)-eM;@il(em=>J2wUHDl%75|;hcC!7~ z-o$tp*S9v^!Cn|Ua-s5B|HZW*g(CogqIM##SDaSD^$fL6dMNF&P6G$(1am5A#u-1g zhLNLjoDuQ*1HFYx#`#$klfp|O6NyUu2D+<~wEd~Wr4Q|}Lo7(XD>uuraANA$U6j^R zeqJ0&8BoTZSEkHNY9YS?cWKQz|00`v&9&C+!V9d^;MJ_8Pj5X~ylDB75Su_i2>5jc zbdpMw^pBdG=`H(*>oGOgU4w2|0exCLzxBOW?P}b4C3OeAZi7$wk|%yXDa?MiApLJC zKNSWqmDOfG`OcRD5WJ$+ud17#6REDR^}!E_=G4~Mh0i~z`i@w8V=Kd=Kl^g(|LT)p zwEZr1x!)GG6Oob4^v4w*=M#$WqY6K_4dcdJ{ikps)I{Wvj&D8@J(Ug$ma))$u#Zf1 z4dM*2WyhffmyU8@z{@*e#!u`r`I#)~C9Qu!nT5%Bf$Y3uO^s+hl(mvQ{4Q;ewprQU KMf(Z9e%Z&x^WE2drw}*?ec)5KqR}H8kK(t+(ys7ap;B zc)o-sDZgH$ByvK?WH4hs`I6-F7S?`e65FD8B0Pn It+U;Tm5e z=PTiPC!+2cVIEmfqcZ*GAsq-0lj5iM1SO({%9mzJqRP{cEKI)SP)1&kr|_8Wt0^6w zicZN>MC9_Fp46h{b|LZ;W$Xh1A;9$yPToLYT>o89X?EF {bJzTGqh>hp^ zk(>*%xlbYAAHkDvn&?qkWp^L;ePy5d_#O7xC_H>vAU~B8V^nIP^5Id~hu7#cz%Ckk zD4uHP1t69RJ9V=C*4h{sj3sss)<5#M=0ko8ax1DM>$x=TPn?$TeNj6RDKyTX?vjEs zq+17@X*AC-jc%qaQfnMGbq17&lDg?7E#k;*QhKJb6bpH$5)tW~e1Y=GU-T%eG0bA* zgW7}mvMrg+l)f1Gc{0>o-)>lx{3hav1p?X2cI7_u`p?TQPSxg>Jd3A(LH(4urk-U% zuOZ4hrSv~#a1TSzpz`W}jWX~`Ll$3gqJX&fKR@&|5_nx&%4auW|37%W&E-I5W7_KP zuRdvC!Ru2tUvX92XU&albBs{RI+a>EUYo*aPf|!SSv9WUxGa~yB<)XpmiK*8I}x0F z1e|=He_oMrj?+!Ha(u(9D^gPZNRuftljsz*Pn04PrKkAT)eA%FY#oK=$c3u6DEYds zqdbx=fqbU|M^uWA$0d-9%UPs+7S4jiA-*vAi7xhmfFt02gK_qUCe8B%Y9sYLuYWv7 zN1}eFK&RsUkL5|9vzNz@v_IiaEk0~WYU(i;=Gh<9d++k)vq+QtrkwfuUo-h)h*zWd zvne0o1&sfB=VkjGZv>_jVNF)VEy(i+uRY~?TS)oE+OGn!Dei@JDkAN V(hlO+|fV~m|gyb7}T+L7+xJzfMg&pu~0ue}bPbGenc`>SjK-n@&F zOq+Gt<=F_rtka-DR<-}W%#oa$=U=qiS6-1EdX=gJ_s8P@0GH1#?ymCSmC;B et-kUfc*(78MQXs%9v?!{6Qz&0TNsFL2>9Xts +b<@u zDQ+hMt#sw%iKMdYk_Cx~ry#Y(p;Dy$DV~wx(ujF1l8Upery>F;()H^{G07TcOt;!s zUss%?g)YN~S^4_w>6n|Dct!)9#H@710oE-m$GpZlbFJp3mzgVhm3!@Joj2KtS@Q+e zRW(4z!+|?WlHpp8J&7^(Y;>WlO-kfOg|phLL-YENU5$C{{1F0ymIy#Q`0qRD8}vu< zEJ3=6GVX0etHtgX+N5bA7A#OQzo2nFWkJ5L3KUsiP4%FhB$N713J+w*a=v2^1#=uE z`I3o*o8NiK_QhRSgL?FCauO(f_LFzp h5${f5$ba80ar?c7 zu`TZDEX<=OrIZJt@=6h~DF{?SzjmVXB}0lKHCNQ3m%6HAK`6MchFCnYfMzvJp5nSY zcb=E0=7F{W5A~H!p6rtH^el>l+B%fW@<7ozZ=UFq{6;(orTna1r+$6KQ`JDv@b2O@ z*Oei^9LMQ2bTFT*M!cZ?>;Ng`J8nu)jo3 6ocA=y_lowpo@z!Qh>uY^P=YVe!#gVRAqVlU5a zl3#-n%Y=OnP@X85Ss;Jv56^I7FKR13_MqM-%&Mw2x*GI~ pN9 =Jz{li}RpQ#$jhp)AX^~+w<&S|1Y@tTph zCX98iJ}gmOCK{cpB-4K`au3IyQz>{~rD8*Z`kxdyWuq!t|E98M@>Bnot}8KTGi&GB zJ@~@mJ$Uz)`i1nL^>@CMpA9{1#cn;}Y}T+RM}OdHU$Fg2e!h~+aYQ#*6l?pb>q+t0 z2I-6x5gC~RF 7;aI?t$gmCz!7w84#hgj2ANIlkXb7 zWb!lCWSZowI6Bah%1 X!#f1_sF5nm3NlVg=3|%g79#(dF}Rf&p*vn3f5D!I z+Qq}ZW<$7s@&($TmmyBEDQYJoouTGriUS>$9_1BE=a>2T7qJ_B4xRmJRc3hy;b2zX zhZa`~+N2yjR4(@3v6#n_*>(Z)spR@o0cT0RywX~b{JgZRp0xbBf})Hikk6Vl1(aNb z{3g1H1p?G*ItuQy4n&26-g0VJdX(v3FMK@e|8^Q~R~~R|e%;}{$>Z!2+<`R>ADg1x z-g~W$?B9oe*S@jo_Ig$$J%G3dz7cZC@FVRHdmV;FZ%o}HygYW49rxd>ZT##R^U1#j zcUaY87N81WJUnf?J?*LkzG-Xs9mr~_KTzBCV_n(*(D@r1SYOXj|7H(l0qs5g$&nw~ zY4_b|qi0M-g4s5BkJi87)K1%eFYDGBxBoiR1-3tfB`;qLVpHS`>rjv!-5|7=3M>tv z;%7%$3Ql^qxJ){B4qffDM7oM0CGx0`)FRGjQJGQzPdOMK7tpAaEkr)KOQ986GWi*M zl*7;;b!I6Ki )2qU7gkBN7M@0V!(If4UQ_fl%kAL_9J(2rpAS0iUAf$DbJZ z{f }-c^xV1iY`(4oPzwTys*^7_qR@)JXr|+SE1_OTaa}V2N zc&C*D`x}DyKbQa5C-2(Vrd@77h11%6rPXY`{wvzmUp>}tz_&sE_QIq1uvNY5c 8y;m7iIT8?N?RjeC2lm|PPwcRbwz6-b&U7A!VYD-B%?<7HmmafQ-$k47WP55q z@zKMMnN4uVS}2&pm)}s_Ya(<8=}hCCvM*2{(-nN-X{8h35{S$>h^P;ul>&p0$~qI} z7U~F Y1{8 (kWZzj=L z8T+IsNk6EVZpUGqnMqnEc%lj@S|GnGzWLGnj2~LfE3ew5Q%^UWTeC>{Nf!4Mkt(6i zyae*S5DR*>SU2xbbdcoO)P51gCJ@LFU^k$$aP%RY$wR6PHtP%+kar+&;Pvk3J#mk{ zhYv+D$e^k(Wb}xSnmXA|x%&p&9(PEczT+@k6<-+CNagL9?eZ5NvFUI;pvE8MssFK0 zQNb51pWhL=9V3IsF?u+ChhetYS{vfw!Cv;KeU88frpDL?xLr4ma2~ 7XTXW4HbzsqiZ`z8Ak>bu#BtJ+2bRu&R8KP) z-cI$aJX0dEd `r@s(m2;m vT3|Up5<5&-BGl7RznF3kxvK2zbSKO+fN_7#ZJESIy{Z; z-^-z~PY#4~c3XWt`}wZ>xxq&nL{vW)hdBEG!sNd{@pC%|&ZK2X^7ELmK4i#W1R+q= zPJ~WF=Q2B~yfUJa=qxM4&vJT44waE-pGu%O7gVHZ*ul^;cyXsw0@I-I%*`tjdO&H{ zuGa5Q7g(QPpNSkgGi>pr{7(3qV#Vg0s`y-dD2np?{P>5~?+@qTHr~nw%NKQX`AJz9 zB%e&0wLdGLHNfl2G}NbM`x9yG0|6o+y`~CNS9t85NXNc$oY-eP%Z>A9nBw~SX4^MS z@u0Bs(LZQ>n}RcDdA%Bi*K_VgY>mGC?Ld6WR=58;*?jVISTI-rLz$;WePoB+aEXnZ z<&K1}H#rH8 2H=1vT@N2Iqaa8bpnw|Ok|z7 <2sSX~VHrL4AGxvrp~aiF`i9bxc#REG~=dQgm|*`i|Br zP)1Gxo1g5w4;Ib`+X3ruX6HV3x4nrGfv#+md^`|LT~Gaj^5@mnA&nf&p2pjMpTdvo zzSH2fmDUSRWa6Az5Qh426wXYP<8@-8d|xbQU07Lc3+k)(b9Q0@@=2>Qk`|ky9*Iyl zq##gUX_1bR>b5yMyK*xyxS2Ac0LL#%h0?G*`;e}Tkm`oHIg4z(qdPPz*5AMeUUOyM zfy}z~4x4i)hQX{10+qe%P`?Mh_PzJ5Zqz87gRd&ix%*z_cV1_08+hH7)_J21sL^@( z%EU&U3w!k;GR^ACd?`KK#$?~AeDm5OC!=8*pJJc`YHB~zBP@FeOrJ5s{(bA6HfQeK z|7qnk&66ojx~ Iu9n!8xpS;%XWX&Hj)Wj%FXi>) zd|4doCKz-!^*_+}K)KGC5@z7q#y<}^&d%I*gci69<*P*M2if%3OJhf>+`$`eX@9}O z|2caetmCP;)p+ETNzLlxZ+VzsepCPB4g3Y22AmcrKiLEcUCuYf?L?@!(p4H`x`k|? zrSaT~gn9T$F_ FRD%h&mrfL@}THiWK7B-{ldGlcH7Mc{Nt~tSCrr?W!fMAY*T)H zHasV;!^9f*HFpP$RWu>9dy-} z>WIDTH}|~$!Jb<=AQH2nx6flN&m^chd5}c=g7%Xz!ZL?IU0uE1eCvPhgp+=3H~#zf z|CuvMU*@EqP+^+%&yN#O7Y+ZK{dCuTb(d5F^#5P4J!=PCd$B$D8AbvHIz}Io^e>fV z-_!J~^87+>It>2TZ~mM8=8-#W`n `p1R+var-ajdw)j>REY5|c; M|e5NP))5}yU4foWGY+S1pDNdF~1>-V_uG&eP1*VsF z>tg-S{ {^)J{{&suV zL3m!HS7jIb*={53t8gf1+;_8$o{k3tah1nE={(Ag#hS?e*Z$G=SYt!mX~^1I@PFXL zw=u1Tr{6(E`JzRc?N(dUzP07fwiO;M %m z+z?vGSDTq0!KWXK6$d}*R9^h3l-O$y7m8UJsC-oLj7;>5ODWk0K+LNzra-H+cmW40 z(A;XP{pb@ }b8IYfNG@HY?LZpYm8H{;z*GP2Y)(PKviLl_J_zi8jD z*|)d;ijKSDVZQ4RKf%_=XKP8KBHS~Q1wQfq+lTM4qi(#+-h`84SbxAEI}SWp6Ux#> zmFkB~l%0g -FDfolWer1HK^#!mM@h(Biqn4z=%Y zyIV8)sr@R>P1b&&d|R;mJneEGDe8rF=`0N`jH|!Cn%6w}F-QK?x~?`UEEoPrDMiyN zAW1z%20L>&iGha0R&V2uJJAMQaE?`NzpYhmyS1%!?LTb%_fNID&&EoArxjPS3a)h^ z%;r=U6(4cduYC2@Y{kF+$;t+;r2IL!Yir`qeu=5Drg|xZ8_LIyz9ZwHET*xZRPg+Q zXV!jYag*{WxIn&pp*+ zF@u$+Isa5tZ>0g0*o^adMc+b@sq(lF7{h1M=50#WQTED6ucEYy1YBbRa zx3@F!-B^`n mt!{H(XM>)aZHKTHVrDkE(Nxq zzDr4z%Qnv^KT$8=`=WLtI>k84O`X6`bm0|_pI$e`)Cf5cDu;TARK(fP7}Jq3$uD{G znvqdi4p% o=lgi5@QpXHppIulvY|ci%5udkvC1tr zvw?s9gOzsgE}ql>@lTt2@n29Gt`bt)^P&{x=!1?=ZmlqziJ;{jTfELvK98mG1w25f z7e7ex)dMI+W%E9>iF}c Aumxh~<@}YC*&RQ#I&uJ}Q?65y1 zeafz0PzTqYRGI3!8t9N;q~QYza3ijqRb^#%45o{|vE@!$cOX5V4Y~TIC+wduKV^HY zwSoQUE5kKSb<7q!+8%3eV840zHatthmmxxzv;LUmH$b=NVuYR#Nxw;mz?v4yXMfMX zxq^KO$JGBa^pjPy_jFy1r|Iu`_jP;a^O1Jr*S=>Js58HK_|cS!dHG_VHmVeu6++NL zej;7o_eJhRpf6A`ooAhIC>NE82?c>^`SFRa@{y1(D?|?-N~eORye1~{HxdH=cwNHe zU;obPMvk(+KRiWi8#%nrf%WJ%Y^bDGzy5~Re?AUtBdkx(>L*P`In)3Pev{8S&u0GT zR?ubqGx_=Ig5Em>s|Z(te!~Z Js&BDFu2Cp)p`us@T}gu?bG+So6<2>dUc zM{2z;pQ!)rC{#<@YVM(bg%@PhOOa%B)X%QvmG<9n{J>V|) ziLl@`hoo)7UlQG^wOZ7z3s9CziD^A^Q!$GxAji1p6mUuP` gv~RO?E(oBy^m542JmXl)=6G*~vTXWxer1sspgjlER5tgSp_bd-Tc_ z^?&x6EHLK$$rt^Hc(jn8XqWeWQ9BX7wUY|uCzz*_-GNF5WzWtmb&%=O0jdKMMB@p_ zOXd}+$kWx3iX;vhg%pgr XD@vAu?=5uQ+xQs zcd#~bpM8o~pQMtcE5U 37{|cfIqP{bG*;Y#>GfBQUkJEnbgu&@~s^wCXv{ z n~&P^kC6BwzZEWM;A6H2gFOY76T3SKP(*`{9S%N M}P%RXo zLP+PN;fSNRP#LA5(iW%7^8g*4ffN*R(L&PfM<3Ni59u@tkw!OaJwy^H41xUTz4(ga ztnh;5i=MRlLHo1%IPHSvH*deQ=N8m{P#`}`Q2RX|+hq-ba=b$4&;v) URJ8{Oq(F|ICwl8TQfC|FYiEQhvsqwevc2I$9!2 z?Cy78v#EGx%5hk$*#;k>`ef=vyYA(u;lz5`*SFkJ>owordKddDo;SJU?W~W2p^9@u z`EY{j|1IRZ+}IYi6XEIOIZn#wE(HmCory9?;fAy6FjJ7s;jp7nNul5($&RUyDKMSt zoea7Yva>YFPg7a*UU|*x#`~36j*q^RAO5|J?_ExF`AHV(G(=|t1g2^%PJUhh+oGWv zy8J@q=Ve7?nL?mf&z^S3f%| Q9ZoK*QVu489RvoZKF z=%a7cKj>knvQn$hzpYo~N}tI${yuT9oQMixQz*Y5rkyxaNr8QwGYH;|QazaqU59+8 zt339F@{|7aZNFT9;fJXBCSh~0v z&tBfQehWphq5x(02a0sYb+WxFJo!|uEPzffg2~bgnr2sYpYwHPR0omS&J#L=yc}|2 zIsDVoXX%D3$hwiEEmvJ_v+lh=69Tg@HrOnId>6#6%T|rapg<0ldO`9F=dGO&c`Qo4 zQ^htA5CYOO?+p`5rr7Cs{>zTM?yvU2B!4q;YHMo0`qo0^C)(wGU*sc^%vj2_(r8L+ z*ar}_(fMsUM2!C$9q`(PjwUnAT*1aMulSkXRGL>g7||*P^i(Jo6BYO961bCO+-YZ6 z3BE_b(F5zjAvZ@ZEJkt8kO%l*kbJVr0*f(d wX6IQfON5f21(6%W0Z zS_2N(9~?pN-Wvb8uHyMr_*!^9`acf&wmtUoyLR3qciPCQc)du{ZRx$)uyr=J)3Nrz z#dC%ex8L2)0j+d7(^Kg` 38qApWA|ZKSAAQ^7a8)z zJ=-(hQoi~*%67%wZ@aFwz76ivFG;XZaL3j=d>0tYqa(9^l0}LF`S}9Nd8DYF2=&lU zaXO1Q-I>O4$DlnoysE<8N_jVt&u}f2QbN-*Uy>CR3n&h{8LpDZT=rjOlX6qVJmm1| zhAhkLk)?B?e{u3NQkC-#;!|1v@E7GSUOstHzNY3 h$)UKstc^{VP(YvaK{6=wg_>ZA8H za$se(M#<3#5=wYw=bl1+fHbKK_>DHr< !b^eGTrOy!yo_?DA(H z!E-G5{ttL)Mi{nZLGoFR2zViFF@rz{A`Qa6$&^EVc=j_5awo$p38c&Gc!sVm=oCPz zg*qsp;sit_PRBzh$Ye-k$qKK7m!`V9&L^M6R5tNljC|56pk}I<%U3;pB@xv^+QQ_g zPRU{%Iw34dzEi|D5b$mxrHAg&4pVW`%_th5 w=QkvkEM8faK?j{P)qPtB5c>HJa`s1VW#4o#Kd1z1K- z2AY!D>HJicjCZCXP32uR)xl7BQSwPiQC_db%g;oq=DhA {SLe0%S3I=$pVywV({|m*_QxGfeD-1(UJ1{@ z*P>j4JGHKVpMDtMPUl00EaQQu z+i!K}vDcGpF|4rCy=tnzM5q``QGC{43gquFcpba)@Z&|r&70#!+u0BO*IpU(DcSqz zhJ_JYw!fu(r;2S+k3?itF^o+{m#0(8lUJ$i1iJ zr{>j}_=46anPktU$Q(v5NIo3^tD*T{m6i}^$Yb$plAmQ{f>=eCXASl2xj2y5!sX{V zpmVtcTDbfs+K2@L)HMy#p`xz;)K98t(m(E#YBt*V*)!~nJFmBE@ih9?c$G;HtOY(h z@ ?r|`OvT0_LvGw;o%S7wsRi79iP>m;$=jdq&bz3 z%$wwsmlp9Mgm?4$cX?QOuRizcf7lnwXaC=Q&GnhBZ^HlPfm`j4H(y5E@CT8qv-&>{ z_`i_$=jDr2Y>L{6&?ivc7<3l}ttL{ST1rbB=`j=n|LP<36@HcCr4tYKB)|HnvG&vw zydGhl4|or|*`y0Ew#k=XkyrL1ci(0eYc 9ND5f6ou1*e-a**-)cykEhz0^s zGir+KlQbU}A X<8VysgO`5cL4MgPCO>j1E-xY~!_d+%UD z1Vn|{m7t=6U>6HUP1I;&kAEzQC6=h5u}6& >? zyd`wPElLZ-e5%ghUp0uPlk5jK h3L1eTUSom`#?EdM;DV9 z%#{&(+{%}_D39>A=pM$ioa)7`p@eDa%}RrgV^Q=in9EZXqY(YbJ?0NNtwek~=^9$) zm~e-NjWQy}(;D`iY0Tv`h>wf P9`9Zk zNtkh+AqZq38-ATG^KZY)+>zYc3Ew!Ga|C3`lh4?<*u$vO -=!{^q1?=-ih=ZUwMfs5d=5J z2Mx7gdJStFMqibrDTR=}21RIh=&-Zc74%&iNK1bpkGzHS6N2)Q2si|cPtgGhxl(z> zo>jPPl%jX6|3$nBMmCdp$H+vPO`YI6R2SGU(Bnohb#?35m*mS?(<1%{OX6o;Iy@kP z-Xr>Ih1s`dTY2b|Kj=dJMtl1`f73=A!E|cY(i8~3u=s`^ -jG~ zwoL#?-SfZZTy4m|OV`M$B_;iBF274J&exnCiC|g^%wdhAai6k=GI4>D2&*<}zEs^@ zFpZ1{{XsbN8Si8uEn_@&j*K}VY4{^fC;)a(K_Bt))AZbezLyl|ZVh_^{fHKFZ*g%! z5_&hg-io2`LR*BSI7QO8lzAZn4gtU0&>eT;_&?%VV@8My_}uXz*cRLjk@Lm{3walg z>Nj7%8Grgy56a|)vt^Uh4lcO*IbDMX#L?97Z(&0E#`GdhbfnR{<366u7I!F+%U^j$ zy0vU4FP--neR;5ov~AE(CeG6jR9XHkry&h JDsS zBM0Rbt8aDiJ2PC}s)I7AvWl}>92Yp8S+d3%g5iY160lHFgwTdm5DrvbSh%F7uc0-( z^>s9yMMPwM+LFF`z)yogrd(l=w(~JMh-vmB=qIG_ Hxl=L;&Lj8u%Wc`X_B|S=| za)k;~Nf+kv+@y9Lsjuf<9ofFKz9HX7y6@&r(B7!)EVFc{m!)dtbo>GNH^EW|+|B4c zRBS2fqp%3Bc{C~BqWob}F~8-s62VlHO)a6K@e6C*7{)nR^@2&jJ78|~KJc0o!q^i$ z^gkNbs$V&k220LjdtSpCL7D*mLr)tZha(pk0Uja4YB(fqiHFa{(GN-44fHYe)@Yh6 z{_wmN`UwfRDTmbb6N2)Q2pIDl$18(chfMj_SY0pRL>y?E%zxm_kk3Q>BAG*jt$#4! zTleUB7^Dg~TVEG6;d0!qaT4f<$;wxH%>2c}o;3Q=h(&3%YuH#uUv#Zh*OA91-H-9X z<*D%>$O`>F2~1J+ecs>?nTH}NivG5gAJJ)a87$X9a5@02;TnFY9JEx9k6rOF8i3M( zaA4}nS;IIQ&sux<>regMD5C%w2#`w;34j)?j)0|Uwq*K92gX8e6MYvR5gdJeaQ`Mi zVL~bChp}B5LAMzCJ}mD<00_A4v0ygkVX(SB8lOAncJ63Zl?k1E;SPTHQlM`w>ltr$ zK%&O6JD6CXO}3B@I%Y-!imIIS+vgzN`ddZQggcnkJPYWnOJqQ+5dB{l&zG~Gdr02- z=?7ga*(Bf2o-UURe?kU~d$m~l?yajZWuu#dew23&TLgWV=O~A @K2DJW>KKp)>^*_`O7c=1S(AQkd?!3bG*!9xj2?j%G7sWmMbWA9ak_HUZ zaD6Wbl&1iZfiNNckUk#Oc(HWC3K}Yu7{aLW({@GCw@g?lC%DDYw_v;wfe-;>L*+9k zCE)e~-i5q`F6ydb%*$dJ|DpkhIro4b%9erLt4D{d*5lTZ2JGNmgunr!uy<+RT5jum zhU}vY=6g16A-BBol4IRM^t~Lw)4vZ=nEr(6Q{}uFkJ`H@_@W|cMez#JuUWa0T&~kz zXLdVSLy0@0UTIoB;ZTS^>@dZZ-vaQG 6Q38mo{VZiPySm0-)4(FUXSOZqI}L%3mPUp^6;D4RW)B+J!rKjxTcvN( z?Gw@R*aiUDi=ywt@lFI30k^R3noH|^fVVNtqwK9+G7SNNz*ES_V9#(4<1XD&d(TPd zNG*Ng@B%%tw@>G8su+gL82!K#V%ofzhOJe#x?HUXG@hj&f~ugS7MQ(TwAE+*^5;fn zQY8j;<>|Y~2nAxe7fl}{f{hv<0~II7Vel~L@Ho0-7rEuw)1*b+`bbD{B>V&VSzHX3 zFw5Yk$&%@BYxym~m|t>!Bm(Avu^TShS{u)qtA26t(1T>xmj}!8(PJfV%sAON7vBwV z3dAvxi-X{%TOdG|XhJHQCfZ_SPjxm5IxPmaOGQ7-ip!V| m4W7?%S8g?)wCq?y$BmR1zWT^U8Q@mP68@noBB2Va2O}7G;#!!#DKj{lL0nfFSfMm-?R&Z$)d43- zIP= G`-R)Azzi^8$R$HxlG=xN*Dic{ zsnKm;`!&DmI$5nR44OiDW1QsApQpm&*b>aQIXrINhTbkIcXeGB eRyG!7r-KDWX*LeR$r9fFYp#2EMatQ++o6H~!Pkk1=A%kLqT;iNN4HFtYOh z!WGNq@?lTN(62s`+mAofz6o-8`%d!qWjD$%x^{sV0b#($LnnMH1Ku1VOV{WbTdvmz zpDpNH1dF5>6(-9@3i^-}8#QS%&@z?ugy0Jg=_ywp-p5WGMl#d%8zI-f_JVx)%Oq1o z7Er@l!bQ+GdMdTWmLCbz {orSP8J|Z zp-fPP^7 VmE-W7NZ*A;Ko`gLwKNn0 zsi)7nZUr+%V*$g9qaUR%m0C8FI=!9ZsIjm1sw|B#uqL$no9`rV^cY$G#+&w%qss{B zrlOxnjn8pMz}OzFq9bPTj>(l{KX7W)&N`k354*4h9I70xGVqC>gVpnaTjkH)kJj~v zW2I`PN|6GYr=QRL)6mCs)H2PY+h|{T*S}0_ER7MncKR@)>C&Q&Rm-8fb_TI826o z@sUljMe>J*dJ7R=l|IrqKtG;!#|izVePeu!1SbS >L&p*EN^f96bC@bDm6_{bos+`5(2?Gq{#O#LXw)%cB$ zQ1sXQroYK*J(eWzb)E7W{ic2+eSXNG1uO!lv-eN*uB- =CdokZKb2zTFFdA$KdEVRT}n59Lc^L3J}O^LFEZ<22*-4SGz_1Mj{eBfcFk|I|^* zk@}qxm}dH=!#^B&q|978To$d&3*|!7_x!K)JGF3&?)$gh&EnuvTh~$soN$($-t}Pl z`^X{k{hS#^(r;L^rrgl$R5^9u12vzJ0+A1Xo~WabAu@f*BBkf#6=gJ;wfra3-`?^g zB^u=m1=pIbTm<8rmU^T#7~ePp#jsTlYe=m;V3-6ySH?i9v~I0y3CBpiJ~}1T#f?y$ zpI)_xvsy !WizCK}miP|Qw~>pU24_=MDiSbzPyR;A z*Q_C{KKnw-={MUkVk)dS*8J~#S@zl+lBbFzfA&0$1xe8VgfiN+nWd ^P&|v#`>Sf-DreV-*AKPhPxkc9{5*M}a z()Ez1$G>L-AMNo;y&5&;nxjvYv-UeIT6CW@f3`gO;W#<>z#h`A^)5(J-+JFH&wl=a z418;ZtX#WJ(+@0sfBiVt-+G-Sb*tB~a0~R~RyV&sOh)SZ2SgysRbt*Xe>TyKZN6Ft zQy{(^zQCc~TML(e&VWIddfYF$twdTlC4x$J4Q4`bBV+(;N3E9C5B#p&;uuSx^`T1Z zma0rJLOFIzg`)Ai67h`U+iUCWgmg tPQ^D5$n>^VF1nY!gxKN(F@PKCv_+FpY0pV?zqj|b$ z;L~cAs>oG6`^p~=JxbRr+)?n~&zWf>i_XnkngXe(Z=b^~*H27C$vijyec7aIeC6~F z^)vT7#1`g#n1w5s%Wb*?Y>KXF-g(klNlNDF&)$=}-Wq8;u)LhOd_riDXN;_jl*@Bi zFesGmE&rSWkGq6oe$8nm@^y>dwfa2Aq9()VLM=Ub2JhTP%%sm4!aF-S3sD1|@`6I? zE%lBoP$>Cx=gIflLvk@Jn4<$yE@F6xeaGo{(l5qUY1bB9u8QMFSvpEb9J*eUKXW#6 znpXN<=lqP!(hvbdpxHHlQ~^;T2piXsiwn>}uU%!zyiHaLyb_;$JQEG|@LcPo?9rr! zHTnl8e3Z(^$0*|kJvi~sH(!xut5^A~IB%ujpn5I2;n;q1y1wV(r>oHD`{!@;aL4OL zJg?t4nPF0hh=u7xJ|RmmmPdVbO@-Qap#1X&{@2oq`z5E9$b{y*8*^iE8Vq9wnnAli zj$ND^7{`=IVr{Ee)FV-Mg!Igg@KtmTrS=iOmDOK t9eNC6-xspw<9WWdS4 z(-#$6THF;na=Byd%QnqrBZ;lhcX`pP=EF?`d(qzh^3T8P3Cly=FFCG6cGHf#GHe0& zYJIw9)-j@aSSuXr7Vquuk?4TARY1G~(j;7bu!n2Rv8e7+b|mcNV1&v+`rYRJf`ZWy z0Yjk4U(YlChs8CnPCVQzfx1kn?#BNr08kHJIzs76DSxixmLQKlf~HZodUZKR*E^;y zUMQnn4lC?~@>RF6U0!6-VEPv$CLE)`+N0lR)PZx!zj& z7u-!Hf_ls8_e8AGM{S3Vxz)b0IoRGYf^kD=K~U%ko8{Fz7Ro)N*jYH>APr#Z0!jt( zOoVU)gCb4Wt8z>D=HOgC_vHY6l!Jx{cm&XXy0f70;CLK(-PY@`WrWulczs@C!+5tyX&^n zN49itmeJqQ@>|Av$#o^-7{`qUlyU9#RlIkhHH+OP`rsHOWH7j^ag$%_rgrx(1w7mk z+ydw`nA*AcxGDfh6Vd@;fKb7ziR2o6S4LimfJ4B2*$?cjasZyz4v{tYYK3Gtqf%pw z>}s ;#Y^Msr_!JzdOp6f$7}A(pJ&Z@OeGh)>ZlVEeI2u =Y 9X8{=X&)-f;`-5CvG z^R8iENxdxutO%qQEGjzXfz;Fk0n9^FQ;rW*2?ZFQL(w=%u&A)-B7KyOh6s2BFxt>Q zmhwIr-*=rL7mokI=TIJK=uH9GI~67*#{cRZ#dUJyMj8kKF|?P$!*q=h2qTX3Fqz6# z`Y1}=O>U1wT$je`5RJbZX@Jok`{L%NwIBdcFK*P~B0z97fHJcUrAqTxdU zLR|N+DVYLtLS;gu(Q=Nyfxrt9Fy>YMMRO0<4?9k NPw6WF_6AB_g1c(3;AOhQs0J;n` zM1Tko0U|&Ib_f9~ksX4Kk%#~hAOb`H2vCWT4~PH}AOb{ShY_F>* Jj?bc{#@hyW2F0#qVd#slluua}7vC(5c-tFkO-%ubvU0AK$4 z>#uQ!phRWguwjF~4&&a%DN#aM7&&7~q*JF(Qm0NGS*PDJ&oy)N%{SYNiX%skOv-xS zzJ1LdFEQfRq)8LG^wLXZ(V|5b?*IPp|D;Ec9!YV$WwjoS{ii?uNov-tDf{fRkJPPO zSGsoXDibD5@Ig}Ef#JgsKTO4w5~1wy$VHxCxNxDIdFGkYs8J(n-@d)HY12l)$M|&Z z&M+N1bdd7p%gglX)02{a T-Z6|_C5F9Q^t%Lv%~qQ zQKN=@_~D1e#{c1mALON%UMe=s&hX5L5<$cG^Ups^ety1;9zA+zlxwG^`P_5QN$uLT zW$@s^Npv>r@eyaAeYVV 7t7-GU89W?Y^x-qYUR{#JZk4Z#9RCF=Ax88cIxfgXoBM&8U|NZxy z0z~ayWmweFvzG3Z62zr*5u_y-q(x#0=|);W0qIzzTb5Emq?cv|l#)^jk?sZoDJ2CY z|7W>h?&tg5PxrZeTXvq`Ilpse-ZSsa*%_Q1$I>nOnPl#j y5Oo}6;pbPm2Wkz!kpZ)P25Merb|`baeBhtJB$2)VYGw-Cd?9>DVr}LBca!tr z*FXm6!}RW34sWgfBu^H@$sU{>|CuulW$FV@=ACQwPOWp9c#WwsoI-Em%i7_TVeXTX z$Sh6lfl%o599HL^61fZXRxJMhS1JYg_JhiO8DIM5XezY|le&VCtIKa^4ECJ`%jzd& zw}ihie!e)G(SF)`;q%k}Q+^6W(jzy)Rj4tnnN$3H@w*ZyILN2* eP zL(YFf)?SK|%k08Z2ZAq7iH0K-<||t-{V_FuS3x1txCTxO_}3;-U3mqRJEp1COIGhw zf?9r_&C5%TC%0 l^T#BiJ Dmw-rj5nsgMJb(S$3lu8|B^0vmK_O!N`XFUVecpx#rd~Y@$-biqc<;8|_ohUZril z(a)AYdj3>-4zA7e;NU@m4s~}yt!7!B2**pH>91FfBgVH?@A%o}A{f>O<#bdi_#Ri@ zcFc#Y*DB@qb_~NKtUSFAZ U|Pd_T@Ui|a81MWn+||8AMi zcHIep4x+XfL9J}jtZ*~LjuoaH> 0${{~>v zpE`B$7tc-roy Gsg$sg^2$XW0hQyxw{kW5 zG%_41$3flp2r@=5A!0_O%n>bV0h=3>x;0jOS7rJrcrJUs6~M+_EtFF2<0l$@*c*6? zKS$c;_*r>~ko3fxs~MK{LQ*eWeOYe5)Cz#DT>_jrT>;*2(~g|2)o+8>(#Rt6B%I$% zndoqvDC%%D% ^l4&d^t+CDA_$in%%6&Y>jL5j!E+SBPj@8B1u>btS7V;l;6HX67JO&9;3lQ zYtEBW?>2)!*Ytt{M!+nUDgEaY`{2e>Nq&*65C8Y=sSg!6rCU67mAi$Xe0A8QPNVbQ zdXF22X+0z$p@R;(d!~`VaPW<=B&w7K=W~CqT@0qEy!lus-pfx_%P+NDbF$9kZ>FpL zxkYpU-iSQ)qKo{S`Lhd~vFKr~b-i^J!c`>zTK|bO4&fHp=ljj(4Ikc+MFUycy)LsV z3t6To^cK~pYj50L6JV-*&(_~ZO$+HVS>9g^#Yslq(wsP1g5DO|eu>2leQIl-SKZ!h zXJL`E?x>8^wcnns$?N6fW!5aVmc@}Dye;KbLMBa8oaX30lowI>J|Fu0n?6z-hrthL zR!A(Aj7XWRiHcnBg%AT*KJ>);V}@|OV^!DrJ JeAwFtb&^-Dn@9_yu4Y=B78>Ys-9MS2oM4>Ji1u>c6+M z;sd;d5?08H6zlYE?iDA>ys{%)(z?G&(=J6=^rUW2v?(bmfUj}4Gf!rkKvVjR*QGn6 z|88`mM|`Da;IZ3{l|r5mb-s=S&FxaW(`3wZGp~8Y;cdBYdm|y$?1_AKGA3sc;=h3g ze>}?jB;L!muACNGND(0Js>Nr(iTI}|`Xw_w@Nh`=ck%1_Ok7%W9OV?pL0?*NBa*@S z*dKz3EU#{6y!K@X!|tb_iE)f3+5IV?>ik&d0)Y~H#RD66v2U?Z-aCT~R`Um-NPBhj z0|l#`$tn>RMjwNX BL?L a+H{WQVG4>%R<>BXns4)u ztbMgrSTg+XFcW%h4qAsQbVi9$oezGZ>q?z&s{b~dA66&j-j;7fuy-C)%knS=-z8+x zwA{c(Vd}*`Wszj#Zq%%1mauX%LAiHZD3_0{|7#;bKTwBW^Uk8BzVruyy<8-9g3qNJ z8Ieojx<|+2pBIiDi88(iz=xRD`Z!);>Ry+y?@2sz_$R9|qm+8cn{sPub2?OD-{$>s zEPrKnPKY%ZA33F{hk$844YQ#sg-UT=Ih#(Hkv_tv-G 8OyL{Fp(Ajcy)B-|_x= z_gdn7o&z5tJ`FW8L+e+Tw7;ZOt;BmpT;#2MA|5Ip8c|Xm3x=4sT!+XZ@=V4zj6$V2 zWDHQ>O=P;8N2ucaDJkrRz5^rZw{iXMS35Pf?F{*(t`@wB+(QbTPg!^AeE7KjFe?7S zV^;8j?}qo;=&9$m_bctPBFUs8V!#>_5?rcA5s7*`8+|_A RU7 zxaRKBqu43p!o8{c)NT651lPA{v~owJsF=$s*6+r4Cld5Y*!-b-{v`(gd3A2Fp02n< z-2EYa{DZb!Q)K&W`JYO3ZS7;bxFtTBc1+d$8(R`WM&a#j4O$=ZGDE(eMruuDLDm#g zD9Yoka%etTRT|)N>3nst_Q0YeEJoNrZTujh_Lkz?G^IGH)smhnYdr%+ppG2F9Iee> zU9p3YMXV}DOF28)1n6AaMHZ#cy^xR|Exw#DPq2fEJI7Dk CR45CGEG80D1#( OS7M**2x#_sG z;A2diM!=(kq*YNhsxSG|xp5dCA_}dZAtu?Iaj8AFYibCT+1A0D7t$GZq;%APglX={ z_z+xzP7vdJp`^6p%6lAiY2Ko4*>}H82rSp@^>%&5rxSfW*<6!f#M5Z{kYxWx!{AhG z1>U3mg%sap)$B_24=K5OK$Uf@5c@b$N_xZM#OOFnC&u6@ohB(!Q`H-Kh{-pJs0RRt zRUhEOSwo!!%N48+@aKIdvJLC$N?R`Vb#0a~GnfewmvZJ2Srr%)OA!t?Atgch+4VKp zbdl1kk>0YBjw?6ycKfaLF@L&}-Bl%~2^;IB(L;odz{5!P6tw;uC<)-0O1Vg?MYRGX z7WaJ&?SvUChH4Z*$GE;LVhAITJ#LAT#x0Lm?AU-$B00gQUbq_?iXoR_aFtLEjvt{{ zl1{OUV4~4&iR`fZ)1KQJ(ogmk3<@8$kPe@D=IOCyAI;{IrY9=1VHbwqSS|7L&?2Bs zR2mLr8v( h!n zuUYRsWiWgZ?SRcq3_BhU1CmlYc8u3k%VdLbQjf$s)Wl$T^acz_GV=(NUB96w;L0&h zUq4J IAWx;TTp;sRv%(#$*pFel5ui zBnxnqixe=Ej^+iEzE|N(_XZ=GOb#Uf|Mg(F|MT6`xmUERWm ^HrpYf?%n3RgU=nZjUt$wnU<$3|sTfuw@`Zo1|TT3Xds3Jt02MlO$HZS*^)s z2%h|`su86$tnU!h!jq^tjLVlA!we=jTlNf$Hh#-rH`tna022jrl?VU+-33j(cao#V zIHW3AI-wh78tYrc3X0I4VdfzJBrCrCSZO$3?x?8~=;tjXE_!}k9@PfDIeoSUy~lmL z*Sfn!x%gUb+<-_#38TE-%s^eA6<22JT`T=KD=-LegAP(Ej9EP+046t!;$K5vYqmGg zdOPzpp$FHtGphjX>cwX4eNK#Z+A#woFn-M0M|nNS3t&7w*^M?@*CuEjbt6eu-63sV zl}!R_%=hnrzB|(IfYUOBiB2%>SsI^4AwF4wNz@U%rSlIwM>VhDrgxFSIA!kbRs95b z46?RS+&!K4UEtj~%R9UeyP7$f3}R>;dJRfg#-x3cr=xDO)RD{SI72?Y(=WHI`owd{ zj%^PCy3s#=FG+T)y>zDbSsL`VjOLpBWo`Mpdm wh6usOHF=`jFY;>7w`k#okIK5UDU?{qF?-gUj#8@sNM6@f z3z)62OIr)tb3-`JyunxxHF3s+3fUf#>X7U%s)BPi`OQfg-@oKH-Ww`Ln}w8J9f#vG zNoe?8P}_{e^{3K3Hs_;t9@C`F20!O=Wr5x2WBOZC2*P=ZrDpO0m6|Olg6QRuJ-V{f zoB*}&E|tjBphPO(R@(rY@qyRuAD*_F(zWitjwYs~fj0#_&G1)+G%(+{)nQAP#!R!g zvYxY8pKS*5DzYcw7tX&`?Z@5U?ET; qvY|bae`S%O{6I4rJowG9B?C;+zyVE*}o>KQqhs#F;@~YFbKKaNpGgSAk z7YBAj^o>;>dY6h58NC>qq@;iSA=~O%!9V5Q3PPRA{JRd3g;lM*e(o(zWHlrRs|-3C z?F0V1>5SRZbiOCt%rSjIzR7)`4=kqMz7wpYkX1YK{o|?S?ic7g-~=@IzIhO@h&Jad zYQ|q-bxHTH{B^i5d({)}X4{YK(@Je-TIU$iR1Kpe^ylg-E`3r;o7RIeeBcdEwmt8p zwZ|`{<`Uq+*+l~)b{FMS^_O9&s>tFTo(lI*({xKD4%TCj7rK$p?;^=a=+!H5jx%H} z{pQCtiKFtf30H`L(V3x8zX!O})b3^e2wImHK1jzheOWS$ZV;6#ZoTZUMO6r37kIQC zEg0kMq(%%-q_KpA>zC+0FhQ}kj~XnArFoAOegE=P>*EV8m`g^$j#>&|b_~o1oR+ ?&~cIOi-5ft!ZZRu&az{^i&7TfZ&6aE<)(u?-oF{1Wv zgs?Tq9);>Vj|%bjj4cFn%O7oUzX37bIY@{a<6AGYSMmZ%EHeNQ>;uTsWJ2`Wl&zvG zny#cj6@Pa7%&}k5urrVDTD+PLfTY+GLDJ&`&5O*R=eOiW4?T*q9++kev_Vg5$^5RG zc=)x^yWo4*Pp{H)^*o7pF|ctqFz4w~b(e2Gu9nUwACiM&el+YX^bi+p&7*%svpN~D z@OX#cJXOnhVVS`gu){fdB)Rue c(rBw}s@3r?& zY@V+XnhNgGRNFJKv%lPubj}==a}3$}Doxd`0o29VSU{Rn?{*9gg0?Hu7Z2f`GzYml z)-qsWNy0mljg)bcZW`CR?3t%m@1sl9aSEPNUK2e_4B_ DjM%`BHF= zYHXdCo Bl~~RI^x0cxZ};8Sjp4{`@*17&Haw1A*o=GmgFV1WR oswKeZ4URksx8xsN%er5{LNew*#@H|8x)V%HFdnz-ukEtNE?9`T41Fdd$;xOkSw z@ZmR`|4Ec=4~lfP_`v0DCM$;*dt>A0?TXiDUNu8_Y>T^J|0T)Vs}{9^ON@^;LOw^h zAN?ZP{%PQQZwNW^%TnWaC%K9cBU`c XkBHc=IIIj5`o(cV}#PSmiC2WCLm;=r|%4dO|gKstiXgxbBk4bmD z1CEl@8XuNAH;tc;gwy|?P}y;teHbIP%;Q?^V*i)oru|sPO*;_y+Hyzm=1uwM&;<*D zlX_;xJvMLI@n6 x*@fO1mXC_Pp D6}goX81cnnp(6NqX)p3SwHvU0LDuT z+bS)%?PA67Zf)vnl&uy`dEL4HYTdl4_(+=cL&aK{8FGy&EeYxqeq~*>dPl7fUTR>dcW3c|Yg5^5C=h zq2sm$^hfG5+TJZjbx=LQKf9A6RZ5n8W|{DxbHMgpUv0Y|UwZuY&*Mz1aq5q!9el^S zLN0{%UM>l+4TcdS>A*`2D@yTieA(ZIzE!Am+!e~I>cwd)v)UbNkM-VCk*=*4A6oCz zE36TpH<+%~DwTo83f_`cN&kElS$?sR!O`l5UROm`e ZHtxYC?KpN zm!45UVw7Y0D0N$>JV-_dK@s10m4j+x-`O$COufrk3CT*Dm}XjiV`!zktMwO!nEA!o zGRDSlx`los56j>n4f$F3&`dpvkEpgdUEZOx!fBCdq>L+2pgl4n9oJF(PsnT0%b<`M z)ar=9pU Lx-GzIGHBJ4ap*Hv+cV*tuzJFfYd)WF%|B1tn)8J2Ehk=k?1 zI-RFM0W&-tD8|30za;aWd-$t1qHW8AcG!m?aSMO_nF}E?oBZAZUi8pR_oQ}ua(Xf* zR`3M=$;Wy%5)U2O`%e-%n=Qf@~CWH)AnA6UZbHdtG)g5$zM)()$hwoRnz2rxG(n| ziwIc7n^qLtjSV-1bPP#y0<{*LAujLw=5j>$WM9p_^YsnL6M|Cl+ygsK{lKV*Iy#Ly z>uSNl?$rCLn)r(--Dg@jN&yEonVW+gNoiaA`>&qi^U*KV(X(93Pv4LRsR#Y(YZ(?L z)-U?|1p<{l_u>S%2v=!m`r|Ta`PbuOB^&4kp qeO&1pw`JfhK}R<{B4%$udU?o(-4F*h?8VmIg!@Uw z)avY^nz7f+(gD|(Ch)DxJ9}=7AC060&_j*gy?*UuOR@td`)k@^>e`LwR08b$ ;%jKlEMGx}RJS8stL8)I|qm zkKc|~nw#QN+ e(0V+3&qW<|~QzUOsnCQ(G#%SV`3`uW1E6OzjWXhtSVfdka`Fxke>U zw8xU-KagZCbeX7BU!oQWJ`AH$i=z|3 FvOSW!8K#MGqc~2p-aq1`UcTJ^FwpDS#mw#S)-&>vYy!hQ zHk3-D{H5 l1EwqNIw_Pt z1Nv8H3EOMHsgvX{Kh1*3?=#Ribm{`6vS@guz%iPDIYw&?eV*o&DA8D!>*lg{sl7L; za~>|%hTIaiQ={J3p#ynl4^hVUk`=HNTEu#j=qEoI^@O8m>fKDQlir~? %Q{cmP7d7h@(1|6D#&D zmC$F%CE{*MKRznF;L#G@R~Z@HnAD=!k!@FE+Q)@pQz82_$HC})EK9UqOf%RC>;O-E z7q)}7A0HqpY?yYE@ep|nkX_>jNxxKlGRDcnbZs0xbA~z&|PKc8Q7E+J8IU^yw6v zWz5n)O EOC84Fcj*V+NX}4mbNf&yw`a^V%Hkp?SPK{e7h;bu~AfTtlPi z>0BeiI~k iLRWR^8&f`cx#{8@C4_G2y-c;Ov&$Ci 1${@O8>4g*xVpsU) $m}u41iOwiY%Ju8t~#V z0XeEk_3MCxjyQ-S@G|}Fh65CX;R+)Fa;aEx>s|xi6bS1TxQ? { + const stack = new Stack(); + new ApiGatewayToSageMakerEndpoint(stack, 'api-gateway-sagemakerendpoint', { + endpointName: 'my-endpoint', + resourcePath: '{my_param}', + requestMappingTemplate: 'my-request-vtl-template' + }); + expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); +}); + +// -------------------------------------------------------------- +// Test construct properties +// -------------------------------------------------------------- +test('Test construct properties', () => { + const stack = new Stack(); + const pattern = new ApiGatewayToSageMakerEndpoint(stack, 'api-gateway-sagemakerendpoint', { + endpointName: 'my-endpoint', + resourcePath: '{my_param}', + requestMappingTemplate: 'my-request-vtl-template' + }); + + expect(pattern.apiGateway !== null); + expect(pattern.apiGatewayRole !== null); + expect(pattern.apiGatewayCloudWatchRole !== null); + expect(pattern.apiGatewayLogGroup !== null); +}); + +// -------------------------------------------------------------- +// Test deployment w/ overwritten properties +// -------------------------------------------------------------- +test('Test deployment w/ overwritten properties', () => { + const stack = new Stack(); + + const existingRole = new iam.Role(stack, 'api-gateway-role', { + assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com'), + description: 'existing role for SageMaker integration', + inlinePolicies: { + InvokePolicy: new iam.PolicyDocument({ + statements: [new iam.PolicyStatement({ + resources: [`arn:${Aws.PARTITION}:sagemaker:${Aws.REGION}:${Aws.ACCOUNT_ID}:endpoint/my-endpoint`], + actions: ['sagemaker:InvokeEndpoint'] + })] + }) + } + }); + + new ApiGatewayToSageMakerEndpoint(stack, 'api-gateway-sagemakerendpoint', { + endpointName: 'my-endpoint', + resourcePath: '{my_param}', + requestMappingTemplate: 'my-request-vtl-template', + + apiGatewayProps: { + restApiName: 'my-api', + deployOptions: { + methodOptions: { + '/*/*': { + throttlingRateLimit: 100, + throttlingBurstLimit: 25 + } + } + } + }, + apiGatewayExecutionRole: existingRole, + resourceName: 'my-resource', + responseMappingTemplate: 'my-response-vtl-template' + }); + + expect(SynthUtils.toCloudFormation(stack)).toMatchSnapshot(); + + expect(stack).toHaveResourceLike('AWS::ApiGateway::Stage', { + MethodSettings: [ + { + DataTraceEnabled: true, + HttpMethod: '*', + LoggingLevel: 'INFO', + ResourcePath: '/*' + }, + { + HttpMethod: '*', + ResourcePath: '/*', + ThrottlingRateLimit: 100, + ThrottlingBurstLimit: 25 + } + ] + }); + + expect(stack).toHaveResourceLike('AWS::ApiGateway::Resource', { + PathPart: 'my-resource' + }); + + expect(stack).toHaveResourceLike('AWS::ApiGateway::Method', { + Integration: { + IntegrationResponses: [ + { + ResponseTemplates: { + 'application/json': 'my-response-vtl-template', + }, + StatusCode: '200' + }, + { + StatusCode: '500', + SelectionPattern: '5\\d{2}' + }, + { + StatusCode: '400', + SelectionPattern: '4\\d{2}' + } + ] + }, + MethodResponses: [ + { StatusCode: '200' }, + { StatusCode: '500' }, + { StatusCode: '400' } + ] + }); + + expect(stack).toHaveResourceLike('AWS::IAM::Role', { + Description: 'existing role for SageMaker integration' + }); +}); diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/test/integ.apigateway-sagemakerendpoint-overwrite.expected.json b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/test/integ.apigateway-sagemakerendpoint-overwrite.expected.json new file mode 100644 index 000000000..453b18de3 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/test/integ.apigateway-sagemakerendpoint-overwrite.expected.json @@ -0,0 +1,358 @@ +{ + "Description": "Integration Test for aws-apigateway-sagemakerendpoint", + "Resources": { + "apigatewayroleE6D48DBD": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Description": "existing role for SageMaker integration", + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": "sagemaker:InvokeEndpoint", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":sagemaker:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":endpoint/my-endpoint" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "InvokePolicy" + } + ] + } + }, + "testapigatewaysagemakerendpointoverwriteApiAccessLogGroupC0933D4E": { + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "testapigatewaysagemakerendpointoverwriteRestApi0EE22754": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "EndpointConfiguration": { + "Types": [ + "EDGE" + ] + }, + "Name": "RestApi" + } + }, + "testapigatewaysagemakerendpointoverwriteRestApiDeployment3B3B03364a19f99c87430d39385b3313a65df211": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "testapigatewaysagemakerendpointoverwriteRestApi0EE22754" + }, + "Description": "Automatically created by the RestApi construct" + }, + "DependsOn": [ + "testapigatewaysagemakerendpointoverwriteRestApipredictedratingsuseridGET06B35696", + "testapigatewaysagemakerendpointoverwriteRestApipredictedratingsuseridB999A556", + "testapigatewaysagemakerendpointoverwriteRestApipredictedratings1C8B9193", + "testapigatewaysagemakerendpointoverwriteRestApirequestvalidatorFA90CC69" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W45", + "reason": "ApiGateway has AccessLogging enabled in AWS::ApiGateway::Stage resource, but cfn_nag checkes for it in AWS::ApiGateway::Deployment resource" + } + ] + } + } + }, + "testapigatewaysagemakerendpointoverwriteRestApiDeploymentStageprod39A2035F": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "RestApiId": { + "Ref": "testapigatewaysagemakerendpointoverwriteRestApi0EE22754" + }, + "AccessLogSetting": { + "DestinationArn": { + "Fn::GetAtt": [ + "testapigatewaysagemakerendpointoverwriteApiAccessLogGroupC0933D4E", + "Arn" + ] + }, + "Format": "{\"requestId\":\"$context.requestId\",\"ip\":\"$context.identity.sourceIp\",\"user\":\"$context.identity.user\",\"caller\":\"$context.identity.caller\",\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"resourcePath\":\"$context.resourcePath\",\"status\":\"$context.status\",\"protocol\":\"$context.protocol\",\"responseLength\":\"$context.responseLength\"}" + }, + "DeploymentId": { + "Ref": "testapigatewaysagemakerendpointoverwriteRestApiDeployment3B3B03364a19f99c87430d39385b3313a65df211" + }, + "MethodSettings": [ + { + "DataTraceEnabled": true, + "HttpMethod": "*", + "LoggingLevel": "INFO", + "ResourcePath": "/*" + } + ], + "StageName": "prod", + "TracingEnabled": true + } + }, + "testapigatewaysagemakerendpointoverwriteRestApipredictedratings1C8B9193": { + "Type": "AWS::ApiGateway::Resource", + "Properties": { + "ParentId": { + "Fn::GetAtt": [ + "testapigatewaysagemakerendpointoverwriteRestApi0EE22754", + "RootResourceId" + ] + }, + "PathPart": "predicted-ratings", + "RestApiId": { + "Ref": "testapigatewaysagemakerendpointoverwriteRestApi0EE22754" + } + } + }, + "testapigatewaysagemakerendpointoverwriteRestApipredictedratingsuseridB999A556": { + "Type": "AWS::ApiGateway::Resource", + "Properties": { + "ParentId": { + "Ref": "testapigatewaysagemakerendpointoverwriteRestApipredictedratings1C8B9193" + }, + "PathPart": "{user_id}", + "RestApiId": { + "Ref": "testapigatewaysagemakerendpointoverwriteRestApi0EE22754" + } + } + }, + "testapigatewaysagemakerendpointoverwriteRestApipredictedratingsuseridGET06B35696": { + "Type": "AWS::ApiGateway::Method", + "Properties": { + "HttpMethod": "GET", + "ResourceId": { + "Ref": "testapigatewaysagemakerendpointoverwriteRestApipredictedratingsuseridB999A556" + }, + "RestApiId": { + "Ref": "testapigatewaysagemakerendpointoverwriteRestApi0EE22754" + }, + "AuthorizationType": "AWS_IAM", + "Integration": { + "Credentials": { + "Fn::GetAtt": [ + "apigatewayroleE6D48DBD", + "Arn" + ] + }, + "IntegrationHttpMethod": "POST", + "IntegrationResponses": [ + { + "ResponseTemplates": { + "application/json": "{\n \"ratings\": [\n#set( $predictions = $input.path(\"$.predictions\") )\n#foreach( $item in $predictions )\n $item.scores[0]#if( $foreach.hasNext ),#end\n $esc.newline\n#end\n ]\n}" + }, + "StatusCode": "200" + }, + { + "SelectionPattern": "5\\d{2}", + "StatusCode": "500" + }, + { + "SelectionPattern": "4\\d{2}", + "StatusCode": "400" + } + ], + "PassthroughBehavior": "NEVER", + "RequestParameters": { + "integration.request.header.Content-Type": "'application/json'" + }, + "RequestTemplates": { + "application/json": "{\n \"instances\": [\n#set( $user_id = $input.params(\"user_id\") )\n#set( $items = $input.params(\"items\") )\n#foreach( $item in $items.split(\",\") )\n {\"in0\": [$user_id], \"in1\": [$item]}#if( $foreach.hasNext ),#end\n $esc.newline\n#end\n ]\n}" + }, + "Type": "AWS", + "Uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + ":runtime.sagemaker:path/endpoints/my-endpoint/invocations" + ] + ] + } + }, + "MethodResponses": [ + { + "StatusCode": "200" + }, + { + "StatusCode": "500" + }, + { + "StatusCode": "400" + } + ], + "RequestValidatorId": { + "Ref": "testapigatewaysagemakerendpointoverwriteRestApirequestvalidatorFA90CC69" + } + } + }, + "testapigatewaysagemakerendpointoverwriteRestApiUsagePlan64BD97E7": { + "Type": "AWS::ApiGateway::UsagePlan", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "testapigatewaysagemakerendpointoverwriteRestApi0EE22754" + }, + "Stage": { + "Ref": "testapigatewaysagemakerendpointoverwriteRestApiDeploymentStageprod39A2035F" + }, + "Throttle": {} + } + ] + } + }, + "testapigatewaysagemakerendpointoverwriteRestApirequestvalidatorFA90CC69": { + "Type": "AWS::ApiGateway::RequestValidator", + "Properties": { + "RestApiId": { + "Ref": "testapigatewaysagemakerendpointoverwriteRestApi0EE22754" + }, + "Name": "request-param-validator", + "ValidateRequestParameters": true + } + }, + "testapigatewaysagemakerendpointoverwriteLambdaRestApiCloudWatchRole2ED1F54A": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:DescribeLogGroups", + "logs:DescribeLogStreams", + "logs:PutLogEvents", + "logs:GetLogEvents", + "logs:FilterLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaRestApiCloudWatchRolePolicy" + } + ] + } + }, + "testapigatewaysagemakerendpointoverwriteLambdaRestApiAccount8093B262": { + "Type": "AWS::ApiGateway::Account", + "Properties": { + "CloudWatchRoleArn": { + "Fn::GetAtt": [ + "testapigatewaysagemakerendpointoverwriteLambdaRestApiCloudWatchRole2ED1F54A", + "Arn" + ] + } + }, + "DependsOn": [ + "testapigatewaysagemakerendpointoverwriteRestApi0EE22754" + ] + } + }, + "Outputs": { + "testapigatewaysagemakerendpointoverwriteRestApiEndpointE0CCBC1C": { + "Value": { + "Fn::Join": [ + "", + [ + "https://", + { + "Ref": "testapigatewaysagemakerendpointoverwriteRestApi0EE22754" + }, + ".execute-api.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "testapigatewaysagemakerendpointoverwriteRestApiDeploymentStageprod39A2035F" + }, + "/" + ] + ] + } + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/test/integ.apigateway-sagemakerendpoint-overwrite.ts b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/test/integ.apigateway-sagemakerendpoint-overwrite.ts new file mode 100644 index 000000000..3bda09e3b --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/test/integ.apigateway-sagemakerendpoint-overwrite.ts @@ -0,0 +1,73 @@ +/** + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +import { App, Stack, Aws } from '@aws-cdk/core'; +import { ApiGatewayToSageMakerEndpoint, ApiGatewayToSageMakerEndpointProps } from '../lib'; +import * as iam from '@aws-cdk/aws-iam'; + +// Setup +const app = new App(); +const stack = new Stack(app, 'test-apigateway-sagemakerendpoint-overwrite'); +stack.templateOptions.description = 'Integration Test for aws-apigateway-sagemakerendpoint'; + +const existingRole = new iam.Role(stack, 'api-gateway-role', { + assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com'), + description: 'existing role for SageMaker integration', + inlinePolicies: { + InvokePolicy: new iam.PolicyDocument({ + statements: [new iam.PolicyStatement({ + resources: [`arn:${Aws.PARTITION}:sagemaker:${Aws.REGION}:${Aws.ACCOUNT_ID}:endpoint/my-endpoint`], + actions: ['sagemaker:InvokeEndpoint'] + })] + }) + } +}); + +// Definitions +const requestTemplate = +`{ + "instances": [ +#set( $user_id = $input.params("user_id") ) +#set( $items = $input.params("items") ) +#foreach( $item in $items.split(",") ) + {"in0": [$user_id], "in1": [$item]}#if( $foreach.hasNext ),#end + $esc.newline +#end + ] +}`; + +const responseTemplate = +`{ + "ratings": [ +#set( $predictions = $input.path("$.predictions") ) +#foreach( $item in $predictions ) + $item.scores[0]#if( $foreach.hasNext ),#end + $esc.newline +#end + ] +}`; + +const props: ApiGatewayToSageMakerEndpointProps = { + endpointName: 'my-endpoint', + resourcePath: '{user_id}', + resourceName: 'predicted-ratings', + requestMappingTemplate: requestTemplate, + responseMappingTemplate: responseTemplate, + apiGatewayExecutionRole: existingRole +}; + +new ApiGatewayToSageMakerEndpoint(stack, 'test-apigateway-sagemakerendpoint-overwrite', props); + +// Synth +app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/test/integ.no-overwrite.expected.json b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/test/integ.no-overwrite.expected.json new file mode 100644 index 000000000..d0e4d7942 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/test/integ.no-overwrite.expected.json @@ -0,0 +1,347 @@ +{ + "Description": "Integration Test for aws-apigateway-sagemakerendpoint", + "Resources": { + "testapigatewaysagemakerendpointdefaultApiAccessLogGroupAD5E1ADF": { + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "testapigatewaysagemakerendpointdefaultRestApi7D1DA11B": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "EndpointConfiguration": { + "Types": [ + "EDGE" + ] + }, + "Name": "RestApi" + } + }, + "testapigatewaysagemakerendpointdefaultRestApiDeployment04BFEB63b4264c277dbe6aa60ff584ba21d5d060": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "testapigatewaysagemakerendpointdefaultRestApi7D1DA11B" + }, + "Description": "Automatically created by the RestApi construct" + }, + "DependsOn": [ + "testapigatewaysagemakerendpointdefaultRestApiuseridGETB3BB79AA", + "testapigatewaysagemakerendpointdefaultRestApiuserid9952BA11", + "testapigatewaysagemakerendpointdefaultRestApirequestvalidator1A23C251" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W45", + "reason": "ApiGateway has AccessLogging enabled in AWS::ApiGateway::Stage resource, but cfn_nag checkes for it in AWS::ApiGateway::Deployment resource" + } + ] + } + } + }, + "testapigatewaysagemakerendpointdefaultRestApiDeploymentStageprodFD1743A7": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "RestApiId": { + "Ref": "testapigatewaysagemakerendpointdefaultRestApi7D1DA11B" + }, + "AccessLogSetting": { + "DestinationArn": { + "Fn::GetAtt": [ + "testapigatewaysagemakerendpointdefaultApiAccessLogGroupAD5E1ADF", + "Arn" + ] + }, + "Format": "{\"requestId\":\"$context.requestId\",\"ip\":\"$context.identity.sourceIp\",\"user\":\"$context.identity.user\",\"caller\":\"$context.identity.caller\",\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"resourcePath\":\"$context.resourcePath\",\"status\":\"$context.status\",\"protocol\":\"$context.protocol\",\"responseLength\":\"$context.responseLength\"}" + }, + "DeploymentId": { + "Ref": "testapigatewaysagemakerendpointdefaultRestApiDeployment04BFEB63b4264c277dbe6aa60ff584ba21d5d060" + }, + "MethodSettings": [ + { + "DataTraceEnabled": true, + "HttpMethod": "*", + "LoggingLevel": "INFO", + "ResourcePath": "/*" + } + ], + "StageName": "prod", + "TracingEnabled": true + } + }, + "testapigatewaysagemakerendpointdefaultRestApiuserid9952BA11": { + "Type": "AWS::ApiGateway::Resource", + "Properties": { + "ParentId": { + "Fn::GetAtt": [ + "testapigatewaysagemakerendpointdefaultRestApi7D1DA11B", + "RootResourceId" + ] + }, + "PathPart": "{user_id}", + "RestApiId": { + "Ref": "testapigatewaysagemakerendpointdefaultRestApi7D1DA11B" + } + } + }, + "testapigatewaysagemakerendpointdefaultRestApiuseridGETB3BB79AA": { + "Type": "AWS::ApiGateway::Method", + "Properties": { + "HttpMethod": "GET", + "ResourceId": { + "Ref": "testapigatewaysagemakerendpointdefaultRestApiuserid9952BA11" + }, + "RestApiId": { + "Ref": "testapigatewaysagemakerendpointdefaultRestApi7D1DA11B" + }, + "AuthorizationType": "AWS_IAM", + "Integration": { + "Credentials": { + "Fn::GetAtt": [ + "testapigatewaysagemakerendpointdefaultapigatewayrole8EA61BE4", + "Arn" + ] + }, + "IntegrationHttpMethod": "POST", + "IntegrationResponses": [ + { + "StatusCode": "200" + }, + { + "SelectionPattern": "5\\d{2}", + "StatusCode": "500" + }, + { + "SelectionPattern": "4\\d{2}", + "StatusCode": "400" + } + ], + "PassthroughBehavior": "NEVER", + "RequestParameters": { + "integration.request.header.Content-Type": "'application/json'" + }, + "RequestTemplates": { + "application/json": "{\n \"instances\": [\n#set( $user_id = $input.params(\"user_id\") )\n#set( $items = $input.params(\"items\") )\n#foreach( $item in $items.split(\",\") )\n {\"in0\": [$user_id], \"in1\": [$item]}#if( $foreach.hasNext ),#end\n $esc.newline\n#end\n ]\n}" + }, + "Type": "AWS", + "Uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + ":runtime.sagemaker:path/endpoints/my-endpoint/invocations" + ] + ] + } + }, + "MethodResponses": [ + { + "StatusCode": "200" + }, + { + "StatusCode": "500" + }, + { + "StatusCode": "400" + } + ], + "RequestValidatorId": { + "Ref": "testapigatewaysagemakerendpointdefaultRestApirequestvalidator1A23C251" + } + } + }, + "testapigatewaysagemakerendpointdefaultRestApiUsagePlan7C5B0716": { + "Type": "AWS::ApiGateway::UsagePlan", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "testapigatewaysagemakerendpointdefaultRestApi7D1DA11B" + }, + "Stage": { + "Ref": "testapigatewaysagemakerendpointdefaultRestApiDeploymentStageprodFD1743A7" + }, + "Throttle": {} + } + ] + } + }, + "testapigatewaysagemakerendpointdefaultRestApirequestvalidator1A23C251": { + "Type": "AWS::ApiGateway::RequestValidator", + "Properties": { + "RestApiId": { + "Ref": "testapigatewaysagemakerendpointdefaultRestApi7D1DA11B" + }, + "Name": "request-param-validator", + "ValidateRequestParameters": true + } + }, + "testapigatewaysagemakerendpointdefaultLambdaRestApiCloudWatchRole56EE67C8": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:DescribeLogGroups", + "logs:DescribeLogStreams", + "logs:PutLogEvents", + "logs:GetLogEvents", + "logs:FilterLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaRestApiCloudWatchRolePolicy" + } + ] + } + }, + "testapigatewaysagemakerendpointdefaultLambdaRestApiAccount6B3C7FDD": { + "Type": "AWS::ApiGateway::Account", + "Properties": { + "CloudWatchRoleArn": { + "Fn::GetAtt": [ + "testapigatewaysagemakerendpointdefaultLambdaRestApiCloudWatchRole56EE67C8", + "Arn" + ] + } + }, + "DependsOn": [ + "testapigatewaysagemakerendpointdefaultRestApi7D1DA11B" + ] + }, + "testapigatewaysagemakerendpointdefaultapigatewayrole8EA61BE4": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + } + } + }, + "testapigatewaysagemakerendpointdefaultInvokeEndpointPolicyB835D2B2": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "sagemaker:InvokeEndpoint", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":sagemaker:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":endpoint/my-endpoint" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "testapigatewaysagemakerendpointdefaultInvokeEndpointPolicyB835D2B2", + "Roles": [ + { + "Ref": "testapigatewaysagemakerendpointdefaultapigatewayrole8EA61BE4" + } + ] + } + } + }, + "Outputs": { + "testapigatewaysagemakerendpointdefaultRestApiEndpoint1EFF6760": { + "Value": { + "Fn::Join": [ + "", + [ + "https://", + { + "Ref": "testapigatewaysagemakerendpointdefaultRestApi7D1DA11B" + }, + ".execute-api.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "testapigatewaysagemakerendpointdefaultRestApiDeploymentStageprodFD1743A7" + }, + "/" + ] + ] + } + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/test/integ.no-overwrite.ts b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/test/integ.no-overwrite.ts new file mode 100644 index 000000000..417c251f0 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/test/integ.no-overwrite.ts @@ -0,0 +1,45 @@ +/** + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +import { App, Stack } from '@aws-cdk/core'; +import { ApiGatewayToSageMakerEndpoint, ApiGatewayToSageMakerEndpointProps } from '../lib'; + +// Setup +const app = new App(); +const stack = new Stack(app, 'test-apigateway-sagemakerendpoint-default'); +stack.templateOptions.description = 'Integration Test for aws-apigateway-sagemakerendpoint'; + +// Definitions +const requestTemplate = +`{ + "instances": [ +#set( $user_id = $input.params("user_id") ) +#set( $items = $input.params("items") ) +#foreach( $item in $items.split(",") ) + {"in0": [$user_id], "in1": [$item]}#if( $foreach.hasNext ),#end + $esc.newline +#end + ] +}`; + +const props: ApiGatewayToSageMakerEndpointProps = { + endpointName: 'my-endpoint', + resourcePath: '{user_id}', + requestMappingTemplate: requestTemplate +}; + +new ApiGatewayToSageMakerEndpoint(stack, 'test-apigateway-sagemakerendpoint-default', props); + +// Synth +app.synth(); From f02997ea4fa6515fc4429771d21821d2c26a8584 Mon Sep 17 00:00:00 2001 From: Hitendra Nishar Date: Fri, 16 Oct 2020 21:46:55 -0400 Subject: [PATCH 2/3] Update to version v1.68.0 --- CHANGELOG.md | 14 +- source/lerna.json | 2 +- source/package.json | 2 +- .../aws-apigateway-dynamodb/package.json | 28 +- .../aws-apigateway-iot/README.md | 8 +- .../aws-apigateway-iot/package.json | 24 +- .../package.json | 28 +- .../aws-apigateway-lambda/package.json | 28 +- .../README.md | 20 +- .../lib/index.ts | 2 +- .../package.json | 24 +- .../aws-apigateway-sqs/package.json | 32 +- .../lib/index.ts | 4 +- .../package.json | 42 +- ....cloudfront-apigateway-lambda.test.js.snap | 36 +- .../test/integ.no-arguments.expected.json | 42 +- .../integ.override-behavior.expected.json | 989 ++++++++++++++++++ .../test/integ.override-behavior.ts | 99 ++ .../aws-cloudfront-apigateway/lib/index.ts | 4 +- .../aws-cloudfront-apigateway/package.json | 32 +- .../test.cloudfront-apigateway.test.js.snap | 36 +- .../test/integ.no-arguments.expected.json | 42 +- .../aws-cloudfront-s3/lib/index.ts | 4 +- .../aws-cloudfront-s3/package.json | 28 +- .../test.cloudfront-s3.test.js.snap | 77 +- .../test/integ.existing-bucket.expected.json | 173 +-- .../test/integ.existing-bucket.ts | 18 +- .../test/integ.no-arguments.expected.json | 93 +- .../integ.no-security-headers.expected.json | 93 +- .../test/test.cloudfront-s3.test.ts | 123 +-- .../package.json | 32 +- .../package.json | 52 +- ...m-lambda-elasticsearch-kibana.test.js.snap | 15 +- .../test/integ.no-arguments.expected.json | 15 +- .../aws-dynamodb-stream-lambda/package.json | 32 +- .../dynamodb-stream-lambda.test.js.snap | 15 +- .../test/dynamodb-stream-lambda.test.ts | 15 +- .../test/integ.no-arguments.expected.json | 15 +- .../aws-events-rule-lambda/package.json | 24 +- .../aws-events-rule-sns/README.md | 5 +- .../aws-events-rule-sns/package.json | 28 +- .../aws-events-rule-sqs/README.md | 6 +- .../aws-events-rule-sqs/package.json | 28 +- .../package.json | 40 +- .../aws-iot-kinesisfirehose-s3/package.json | 36 +- .../test.iot-kinesisfirehose-s3.test.js.snap | 24 + .../test/integ.no-arguments.expected.json | 24 + .../aws-iot-lambda-dynamodb/package.json | 32 +- .../aws-iot-lambda/package.json | 24 +- .../package.json | 40 +- ....kinesisfirehose-analytics-s3.test.js.snap | 24 + .../test/integ.no-arguments.expected.json | 24 + .../aws-kinesisfirehose-s3/README.md | 2 +- .../aws-kinesisfirehose-s3/lib/index.ts | 5 +- .../aws-kinesisfirehose-s3/package.json | 32 +- .../test.kinesisfirehose-s3.test.js.snap | 24 + .../test/integ.no-arguments.expected.json | 24 + .../integ.pre-existing-bucket.expected.json | 16 + .../test/integ.pre-existing-bucket.ts | 12 +- .../.eslintignore | 5 + .../.gitignore | 16 + .../.npmignore | 21 + .../README.md | 98 ++ .../architecture.png | Bin 0 -> 53569 bytes .../lib/index.ts | 144 +++ .../package.json | 93 ++ .../test.kinesisfirehose-s3.test.js.snap | 423 ++++++++ .../integ.existingStreamObj.expected.json | 740 +++++++++++++ .../test/integ.existingStreamObj.ts | 39 + .../test/integ.no-arguments.expected.json | 420 ++++++++ .../test/integ.no-arguments.ts | 26 + .../test/lambda/index.js | 21 + .../test/test.kinesisfirehose-s3.test.ts | 144 +++ .../aws-kinesisstreams-lambda/README.md | 2 +- .../aws-kinesisstreams-lambda/package.json | 40 +- .../aws-lambda-dynamodb/package.json | 20 +- .../package.json | 32 +- .../aws-lambda-s3/package.json | 20 +- .../aws-lambda-sns/package.json | 24 +- .../aws-lambda-sqs-lambda/package.json | 28 +- .../aws-lambda-sqs/package.json | 20 +- .../aws-lambda-step-function/package.json | 28 +- .../aws-s3-lambda/package.json | 32 +- .../aws-s3-step-function/package.json | 52 +- .../aws-sns-lambda/package.json | 32 +- .../aws-sns-sqs/package.json | 32 +- .../aws-sqs-lambda/package.json | 28 +- .../lib/cloudfront-distribution-defaults.ts | 114 +- .../lib/cloudfront-distribution-helper.ts | 53 +- .../core/lib/kinesis-firehose-s3-defaults.ts | 9 +- .../core/package.json | 94 +- ...stribution-api-gateway-helper.test.js.snap | 669 +++++++++++- ...dfront-distribution-s3-helper.test.js.snap | 77 +- .../kinesis-firehose-s3-defaults.test.js.snap | 24 + ...nt-distribution-api-gateway-helper.test.ts | 242 +++-- .../cloudfront-distribution-s3-helper.test.ts | 438 +++----- .../test/kinesis-firehose-s3-defaults.test.ts | 33 +- source/tools/cdk-integ-tools/package.json | 6 +- .../aws-s3-static-website/package.json | 22 +- .../aws-serverless-image-handler/package.json | 40 +- .../aws-serverless-web-app/package.json | 32 +- 101 files changed, 5456 insertions(+), 1789 deletions(-) create mode 100644 source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.override-behavior.expected.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.override-behavior.ts create mode 100644 source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/.eslintignore create mode 100644 source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/.gitignore create mode 100644 source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/.npmignore create mode 100644 source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/README.md create mode 100644 source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/architecture.png create mode 100644 source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/lib/index.ts create mode 100644 source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/package.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/__snapshots__/test.kinesisfirehose-s3.test.js.snap create mode 100644 source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.existingStreamObj.expected.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.existingStreamObj.ts create mode 100644 source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.no-arguments.expected.json create mode 100644 source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/integ.no-arguments.ts create mode 100644 source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/lambda/index.js create mode 100644 source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/test/test.kinesisfirehose-s3.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ba1c8f5d..5528a0aea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,20 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.68.0] - 2020-10-16 + +### Added +- aws-kinesisstreams-kinesisfirehose-s3 pattern added ([#74](https://github.com/awslabs/aws-solutions-constructs/issues/74)) +- aws-apigateway-sagemakerendpoint pattern added ([#75](https://github.com/awslabs/aws-solutions-constructs/issues/75)) + +### Changed +- Upgraded all patterns to CDK v1.68.0 +- BREAKING CHANGE: For All `aws-cloudfront-*` patterns, changed the underlying CloudFront L2 construct from `CloudFrontWebDistribution` to `Distribution` + ## [1.67.0] - 2020-10-09 ### Changed -- Upgraded all patterns to CDK v1.66.0 +- Upgraded all patterns to CDK v1.67.0 - Make CloudWatch alarm creation optional ([#85](https://github.com/awslabs/aws-solutions-constructs/issues/85)) ## [1.66.0] - 2020-10-06 @@ -219,4 +229,4 @@ General Availability of the AWS Solutions Constructs!! 🎉🎉🥂🥂🍾🍾 - aws-s3-lambda pattern added - aws-sns-lambda pattern added - aws-sqs-lambda pattern added -- core pattern added \ No newline at end of file +- core pattern added diff --git a/source/lerna.json b/source/lerna.json index ed3cb1322..1b9557132 100644 --- a/source/lerna.json +++ b/source/lerna.json @@ -6,5 +6,5 @@ "./patterns/@aws-solutions-constructs/*" ], "rejectCycles": "true", - "version": "1.67.0" + "version": "1.68.0" } diff --git a/source/package.json b/source/package.json index 1f41e4a19..bed71d9ed 100644 --- a/source/package.json +++ b/source/package.json @@ -1,6 +1,6 @@ { "name": "aws-solutions-constructs", - "version": "1.67.0", + "version": "1.68.0", "description": "AWS Solutions Constructs Library", "repository": { "type": "git", diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-dynamodb/package.json b/source/patterns/@aws-solutions-constructs/aws-apigateway-dynamodb/package.json index d6905d0c3..adacc3e47 100644 --- a/source/patterns/@aws-solutions-constructs/aws-apigateway-dynamodb/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-dynamodb/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-apigateway-dynamodb", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK Constructs for AWS API Gateway and Amazon DynamoDB integration.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,16 +53,16 @@ } }, "dependencies": { - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-apigateway": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-dynamodb": "~1.67.0", - "@aws-cdk/aws-logs": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-apigateway": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-dynamodb": "~1.68.0", + "@aws-cdk/aws-logs": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -72,12 +72,12 @@ ] }, "peerDependencies": { - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-apigateway": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-dynamodb": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-apigateway": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-dynamodb": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4", - "@aws-cdk/aws-logs": "~1.67.0" + "@aws-cdk/aws-logs": "~1.68.0" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-iot/README.md b/source/patterns/@aws-solutions-constructs/aws-apigateway-iot/README.md index ee1be1102..8778cbf22 100755 --- a/source/patterns/@aws-solutions-constructs/aws-apigateway-iot/README.md +++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-iot/README.md @@ -32,13 +32,11 @@ This implementation enables write-only messages to be published on given MQTT to Here is a minimal deployable pattern definition in Typescript: -``` javascript -const { ApiGatewayToIot } from '@aws-solutions-constructs/aws-apigateway-iot'; +``` typescript +import { ApiGatewayToIot } from '@aws-solutions-constructs/aws-apigateway-iot'; new ApiGatewayToIot(this, 'ApiGatewayToIotPattern', { - apiGatewayToIotProps: { - iotEndpoint: 'a1234567890123-ats' - } + iotEndpoint: 'a1234567890123-ats' }); ``` diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-iot/package.json b/source/patterns/@aws-solutions-constructs/aws-apigateway-iot/package.json index 2f783e3aa..ddbba1812 100755 --- a/source/patterns/@aws-solutions-constructs/aws-apigateway-iot/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-iot/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-apigateway-iot", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK constructs to proxy communication to IotCore using a APIGateway(REST).", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,15 +53,15 @@ } }, "dependencies": { - "@aws-cdk/aws-apigateway": "~1.67.0", - "@aws-cdk/aws-logs": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/aws-apigateway": "~1.68.0", + "@aws-cdk/aws-logs": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -71,11 +71,11 @@ ] }, "peerDependencies": { - "@aws-cdk/aws-apigateway": "~1.67.0", - "@aws-cdk/aws-logs": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/aws-apigateway": "~1.68.0", + "@aws-cdk/aws-logs": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4", - "@aws-cdk/aws-iam": "~1.67.0" + "@aws-cdk/aws-iam": "~1.68.0" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-kinesisstreams/package.json b/source/patterns/@aws-solutions-constructs/aws-apigateway-kinesisstreams/package.json index 15fcda7fd..0566f81ad 100644 --- a/source/patterns/@aws-solutions-constructs/aws-apigateway-kinesisstreams/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-kinesisstreams/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-apigateway-kinesisstreams", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK Constructs for AWS API Gateway and Amazon Kinesis Data Streams integration.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,16 +53,16 @@ } }, "dependencies": { - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-apigateway": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-kinesis": "~1.67.0", - "@aws-cdk/aws-logs": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-apigateway": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-kinesis": "~1.68.0", + "@aws-cdk/aws-logs": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -72,12 +72,12 @@ ] }, "peerDependencies": { - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-apigateway": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-kinesis": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-apigateway": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-kinesis": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4", - "@aws-cdk/aws-logs": "~1.67.0" + "@aws-cdk/aws-logs": "~1.68.0" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-lambda/package.json b/source/patterns/@aws-solutions-constructs/aws-apigateway-lambda/package.json index fceae933c..05c5b2b90 100644 --- a/source/patterns/@aws-solutions-constructs/aws-apigateway-lambda/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-lambda/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-apigateway-lambda", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK constructs for defining an interaction between an API Gateway and a Lambda function.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,16 +53,16 @@ } }, "dependencies": { - "@aws-cdk/aws-apigateway": "~1.67.0", - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/aws-logs": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/aws-apigateway": "~1.68.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/aws-logs": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -72,12 +72,12 @@ ] }, "peerDependencies": { - "@aws-cdk/aws-apigateway": "~1.67.0", - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/aws-logs": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/aws-apigateway": "~1.68.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/aws-logs": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4", - "@aws-cdk/aws-iam": "~1.67.0" + "@aws-cdk/aws-iam": "~1.68.0" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/README.md b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/README.md index 394a64a52..c328c3c88 100644 --- a/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/README.md +++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/README.md @@ -31,10 +31,24 @@ Here is a minimal deployable pattern definition in Typescript: ``` javascript import { ApiGatewayToSageMakerEndpoint, ApiGatewayToSageMakerEndpointProps } from '@aws-solutions-constructs/aws-apigateway-sagemakerendpoint'; +// Below is an example VTL (Velocity Template Language) mapping template for mapping the Api GET request to the Sagemaker POST request +const requestTemplate = +`{ + "instances": [ +#set( $user_id = $input.params("user_id") ) +#set( $items = $input.params("items") ) +#foreach( $item in $items.split(",") ) + {"in0": [$user_id], "in1": [$item]}#if( $foreach.hasNext ),#end + $esc.newline +#end + ] +}`; + +// Replace 'my-endpoint' with your Sagemaker Inference Endpoint new ApiGatewayToSageMakerEndpoint(this, 'test-apigw-sagemakerendpoint', { endpointName: 'my-endpoint', - resourcePath: '{my_param}', - requestMappingTemplate: 'my-request-vtl-template' + resourcePath: '{user_id}', + requestMappingTemplate: requestTemplate }); ``` @@ -77,7 +91,7 @@ _Parameters_ | **Method** | **Request Path** | **Query String** | **SageMaker Action** | **Description** | |:-------------|:----------------|-----------------|-----------------|-----------------| -|GET|`/predicted-ratings/321`| `items=101,131,162` |`sagemaker:InvokeEndpoint`|Retrieves the predictions for a specific user and items.| +|GET|`/321`| `items=101,131,162` |`sagemaker:InvokeEndpoint`|Retrieves the predictions for a specific user and items.| ## Default settings Out of the box implementation of the Construct without any override will set the following defaults: diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/lib/index.ts index 6deda0d6d..973cfc5f5 100644 --- a/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/lib/index.ts +++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/lib/index.ts @@ -86,7 +86,7 @@ export class ApiGatewayToSageMakerEndpoint extends Construct { * @param {cdk.App} scope - represents the scope for all the resources. * @param {string} id - this is a a scope-unique id. * @param {ApiGatewayToSageMakerEndpointProps} props - user provided props for the construct. - * @since 1.67.0 + * @since 1.68.0 * @access public */ constructor(scope: Construct, id: string, props: ApiGatewayToSageMakerEndpointProps) { diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/package.json b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/package.json index 4ba5bcc07..f541716f8 100644 --- a/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-sagemakerendpoint/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-apigateway-sagemakerendpoint", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK Constructs for AWS API Gateway and Amazon SageMaker Endpoint integration.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,15 +53,15 @@ } }, "dependencies": { - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-apigateway": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-logs": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-apigateway": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-logs": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -71,11 +71,11 @@ ] }, "peerDependencies": { - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-apigateway": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-apigateway": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4", - "@aws-cdk/aws-logs": "~1.67.0" + "@aws-cdk/aws-logs": "~1.68.0" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-apigateway-sqs/package.json b/source/patterns/@aws-solutions-constructs/aws-apigateway-sqs/package.json index d3502115c..0557d1713 100644 --- a/source/patterns/@aws-solutions-constructs/aws-apigateway-sqs/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-apigateway-sqs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-apigateway-sqs", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK constructs for defining an interaction between an AWS Lambda function and an Amazon S3 bucket.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,17 +53,17 @@ } }, "dependencies": { - "@aws-cdk/aws-apigateway": "~1.67.0", - "@aws-cdk/aws-sqs": "~1.67.0", - "@aws-cdk/aws-kms": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-logs": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/aws-apigateway": "~1.68.0", + "@aws-cdk/aws-sqs": "~1.68.0", + "@aws-cdk/aws-kms": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-logs": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -73,13 +73,13 @@ ] }, "peerDependencies": { - "@aws-cdk/aws-apigateway": "~1.67.0", - "@aws-cdk/aws-sqs": "~1.67.0", - "@aws-cdk/aws-kms": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/aws-apigateway": "~1.68.0", + "@aws-cdk/aws-sqs": "~1.68.0", + "@aws-cdk/aws-kms": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4", - "@aws-cdk/aws-logs": "~1.67.0" + "@aws-cdk/aws-logs": "~1.68.0" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/lib/index.ts index 7932abee6..7e874ce3a 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/lib/index.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/lib/index.ts @@ -48,7 +48,7 @@ export interface CloudFrontToApiGatewayToLambdaProps { * * @default - Default props are used */ - readonly cloudFrontDistributionProps?: cloudfront.CloudFrontWebDistributionProps | any, + readonly cloudFrontDistributionProps?: cloudfront.DistributionProps | any, /** * Optional user provided props to turn on/off the automatic injection of best practice HTTP * security headers in all responses from cloudfront @@ -59,7 +59,7 @@ export interface CloudFrontToApiGatewayToLambdaProps { } export class CloudFrontToApiGatewayToLambda extends Construct { - public readonly cloudFrontWebDistribution: cloudfront.CloudFrontWebDistribution; + public readonly cloudFrontWebDistribution: cloudfront.Distribution; public readonly edgeLambdaFunctionVersion?: lambda.Version; public readonly cloudFrontLoggingBucket?: s3.Bucket; public readonly apiGateway: api.RestApi; diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/package.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/package.json index 36bbcdd9e..21b6a6e9d 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-cloudfront-apigateway-lambda", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK Constructs for AWS Cloudfront to AWS API Gateway to AWS Lambda integration.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,19 +53,20 @@ } }, "dependencies": { - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-cloudfront": "~1.67.0", - "@aws-cdk/aws-apigateway": "~1.67.0", - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/aws-logs": "~1.67.0", - "@aws-cdk/aws-s3": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-solutions-constructs/aws-cloudfront-apigateway": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-cloudfront": "~1.68.0", + "@aws-cdk/aws-apigateway": "~1.68.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/aws-logs": "~1.68.0", + "@aws-cdk/aws-s3": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-solutions-constructs/aws-cloudfront-apigateway": "~1.68.0", + "@aws-cdk/aws-cloudfront-origins": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -75,15 +76,16 @@ ] }, "peerDependencies": { - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-cloudfront": "~1.67.0", - "@aws-cdk/aws-apigateway": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/aws-logs": "~1.67.0", - "@aws-solutions-constructs/aws-cloudfront-apigateway": "~1.67.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-cloudfront": "~1.68.0", + "@aws-cdk/aws-apigateway": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/aws-logs": "~1.68.0", + "@aws-solutions-constructs/aws-cloudfront-apigateway": "~1.68.0", "constructs": "^3.0.4", - "@aws-cdk/aws-s3": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0" + "@aws-cdk/aws-s3": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-cloudfront-origins": "~1.68.0" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/__snapshots__/test.cloudfront-apigateway-lambda.test.js.snap b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/__snapshots__/test.cloudfront-apigateway-lambda.test.js.snap index 5959db79c..c2e28a333 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/__snapshots__/test.cloudfront-apigateway-lambda.test.js.snap +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/__snapshots__/test.cloudfront-apigateway-lambda.test.js.snap @@ -50,7 +50,7 @@ Object { "Type": "AWS::Logs::LogGroup", "UpdateReplacePolicy": "Retain", }, - "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudFrontDistributionCFDistribution4AF2BFE4": Object { + "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudFrontDistribution0AFC98FC": Object { "Metadata": Object { "cfn_nag": Object { "rules_to_suppress": Array [ @@ -64,21 +64,8 @@ Object { "Properties": Object { "DistributionConfig": Object { "DefaultCacheBehavior": Object { - "AllowedMethods": Array [ - "GET", - "HEAD", - ], - "CachedMethods": Array [ - "GET", - "HEAD", - ], + "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", "Compress": true, - "ForwardedValues": Object { - "Cookies": Object { - "Forward": "none", - }, - "QueryString": false, - }, "LambdaFunctionAssociations": Array [ Object { "EventType": "origin-response", @@ -87,10 +74,9 @@ Object { }, }, ], - "TargetOriginId": "origin1", + "TargetOriginId": "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudFrontDistributionOrigin11F34FD46", "ViewerProtocolPolicy": "redirect-to-https", }, - "DefaultRootObject": "index.html", "Enabled": true, "HttpVersion": "http2", "IPV6Enabled": true, @@ -101,21 +87,11 @@ Object { "DomainName", ], }, - "IncludeCookies": false, }, "Origins": Array [ Object { - "ConnectionAttempts": 3, - "ConnectionTimeout": 10, "CustomOriginConfig": Object { - "HTTPPort": 80, - "HTTPSPort": 443, - "OriginKeepaliveTimeout": 5, "OriginProtocolPolicy": "https-only", - "OriginReadTimeout": 30, - "OriginSSLProtocols": Array [ - "TLSv1.2", - ], }, "DomainName": Object { "Fn::Select": Array [ @@ -161,7 +137,7 @@ Object { }, ], }, - "Id": "origin1", + "Id": "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudFrontDistributionOrigin11F34FD46", "OriginPath": Object { "Fn::Join": Array [ "", @@ -175,10 +151,6 @@ Object { }, }, ], - "PriceClass": "PriceClass_100", - "ViewerCertificate": Object { - "CloudFrontDefaultCertificate": true, - }, }, }, "Type": "AWS::CloudFront::Distribution", diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.no-arguments.expected.json index 3c513ce49..a9d81031e 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.no-arguments.expected.json @@ -804,26 +804,13 @@ } } }, - "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudFrontDistributionCFDistribution4AF2BFE4": { + "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudFrontDistribution0AFC98FC": { "Type": "AWS::CloudFront::Distribution", "Properties": { "DistributionConfig": { "DefaultCacheBehavior": { - "AllowedMethods": [ - "GET", - "HEAD" - ], - "CachedMethods": [ - "GET", - "HEAD" - ], + "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", "Compress": true, - "ForwardedValues": { - "Cookies": { - "Forward": "none" - }, - "QueryString": false - }, "LambdaFunctionAssociations": [ { "EventType": "origin-response", @@ -832,10 +819,9 @@ } } ], - "TargetOriginId": "origin1", + "TargetOriginId": "testcloudfrontapigatewaylambdastacktestcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudFrontDistributionOrigin1F0C574BA", "ViewerProtocolPolicy": "redirect-to-https" }, - "DefaultRootObject": "index.html", "Enabled": true, "HttpVersion": "http2", "IPV6Enabled": true, @@ -845,22 +831,12 @@ "testcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudfrontLoggingBucket7F467421", "DomainName" ] - }, - "IncludeCookies": false + } }, "Origins": [ { - "ConnectionAttempts": 3, - "ConnectionTimeout": 10, "CustomOriginConfig": { - "HTTPPort": 80, - "HTTPSPort": 443, - "OriginKeepaliveTimeout": 5, - "OriginProtocolPolicy": "https-only", - "OriginReadTimeout": 30, - "OriginSSLProtocols": [ - "TLSv1.2" - ] + "OriginProtocolPolicy": "https-only" }, "DomainName": { "Fn::Select": [ @@ -906,7 +882,7 @@ } ] }, - "Id": "origin1", + "Id": "testcloudfrontapigatewaylambdastacktestcloudfrontapigatewaylambdaCloudFrontToApiGatewayCloudFrontDistributionOrigin1F0C574BA", "OriginPath": { "Fn::Join": [ "", @@ -919,11 +895,7 @@ ] } } - ], - "PriceClass": "PriceClass_100", - "ViewerCertificate": { - "CloudFrontDefaultCertificate": true - } + ] } }, "Metadata": { diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.override-behavior.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.override-behavior.expected.json new file mode 100644 index 000000000..f988b8926 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.override-behavior.expected.json @@ -0,0 +1,989 @@ +{ + "Description": "Integration Test for aws-cloudfront-apigateway-lambda", + "Resources": { + "SomeCachePolicy40B9E4D4": { + "Type": "AWS::CloudFront::CachePolicy", + "Properties": { + "CachePolicyConfig": { + "DefaultTTL": 28800, + "MaxTTL": 36000, + "MinTTL": 18000, + "Name": "SomeCachePolicy", + "ParametersInCacheKeyAndForwardedToOrigin": { + "CookiesConfig": { + "CookieBehavior": "none" + }, + "EnableAcceptEncodingGzip": false, + "HeadersConfig": { + "HeaderBehavior": "none" + }, + "QueryStringsConfig": { + "QueryStringBehavior": "none" + } + } + } + } + }, + "NoCachePolicy1F71EC46": { + "Type": "AWS::CloudFront::CachePolicy", + "Properties": { + "CachePolicyConfig": { + "DefaultTTL": 0, + "MaxTTL": 0, + "MinTTL": 0, + "Name": "NoCachePolicy", + "ParametersInCacheKeyAndForwardedToOrigin": { + "CookiesConfig": { + "CookieBehavior": "none" + }, + "EnableAcceptEncodingGzip": false, + "HeadersConfig": { + "HeaderBehavior": "none" + }, + "QueryStringsConfig": { + "QueryStringBehavior": "none" + } + } + } + } + }, + "cfapilambdaoverrideLambdaFunctionServiceRole4B1A4043": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/lambda/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaFunctionServiceRolePolicy" + } + ] + } + }, + "cfapilambdaoverrideLambdaFunctionServiceRoleDefaultPolicy1A3D9202": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "cfapilambdaoverrideLambdaFunctionServiceRoleDefaultPolicy1A3D9202", + "Roles": [ + { + "Ref": "cfapilambdaoverrideLambdaFunctionServiceRole4B1A4043" + } + ] + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W12", + "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray." + } + ] + } + } + }, + "cfapilambdaoverrideLambdaFunction74CE466F": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParameters42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198cS3Bucket1F467BCC" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198cS3VersionKey9E4F7872" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198cS3VersionKey9E4F7872" + } + ] + } + ] + } + ] + ] + } + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "cfapilambdaoverrideLambdaFunctionServiceRole4B1A4043", + "Arn" + ] + }, + "Runtime": "nodejs10.x", + "Environment": { + "Variables": { + "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1" + } + }, + "TracingConfig": { + "Mode": "Active" + } + }, + "DependsOn": [ + "cfapilambdaoverrideLambdaFunctionServiceRoleDefaultPolicy1A3D9202", + "cfapilambdaoverrideLambdaFunctionServiceRole4B1A4043" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with more tighter permissions." + } + ] + } + } + }, + "cfapilambdaoverrideApiAccessLogGroup2665068D": { + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain" + }, + "cfapilambdaoverrideLambdaRestApi6E7952FC": { + "Type": "AWS::ApiGateway::RestApi", + "Properties": { + "EndpointConfiguration": { + "Types": [ + "REGIONAL" + ] + }, + "Name": "LambdaRestApi" + } + }, + "cfapilambdaoverrideLambdaRestApiDeployment82ACBB00e7f3a114a506221ddcaf53765c4dd518": { + "Type": "AWS::ApiGateway::Deployment", + "Properties": { + "RestApiId": { + "Ref": "cfapilambdaoverrideLambdaRestApi6E7952FC" + }, + "Description": "Automatically created by the RestApi construct" + }, + "DependsOn": [ + "cfapilambdaoverrideLambdaRestApidynamicGET15050D54", + "cfapilambdaoverrideLambdaRestApidynamic88206171", + "cfapilambdaoverrideLambdaRestApistaticGET81EF9C24", + "cfapilambdaoverrideLambdaRestApistaticC2ECB649" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W45", + "reason": "ApiGateway has AccessLogging enabled in AWS::ApiGateway::Stage resource, but cfn_nag checkes for it in AWS::ApiGateway::Deployment resource" + } + ] + } + } + }, + "cfapilambdaoverrideLambdaRestApiDeploymentStageprodC4F6FBB5": { + "Type": "AWS::ApiGateway::Stage", + "Properties": { + "RestApiId": { + "Ref": "cfapilambdaoverrideLambdaRestApi6E7952FC" + }, + "AccessLogSetting": { + "DestinationArn": { + "Fn::GetAtt": [ + "cfapilambdaoverrideApiAccessLogGroup2665068D", + "Arn" + ] + }, + "Format": "{\"requestId\":\"$context.requestId\",\"ip\":\"$context.identity.sourceIp\",\"user\":\"$context.identity.user\",\"caller\":\"$context.identity.caller\",\"requestTime\":\"$context.requestTime\",\"httpMethod\":\"$context.httpMethod\",\"resourcePath\":\"$context.resourcePath\",\"status\":\"$context.status\",\"protocol\":\"$context.protocol\",\"responseLength\":\"$context.responseLength\"}" + }, + "DeploymentId": { + "Ref": "cfapilambdaoverrideLambdaRestApiDeployment82ACBB00e7f3a114a506221ddcaf53765c4dd518" + }, + "MethodSettings": [ + { + "DataTraceEnabled": true, + "HttpMethod": "*", + "LoggingLevel": "INFO", + "ResourcePath": "/*" + } + ], + "StageName": "prod", + "TracingEnabled": true + } + }, + "cfapilambdaoverrideLambdaRestApistaticC2ECB649": { + "Type": "AWS::ApiGateway::Resource", + "Properties": { + "ParentId": { + "Fn::GetAtt": [ + "cfapilambdaoverrideLambdaRestApi6E7952FC", + "RootResourceId" + ] + }, + "PathPart": "static", + "RestApiId": { + "Ref": "cfapilambdaoverrideLambdaRestApi6E7952FC" + } + } + }, + "cfapilambdaoverrideLambdaRestApistaticGET81EF9C24": { + "Type": "AWS::ApiGateway::Method", + "Properties": { + "HttpMethod": "GET", + "ResourceId": { + "Ref": "cfapilambdaoverrideLambdaRestApistaticC2ECB649" + }, + "RestApiId": { + "Ref": "cfapilambdaoverrideLambdaRestApi6E7952FC" + }, + "AuthorizationType": "NONE", + "Integration": { + "IntegrationHttpMethod": "GET", + "Type": "HTTP_PROXY", + "Uri": "http://amazon.com" + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W59", + "reason": "AWS::ApiGateway::Method AuthorizationType is set to 'NONE' because API Gateway behind CloudFront does not support AWS_IAM authentication" + } + ] + } + } + }, + "cfapilambdaoverrideLambdaRestApidynamic88206171": { + "Type": "AWS::ApiGateway::Resource", + "Properties": { + "ParentId": { + "Fn::GetAtt": [ + "cfapilambdaoverrideLambdaRestApi6E7952FC", + "RootResourceId" + ] + }, + "PathPart": "dynamic", + "RestApiId": { + "Ref": "cfapilambdaoverrideLambdaRestApi6E7952FC" + } + } + }, + "cfapilambdaoverrideLambdaRestApidynamicGETApiPermissiontestcfapilambdaoverridestackcfapilambdaoverrideLambdaRestApi6D58BDA9GETdynamic00489F37": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "cfapilambdaoverrideLambdaFunction74CE466F", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "cfapilambdaoverrideLambdaRestApi6E7952FC" + }, + "/", + { + "Ref": "cfapilambdaoverrideLambdaRestApiDeploymentStageprodC4F6FBB5" + }, + "/GET/dynamic" + ] + ] + } + } + }, + "cfapilambdaoverrideLambdaRestApidynamicGETApiPermissionTesttestcfapilambdaoverridestackcfapilambdaoverrideLambdaRestApi6D58BDA9GETdynamic537B9015": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "cfapilambdaoverrideLambdaFunction74CE466F", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "cfapilambdaoverrideLambdaRestApi6E7952FC" + }, + "/test-invoke-stage/GET/dynamic" + ] + ] + } + } + }, + "cfapilambdaoverrideLambdaRestApidynamicGET15050D54": { + "Type": "AWS::ApiGateway::Method", + "Properties": { + "HttpMethod": "GET", + "ResourceId": { + "Ref": "cfapilambdaoverrideLambdaRestApidynamic88206171" + }, + "RestApiId": { + "Ref": "cfapilambdaoverrideLambdaRestApi6E7952FC" + }, + "AuthorizationType": "NONE", + "Integration": { + "IntegrationHttpMethod": "POST", + "Type": "AWS_PROXY", + "Uri": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":apigateway:", + { + "Ref": "AWS::Region" + }, + ":lambda:path/2015-03-31/functions/", + { + "Fn::GetAtt": [ + "cfapilambdaoverrideLambdaFunction74CE466F", + "Arn" + ] + }, + "/invocations" + ] + ] + } + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W59", + "reason": "AWS::ApiGateway::Method AuthorizationType is set to 'NONE' because API Gateway behind CloudFront does not support AWS_IAM authentication" + } + ] + } + } + }, + "cfapilambdaoverrideLambdaRestApiUsagePlanCF4B0BE0": { + "Type": "AWS::ApiGateway::UsagePlan", + "Properties": { + "ApiStages": [ + { + "ApiId": { + "Ref": "cfapilambdaoverrideLambdaRestApi6E7952FC" + }, + "Stage": { + "Ref": "cfapilambdaoverrideLambdaRestApiDeploymentStageprodC4F6FBB5" + }, + "Throttle": {} + } + ] + } + }, + "cfapilambdaoverrideLambdaRestApiCloudWatchRole0F1F3559": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "apigateway.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:DescribeLogGroups", + "logs:DescribeLogStreams", + "logs:PutLogEvents", + "logs:GetLogEvents", + "logs:FilterLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaRestApiCloudWatchRolePolicy" + } + ] + } + }, + "cfapilambdaoverrideLambdaRestApiAccountFB480D92": { + "Type": "AWS::ApiGateway::Account", + "Properties": { + "CloudWatchRoleArn": { + "Fn::GetAtt": [ + "cfapilambdaoverrideLambdaRestApiCloudWatchRole0F1F3559", + "Arn" + ] + } + }, + "DependsOn": [ + "cfapilambdaoverrideLambdaRestApi6E7952FC" + ] + }, + "cfapilambdaoverrideCloudFrontToApiGatewaySetHttpSecurityHeadersServiceRole60E3862B": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + }, + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "edgelambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":logs:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":log-group:/aws/lambda/*" + ] + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "LambdaFunctionServiceRolePolicy" + } + ] + } + }, + "cfapilambdaoverrideCloudFrontToApiGatewaySetHttpSecurityHeadersServiceRoleDefaultPolicy3AE9A74C": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "cfapilambdaoverrideCloudFrontToApiGatewaySetHttpSecurityHeadersServiceRoleDefaultPolicy3AE9A74C", + "Roles": [ + { + "Ref": "cfapilambdaoverrideCloudFrontToApiGatewaySetHttpSecurityHeadersServiceRole60E3862B" + } + ] + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W12", + "reason": "Lambda needs the following minimum required permissions to send trace data to X-Ray." + } + ] + } + } + }, + "cfapilambdaoverrideCloudFrontToApiGatewaySetHttpSecurityHeaders67E61E6E": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = (event, context, callback) => { const response = event.Records[0].cf.response; const headers = response.headers; headers['x-xss-protection'] = [ { key: 'X-XSS-Protection', value: '1; mode=block' } ]; headers['x-frame-options'] = [ { key: 'X-Frame-Options', value: 'DENY' } ]; headers['x-content-type-options'] = [ { key: 'X-Content-Type-Options', value: 'nosniff' } ]; headers['strict-transport-security'] = [ { key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubdomains; preload' } ]; headers['referrer-policy'] = [ { key: 'Referrer-Policy', value: 'same-origin' } ]; headers['content-security-policy'] = [ { key: 'Content-Security-Policy', value: \"default-src 'none'; base-uri 'self'; img-src 'self'; script-src 'self'; style-src 'self' https:; object-src 'none'; frame-ancestors 'none'; font-src 'self' https:; form-action 'self'; manifest-src 'self'; connect-src 'self'\" } ]; callback(null, response); };" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "cfapilambdaoverrideCloudFrontToApiGatewaySetHttpSecurityHeadersServiceRole60E3862B", + "Arn" + ] + }, + "Runtime": "nodejs12.x", + "TracingConfig": { + "Mode": "Active" + } + }, + "DependsOn": [ + "cfapilambdaoverrideCloudFrontToApiGatewaySetHttpSecurityHeadersServiceRoleDefaultPolicy3AE9A74C", + "cfapilambdaoverrideCloudFrontToApiGatewaySetHttpSecurityHeadersServiceRole60E3862B" + ], + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W58", + "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with more tighter permissions." + } + ] + } + } + }, + "cfapilambdaoverrideCloudFrontToApiGatewaySetHttpSecurityHeadersVersionBECBEC80": { + "Type": "AWS::Lambda::Version", + "Properties": { + "FunctionName": { + "Ref": "cfapilambdaoverrideCloudFrontToApiGatewaySetHttpSecurityHeaders67E61E6E" + } + } + }, + "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucket3A71B9E0": { + "Type": "AWS::S3::Bucket", + "Properties": { + "AccessControl": "LogDeliveryWrite", + "BucketEncryption": { + "ServerSideEncryptionConfiguration": [ + { + "ServerSideEncryptionByDefault": { + "SSEAlgorithm": "AES256" + } + } + ] + }, + "PublicAccessBlockConfiguration": { + "BlockPublicAcls": true, + "BlockPublicPolicy": true, + "IgnorePublicAcls": true, + "RestrictPublicBuckets": true + } + }, + "UpdateReplacePolicy": "Retain", + "DeletionPolicy": "Retain", + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W35", + "reason": "This S3 bucket is used as the access logging bucket for CloudFront Distribution" + } + ] + } + } + }, + "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucketPolicyC3092436": { + "Type": "AWS::S3::BucketPolicy", + "Properties": { + "Bucket": { + "Ref": "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucket3A71B9E0" + }, + "PolicyDocument": { + "Statement": [ + { + "Action": "*", + "Condition": { + "Bool": { + "aws:SecureTransport": "false" + } + }, + "Effect": "Deny", + "Principal": "*", + "Resource": { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucket3A71B9E0", + "Arn" + ] + }, + "/*" + ] + ] + }, + "Sid": "HttpsOnly" + } + ], + "Version": "2012-10-17" + } + } + }, + "cfapilambdaoverrideCloudFrontToApiGatewayCloudFrontDistribution94A35932": { + "Type": "AWS::CloudFront::Distribution", + "Properties": { + "DistributionConfig": { + "CacheBehaviors": [ + { + "CachePolicyId": { + "Ref": "NoCachePolicy1F71EC46" + }, + "Compress": true, + "PathPattern": "/dynamic", + "TargetOriginId": "testcfapilambdaoverridestackcfapilambdaoverrideCloudFrontToApiGatewayCloudFrontDistributionOrigin27CE3F449", + "ViewerProtocolPolicy": "allow-all" + } + ], + "DefaultCacheBehavior": { + "CachePolicyId": { + "Ref": "SomeCachePolicy40B9E4D4" + }, + "Compress": true, + "LambdaFunctionAssociations": [ + { + "EventType": "origin-response", + "LambdaFunctionARN": { + "Ref": "cfapilambdaoverrideCloudFrontToApiGatewaySetHttpSecurityHeadersVersionBECBEC80" + } + } + ], + "TargetOriginId": "testcfapilambdaoverridestackcfapilambdaoverrideCloudFrontToApiGatewayCloudFrontDistributionOrigin19A7DB81E", + "ViewerProtocolPolicy": "redirect-to-https" + }, + "Enabled": true, + "HttpVersion": "http2", + "IPV6Enabled": true, + "Logging": { + "Bucket": { + "Fn::GetAtt": [ + "cfapilambdaoverrideCloudFrontToApiGatewayCloudfrontLoggingBucket3A71B9E0", + "DomainName" + ] + } + }, + "Origins": [ + { + "CustomOriginConfig": { + "OriginProtocolPolicy": "https-only" + }, + "DomainName": { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "/", + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "://", + { + "Fn::Join": [ + "", + [ + "https://", + { + "Ref": "cfapilambdaoverrideLambdaRestApi6E7952FC" + }, + ".execute-api.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "cfapilambdaoverrideLambdaRestApiDeploymentStageprodC4F6FBB5" + }, + "/" + ] + ] + } + ] + } + ] + } + ] + } + ] + }, + "Id": "testcfapilambdaoverridestackcfapilambdaoverrideCloudFrontToApiGatewayCloudFrontDistributionOrigin19A7DB81E", + "OriginPath": { + "Fn::Join": [ + "", + [ + "/", + { + "Ref": "cfapilambdaoverrideLambdaRestApiDeploymentStageprodC4F6FBB5" + } + ] + ] + } + }, + { + "CustomOriginConfig": { + "OriginProtocolPolicy": "https-only" + }, + "DomainName": { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "/", + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "://", + { + "Fn::Join": [ + "", + [ + "https://", + { + "Ref": "cfapilambdaoverrideLambdaRestApi6E7952FC" + }, + ".execute-api.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "cfapilambdaoverrideLambdaRestApiDeploymentStageprodC4F6FBB5" + }, + "/" + ] + ] + } + ] + } + ] + } + ] + } + ] + }, + "Id": "testcfapilambdaoverridestackcfapilambdaoverrideCloudFrontToApiGatewayCloudFrontDistributionOrigin27CE3F449", + "OriginPath": { + "Fn::Join": [ + "", + [ + "/", + { + "Ref": "cfapilambdaoverrideLambdaRestApiDeploymentStageprodC4F6FBB5" + }, + "/dynamic" + ] + ] + } + } + ] + } + }, + "Metadata": { + "cfn_nag": { + "rules_to_suppress": [ + { + "id": "W70", + "reason": "Since the distribution uses the CloudFront domain name, CloudFront automatically sets the security policy to TLSv1 regardless of the value of MinimumProtocolVersion" + } + ] + } + } + } + }, + "Outputs": { + "cfapilambdaoverrideLambdaRestApiEndpointF8A561AB": { + "Value": { + "Fn::Join": [ + "", + [ + "https://", + { + "Ref": "cfapilambdaoverrideLambdaRestApi6E7952FC" + }, + ".execute-api.", + { + "Ref": "AWS::Region" + }, + ".", + { + "Ref": "AWS::URLSuffix" + }, + "/", + { + "Ref": "cfapilambdaoverrideLambdaRestApiDeploymentStageprodC4F6FBB5" + }, + "/" + ] + ] + } + } + }, + "Parameters": { + "AssetParameters42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198cS3Bucket1F467BCC": { + "Type": "String", + "Description": "S3 bucket for asset \"42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198c\"" + }, + "AssetParameters42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198cS3VersionKey9E4F7872": { + "Type": "String", + "Description": "S3 key for asset version \"42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198c\"" + }, + "AssetParameters42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198cArtifactHash00A70A91": { + "Type": "String", + "Description": "Artifact hash for asset \"42a35bbf0dec9ef0ac5b0dde87e71a1b8929e8d2d178dd09ccfb2c928ec0198c\"" + } + } +} \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.override-behavior.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.override-behavior.ts new file mode 100644 index 000000000..0161cd60f --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway-lambda/test/integ.override-behavior.ts @@ -0,0 +1,99 @@ +/** + * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +/// !cdk-integ * +import { App, Stack } from "@aws-cdk/core"; +import { CloudFrontToApiGatewayToLambda } from "../lib"; +import * as lambda from '@aws-cdk/aws-lambda'; +import * as cloudfront from '@aws-cdk/aws-cloudfront'; +import * as apigateway from '@aws-cdk/aws-apigateway'; +import * as cdk from '@aws-cdk/core'; +import { Duration } from "@aws-cdk/core/lib/duration"; +import * as origins from '@aws-cdk/aws-cloudfront-origins'; + +// Setup +const app = new App(); +const stack = new Stack(app, 'test-cf-api-lambda-override-stack'); +stack.templateOptions.description = 'Integration Test for aws-cloudfront-apigateway-lambda'; + +const lambdaProps: lambda.FunctionProps = { + code: lambda.Code.fromAsset(`${__dirname}/lambda`), + runtime: lambda.Runtime.NODEJS_10_X, + handler: 'index.handler' +}; + +// Some Caching for static content +const someCachePolicy = new cloudfront.CachePolicy(stack, 'SomeCachePolicy', { + cachePolicyName: 'SomeCachePolicy', + defaultTtl: Duration.hours(8), + minTtl: Duration.hours(5), + maxTtl: Duration.hours(10), +}); + +// Disable Caching for dynamic content +const noCachePolicy = new cloudfront.CachePolicy(stack, 'NoCachePolicy', { + cachePolicyName: 'NoCachePolicy', + defaultTtl: Duration.minutes(0), + minTtl: Duration.minutes(0), + maxTtl: Duration.minutes(0), +}); + +const construct = new CloudFrontToApiGatewayToLambda(stack, 'cf-api-lambda-override', { + lambdaFunctionProps: lambdaProps, + apiGatewayProps: { + proxy: false, + defaultMethodOptions: { + authorizationType: apigateway.AuthorizationType.NONE, + }, + }, + cloudFrontDistributionProps: { + defaultBehavior: { + cachePolicy: someCachePolicy + } + } +}); + +const apiEndPoint = construct.apiGateway; +const apiEndPointUrlWithoutProtocol = cdk.Fn.select(1, cdk.Fn.split("://", apiEndPoint.url)); +const apiEndPointDomainName = cdk.Fn.select(0, cdk.Fn.split("/", apiEndPointUrlWithoutProtocol)); + +const staticResource = construct.apiGateway.root.addResource('static'); +const staticMethod = staticResource.addMethod('GET', new apigateway.HttpIntegration('http://amazon.com')); + +const dynamicResource = construct.apiGateway.root.addResource('dynamic'); +const dynamicMethod = dynamicResource.addMethod('GET'); + +// Add behavior +construct.cloudFrontWebDistribution.addBehavior('/dynamic', new origins.HttpOrigin(apiEndPointDomainName, { + originPath: `/${apiEndPoint.deploymentStage.stageName}/dynamic` + }), { + cachePolicy: noCachePolicy +}); +// Suppress CFN_NAG warnings +suppressWarnings(staticMethod); +suppressWarnings(dynamicMethod); + +function suppressWarnings(method: apigateway.Method) { + const child = method.node.findChild('Resource') as apigateway.CfnMethod; + child.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [{ + id: 'W59', + reason: `AWS::ApiGateway::Method AuthorizationType is set to 'NONE' because API Gateway behind CloudFront does not support AWS_IAM authentication` + }] + } + }; +} + +// Synth +app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/lib/index.ts index 0ddd74310..680add064 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/lib/index.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/lib/index.ts @@ -33,7 +33,7 @@ export interface CloudFrontToApiGatewayProps { * * @default - Default props are used */ - readonly cloudFrontDistributionProps?: cloudfront.CloudFrontWebDistributionProps | any, + readonly cloudFrontDistributionProps?: cloudfront.DistributionProps | any, /** * Optional user provided props to turn on/off the automatic injection of best practice HTTP * security headers in all responses from cloudfront @@ -44,7 +44,7 @@ export interface CloudFrontToApiGatewayProps { } export class CloudFrontToApiGateway extends Construct { - public readonly cloudFrontWebDistribution: cloudfront.CloudFrontWebDistribution; + public readonly cloudFrontWebDistribution: cloudfront.Distribution; public readonly apiGateway: api.RestApi; public readonly edgeLambdaFunctionVersion?: lambda.Version; public readonly cloudFrontLoggingBucket?: s3.Bucket; diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/package.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/package.json index c64867ad2..6cd700a0f 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-cloudfront-apigateway", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK Constructs for AWS Cloudfront to AWS API Gateway integration.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,17 +53,17 @@ } }, "dependencies": { - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-cloudfront": "~1.67.0", - "@aws-cdk/aws-apigateway": "~1.67.0", - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/aws-logs": "~1.67.0", - "@aws-cdk/aws-s3": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-cloudfront": "~1.68.0", + "@aws-cdk/aws-apigateway": "~1.68.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/aws-logs": "~1.68.0", + "@aws-cdk/aws-s3": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -73,13 +73,13 @@ ] }, "peerDependencies": { - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-cloudfront": "~1.67.0", - "@aws-cdk/aws-apigateway": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/aws-logs": "~1.67.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-cloudfront": "~1.68.0", + "@aws-cdk/aws-apigateway": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/aws-logs": "~1.68.0", "constructs": "^3.0.4", - "@aws-cdk/aws-s3": "~1.67.0" + "@aws-cdk/aws-s3": "~1.68.0" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/__snapshots__/test.cloudfront-apigateway.test.js.snap b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/__snapshots__/test.cloudfront-apigateway.test.js.snap index e5c764e37..5c2397d40 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/__snapshots__/test.cloudfront-apigateway.test.js.snap +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/__snapshots__/test.cloudfront-apigateway.test.js.snap @@ -620,7 +620,7 @@ Object { }, "Type": "AWS::Lambda::Permission", }, - "testcloudfrontapigatewayCloudFrontDistributionCFDistribution2270C4C1": Object { + "testcloudfrontapigatewayCloudFrontDistribution159820CC": Object { "Metadata": Object { "cfn_nag": Object { "rules_to_suppress": Array [ @@ -634,21 +634,8 @@ Object { "Properties": Object { "DistributionConfig": Object { "DefaultCacheBehavior": Object { - "AllowedMethods": Array [ - "GET", - "HEAD", - ], - "CachedMethods": Array [ - "GET", - "HEAD", - ], + "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", "Compress": true, - "ForwardedValues": Object { - "Cookies": Object { - "Forward": "none", - }, - "QueryString": false, - }, "LambdaFunctionAssociations": Array [ Object { "EventType": "origin-response", @@ -657,10 +644,9 @@ Object { }, }, ], - "TargetOriginId": "origin1", + "TargetOriginId": "testcloudfrontapigatewayCloudFrontDistributionOrigin1FA7A74D5", "ViewerProtocolPolicy": "redirect-to-https", }, - "DefaultRootObject": "index.html", "Enabled": true, "HttpVersion": "http2", "IPV6Enabled": true, @@ -671,21 +657,11 @@ Object { "DomainName", ], }, - "IncludeCookies": false, }, "Origins": Array [ Object { - "ConnectionAttempts": 3, - "ConnectionTimeout": 10, "CustomOriginConfig": Object { - "HTTPPort": 80, - "HTTPSPort": 443, - "OriginKeepaliveTimeout": 5, "OriginProtocolPolicy": "https-only", - "OriginReadTimeout": 30, - "OriginSSLProtocols": Array [ - "TLSv1.2", - ], }, "DomainName": Object { "Fn::Select": Array [ @@ -731,7 +707,7 @@ Object { }, ], }, - "Id": "origin1", + "Id": "testcloudfrontapigatewayCloudFrontDistributionOrigin1FA7A74D5", "OriginPath": Object { "Fn::Join": Array [ "", @@ -745,10 +721,6 @@ Object { }, }, ], - "PriceClass": "PriceClass_100", - "ViewerCertificate": Object { - "CloudFrontDefaultCertificate": true, - }, }, }, "Type": "AWS::CloudFront::Distribution", diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.no-arguments.expected.json index 1eadc8db8..e6f20f42d 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-apigateway/test/integ.no-arguments.expected.json @@ -804,26 +804,13 @@ } } }, - "testcloudfrontapigatewayCloudFrontDistributionCFDistribution2270C4C1": { + "testcloudfrontapigatewayCloudFrontDistribution159820CC": { "Type": "AWS::CloudFront::Distribution", "Properties": { "DistributionConfig": { "DefaultCacheBehavior": { - "AllowedMethods": [ - "GET", - "HEAD" - ], - "CachedMethods": [ - "GET", - "HEAD" - ], + "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", "Compress": true, - "ForwardedValues": { - "Cookies": { - "Forward": "none" - }, - "QueryString": false - }, "LambdaFunctionAssociations": [ { "EventType": "origin-response", @@ -832,10 +819,9 @@ } } ], - "TargetOriginId": "origin1", + "TargetOriginId": "testcloudfrontapigatewaystacktestcloudfrontapigatewayCloudFrontDistributionOrigin1D5DE2087", "ViewerProtocolPolicy": "redirect-to-https" }, - "DefaultRootObject": "index.html", "Enabled": true, "HttpVersion": "http2", "IPV6Enabled": true, @@ -845,22 +831,12 @@ "testcloudfrontapigatewayCloudfrontLoggingBucket9811F6E8", "DomainName" ] - }, - "IncludeCookies": false + } }, "Origins": [ { - "ConnectionAttempts": 3, - "ConnectionTimeout": 10, "CustomOriginConfig": { - "HTTPPort": 80, - "HTTPSPort": 443, - "OriginKeepaliveTimeout": 5, - "OriginProtocolPolicy": "https-only", - "OriginReadTimeout": 30, - "OriginSSLProtocols": [ - "TLSv1.2" - ] + "OriginProtocolPolicy": "https-only" }, "DomainName": { "Fn::Select": [ @@ -906,7 +882,7 @@ } ] }, - "Id": "origin1", + "Id": "testcloudfrontapigatewaystacktestcloudfrontapigatewayCloudFrontDistributionOrigin1D5DE2087", "OriginPath": { "Fn::Join": [ "", @@ -919,11 +895,7 @@ ] } } - ], - "PriceClass": "PriceClass_100", - "ViewerCertificate": { - "CloudFrontDefaultCertificate": true - } + ] } }, "Metadata": { diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/lib/index.ts index 1d3775d37..5cac59dce 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/lib/index.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/lib/index.ts @@ -38,7 +38,7 @@ export interface CloudFrontToS3Props { * * @default - Default props are used */ - readonly cloudFrontDistributionProps?: cloudfront.CloudFrontWebDistributionProps | any, + readonly cloudFrontDistributionProps?: cloudfront.DistributionProps | any, /** * Optional user provided props to turn on/off the automatic injection of best practice HTTP * security headers in all responses from cloudfront @@ -49,7 +49,7 @@ export interface CloudFrontToS3Props { } export class CloudFrontToS3 extends Construct { - public readonly cloudFrontWebDistribution: cloudfront.CloudFrontWebDistribution; + public readonly cloudFrontWebDistribution: cloudfront.Distribution; public readonly edgeLambdaFunctionVersion?: lambda.Version; public readonly cloudFrontLoggingBucket?: s3.Bucket; public readonly s3Bucket?: s3.Bucket; diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/package.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/package.json index 8d62037c5..4ca66a3cc 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-cloudfront-s3", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK Constructs for AWS Cloudfront to AWS S3 integration.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,15 +53,17 @@ } }, "dependencies": { - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-cloudfront": "~1.67.0", - "@aws-cdk/aws-s3": "~1.67.0", - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-cloudfront": "~1.68.0", + "@aws-cdk/aws-s3": "~1.68.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", + "@aws-cdk/aws-certificatemanager": "~1.68.0", + "@aws-cdk/aws-cloudfront-origins": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -71,11 +73,13 @@ ] }, "peerDependencies": { - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-cloudfront": "~1.67.0", - "@aws-cdk/aws-s3": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-cloudfront": "~1.68.0", + "@aws-cdk/aws-s3": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4", - "@aws-cdk/aws-lambda": "~1.67.0" + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/aws-certificatemanager": "~1.68.0", + "@aws-cdk/aws-cloudfront-origins": "~1.68.0" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/__snapshots__/test.cloudfront-s3.test.js.snap b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/__snapshots__/test.cloudfront-s3.test.js.snap index a3ed92b99..c3a266a58 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/__snapshots__/test.cloudfront-s3.test.js.snap +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/__snapshots__/test.cloudfront-s3.test.js.snap @@ -3,7 +3,7 @@ exports[`snapshot test CloudFrontToS3 default params 1`] = ` Object { "Resources": Object { - "testcloudfronts3CloudFrontDistributionCFDistribution61FCC088": Object { + "testcloudfronts3CloudFrontDistribution0565DEE8": Object { "Metadata": Object { "cfn_nag": Object { "rules_to_suppress": Array [ @@ -17,21 +17,8 @@ Object { "Properties": Object { "DistributionConfig": Object { "DefaultCacheBehavior": Object { - "AllowedMethods": Array [ - "GET", - "HEAD", - ], - "CachedMethods": Array [ - "GET", - "HEAD", - ], + "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", "Compress": true, - "ForwardedValues": Object { - "Cookies": Object { - "Forward": "none", - }, - "QueryString": false, - }, "LambdaFunctionAssociations": Array [ Object { "EventType": "origin-response", @@ -40,7 +27,7 @@ Object { }, }, ], - "TargetOriginId": "origin1", + "TargetOriginId": "testcloudfronts3CloudFrontDistributionOrigin124051039", "ViewerProtocolPolicy": "redirect-to-https", }, "DefaultRootObject": "index.html", @@ -54,19 +41,16 @@ Object { "DomainName", ], }, - "IncludeCookies": false, }, "Origins": Array [ Object { - "ConnectionAttempts": 3, - "ConnectionTimeout": 10, "DomainName": Object { "Fn::GetAtt": Array [ "testcloudfronts3S3BucketE0C5F76E", "RegionalDomainName", ], }, - "Id": "origin1", + "Id": "testcloudfronts3CloudFrontDistributionOrigin124051039", "S3OriginConfig": Object { "OriginAccessIdentity": Object { "Fn::Join": Array [ @@ -74,7 +58,7 @@ Object { Array [ "origin-access-identity/cloudfront/", Object { - "Ref": "testcloudfronts3CloudFrontOriginAccessIdentity2C681839", + "Ref": "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058", }, ], ], @@ -82,18 +66,14 @@ Object { }, }, ], - "PriceClass": "PriceClass_100", - "ViewerCertificate": Object { - "CloudFrontDefaultCertificate": true, - }, }, }, "Type": "AWS::CloudFront::Distribution", }, - "testcloudfronts3CloudFrontOriginAccessIdentity2C681839": Object { + "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058": Object { "Properties": Object { "CloudFrontOriginAccessIdentityConfig": Object { - "Comment": "Access S3 bucket content only through CloudFront", + "Comment": "Identity for testcloudfronts3CloudFrontDistributionOrigin124051039", }, }, "Type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", @@ -262,19 +242,10 @@ Object { ], "Effect": "Allow", "Principal": Object { - "AWS": Object { - "Fn::Join": Array [ - "", - Array [ - "arn:", - Object { - "Ref": "AWS::Partition", - }, - ":iam::cloudfront:user/CloudFront Origin Access Identity ", - Object { - "Ref": "testcloudfronts3CloudFrontOriginAccessIdentity2C681839", - }, - ], + "CanonicalUser": Object { + "Fn::GetAtt": Array [ + "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058", + "S3CanonicalUserId", ], }, }, @@ -301,32 +272,6 @@ Object { }, ], }, - Object { - "Action": "s3:GetObject", - "Effect": "Allow", - "Principal": Object { - "CanonicalUser": Object { - "Fn::GetAtt": Array [ - "testcloudfronts3CloudFrontOriginAccessIdentity2C681839", - "S3CanonicalUserId", - ], - }, - }, - "Resource": Object { - "Fn::Join": Array [ - "", - Array [ - Object { - "Fn::GetAtt": Array [ - "testcloudfronts3S3BucketE0C5F76E", - "Arn", - ], - }, - "/*", - ], - ], - }, - }, ], "Version": "2012-10-17", }, diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.existing-bucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.existing-bucket.expected.json index ad328e5b9..c215907d2 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.existing-bucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.existing-bucket.expected.json @@ -155,19 +155,10 @@ ], "Effect": "Allow", "Principal": { - "AWS": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::cloudfront:user/CloudFront Origin Access Identity ", - { - "Ref": "testcloudfronts3CloudFrontOriginAccessIdentity2C681839" - } - ] + "CanonicalUser": { + "Fn::GetAtt": [ + "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058", + "S3CanonicalUserId" ] } }, @@ -195,30 +186,42 @@ ] }, { - "Action": "s3:GetObject", + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*" + ], "Effect": "Allow", "Principal": { "CanonicalUser": { "Fn::GetAtt": [ - "testcloudfronts3CloudFrontOriginAccessIdentity2C681839", + "testcloudfronts3CloudFrontDistributionOrigin2S3OriginC54B5C65", "S3CanonicalUserId" ] } }, - "Resource": { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "S3Bucket07682993", - "Arn" - ] - }, - "/*" + "Resource": [ + { + "Fn::GetAtt": [ + "S3Bucket07682993", + "Arn" ] - ] - } + }, + { + "Fn::Join": [ + "", + [ + { + "Fn::GetAtt": [ + "S3Bucket07682993", + "Arn" + ] + }, + "/*" + ] + ] + } + ] } ], "Version": "2012-10-17" @@ -235,14 +238,6 @@ } } }, - "testcloudfronts3CloudFrontOriginAccessIdentity2C681839": { - "Type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", - "Properties": { - "CloudFrontOriginAccessIdentityConfig": { - "Comment": "Access S3 bucket content only through CloudFront" - } - } - }, "testcloudfronts3SetHttpSecurityHeadersServiceRole74D1E252": { "Type": "AWS::IAM::Role", "Properties": { @@ -451,26 +446,32 @@ } } }, - "testcloudfronts3CloudFrontDistributionCFDistribution61FCC088": { + "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058": { + "Type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", + "Properties": { + "CloudFrontOriginAccessIdentityConfig": { + "Comment": "Identity for testcloudfronts3existingbucketstacktestcloudfronts3CloudFrontDistributionOrigin1797491E4" + } + } + }, + "testcloudfronts3CloudFrontDistribution0565DEE8": { "Type": "AWS::CloudFront::Distribution", "Properties": { "DistributionConfig": { + "CacheBehaviors": [ + { + "CachePolicyId": { + "Ref": "myCachePolicy16CE2FCF" + }, + "Compress": true, + "PathPattern": "/images/*.jpg", + "TargetOriginId": "testcloudfronts3existingbucketstacktestcloudfronts3CloudFrontDistributionOrigin2DE0DE37C", + "ViewerProtocolPolicy": "allow-all" + } + ], "DefaultCacheBehavior": { - "AllowedMethods": [ - "GET", - "HEAD" - ], - "CachedMethods": [ - "GET", - "HEAD" - ], + "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", "Compress": true, - "ForwardedValues": { - "Cookies": { - "Forward": "none" - }, - "QueryString": false - }, "LambdaFunctionAssociations": [ { "EventType": "origin-response", @@ -479,7 +480,7 @@ } } ], - "TargetOriginId": "origin1", + "TargetOriginId": "testcloudfronts3existingbucketstacktestcloudfronts3CloudFrontDistributionOrigin1797491E4", "ViewerProtocolPolicy": "redirect-to-https" }, "DefaultRootObject": "index.html", @@ -492,20 +493,39 @@ "testcloudfronts3CloudfrontLoggingBucket985C0FE8", "DomainName" ] - }, - "IncludeCookies": false + } }, "Origins": [ { - "ConnectionAttempts": 3, - "ConnectionTimeout": 10, "DomainName": { "Fn::GetAtt": [ "S3Bucket07682993", "RegionalDomainName" ] }, - "Id": "origin1", + "Id": "testcloudfronts3existingbucketstacktestcloudfronts3CloudFrontDistributionOrigin1797491E4", + "S3OriginConfig": { + "OriginAccessIdentity": { + "Fn::Join": [ + "", + [ + "origin-access-identity/cloudfront/", + { + "Ref": "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058" + } + ] + ] + } + } + }, + { + "DomainName": { + "Fn::GetAtt": [ + "S3Bucket07682993", + "RegionalDomainName" + ] + }, + "Id": "testcloudfronts3existingbucketstacktestcloudfronts3CloudFrontDistributionOrigin2DE0DE37C", "S3OriginConfig": { "OriginAccessIdentity": { "Fn::Join": [ @@ -513,18 +533,14 @@ [ "origin-access-identity/cloudfront/", { - "Ref": "testcloudfronts3CloudFrontOriginAccessIdentity2C681839" + "Ref": "testcloudfronts3CloudFrontDistributionOrigin2S3OriginC54B5C65" } ] ] } } } - ], - "PriceClass": "PriceClass_100", - "ViewerCertificate": { - "CloudFrontDefaultCertificate": true - } + ] } }, "Metadata": { @@ -537,6 +553,37 @@ ] } } + }, + "testcloudfronts3CloudFrontDistributionOrigin2S3OriginC54B5C65": { + "Type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", + "Properties": { + "CloudFrontOriginAccessIdentityConfig": { + "Comment": "Identity for testcloudfronts3existingbucketstacktestcloudfronts3CloudFrontDistributionOrigin2DE0DE37C" + } + } + }, + "myCachePolicy16CE2FCF": { + "Type": "AWS::CloudFront::CachePolicy", + "Properties": { + "CachePolicyConfig": { + "DefaultTTL": 0, + "MaxTTL": 0, + "MinTTL": 0, + "Name": "MyPolicy", + "ParametersInCacheKeyAndForwardedToOrigin": { + "CookiesConfig": { + "CookieBehavior": "none" + }, + "EnableAcceptEncodingGzip": false, + "HeadersConfig": { + "HeaderBehavior": "none" + }, + "QueryStringsConfig": { + "QueryStringBehavior": "none" + } + } + } + } } } } \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.existing-bucket.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.existing-bucket.ts index 79d9a443e..98ca1fa08 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.existing-bucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.existing-bucket.ts @@ -16,6 +16,9 @@ import { App, Stack } from "@aws-cdk/core"; import * as s3 from "@aws-cdk/aws-s3"; import * as defaults from "@aws-solutions-constructs/core"; import { CloudFrontToS3 } from "../lib"; +import * as origins from '@aws-cdk/aws-cloudfront-origins'; +import * as cloudfront from '@aws-cdk/aws-cloudfront'; +import { Duration } from "@aws-cdk/core/lib/duration"; // Setup const app = new App(); @@ -24,9 +27,22 @@ const stack = new Stack(app, 'test-cloudfront-s3-existing-bucket-stack'); let mybucket: s3.Bucket; [mybucket] = defaults.buildS3Bucket(stack, {}); -new CloudFrontToS3(stack, 'test-cloudfront-s3', { +const _construct = new CloudFrontToS3(stack, 'test-cloudfront-s3', { existingBucketObj: mybucket }); +// Add Cache Policy +const myCachePolicy = new cloudfront.CachePolicy(stack, 'myCachePolicy', { + cachePolicyName: 'MyPolicy', + defaultTtl: Duration.minutes(0), + minTtl: Duration.minutes(0), + maxTtl: Duration.minutes(0), +}); + +// Add behavior +_construct.cloudFrontWebDistribution.addBehavior('/images/*.jpg', new origins.S3Origin(mybucket), { + cachePolicy: myCachePolicy +}); + // Synth app.synth(); diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.no-arguments.expected.json index db14c760b..e9db44b8f 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.no-arguments.expected.json @@ -156,19 +156,10 @@ ], "Effect": "Allow", "Principal": { - "AWS": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::cloudfront:user/CloudFront Origin Access Identity ", - { - "Ref": "testcloudfronts3CloudFrontOriginAccessIdentity2C681839" - } - ] + "CanonicalUser": { + "Fn::GetAtt": [ + "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058", + "S3CanonicalUserId" ] } }, @@ -194,32 +185,6 @@ ] } ] - }, - { - "Action": "s3:GetObject", - "Effect": "Allow", - "Principal": { - "CanonicalUser": { - "Fn::GetAtt": [ - "testcloudfronts3CloudFrontOriginAccessIdentity2C681839", - "S3CanonicalUserId" - ] - } - }, - "Resource": { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "testcloudfronts3S3BucketE0C5F76E", - "Arn" - ] - }, - "/*" - ] - ] - } } ], "Version": "2012-10-17" @@ -236,14 +201,6 @@ } } }, - "testcloudfronts3CloudFrontOriginAccessIdentity2C681839": { - "Type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", - "Properties": { - "CloudFrontOriginAccessIdentityConfig": { - "Comment": "Access S3 bucket content only through CloudFront" - } - } - }, "testcloudfronts3SetHttpSecurityHeadersServiceRole74D1E252": { "Type": "AWS::IAM::Role", "Properties": { @@ -452,26 +409,21 @@ } } }, - "testcloudfronts3CloudFrontDistributionCFDistribution61FCC088": { + "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058": { + "Type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", + "Properties": { + "CloudFrontOriginAccessIdentityConfig": { + "Comment": "Identity for testcloudfronts3stacktestcloudfronts3CloudFrontDistributionOrigin1572F5BE9" + } + } + }, + "testcloudfronts3CloudFrontDistribution0565DEE8": { "Type": "AWS::CloudFront::Distribution", "Properties": { "DistributionConfig": { "DefaultCacheBehavior": { - "AllowedMethods": [ - "GET", - "HEAD" - ], - "CachedMethods": [ - "GET", - "HEAD" - ], + "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", "Compress": true, - "ForwardedValues": { - "Cookies": { - "Forward": "none" - }, - "QueryString": false - }, "LambdaFunctionAssociations": [ { "EventType": "origin-response", @@ -480,7 +432,7 @@ } } ], - "TargetOriginId": "origin1", + "TargetOriginId": "testcloudfronts3stacktestcloudfronts3CloudFrontDistributionOrigin1572F5BE9", "ViewerProtocolPolicy": "redirect-to-https" }, "DefaultRootObject": "index.html", @@ -493,20 +445,17 @@ "testcloudfronts3CloudfrontLoggingBucket985C0FE8", "DomainName" ] - }, - "IncludeCookies": false + } }, "Origins": [ { - "ConnectionAttempts": 3, - "ConnectionTimeout": 10, "DomainName": { "Fn::GetAtt": [ "testcloudfronts3S3BucketE0C5F76E", "RegionalDomainName" ] }, - "Id": "origin1", + "Id": "testcloudfronts3stacktestcloudfronts3CloudFrontDistributionOrigin1572F5BE9", "S3OriginConfig": { "OriginAccessIdentity": { "Fn::Join": [ @@ -514,18 +463,14 @@ [ "origin-access-identity/cloudfront/", { - "Ref": "testcloudfronts3CloudFrontOriginAccessIdentity2C681839" + "Ref": "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058" } ] ] } } } - ], - "PriceClass": "PriceClass_100", - "ViewerCertificate": { - "CloudFrontDefaultCertificate": true - } + ] } }, "Metadata": { diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.no-security-headers.expected.json b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.no-security-headers.expected.json index ea8a9551b..89ad2693c 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.no-security-headers.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/integ.no-security-headers.expected.json @@ -156,19 +156,10 @@ ], "Effect": "Allow", "Principal": { - "AWS": { - "Fn::Join": [ - "", - [ - "arn:", - { - "Ref": "AWS::Partition" - }, - ":iam::cloudfront:user/CloudFront Origin Access Identity ", - { - "Ref": "testcloudfronts3nosecurityheadersCloudFrontOriginAccessIdentity5321D214" - } - ] + "CanonicalUser": { + "Fn::GetAtt": [ + "testcloudfronts3nosecurityheadersCloudFrontDistributionOrigin1S3Origin38CFDB89", + "S3CanonicalUserId" ] } }, @@ -194,32 +185,6 @@ ] } ] - }, - { - "Action": "s3:GetObject", - "Effect": "Allow", - "Principal": { - "CanonicalUser": { - "Fn::GetAtt": [ - "testcloudfronts3nosecurityheadersCloudFrontOriginAccessIdentity5321D214", - "S3CanonicalUserId" - ] - } - }, - "Resource": { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "testcloudfronts3nosecurityheadersS3Bucket4D06173D", - "Arn" - ] - }, - "/*" - ] - ] - } } ], "Version": "2012-10-17" @@ -236,14 +201,6 @@ } } }, - "testcloudfronts3nosecurityheadersCloudFrontOriginAccessIdentity5321D214": { - "Type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", - "Properties": { - "CloudFrontOriginAccessIdentityConfig": { - "Comment": "Access S3 bucket content only through CloudFront" - } - } - }, "testcloudfronts3nosecurityheadersCloudfrontLoggingBucket92A5E2A5": { "Type": "AWS::S3::Bucket", "Properties": { @@ -315,27 +272,22 @@ } } }, - "testcloudfronts3nosecurityheadersCloudFrontDistributionCFDistribution3F3C52A8": { + "testcloudfronts3nosecurityheadersCloudFrontDistributionOrigin1S3Origin38CFDB89": { + "Type": "AWS::CloudFront::CloudFrontOriginAccessIdentity", + "Properties": { + "CloudFrontOriginAccessIdentityConfig": { + "Comment": "Identity for testcloudfronts3nosecurityheadersCloudFrontDistributionOrigin1BB2D7D46" + } + } + }, + "testcloudfronts3nosecurityheadersCloudFrontDistribution3BC8CDED": { "Type": "AWS::CloudFront::Distribution", "Properties": { "DistributionConfig": { "DefaultCacheBehavior": { - "AllowedMethods": [ - "GET", - "HEAD" - ], - "CachedMethods": [ - "GET", - "HEAD" - ], + "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", "Compress": true, - "ForwardedValues": { - "Cookies": { - "Forward": "none" - }, - "QueryString": false - }, - "TargetOriginId": "origin1", + "TargetOriginId": "testcloudfronts3nosecurityheadersCloudFrontDistributionOrigin1BB2D7D46", "ViewerProtocolPolicy": "redirect-to-https" }, "DefaultRootObject": "index.html", @@ -348,20 +300,17 @@ "testcloudfronts3nosecurityheadersCloudfrontLoggingBucket92A5E2A5", "DomainName" ] - }, - "IncludeCookies": false + } }, "Origins": [ { - "ConnectionAttempts": 3, - "ConnectionTimeout": 10, "DomainName": { "Fn::GetAtt": [ "testcloudfronts3nosecurityheadersS3Bucket4D06173D", "RegionalDomainName" ] }, - "Id": "origin1", + "Id": "testcloudfronts3nosecurityheadersCloudFrontDistributionOrigin1BB2D7D46", "S3OriginConfig": { "OriginAccessIdentity": { "Fn::Join": [ @@ -369,18 +318,14 @@ [ "origin-access-identity/cloudfront/", { - "Ref": "testcloudfronts3nosecurityheadersCloudFrontOriginAccessIdentity5321D214" + "Ref": "testcloudfronts3nosecurityheadersCloudFrontDistributionOrigin1S3Origin38CFDB89" } ] ] } } } - ], - "PriceClass": "PriceClass_100", - "ViewerCertificate": { - "CloudFrontDefaultCertificate": true - } + ] } }, "Metadata": { diff --git a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts index e21f6a04f..53dae0aac 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-cloudfront-s3/test/test.cloudfront-s3.test.ts @@ -16,6 +16,7 @@ import { CloudFrontToS3, CloudFrontToS3Props } from "../lib"; import * as cdk from "@aws-cdk/core"; import * as s3 from '@aws-cdk/aws-s3'; import '@aws-cdk/assert/jest'; +import * as acm from '@aws-cdk/aws-certificatemanager'; function deploy(stack: cdk.Stack) { return new CloudFrontToS3(stack, 'test-cloudfront-s3', {}); @@ -102,12 +103,12 @@ test('check existing bucket', () => { test('test cloudfront with custom domain names', () => { const stack = new cdk.Stack(); + const certificate = acm.Certificate.fromCertificateArn(stack, 'Cert', 'arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012'); + const props: CloudFrontToS3Props = { cloudFrontDistributionProps: { - aliasConfiguration: { - acmCertRef: '/acm/mycertificate', - names: ['mydomains'] - } + domainNames: ['mydomains'], + certificate } }; @@ -115,79 +116,65 @@ test('test cloudfront with custom domain names', () => { expect(stack).toHaveResourceLike("AWS::CloudFront::Distribution", { DistributionConfig: { - Aliases: [ - "mydomains" - ], - DefaultCacheBehavior: { - AllowedMethods: [ - "GET", - "HEAD" - ], - CachedMethods: [ - "GET", - "HEAD" - ], - Compress: true, - ForwardedValues: { - Cookies: { - Forward: "none" - }, - QueryString: false - }, - LambdaFunctionAssociations: [ - { - EventType: "origin-response", - LambdaFunctionARN: { - Ref: "testcloudfronts3SetHttpSecurityHeadersVersionF1C744BB" - } + Aliases: [ + "mydomains" + ], + DefaultCacheBehavior: { + CachePolicyId: "658327ea-f89d-4fab-a63d-7e88639e58f6", + Compress: true, + LambdaFunctionAssociations: [ + { + EventType: "origin-response", + LambdaFunctionARN: { + Ref: "testcloudfronts3SetHttpSecurityHeadersVersionF1C744BB" } - ], - TargetOriginId: "origin1", - ViewerProtocolPolicy: "redirect-to-https" - }, - DefaultRootObject: "index.html", - Enabled: true, - HttpVersion: "http2", - IPV6Enabled: true, - Logging: { - Bucket: { + } + ], + TargetOriginId: "testcloudfronts3CloudFrontDistributionOrigin124051039", + ViewerProtocolPolicy: "redirect-to-https" + }, + DefaultRootObject: "index.html", + Enabled: true, + HttpVersion: "http2", + IPV6Enabled: true, + Logging: { + Bucket: { + "Fn::GetAtt": [ + "testcloudfronts3CloudfrontLoggingBucket985C0FE8", + "DomainName" + ] + } + }, + Origins: [ + { + DomainName: { "Fn::GetAtt": [ - "testcloudfronts3CloudfrontLoggingBucket985C0FE8", - "DomainName" + "testcloudfronts3S3BucketE0C5F76E", + "RegionalDomainName" ] }, - IncludeCookies: false - }, - Origins: [ - { - DomainName: { - "Fn::GetAtt": [ - "testcloudfronts3S3BucketE0C5F76E", - "RegionalDomainName" + Id: "testcloudfronts3CloudFrontDistributionOrigin124051039", + S3OriginConfig: { + OriginAccessIdentity: { + "Fn::Join": [ + "", + [ + "origin-access-identity/cloudfront/", + { + Ref: "testcloudfronts3CloudFrontDistributionOrigin1S3Origin4695F058" + } + ] ] - }, - Id: "origin1", - S3OriginConfig: { - OriginAccessIdentity: { - "Fn::Join": [ - "", - [ - "origin-access-identity/cloudfront/", - { - Ref: "testcloudfronts3CloudFrontOriginAccessIdentity2C681839" - } - ] - ] - } } } - ], - PriceClass: "PriceClass_100", - ViewerCertificate: { - AcmCertificateArn: "/acm/mycertificate", - SslSupportMethod: "sni-only" } + ], + ViewerCertificate: { + AcmCertificateArn: "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012", + MinimumProtocolVersion: "TLSv1.2_2019", + SslSupportMethod: "sni-only" } + } }); }); diff --git a/source/patterns/@aws-solutions-constructs/aws-cognito-apigateway-lambda/package.json b/source/patterns/@aws-solutions-constructs/aws-cognito-apigateway-lambda/package.json index 743ac7fab..054bcec70 100644 --- a/source/patterns/@aws-solutions-constructs/aws-cognito-apigateway-lambda/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-cognito-apigateway-lambda/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-cognito-apigateway-lambda", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK Constructs for AWS Cognito to AWS API Gateway to AWS Lambda integration", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,17 +53,17 @@ } }, "dependencies": { - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-cognito": "~1.67.0", - "@aws-cdk/aws-apigateway": "~1.67.0", - "@aws-cdk/aws-logs": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-cognito": "~1.68.0", + "@aws-cdk/aws-apigateway": "~1.68.0", + "@aws-cdk/aws-logs": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -73,13 +73,13 @@ ] }, "peerDependencies": { - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-cognito": "~1.67.0", - "@aws-cdk/aws-apigateway": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-cognito": "~1.68.0", + "@aws-cdk/aws-apigateway": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4", - "@aws-cdk/aws-logs": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0" + "@aws-cdk/aws-logs": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda-elasticsearch-kibana/package.json b/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda-elasticsearch-kibana/package.json index 1944a920c..ae8791b0a 100644 --- a/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda-elasticsearch-kibana/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda-elasticsearch-kibana/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-dynamodb-stream-lambda-elasticsearch-kibana", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK Constructs for Amazon Dynamodb stream to AWS Lambda to AWS Elasticsearch with Kibana integration", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,22 +53,22 @@ } }, "dependencies": { - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/aws-lambda-event-sources": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-cognito": "~1.67.0", - "@aws-cdk/aws-elasticsearch": "~1.67.0", - "@aws-cdk/aws-dynamodb": "~1.67.0", - "@aws-cdk/aws-cloudwatch": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-sqs": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", - "@aws-solutions-constructs/aws-dynamodb-stream-lambda": "~1.67.0", - "@aws-solutions-constructs/aws-lambda-elasticsearch-kibana": "~1.67.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/aws-lambda-event-sources": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-cognito": "~1.68.0", + "@aws-cdk/aws-elasticsearch": "~1.68.0", + "@aws-cdk/aws-dynamodb": "~1.68.0", + "@aws-cdk/aws-cloudwatch": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-sqs": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", + "@aws-solutions-constructs/aws-dynamodb-stream-lambda": "~1.68.0", + "@aws-solutions-constructs/aws-lambda-elasticsearch-kibana": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -78,18 +78,18 @@ ] }, "peerDependencies": { - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-cognito": "~1.67.0", - "@aws-cdk/aws-elasticsearch": "~1.67.0", - "@aws-cdk/aws-dynamodb": "~1.67.0", - "@aws-cdk/aws-cloudwatch": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", - "@aws-solutions-constructs/aws-dynamodb-stream-lambda": "~1.67.0", - "@aws-solutions-constructs/aws-lambda-elasticsearch-kibana": "~1.67.0", - "@aws-cdk/aws-lambda-event-sources": "~1.67.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-cognito": "~1.68.0", + "@aws-cdk/aws-elasticsearch": "~1.68.0", + "@aws-cdk/aws-dynamodb": "~1.68.0", + "@aws-cdk/aws-cloudwatch": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", + "@aws-solutions-constructs/aws-dynamodb-stream-lambda": "~1.68.0", + "@aws-solutions-constructs/aws-lambda-elasticsearch-kibana": "~1.68.0", + "@aws-cdk/aws-lambda-event-sources": "~1.68.0", "constructs": "^3.0.4", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-sqs": "~1.67.0" + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-sqs": "~1.68.0" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda-elasticsearch-kibana/test/__snapshots__/dynamodb-stream-lambda-elasticsearch-kibana.test.js.snap b/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda-elasticsearch-kibana/test/__snapshots__/dynamodb-stream-lambda-elasticsearch-kibana.test.js.snap index daee4153b..8256e9a2b 100644 --- a/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda-elasticsearch-kibana/test/__snapshots__/dynamodb-stream-lambda-elasticsearch-kibana.test.js.snap +++ b/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda-elasticsearch-kibana/test/__snapshots__/dynamodb-stream-lambda-elasticsearch-kibana.test.js.snap @@ -234,20 +234,7 @@ Object { Object { "Action": "dynamodb:ListStreams", "Effect": "Allow", - "Resource": Object { - "Fn::Join": Array [ - "", - Array [ - Object { - "Fn::GetAtt": Array [ - "testdynamodbstreamlambdaelasticsearchstackDynamoDBStreamToLambdaDynamoTable9A779B83", - "Arn", - ], - }, - "/stream/*", - ], - ], - }, + "Resource": "*", }, Object { "Action": Array [ diff --git a/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda-elasticsearch-kibana/test/integ.no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda-elasticsearch-kibana/test/integ.no-arguments.expected.json index 5fe85b752..36f12a48a 100644 --- a/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda-elasticsearch-kibana/test/integ.no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda-elasticsearch-kibana/test/integ.no-arguments.expected.json @@ -71,20 +71,7 @@ { "Action": "dynamodb:ListStreams", "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "testdynamodbstreamlambdaelasticsearchkibanaDynamoDBStreamToLambdaDynamoTable90CA17D8", - "Arn" - ] - }, - "/stream/*" - ] - ] - } + "Resource": "*" }, { "Action": [ diff --git a/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda/package.json b/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda/package.json index b823619e5..97dcbaa9c 100644 --- a/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-dynamodb-stream-lambda", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK Constructs for AWS DynamoDB Stream to AWS Lambda integration.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,17 +53,17 @@ } }, "dependencies": { - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/aws-lambda-event-sources": "~1.67.0", - "@aws-cdk/aws-dynamodb": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-sqs": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/aws-lambda-event-sources": "~1.68.0", + "@aws-cdk/aws-dynamodb": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-sqs": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -73,13 +73,13 @@ ] }, "peerDependencies": { - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/aws-dynamodb": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", - "@aws-cdk/aws-lambda-event-sources": "~1.67.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/aws-dynamodb": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", + "@aws-cdk/aws-lambda-event-sources": "~1.68.0", "constructs": "^3.0.4", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-sqs": "~1.67.0" + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-sqs": "~1.68.0" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda/test/__snapshots__/dynamodb-stream-lambda.test.js.snap b/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda/test/__snapshots__/dynamodb-stream-lambda.test.js.snap index 296a7bc80..637c6254a 100644 --- a/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda/test/__snapshots__/dynamodb-stream-lambda.test.js.snap +++ b/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda/test/__snapshots__/dynamodb-stream-lambda.test.js.snap @@ -228,20 +228,7 @@ Object { Object { "Action": "dynamodb:ListStreams", "Effect": "Allow", - "Resource": Object { - "Fn::Join": Array [ - "", - Array [ - Object { - "Fn::GetAtt": Array [ - "testlambdadynamodbstackDynamoTable8138E93B", - "Arn", - ], - }, - "/stream/*", - ], - ], - }, + "Resource": "*", }, Object { "Action": Array [ diff --git a/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda/test/dynamodb-stream-lambda.test.ts b/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda/test/dynamodb-stream-lambda.test.ts index 3a092315a..22aa09ad8 100644 --- a/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda/test/dynamodb-stream-lambda.test.ts +++ b/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda/test/dynamodb-stream-lambda.test.ts @@ -107,20 +107,7 @@ test('check lambda permission to read dynamodb stream', () => { { Action: "dynamodb:ListStreams", Effect: "Allow", - Resource: { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "testlambdadynamodbstackDynamoTable8138E93B", - "Arn" - ] - }, - "/stream/*" - ] - ] - } + Resource: "*" }, { Action: [ diff --git a/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda/test/integ.no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda/test/integ.no-arguments.expected.json index 5fa6a8b01..c38f348c7 100644 --- a/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda/test/integ.no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-dynamodb-stream-lambda/test/integ.no-arguments.expected.json @@ -71,20 +71,7 @@ { "Action": "dynamodb:ListStreams", "Effect": "Allow", - "Resource": { - "Fn::Join": [ - "", - [ - { - "Fn::GetAtt": [ - "testdynamodbstreamlambdaDynamoTable6EB2ED0F", - "Arn" - ] - }, - "/stream/*" - ] - ] - } + "Resource": "*" }, { "Action": [ diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-lambda/package.json b/source/patterns/@aws-solutions-constructs/aws-events-rule-lambda/package.json index ea8e4279a..48278b50a 100644 --- a/source/patterns/@aws-solutions-constructs/aws-events-rule-lambda/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-lambda/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-events-rule-lambda", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK Constructs for deploying AWS Events Rule that inveokes AWS Lambda", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,15 +53,15 @@ } }, "dependencies": { - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-events": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-events": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -71,11 +71,11 @@ ] }, "peerDependencies": { - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-events": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-events": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-sns/README.md b/source/patterns/@aws-solutions-constructs/aws-events-rule-sns/README.md index 48a13eca3..0f59b95f9 100644 --- a/source/patterns/@aws-solutions-constructs/aws-events-rule-sns/README.md +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-sns/README.md @@ -27,6 +27,9 @@ This AWS Solutions Construct implements an AWS Events rule and an AWS SNS Topic. Here is a minimal deployable pattern definition in Typescript: ``` typescript +import { Duration } from '@aws-cdk/core'; +import * as events from '@aws-cdk/aws-events'; +import * as iam from '@aws-cdk/aws-iam'; import { EventsRuleToSNSProps, EventsRuleToSNS } from "@aws-solutions-constructs/aws-events-rule-sns"; const props: EventsRuleToSNSProps = { @@ -35,7 +38,7 @@ const props: EventsRuleToSNSProps = { } }; -const constructStack = new EventsRuleToSNS(this, 'test-events-rule-sns', props); +const constructStack = new EventsRuleToSNS(this, 'test-construct', props); // Grant yourself permissions to use the Customer Managed KMS Key const policyStatement = new iam.PolicyStatement({ diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-sns/package.json b/source/patterns/@aws-solutions-constructs/aws-events-rule-sns/package.json index 853c14d47..85ebc7879 100644 --- a/source/patterns/@aws-solutions-constructs/aws-events-rule-sns/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-sns/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-events-rule-sns", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK Constructs for deploying AWS Events Rule that invokes AWS SNS", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,16 +53,16 @@ } }, "dependencies": { - "@aws-cdk/aws-events": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-sns": "~1.67.0", - "@aws-cdk/aws-kms": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/aws-events": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-sns": "~1.68.0", + "@aws-cdk/aws-kms": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -72,12 +72,12 @@ ] }, "peerDependencies": { - "@aws-cdk/aws-sns": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-events": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-kms": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/aws-sns": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-events": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-kms": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-sqs/README.md b/source/patterns/@aws-solutions-constructs/aws-events-rule-sqs/README.md index 80014b038..4c122aa58 100644 --- a/source/patterns/@aws-solutions-constructs/aws-events-rule-sqs/README.md +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-sqs/README.md @@ -27,8 +27,10 @@ This AWS Solutions Construct implements an AWS Events rule and an AWS SQS Queue. Here is a minimal deployable pattern definition in Typescript: ``` typescript -import { EventsRuleToSQSProps, EventsRuleToSQS } from "@aws-solutions-constructs/aws-events-rule-sqs"; +import { Duration } from '@aws-cdk/core'; +import * as events from '@aws-cdk/aws-events'; import * as iam from '@aws-cdk/aws-iam'; +import { EventsRuleToSQSProps, EventsRuleToSQS } from "@aws-solutions-constructs/aws-events-rule-sqs"; const props: EventsRuleToSQSProps = { eventRuleProps: { @@ -36,7 +38,7 @@ const props: EventsRuleToSQSProps = { } }; -const constructStack = new EventsRuleToSQS(this, 'test-events-rule-sqs', props); +const constructStack = new EventsRuleToSQS(this, 'test-construct', props); // Grant yourself permissions to use the Customer Managed KMS Key const policyStatement = new iam.PolicyStatement({ diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-sqs/package.json b/source/patterns/@aws-solutions-constructs/aws-events-rule-sqs/package.json index f2cd41d4d..b3956ce03 100644 --- a/source/patterns/@aws-solutions-constructs/aws-events-rule-sqs/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-sqs/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-events-rule-sqs", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK Constructs for deploying AWS Events Rule that invokes AWS SQS", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,16 +53,16 @@ } }, "dependencies": { - "@aws-cdk/aws-events": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-sqs": "~1.67.0", - "@aws-cdk/aws-kms": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/aws-events": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-sqs": "~1.68.0", + "@aws-cdk/aws-kms": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -72,12 +72,12 @@ ] }, "peerDependencies": { - "@aws-cdk/aws-sqs": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-events": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-kms": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/aws-sqs": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-events": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-kms": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-events-rule-step-function/package.json b/source/patterns/@aws-solutions-constructs/aws-events-rule-step-function/package.json index 96626d4c8..656d8061f 100644 --- a/source/patterns/@aws-solutions-constructs/aws-events-rule-step-function/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-events-rule-step-function/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-events-rule-step-function", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK Constructs for deploying AWS Events Rule that invokes AWS Step Function", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,19 +53,19 @@ } }, "dependencies": { - "@aws-cdk/aws-stepfunctions": "~1.67.0", - "@aws-cdk/aws-stepfunctions-tasks": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-events": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/aws-logs": "~1.67.0", - "@aws-cdk/aws-cloudwatch": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/aws-stepfunctions": "~1.68.0", + "@aws-cdk/aws-stepfunctions-tasks": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-events": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/aws-logs": "~1.68.0", + "@aws-cdk/aws-cloudwatch": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -75,15 +75,15 @@ ] }, "peerDependencies": { - "@aws-cdk/aws-stepfunctions": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-events": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/aws-stepfunctions": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-events": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4", - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/aws-cloudwatch": "~1.67.0", - "@aws-cdk/aws-stepfunctions-tasks": "~1.67.0", - "@aws-cdk/aws-logs": "~1.67.0" + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/aws-cloudwatch": "~1.68.0", + "@aws-cdk/aws-stepfunctions-tasks": "~1.68.0", + "@aws-cdk/aws-logs": "~1.68.0" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/package.json b/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/package.json index fc30f888e..a3f03eccc 100644 --- a/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-iot-kinesisfirehose-s3", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK Constructs for AWS IoT to AWS Kinesis Firehose to AWS S3 integration.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,18 +53,18 @@ } }, "dependencies": { - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-kinesisfirehose": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-iot": "~1.67.0", - "@aws-cdk/aws-s3": "~1.67.0", - "@aws-cdk/aws-logs": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", - "@aws-solutions-constructs/aws-kinesisfirehose-s3": "~1.67.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-kinesisfirehose": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-iot": "~1.68.0", + "@aws-cdk/aws-s3": "~1.68.0", + "@aws-cdk/aws-logs": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", + "@aws-solutions-constructs/aws-kinesisfirehose-s3": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -74,14 +74,14 @@ ] }, "peerDependencies": { - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-kinesisfirehose": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-iot": "~1.67.0", - "@aws-cdk/aws-s3": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", - "@aws-solutions-constructs/aws-kinesisfirehose-s3": "~1.67.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-kinesisfirehose": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-iot": "~1.68.0", + "@aws-cdk/aws-s3": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", + "@aws-solutions-constructs/aws-kinesisfirehose-s3": "~1.68.0", "constructs": "^3.0.4", - "@aws-cdk/aws-logs": "~1.67.0" + "@aws-cdk/aws-logs": "~1.68.0" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/test/__snapshots__/test.iot-kinesisfirehose-s3.test.js.snap b/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/test/__snapshots__/test.iot-kinesisfirehose-s3.test.js.snap index 0426da7c6..cca0198bd 100644 --- a/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/test/__snapshots__/test.iot-kinesisfirehose-s3.test.js.snap +++ b/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/test/__snapshots__/test.iot-kinesisfirehose-s3.test.js.snap @@ -94,6 +94,30 @@ Object { }, }, "CompressionFormat": "GZIP", + "EncryptionConfiguration": Object { + "KMSEncryptionConfig": Object { + "AWSKMSKeyARN": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":kms:", + Object { + "Ref": "AWS::Region", + }, + ":", + Object { + "Ref": "AWS::AccountId", + }, + ":alias/aws/s3", + ], + ], + }, + }, + }, "RoleARN": Object { "Fn::GetAtt": Array [ "testiotfirehoses3KinesisFirehoseToS3KinesisFirehoseRole93DE9170", diff --git a/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/test/integ.no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/test/integ.no-arguments.expected.json index 5b21a745f..5c3f0fc34 100644 --- a/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/test/integ.no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-iot-kinesisfirehose-s3/test/integ.no-arguments.expected.json @@ -289,6 +289,30 @@ } }, "CompressionFormat": "GZIP", + "EncryptionConfiguration": { + "KMSEncryptionConfig": { + "AWSKMSKeyARN": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":kms:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":alias/aws/s3" + ] + ] + } + } + }, "RoleARN": { "Fn::GetAtt": [ "testiotfirehoses3KinesisFirehoseToS3KinesisFirehoseRole93DE9170", diff --git a/source/patterns/@aws-solutions-constructs/aws-iot-lambda-dynamodb/package.json b/source/patterns/@aws-solutions-constructs/aws-iot-lambda-dynamodb/package.json index 4dcd1e563..4226d8d96 100644 --- a/source/patterns/@aws-solutions-constructs/aws-iot-lambda-dynamodb/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-iot-lambda-dynamodb/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-iot-lambda-dynamodb", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK Constructs for AWS IoT to AWS Lambda to AWS DyanmoDB integration.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,17 +53,17 @@ } }, "dependencies": { - "@aws-cdk/aws-dynamodb": "~1.67.0", - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/aws-iot": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", - "@aws-solutions-constructs/aws-iot-lambda": "~1.67.0", - "@aws-solutions-constructs/aws-lambda-dynamodb": "~1.67.0", + "@aws-cdk/aws-dynamodb": "~1.68.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/aws-iot": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", + "@aws-solutions-constructs/aws-iot-lambda": "~1.68.0", + "@aws-solutions-constructs/aws-lambda-dynamodb": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -73,13 +73,13 @@ ] }, "peerDependencies": { - "@aws-cdk/aws-dynamodb": "~1.67.0", - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/aws-iot": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", - "@aws-solutions-constructs/aws-iot-lambda": "~1.67.0", - "@aws-solutions-constructs/aws-lambda-dynamodb": "~1.67.0", + "@aws-cdk/aws-dynamodb": "~1.68.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/aws-iot": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", + "@aws-solutions-constructs/aws-iot-lambda": "~1.68.0", + "@aws-solutions-constructs/aws-lambda-dynamodb": "~1.68.0", "constructs": "^3.0.4" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-iot-lambda/package.json b/source/patterns/@aws-solutions-constructs/aws-iot-lambda/package.json index c9cb58525..511a7f6a4 100644 --- a/source/patterns/@aws-solutions-constructs/aws-iot-lambda/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-iot-lambda/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-iot-lambda", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK Constructs for AWS IoT to AWS Lambda integration", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,15 +53,15 @@ } }, "dependencies": { - "@aws-cdk/aws-iot": "~1.67.0", - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/aws-iot": "~1.68.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -71,11 +71,11 @@ ] }, "peerDependencies": { - "@aws-cdk/aws-iot": "~1.67.0", - "@aws-cdk/aws-lambda": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", - "@aws-cdk/aws-iam": "~1.67.0", + "@aws-cdk/aws-iot": "~1.68.0", + "@aws-cdk/aws-lambda": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", + "@aws-cdk/aws-iam": "~1.68.0", "constructs": "^3.0.4" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3-and-kinesisanalytics/package.json b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3-and-kinesisanalytics/package.json index b035d840a..17c1e892a 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3-and-kinesisanalytics/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3-and-kinesisanalytics/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-kinesisfirehose-s3-and-kinesisanalytics", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK constructs for defining an interaction between an Amazon Kinesis Data Firehose delivery stream and (1) an Amazon S3 bucket, and (2) an Amazon Kinesis Data Analytics application.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,19 +53,19 @@ } }, "dependencies": { - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-kinesis": "~1.67.0", - "@aws-cdk/aws-kinesisanalytics": "~1.67.0", - "@aws-cdk/aws-kinesisfirehose": "~1.67.0", - "@aws-cdk/aws-s3": "~1.67.0", - "@aws-cdk/aws-logs": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", - "@aws-solutions-constructs/aws-kinesisfirehose-s3": "~1.67.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-kinesis": "~1.68.0", + "@aws-cdk/aws-kinesisanalytics": "~1.68.0", + "@aws-cdk/aws-kinesisfirehose": "~1.68.0", + "@aws-cdk/aws-s3": "~1.68.0", + "@aws-cdk/aws-logs": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", + "@aws-solutions-constructs/aws-kinesisfirehose-s3": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -75,15 +75,15 @@ ] }, "peerDependencies": { - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-kinesis": "~1.67.0", - "@aws-cdk/aws-kinesisanalytics": "~1.67.0", - "@aws-cdk/aws-kinesisfirehose": "~1.67.0", - "@aws-cdk/aws-s3": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", - "@aws-solutions-constructs/aws-kinesisfirehose-s3": "~1.67.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-kinesis": "~1.68.0", + "@aws-cdk/aws-kinesisanalytics": "~1.68.0", + "@aws-cdk/aws-kinesisfirehose": "~1.68.0", + "@aws-cdk/aws-s3": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", + "@aws-solutions-constructs/aws-kinesisfirehose-s3": "~1.68.0", "constructs": "^3.0.4", - "@aws-cdk/aws-logs": "~1.67.0" + "@aws-cdk/aws-logs": "~1.68.0" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3-and-kinesisanalytics/test/__snapshots__/test.kinesisfirehose-analytics-s3.test.js.snap b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3-and-kinesisanalytics/test/__snapshots__/test.kinesisfirehose-analytics-s3.test.js.snap index a2247db04..a4994bd3d 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3-and-kinesisanalytics/test/__snapshots__/test.kinesisfirehose-analytics-s3.test.js.snap +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3-and-kinesisanalytics/test/__snapshots__/test.kinesisfirehose-analytics-s3.test.js.snap @@ -127,6 +127,30 @@ Object { }, }, "CompressionFormat": "GZIP", + "EncryptionConfiguration": Object { + "KMSEncryptionConfig": Object { + "AWSKMSKeyARN": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":kms:", + Object { + "Ref": "AWS::Region", + }, + ":", + Object { + "Ref": "AWS::AccountId", + }, + ":alias/aws/s3", + ], + ], + }, + }, + }, "RoleARN": Object { "Fn::GetAtt": Array [ "testfirehoses3andanalyticsstackKinesisFirehoseToS3KinesisFirehoseRoleE7F8ADDA", diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3-and-kinesisanalytics/test/integ.no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3-and-kinesisanalytics/test/integ.no-arguments.expected.json index 14273ed75..03b881611 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3-and-kinesisanalytics/test/integ.no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3-and-kinesisanalytics/test/integ.no-arguments.expected.json @@ -288,6 +288,30 @@ } }, "CompressionFormat": "GZIP", + "EncryptionConfiguration": { + "KMSEncryptionConfig": { + "AWSKMSKeyARN": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":kms:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":alias/aws/s3" + ] + ] + } + } + }, "RoleARN": { "Fn::GetAtt": [ "testfirehoses3andanalyticsstackKinesisFirehoseToS3KinesisFirehoseRoleE7F8ADDA", diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/README.md b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/README.md index 6eddbea68..438f0a8ca 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/README.md +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/README.md @@ -18,7 +18,7 @@ | **Language** | **Package** | |:-------------|-----------------| -|![Python Logo](https://docs.aws.amazon.com/cdk/api/latest/img/python32.png) Python|`aws_solutions_constructs.aws_kinesisfirehose_s3`| +|![Python Logo](https://docs.aws.amazon.com/cdk/api/latest/img/python32.png) Python|`aws_solutions_constructs.aws-kinesis-firehose-s3`| |![Typescript Logo](https://docs.aws.amazon.com/cdk/api/latest/img/typescript32.png) Typescript|`@aws-solutions-constructs/aws-kinesisfirehose-s3`| |![Java Logo](https://docs.aws.amazon.com/cdk/api/latest/img/java32.png) Java|`software.amazon.awsconstructs.services.kinesisfirehoses3`| diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/lib/index.ts b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/lib/index.ts index 17ae0afb3..8af316110 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/lib/index.ts +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/lib/index.ts @@ -19,6 +19,7 @@ import * as iam from '@aws-cdk/aws-iam'; import { overrideProps } from '@aws-solutions-constructs/core'; import * as logs from '@aws-cdk/aws-logs'; import * as cdk from '@aws-cdk/core'; +import * as kms from '@aws-cdk/aws-kms'; /** * The properties for the KinesisFirehoseToS3 class. @@ -103,10 +104,12 @@ export class KinesisFirehoseToS3 extends Construct { // Attach policy to role firehosePolicy.attachToRole(this.kinesisFirehoseRole); + const awsManagedKey: kms.IKey = kms.Alias.fromAliasName(scope, 'aws-managed-key', 'alias/aws/s3'); + // Setup the default Kinesis Firehose props const defaultKinesisFirehoseProps: kinesisfirehose.CfnDeliveryStreamProps = defaults.DefaultCfnDeliveryStreamProps(bucket.bucketArn, this.kinesisFirehoseRole.roleArn, - this.kinesisFirehoseLogGroup.logGroupName, cwLogStream.logStreamName); + this.kinesisFirehoseLogGroup.logGroupName, cwLogStream.logStreamName, awsManagedKey); // Override with the input props if (props.kinesisFirehoseProps) { diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/package.json b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/package.json index fe37ff8a5..32f455b38 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/package.json +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/package.json @@ -1,6 +1,6 @@ { "name": "@aws-solutions-constructs/aws-kinesisfirehose-s3", - "version": "1.67.0", + "version": "1.68.0", "description": "CDK constructs for defining an interaction between an Amazon Kinesis Data Firehose delivery stream and an Amazon S3 bucket.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -53,16 +53,17 @@ } }, "dependencies": { - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-kinesisfirehose": "~1.67.0", - "@aws-cdk/aws-s3": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-logs": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-kinesisfirehose": "~1.68.0", + "@aws-cdk/aws-s3": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-logs": "~1.68.0", + "@aws-cdk/aws-kms": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", "constructs": "^3.0.4" }, "devDependencies": { - "@aws-cdk/assert": "~1.67.0", + "@aws-cdk/assert": "~1.68.0", "@types/jest": "^24.0.23", "@types/node": "^10.3.0" }, @@ -72,12 +73,13 @@ ] }, "peerDependencies": { - "@aws-cdk/aws-iam": "~1.67.0", - "@aws-cdk/aws-kinesisfirehose": "~1.67.0", - "@aws-cdk/aws-s3": "~1.67.0", - "@aws-cdk/core": "~1.67.0", - "@aws-cdk/aws-logs": "~1.67.0", - "@aws-solutions-constructs/core": "~1.67.0", - "constructs": "^3.0.4" + "@aws-cdk/aws-iam": "~1.68.0", + "@aws-cdk/aws-kinesisfirehose": "~1.68.0", + "@aws-cdk/aws-s3": "~1.68.0", + "@aws-cdk/core": "~1.68.0", + "@aws-cdk/aws-logs": "~1.68.0", + "@aws-solutions-constructs/core": "~1.68.0", + "constructs": "^3.0.4", + "@aws-cdk/aws-kms": "~1.68.0" } } diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/__snapshots__/test.kinesisfirehose-s3.test.js.snap b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/__snapshots__/test.kinesisfirehose-s3.test.js.snap index d93d09215..eebe0aabe 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/__snapshots__/test.kinesisfirehose-s3.test.js.snap +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/__snapshots__/test.kinesisfirehose-s3.test.js.snap @@ -26,6 +26,30 @@ Object { }, }, "CompressionFormat": "GZIP", + "EncryptionConfiguration": Object { + "KMSEncryptionConfig": Object { + "AWSKMSKeyARN": Object { + "Fn::Join": Array [ + "", + Array [ + "arn:", + Object { + "Ref": "AWS::Partition", + }, + ":kms:", + Object { + "Ref": "AWS::Region", + }, + ":", + Object { + "Ref": "AWS::AccountId", + }, + ":alias/aws/s3", + ], + ], + }, + }, + }, "RoleARN": Object { "Fn::GetAtt": Array [ "testfirehoses3KinesisFirehoseRole9BC5362D", diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.no-arguments.expected.json b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.no-arguments.expected.json index 1fb6fbd95..eb52ec8fa 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.no-arguments.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.no-arguments.expected.json @@ -289,6 +289,30 @@ } }, "CompressionFormat": "GZIP", + "EncryptionConfiguration": { + "KMSEncryptionConfig": { + "AWSKMSKeyARN": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":kms:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":alias/aws/s3" + ] + ] + } + } + }, "RoleARN": { "Fn::GetAtt": [ "testfirehoses3KinesisFirehoseRole9BC5362D", diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.pre-existing-bucket.expected.json b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.pre-existing-bucket.expected.json index ee4200ba5..4e5bd0e29 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.pre-existing-bucket.expected.json +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.pre-existing-bucket.expected.json @@ -147,6 +147,22 @@ } }, "CompressionFormat": "GZIP", + "EncryptionConfiguration": { + "KMSEncryptionConfig": { + "AWSKMSKeyARN": { + "Fn::Join": [ + "", + [ + "arn:aws:kms:us-west-2:", + { + "Ref": "AWS::AccountId" + }, + ":alias/aws/s3" + ] + ] + } + } + }, "RoleARN": { "Fn::GetAtt": [ "testfirehoses3preexistingbucketstackKinesisFirehoseRole84325204", diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.pre-existing-bucket.ts b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.pre-existing-bucket.ts index 931584ca8..2df4b7eb8 100644 --- a/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.pre-existing-bucket.ts +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisfirehose-s3/test/integ.pre-existing-bucket.ts @@ -15,6 +15,7 @@ import { App, Stack } from "@aws-cdk/core"; import { KinesisFirehoseToS3 } from "../lib"; import * as s3 from "@aws-cdk/aws-s3"; +import * as cdk from "@aws-cdk/core"; // Setup const app = new App(); @@ -23,7 +24,16 @@ stack.templateOptions.description = 'Integration Test for aws-kinesisfirehose-s3 const mybucket: s3.IBucket = s3.Bucket.fromBucketName(stack, 'mybucket', 'cdktoolkit-stagingbucket-1cjqz1mn5psg3'); new KinesisFirehoseToS3(stack, 'test-firehose-s3-pre-existing-bucket-stack', { - existingBucketObj: mybucket + existingBucketObj: mybucket, + kinesisFirehoseProps: { + extendedS3DestinationConfiguration : { + encryptionConfiguration: { + kmsEncryptionConfig: { + awskmsKeyArn: `arn:aws:kms:us-west-2:${cdk.Aws.ACCOUNT_ID}:alias/aws/s3` + } + } + } + } }); // Synth diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/.eslintignore b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/.eslintignore new file mode 100644 index 000000000..0819e2e65 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/.eslintignore @@ -0,0 +1,5 @@ +lib/*.js +test/*.js +*.d.ts +coverage +test/lambda/index.js \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/.gitignore b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/.gitignore new file mode 100644 index 000000000..8626f2274 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/.gitignore @@ -0,0 +1,16 @@ +lib/*.js +test/*.js +!test/lambda/* +*.js.map +*.d.ts +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +.nycrc +.LAST_PACKAGE +*.snk \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/.npmignore b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/.npmignore new file mode 100644 index 000000000..f66791629 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/.npmignore @@ -0,0 +1,21 @@ +# Exclude typescript source and config +*.ts +tsconfig.json +coverage +.nyc_output +*.tgz +*.snk +*.tsbuildinfo + +# Include javascript files and typescript declarations +!*.js +!*.d.ts + +# Exclude jsii outdir +dist + +# Include .jsii +!.jsii + +# Include .jsii +!.jsii \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/README.md b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/README.md new file mode 100644 index 000000000..b4f68d0e8 --- /dev/null +++ b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/README.md @@ -0,0 +1,98 @@ +# aws-kinesisstreams-kinesisfirehose-s3 module + + +--- + +![Stability: Experimental](https://img.shields.io/badge/stability-Experimental-important.svg?style=for-the-badge) + +> All classes are under active development and subject to non-backward compatible changes or removal in any +> future version. These are not subject to the [Semantic Versioning](https://semver.org/) model. +> This means that while you may use them, you may need to update your source code when upgrading to a newer version of this package. + +--- + + +| **Reference Documentation**:| https://docs.aws.amazon.com/solutions/latest/constructs/| +|:-------------|:-------------| + + +| **Language** | **Package** | +|:-------------|-----------------| +|![Python Logo](https://docs.aws.amazon.com/cdk/api/latest/img/python32.png) Python|`aws_solutions_constructs.aws_kinesisstreams_kinesisfirehose_s3`| +|![Typescript Logo](https://docs.aws.amazon.com/cdk/api/latest/img/typescript32.png) Typescript|`@aws-solutions-constructs/aws-kinesis-streams-kinesis-firehose-s3`| +|![Java Logo](https://docs.aws.amazon.com/cdk/api/latest/img/java32.png) Java|`software.amazon.awsconstructs.services.kinesisstreamskinesisfirehoses3`| + +This AWS Solutions Construct implements an Amazon Kinesis Data Stream (KDS) connected to Amazon Kinesis Data Firehose (KDF) delivery stream connected to an Amazon S3 bucket. + +Here is a minimal deployable pattern definition in Typescript: + +``` javascript +import { KinesisStreamsToKinesisFirehoseToS3 } from '@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3'; + +new KinesisStreamsToKinesisFirehoseToS3(this, 'test-stream-firehose-s3', {}); + +``` + +## Initializer + +``` text +new KinesisStreamsToKinesisFirehoseToS3(scope: Construct, id: string, props: KinesisStreamsToKinesisFirehoseToS3Props); +``` + +_Parameters_ + +* scope [`Construct`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.Construct.html) +* id `string` +* props [`KinesisStreamsToKinesisFirehoseToS3Props`](#pattern-construct-props) + +## Pattern Construct Props + +| **Name** | **Type** | **Description** | +|:-------------|:----------------|-----------------| +|kinesisFirehoseProps?|[`kinesisfirehose.CfnDeliveryStreamProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-kinesisfirehose.CfnDeliveryStreamProps.html)|Optional user provided props to override the default props for Kinesis Firehose Delivery Stream| +|existingBucketObj?|[`s3.IBucket`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-s3.IBucket.html)|Existing instance of S3 Bucket object, if this is set then the bucketProps is ignored.| +|bucketProps?|[`s3.BucketProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-s3.BucketProps.html)|User provided props to override the default props for the S3 Bucket.| +|kinesisStreamProps?|[`kinesis.StreamProps`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-kinesis.StreamProps.html)|Optional user-provided props to override the default props for the Kinesis stream.| +|existingStreamObj?|[`kinesis.Stream`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-kinesis.Stream.html)|Existing instance of Kinesis Stream, if this is set then kinesisStreamProps is ignored.| +|createCloudWatchAlarms?|`boolean`|Whether to create recommended CloudWatch alarms| + +## Pattern Properties + +| **Name** | **Type** | **Description** | +|:-------------|:----------------|-----------------| +|kinesisFirehose|[`kinesisfirehose.CfnDeliveryStream`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-kinesisfirehose.CfnDeliveryStream.html)|Returns an instance of kinesisfirehose.CfnDeliveryStream created by the construct| +|s3Bucket?|[`s3.Bucket`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-s3.Bucket.html)|Returns an instance of s3.Bucket created by the construct| +|s3LoggingBucket?|[`s3.Bucket`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-s3.Bucket.html)|Returns an instance of s3.Bucket created by the construct as the logging bucket for the primary bucket.| +|kinesisFirehoseRole|[`iam.Role`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-iam.Role.html)|Returns an instance of the iam.Role created by the construct for Kinesis Data Firehose delivery stream| +|kinesisFirehoseLogGroup|[`logs.LogGroup`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-logs.LogGroup.html)|Returns an instance of the LogGroup created by the construct for Kinesis Data Firehose delivery stream| +|kinesisStream|[`kinesis.Stream`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-kinesis.Stream.html)|Returns an instance of the Kinesis stream created by the pattern.| +|kinesisStreamRole|[`iam.Role`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-iam.Role.html)|Returns an instance of the iam.Role created by the construct for Kinesis stream.| +|cloudwatchAlarms?|[`cloudwatch.Alarm[]`](https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-cloudwatch.Alarm.html)|Returns a list of cloudwatch.Alarm created by the construct| + +## Default settings + +Out of the box implementation of the Construct without any override will set the following defaults: + +### Amazon Kinesis Stream +* Configure least privilege access IAM role for Kinesis Stream +* Enable server-side encryption for Kinesis Stream using AWS Managed KMS Key +* Deploy best practices CloudWatch Alarms for the Kinesis Stream + +### Amazon Kinesis Firehose +* Enable CloudWatch logging for Kinesis Firehose +* Configure least privilege access IAM role for Amazon Kinesis Firehose + +### Amazon S3 Bucket +* Configure Access logging for S3 Bucket +* Enable server-side encryption for S3 Bucket using AWS managed KMS Key +* Enforce encryption of data in transit +* Turn on the versioning for S3 Bucket +* Don't allow public access for S3 Bucket +* Retain the S3 Bucket when deleting the CloudFormation stack +* Applies Lifecycle rule to move noncurrent object versions to Glacier storage after 90 days + +## Architecture +![Architecture Diagram](architecture.png) + +*** +© Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. \ No newline at end of file diff --git a/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/architecture.png b/source/patterns/@aws-solutions-constructs/aws-kinesisstreams-kinesisfirehose-s3/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..b57949e3772cf9b0f8cb7c8255ed46950e7eeb57 GIT binary patch literal 53569 zcmdSBWpG@v(l%&{?ZgZ*V~jB~Gcz+YGcz+Y+i}dy%*@QpJjOBeI`_Ws)_(u?-_}mm zDQQmWOiL|w_w%%LCR|QN6z&Js4-gO#IB_u{1rQK0F%S??W@yN-J2(>f)L#Xtqk^aa zNYxb1>DL!GV>NLTX=xCOuR1gc1Sl5Bw|`B(E*? %N)7`4pZj1SAfe_U5dYIg z=Bxbo`I7OUXZ~A)=YsxE>#yI*1^eIDU}Cx8|62!T{`Z)b)1}{6@!d{L-4O%?2Ib!c z3X+k90RqAYA}++QL~kokcA@k3y$6N8-!|j2(zg+Qh$~H&u3@n z-YgY$byx@_c?j_UI#_T)EIo%QcIQVYnXAsvR~hBa#}{cBBKR2u8RtKm&q|wDIf{=R zZO(v*G)4$mP{AHVy8kbVoko6whczb4N-VGtK3x$4&ry=_!)Y4KWa+{oVum?hY$)To z(u#q##B?=nSB82~4&(VdI7!#L!`qrJOt26?!fOX4I`GrOU#9o4{>y}5Fs74bQY_>4 zr9ux4PAVqiRGeIy(S!D7*|}CK#Qyw5!FHlrb(wM3QmF$+6>PvbbehihhdTfwGAGHG z3fuyJ=|Rq6-}SAVV}k`lQ~hDObyMTL8Z|m3KUnPbCBz=5i5WD=gs2xebtA= W%3VB=kTUjftD;wKbOUT1_TxSh}5F?h>k0 z*gKf;+R5=EJDG{ 5L8PRZDnn$D2v|RxFl8_WuZrKmlcpi@0r5uZHsv4CsjnXRMA_ zJ6BZq|4tmB{>!|_u!)2&BE{ICPN%JaCuhQF4zsi`zbU>{?R=(TASQaa4jg%PWuwXJ zNR)Az#yucgoy~lyL}AniTV(F{soD`^bEVEparw8Rr2i3z_)pw*o_gw+IRBmm@p)dg zU*Wa}DrRD$EbZ8_i$2Md16MK0&XuD>`=0OTatjMrwS2E}^o*~A39I3wZs$}O0$05U z^6sQ5?@Jp#B%hD*-(cEuvKfr!jEl}c_4uU2P3Kjmbn4Ha!~DPf#~jFied&`TFVa|> z83G;+EOK}{O*tj%vy2LjH;*KHD`(P#r;&E+X2;%gi3v{(dm7JKsm#lEJ-$>4c#9!4 zC1uL+nM^bO5ht>Eivs^g3vc@ETkI)XvatpaoBbw7l_i5IG{)DOmnZbY_doiQxDy=$ zf|u&emXqAS#*p6RMCExH;~e =_-^3VIUKTxJAD)n7~w5ZdmHbzs1L`F*UjD8>akV!5w##l<2_2< z^G>Z=eD(Ot
x4I?`}j>c*mcN1e)6a3~%wD6)T5sU@<}Ha6+KL zXDfd% )tw<3s40eXsZFbTT zC4DWI8O=eL9e~&O;lO>RJact5PAI0y OrvX(Bno{4(U5 z+3563nRC^ &bW~L1=>`Z40liOyj#xqc1ae!{87U-z_^dU zON(j3|14*)f6IA~L7nVtIS2Bw D0OKhzff#O+6FnvO#Sv$6kD^+`Q+n*V|+ZM5?QBHI)6j&;4yQ=ZkguKK3m zB-$6j)WizO%S|S0HX8}JjUOG%kG)%`o7|LclfF{xsK3K?gVggM2Z&h;C^O$JiY&Kf z=SIi&$5BXlrA_ufw`kJ6f8Gd5OBTISSi#wmZ2JJR^9#Q`yC309Gvb#EwADrcL=)Q0 zAYH>07*ZC?;AT!4(e|yI^v{e)P-W8S8%_*cAtjKon5qqS5w>e)3^iNf*LuuN)Y27+ zI1d{UcQGqOMiZl~7<-zs&_r?+dOkNOkEi5sDV*ne`#MuSIMO{oRw+%s*3z}-5hY^I zWsmw$&66OoBo`g$ep~||Tr^9(XR52{nZB$;^#>?+jm~eQ=xV|lC`7k|hWP}+rpp|p z@d2KMu;FUIpGm!Ie=ow-teE T*F5Tohnb%j9A zF2ZK3xrs##Go6tnlP&cYPFH-tkv6^mIECteQ$$IXVg`-M3-&0Jp~Rt1u@#-3XWNS* zVW6A0z0XL ;a;V |j^LD{~hsd+7-VV*~to|2ws$AO^vEemg0)ViG!Xr;l>eIk{@r9@aMeQLyzd=|01 z2X52P82+TECdt}E;YqHU;@*rYHb|@L0vXbS{SDM};6bEy&Lzi|4PwYdQnYxl)^%@I z)k3%Fbv(Rb0|_uSpd!s~h0&G#proq$F#MfLqAZHUq{);~T?$1TMb^YNqt4={4IR`H z3aAqBQDZhS@i8j&U|lP84*os-Q`Wt$ot=n;I$?^mcBX_0Ei!d4u*r>r>>^>3tH$h< ziK|xg A!!i<@(AtX5XJmt;Yxm>K5{*~ zH?hpn;PWC@;{=c7g9csAW&A$@W3C$#Mo`r!z8SZfJzgdM6X*=pjQ^XN2g=yycMb@n z|3;!^60p(bL-kehFUZDc3vIV##&DuPbDs;3IB&+L4fR5Q0w#xYGN?tuAQ~W{TM>8o zA802x@nr@|6ROiMHqdYT&@%*U5FU67-}j=B@au18*QIlja+3)-LSzj=F2Bz}LUxSz zm~ywFo`}sb&1lRh5@6~63#&bd n|s?0~2ZHvcxntbNGIJi!!KG?YCYvT1mc9}mdh`P>ab8-1=R|A8{t|Hk}ZdW>Si z?yD8^AwA ?3v zIJ^6SyN*ldIAKFe;RH?k|5b0EP|p&<&qL#S8Ei0GY*3n-kDn*&IpC%~Hs3li8z2^b z?7=FulewLlvEAHY-=+kbnc2s)gp16f^U=#Pm9(IRi~KLnLjD(+fOYD{aQ>;$d};i9 z`?NH3IS4Plxw?KTfi|Gl;L3g>{(Tt*7D3NpHk4tE(@Y^|LbJJTD0e<`c8W;3_6r!u z6aNLhD~L6sR)a2s72RK>6!jB Cy^(GjCE$s$gxo~P zs~t!u!jiq^^CEuaK{QR3`F3N!vCjWH?!Q6vKXXY3@xlbgq-O)A{hdOKPnyUK^8`T9 zwn{KX!wva@Z3cgWfrz_|wF$Q;g%q9p31YAR(4Q-u`}u9Q=6_T_`>RJc>A!HxAH*~^ z#J^>v4R2uB`q_r~moY&Ve%`)eQ$Al7bsp#fNZvk2k;aS-BL4hrG`KV<{e}Dg*_Hq4 z(f{8He^pG-S2b(U9bGn4dlmlh!Gh3OHCS_}j6eeK)Pmn) ov(f6&jB?a!6r_xMIZR0~dsQ)m5|BU_w&fmy3-1fYIipL(_kO&4pMD&2@ zr?^Fu+4nU0RSe{v;hk FiZ;F_4=%u&2Z%p6ATOGLT1_uR7bKF}wxCN+CDeguP zr=!E62E;Wu0EYMEn>wx4`)9h*wzPVt@`Aat1a@zaBFU;Q@YLeU#PkB_EnM=`FcP>< zLi^9*-;{M3GOA5!$3oMegLM(MqgNW1JBv$2aVD_a{9fIch9y!xT&ierlPRLg4!K zCYm;&CdMn-wV_e}f>;|*_4z%kNnFe#Z&kCGo^8+=hr|A|GAT|h2+XJ{!I6d8^f!Z{l>`om-km(AupuCDz}tS+EBY> zAlY|3FW&tVqY5U>a`B`F BREW>3u6|bHhmdk m?>tUM_Dv9am?CIqA*yE$7=>^l+}l5hh3YFHi802!SE$&PX#0i%dn9E#D`1 z6;+Z`&p&8DeBs)bBF6+>Fuxi dPw7Cm7|DC~MdSb2N5%EWBI71sKu?w2QVG9jxi~1Y=%L zii@B)-ngZ8f_3Xz1=pSPf9O?9Gr;*%vALsv69{ tg|M}ox zHaja7L|V1fVJ|Pesz@RjeyL)d0n@fZ$T^wQpo?2qaJ-Gd=nTWu{n1WZ^f{w&)gS5y zoqYDKpeD~O*O!g`;sa#YVI3?+cAkZ}s2ye*1EGVNs`6rLVmY(z-7V^C;HlEc|J-zN z`17(`asV4#2BrtKaaGxWymGYq)Kt)-Jh?kctk((#`C*D7q0;OKEy3s$bI~Lxq@z%! zn6gXjixtLnNzS1b{Cs+L_9!ja67;xEc9rG9nkRL eXm?T-lW?u9}N!GI%xT0Vco0;mcBs-f+Mn znGbH&rwfHK*Ftb%{ktOgNb~1Kxt+-Fm``wnxlY2y+R?86?v%m@WpMZgLQ`_!I`nE; zrzm;LZ{rG7y;PJ1pM-` jBMpzEyZ-E?VVKmC{aS8k;h{gGP!w@GorHPDMFh@+ z(|?`*5b7Jv0 23#M%Z?CbblIcCwXb3}`@s}AyS!R4%^L6zO>|kr9mJ;Mo z=BE1NS3LxsZl|I9EmZW4UOKX^Ci3S9%DM@=$x-rhL{;6<^CFTvm-cs6^fzbuAclDc z4wRqtwO=gq5z+3Kth9gr$(e2eB==rdQjX9JWhI)j3Z&8g*bc^xp~TJ=Oq}Qr%4ytD zZ7$_OBn7_eAFnjo>HCqN_Z?|Rw<`s$IseEN3Ids!PF)9^`>JEW0N8L2FCWd@3SCOx z0z3XNK}VoS@xk6Se5!xGLTlTzv1cjD83k*y=^@_DdHU7*otMYLbGx&5cHeEI8xrA; z4~zp}KF-6nw$LGKqDc;s?%bfd R7Zi+^Cu$+I`Ho+vy_PlJTJQ z%H7 E{pg(yZ#kItAs-r2gp>GG*o!bkc5`^%qWOtrQf6 zL#&rvWv@0s5RmF%K7VSv67W<*?Kl|DCj;+JZNGS*aJHzrx~EAUj_SfKKWWlMTU k3+ZA6t_TYS2*zoyr>*0IdQSTK zN|D>NOz)^t+pxsPI_Ucr9KG0_vLe !Td8gQD$lWwPhe