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());
}