AWSTemplateFormatVersion: 2010-09-09 Description: API-Gateway stack for the Application Modernization Workshop/ Immersion Day Parameters: UniShopBackendDnsName: Type: AWS::SSM::Parameter::Value Default: UniShopPublicDnsName Resources: ApiGatewayRestApi: Type: AWS::ApiGateway::RestApi Properties: Name: unicorns EndpointConfiguration: Types: - EDGE Tags: - Key: AppName Value: UnicornShop UnicornsResource: Type: AWS::ApiGateway::Resource Properties: ParentId: !GetAtt ApiGatewayRestApi.RootResourceId PathPart: unicorns RestApiId: !Ref ApiGatewayRestApi # Enable Api Gateway CORS for Unicorns Resource EnableApiGatewayCORS4UnicornsResourceMethod: Type: AWS::ApiGateway::Method Properties: AuthorizationType: NONE ResourceId: !Ref UnicornsResource RestApiId: !Ref ApiGatewayRestApi HttpMethod: OPTIONS Integration: IntegrationResponses: - StatusCode: 200 ResponseParameters: method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS'" method.response.header.Access-Control-Allow-Origin: "'*'" ResponseTemplates: application/json: '' PassthroughBehavior: WHEN_NO_MATCH RequestTemplates: application/json: '{"statusCode": 200}' Type: MOCK MethodResponses: - StatusCode: 200 ResponseModels: application/json: Empty ResponseParameters: # Indicate the following are not required params method.response.header.Access-Control-Allow-Headers: false method.response.header.Access-Control-Allow-Methods: false method.response.header.Access-Control-Allow-Origin: false UnicornsGetMethod: Type: AWS::ApiGateway::Method Properties: HttpMethod: GET ResourceId: !Ref UnicornsResource RestApiId: !Ref ApiGatewayRestApi AuthorizationType: NONE MethodResponses: - StatusCode: 200 ResponseModels: application/json: Empty ResponseParameters: # Indicate the following are not required params method.response.header.Access-Control-Allow-Headers: false method.response.header.Access-Control-Allow-Methods: false method.response.header.Access-Control-Allow-Origin: false Integration: IntegrationHttpMethod: GET Type: HTTP Uri: !Join [ "", [ !Ref UniShopBackendDnsName, '/unicorns' ] ] IntegrationResponses: - StatusCode: 200 ResponseParameters: method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS'" # Ideally, we would put our client app TLD here for extra security method.response.header.Access-Control-Allow-Origin: "'*'" ResponseTemplates: application/json: '' BasketResource: Type: AWS::ApiGateway::Resource Properties: ParentId: !Ref UnicornsResource PathPart: basket RestApiId: !Ref ApiGatewayRestApi # Enable Api Gateway CORS for Basket Resource EnableApiGatewayCORS4BasketResourceMethod: Type: AWS::ApiGateway::Method Properties: AuthorizationType: NONE ResourceId: !Ref BasketResource RestApiId: !Ref ApiGatewayRestApi HttpMethod: OPTIONS Integration: IntegrationResponses: - StatusCode: 200 ResponseParameters: method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" method.response.header.Access-Control-Allow-Methods: "'POST,DELETE,GET,OPTIONS'" method.response.header.Access-Control-Allow-Origin: "'*'" ResponseTemplates: application/json: '' PassthroughBehavior: WHEN_NO_MATCH RequestTemplates: application/json: '{"statusCode": 200}' Type: MOCK MethodResponses: - StatusCode: 200 ResponseModels: application/json: Empty ResponseParameters: # Indicate the following are not required params method.response.header.Access-Control-Allow-Headers: false method.response.header.Access-Control-Allow-Methods: false method.response.header.Access-Control-Allow-Origin: false BasketPostMethod: Type: AWS::ApiGateway::Method Properties: HttpMethod: POST ResourceId: !Ref BasketResource RestApiId: !Ref ApiGatewayRestApi AuthorizationType: NONE MethodResponses: - StatusCode: 200 ResponseModels: application/json: Empty ResponseParameters: # Indicate the following are not required params method.response.header.Access-Control-Allow-Headers: false method.response.header.Access-Control-Allow-Methods: false method.response.header.Access-Control-Allow-Origin: false Integration: IntegrationHttpMethod: POST Type: HTTP Uri: !Join [ "", [ !Ref UniShopBackendDnsName, '/unicorns/basket' ] ] IntegrationResponses: - StatusCode: 200 ResponseParameters: method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS'" # Ideally, we would put our client app TLD here for extra security method.response.header.Access-Control-Allow-Origin: "'*'" ResponseTemplates: application/json: '' BasketDeleteMethod: Type: AWS::ApiGateway::Method Properties: HttpMethod: DELETE ResourceId: !Ref BasketResource RestApiId: !Ref ApiGatewayRestApi AuthorizationType: NONE MethodResponses: - StatusCode: 200 ResponseModels: application/json: Empty ResponseParameters: # Indicate the following are not required params method.response.header.Access-Control-Allow-Headers: false method.response.header.Access-Control-Allow-Methods: false method.response.header.Access-Control-Allow-Origin: false Integration: IntegrationHttpMethod: DELETE Type: HTTP Uri: !Join [ "", [ !Ref UniShopBackendDnsName, '/unicorns/basket' ] ] IntegrationResponses: - StatusCode: 200 ResponseParameters: method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" method.response.header.Access-Control-Allow-Methods: "'DELETE,GET,OPTIONS'" # Ideally, we would put our client app TLD here for extra security method.response.header.Access-Control-Allow-Origin: "'*'" ResponseTemplates: application/json: '' UUIDResource: Type: AWS::ApiGateway::Resource Properties: ParentId: !Ref BasketResource PathPart: "{uuid}" RestApiId: !Ref ApiGatewayRestApi # Enable Api Gateway CORS for UUID Resource EnableApiGatewayCORS4UUIDResourceMethod: Type: AWS::ApiGateway::Method Properties: AuthorizationType: NONE ResourceId: !Ref UUIDResource RestApiId: !Ref ApiGatewayRestApi HttpMethod: OPTIONS Integration: IntegrationResponses: - StatusCode: 200 ResponseParameters: method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS'" method.response.header.Access-Control-Allow-Origin: "'*'" ResponseTemplates: application/json: '' PassthroughBehavior: WHEN_NO_MATCH RequestTemplates: application/json: '{"statusCode": 200}' Type: MOCK MethodResponses: - StatusCode: 200 ResponseModels: application/json: Empty ResponseParameters: # Indicate the following are not required params method.response.header.Access-Control-Allow-Headers: false method.response.header.Access-Control-Allow-Methods: false method.response.header.Access-Control-Allow-Origin: false UUIDGetMethod: Type: AWS::ApiGateway::Method Properties: HttpMethod: GET ResourceId: !Ref UUIDResource RestApiId: !Ref ApiGatewayRestApi AuthorizationType: NONE RequestParameters: method.request.path.uuid : true MethodResponses: - StatusCode: 200 ResponseModels: application/json: Empty ResponseParameters: # Indicate the following are not required params method.response.header.Access-Control-Allow-Headers: false method.response.header.Access-Control-Allow-Methods: false method.response.header.Access-Control-Allow-Origin: false Integration: IntegrationHttpMethod: GET Type: HTTP Uri: !Join [ "", [ !Ref UniShopBackendDnsName, '/unicorns/basket/{uuid}' ] ] RequestParameters: integration.request.path.uuid: 'method.request.path.uuid' IntegrationResponses: - StatusCode: 200 ResponseParameters: method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS'" # Ideally, we would put our client app TLD here for extra security method.response.header.Access-Control-Allow-Origin: "'*'" ResponseTemplates: application/json: '' UserResource: Type: AWS::ApiGateway::Resource Properties: ParentId: !GetAtt ApiGatewayRestApi.RootResourceId PathPart: user RestApiId: !Ref ApiGatewayRestApi # Enable Api Gateway CORS for User Resource EnableApiGatewayCORS4UserResourceMethod: Type: AWS::ApiGateway::Method Properties: AuthorizationType: NONE ResourceId: !Ref UserResource RestApiId: !Ref ApiGatewayRestApi HttpMethod: OPTIONS Integration: IntegrationResponses: - StatusCode: 200 ResponseParameters: method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS'" method.response.header.Access-Control-Allow-Origin: "'*'" ResponseTemplates: application/json: '' PassthroughBehavior: WHEN_NO_MATCH RequestTemplates: application/json: '{"statusCode": 200}' Type: MOCK MethodResponses: - StatusCode: 200 ResponseModels: application/json: Empty ResponseParameters: # Indicate the following are not required params method.response.header.Access-Control-Allow-Headers: false method.response.header.Access-Control-Allow-Methods: false method.response.header.Access-Control-Allow-Origin: false CreateUserMethod: Type: AWS::ApiGateway::Method Properties: HttpMethod: POST ResourceId: !Ref UserResource RestApiId: !Ref ApiGatewayRestApi AuthorizationType: NONE MethodResponses: - StatusCode: 200 ResponseModels: application/json: Empty ResponseParameters: # Indicate the following are not required params method.response.header.Access-Control-Allow-Headers: false method.response.header.Access-Control-Allow-Methods: false method.response.header.Access-Control-Allow-Origin: false Integration: IntegrationHttpMethod: POST Type: HTTP Uri: !Join [ "", [ !Ref UniShopBackendDnsName, '/user' ] ] IntegrationResponses: - StatusCode: 200 ResponseParameters: method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS'" # Ideally, we would put our client app TLD here for extra security method.response.header.Access-Control-Allow-Origin: "'*'" ResponseTemplates: application/json: '' LoginResource: Type: AWS::ApiGateway::Resource Properties: ParentId: !Ref UserResource PathPart: login RestApiId: !Ref ApiGatewayRestApi # Enable Api Gateway CORS for Login Resource EnableApiGatewayCORS4LoginResourceMethod: Type: AWS::ApiGateway::Method Properties: AuthorizationType: NONE ResourceId: !Ref LoginResource RestApiId: !Ref ApiGatewayRestApi HttpMethod: OPTIONS Integration: IntegrationResponses: - StatusCode: 200 ResponseParameters: method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS'" method.response.header.Access-Control-Allow-Origin: "'*'" ResponseTemplates: application/json: '' PassthroughBehavior: WHEN_NO_MATCH RequestTemplates: application/json: '{"statusCode": 200}' Type: MOCK MethodResponses: - StatusCode: 200 ResponseModels: application/json: Empty ResponseParameters: # Indicate the following are not required params method.response.header.Access-Control-Allow-Headers: false method.response.header.Access-Control-Allow-Methods: false method.response.header.Access-Control-Allow-Origin: false UserLoginMethod: Type: AWS::ApiGateway::Method Properties: HttpMethod: POST ResourceId: !Ref LoginResource RestApiId: !Ref ApiGatewayRestApi AuthorizationType: NONE MethodResponses: - StatusCode: 200 ResponseModels: application/json: Empty ResponseParameters: # Indicate the following are not required params method.response.header.Access-Control-Allow-Headers: false method.response.header.Access-Control-Allow-Methods: false method.response.header.Access-Control-Allow-Origin: false Integration: IntegrationHttpMethod: POST Type: HTTP Uri: !Join [ "", [ !Ref UniShopBackendDnsName, '/user/login' ] ] IntegrationResponses: - StatusCode: 200 ResponseParameters: method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS'" # Ideally, we would put our client app TLD here for extra security method.response.header.Access-Control-Allow-Origin: "'*'" ResponseTemplates: application/json: '' ApiDeployment: Type: AWS::ApiGateway::Deployment Properties: RestApiId: !Ref ApiGatewayRestApi DependsOn: - UnicornsGetMethod - BasketPostMethod - BasketDeleteMethod - UUIDGetMethod - CreateUserMethod - UserLoginMethod ApiLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub "/aws/apigateway/${ApiGatewayRestApi}-logs" RetentionInDays: 30 ApiStage: Type: AWS::ApiGateway::Stage Properties: DeploymentId: !Ref ApiDeployment RestApiId: !Ref ApiGatewayRestApi StageName: dev Description: development TracingEnabled: true Tags: - Key: AppName Value: UnicornShop AccessLogSetting: DestinationArn: !GetAtt ApiLogGroup.Arn Format: > { "apigw":{ "apiid":"$context.apiId", "stage":"$context.stage", "routekey":"$context.routeKey", }, "client":{ "ip":"$context.identity.sourceIp" }, "request":{ "requestId":"$context.requestId", "time":"$context.requestTime", "time_epoch":"$context.requestTimeEpoch", "http_method":"$context.httpMethod", "path":"$context.path", "protocol":"$context.protocol", "awsendpointrequestid":"$context.awsEndpointRequestId", "extendedrequestid":"$context.extendedRequestId" }, "integration":{ "latency":"$context.integrationLatency", "status":"$context.integrationStatus", "error":"$context.integration.error", "status":"$context.integration.status", "latency":"$context.integration.latency", "requestId":"$context.integration.requestId" }, "response":{ "latency":"$context.responseLatency", "status":"$context.status", "length":"$context.responseLength" } MethodSettings: - ResourcePath: "/*" HttpMethod: "*" #CloudWatch metrics are enabled MetricsEnabled: true #Log full requests/responses data DataTraceEnabled: false ApiCloudWatchRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - apigateway.amazonaws.com Action: "sts:AssumeRole" ManagedPolicyArns: - "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs" ApiAccount: Type: "AWS::ApiGateway::Account" Properties: CloudWatchRoleArn: !GetAtt ApiCloudWatchRole.Arn #by adding this resource, Cloudwatch automatically generates an observability dashboard for monitoring stack resources. TagBasedGroup: Type: "AWS::ResourceGroups::Group" Properties: Name: "UnicornShopResourcesGroup" ResourceQuery: Type: "TAG_FILTERS_1_0" Query: ResourceTypeFilters: - "AWS::AllSupported" TagFilters: - Key: "AppName" Values: - "UnicornShop" ApplicationInsights: Type: "AWS::ApplicationInsights::Application" Properties: ResourceGroupName: !Ref TagBasedGroup CWEMonitorEnabled: true AutoConfigurationEnabled: true DependsOn: TagBasedGroup Outputs: InvokeURL: Value: !Sub "https://${ApiGatewayRestApi}.execute-api.${AWS::Region}.amazonaws.com/${ApiStage}"