private void createApiGateway()

in DeliveryApi/cdk/src/main/java/com/ilmlf/delivery/api/ApiStack.java [248:417]


  private void createApiGateway(ApiStackProps props) throws IOException {
    final String apiStageName = "Prod";

    LogGroup accessLogGroup =
        new LogGroup(
            this,
            "ILMLFDeliveryAccess",
            LogGroupProps.builder().retention(RetentionDays.TWO_MONTHS).build());

    Map logFormat = new LinkedHashMap();
    logFormat.put("status", "$context.status");
    logFormat.put("profile", "$context.authorizer.claims.profile");
    logFormat.put("ip", "$context.identity.sourceIp");
    logFormat.put("requestId", "$context.requestId");
    logFormat.put("responseLength", "$context.responseLength");
    logFormat.put("httpMethod", "$context.httpMethod");
    logFormat.put("protocol", "$context.protocol");
    logFormat.put("resourcePath", "$context.resourcePath");
    logFormat.put("requestTime", "$context.requestTime");
    logFormat.put("username", "$context.authorizer.claims['cognito:username']");

    Topic errorAlarmTopic = new Topic(this, "ErrorAlarmTopic", TopicProps.builder()
        .topicName("ErrorAlarmTopic")
        .build());

    // Need to check for null as the tryGetContext() call will be null if parameter is not passed in
    if (props.alertEmail != null && !props.alertEmail.isEmpty()) {
      errorAlarmTopic.addSubscription(new EmailSubscription(props.alertEmail));
    }

    ApiFunction createSlotsHandler =
        this.defaultLambdaRdsProxy("CreateSlots", props, this.lambdaRdsProxyRoleWithIam);

    ApiFunction getSlotsHandler =
        this.defaultLambdaRdsProxy("GetSlots", props, this.lambdaRdsProxyRoleWithIam);

    ApiFunction bookDeliveryHandler =
        this.defaultLambdaRdsProxy("BookDelivery", props, this.lambdaRdsProxyRoleWithIam);

    FunctionDashboard createSlotsDashboard = new FunctionDashboard(this, "FunctionDashboard",
        FunctionDashboard.FunctionDashboardProps.builder()
            .dashboardName("FunctionDashboard")
            .getSlotsApiMethodName(getSlotsHandler.getApiMethodName())
            .getSlotsFunctionName(getSlotsHandler.getFunctionName())
            .createSlotsApiMethodName(createSlotsHandler.getApiMethodName())
            .createSlotsFunctionName(createSlotsHandler.getFunctionName())
            .bookDeliveryApiMethodName(bookDeliveryHandler.getApiMethodName())
            .bookDeliveryFunctionName(bookDeliveryHandler.getFunctionName())
            .alarmTopic(errorAlarmTopic)
            .build());

    Role apiRole =
        new Role(
            this,
            "apiRole",
            RoleProps.builder()
                .assumedBy(new ServicePrincipal("apigateway.amazonaws.com"))
                .build());

    apiRole.addToPolicy(
        new PolicyStatement(
            PolicyStatementProps.builder()
                .resources(
                    List.of(
                        createSlotsHandler.getFunctionArn(),
                        getSlotsHandler.getFunctionArn(),
                        bookDeliveryHandler.getFunctionArn()))
                .actions(List.of("lambda:InvokeFunction"))
                .build()));

    apiRole.addManagedPolicy(
        ManagedPolicy.fromAwsManagedPolicyName(
            "service-role/AmazonAPIGatewayPushToCloudWatchLogs"));

    Map<String, Object> variables = new HashMap<>();

    variables.put(
        "CreateSlots",
        String.format(
            "arn:aws:apigateway:%s:lambda:path/2015-03-31/functions/%s/invocations",
            Stack.of(this).getRegion(), createSlotsHandler.getFunctionArn()));

    variables.put(
        "GetSlots",
        String.format(
            "arn:aws:apigateway:%s:lambda:path/2015-03-31/functions/%s/invocations",
            Stack.of(this).getRegion(), getSlotsHandler.getFunctionArn()));

    variables.put(
        "BookDelivery",
        String.format(
            "arn:aws:apigateway:%s:lambda:path/2015-03-31/functions/%s/invocations",
            Stack.of(this).getRegion(), bookDeliveryHandler.getFunctionArn()));

    variables.put("ApiRole", apiRole.getRoleArn());

    Writer writer = new StringWriter();
    MustacheFactory mf = new DefaultMustacheFactory();

    Object openapiSpecAsObject;
    try (Reader reader =
             new InputStreamReader(getClass().getClassLoader().getResourceAsStream("apiSchema.json"))) {
      Mustache mustache = mf.compile(reader, "OAS");
      mustache.execute(writer, variables);
      writer.flush();

      ObjectMapper jsonMapper = new ObjectMapper(new JsonFactory());
      openapiSpecAsObject = jsonMapper.readValue(writer.toString(), Object.class);
    }

    CfnApi apiGw =
        new CfnApi(
            this,
            "ILMLFDelivery",
            CfnApiProps.builder()
                .stageName(apiStageName)
                .definitionBody(openapiSpecAsObject)
                .accessLogSetting(
                    CfnApi.AccessLogSettingProperty.builder()
                        .destinationArn(accessLogGroup.getLogGroupArn())
                        .format(logFormat.toString())
                        .build())
                .cors(
                    // In production, limit this to only your domain name
                    CfnApi.CorsConfigurationProperty.builder()
                        .allowOrigin("'*'")
                        .allowHeaders("'*'")
                        .allowMethods("'*'")
                        .build())
                .build());

    /*
     * Enable API Gateway logging.
     * The logging requires a role with permissions to let API Gateway put logs in CloudWatch.
     * The permission is in the managed policy "AmazonAPIGatewayPushToCloudWatchLogs"
     *
     * Note that this needs to be done once per region.
     *
     * See https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-logging.html for details
     */
    Role accountApiCwRole =
        new Role(
            this,
            "AccountApiCwRole",
            RoleProps.builder()
                .assumedBy(new ServicePrincipal("apigateway.amazonaws.com"))
                .managedPolicies(
                    Collections.singletonList(
                        ManagedPolicy.fromAwsManagedPolicyName(
                            "service-role/AmazonAPIGatewayPushToCloudWatchLogs")))
                .build());

    // This construct is from aws-apigateway package. It is used specifically for enable logging.
    // See https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_aws-apigateway.CfnAccount.html
    CfnAccount cfnAccount =
        new CfnAccount(
            this,
            "ApiGtwyAccountCwRole",
            CfnAccountProps.builder().cloudWatchRoleArn(accountApiCwRole.getRoleArn()).build());

    cfnAccount.getNode().addDependency(apiGw);

    new CfnOutput(this, "ApiUrl",
        CfnOutputProps.builder()
            .value(String.format("https://%s.execute-api.%s.amazonaws.com/%s",
                apiGw.getRef(),
                Stack.of(this).getRegion(),
                apiStageName))
            .build());
  }