cdk/lib/graphiql-explorer.ts (72 lines of code) (raw):

import {Construct} from "constructs"; import type { GuStack } from "@guardian/cdk/lib/constructs/core"; import { GuStringParameter } from "@guardian/cdk/lib/constructs/core"; import { CfnOutput, Duration } from "aws-cdk-lib"; import { Certificate } from "aws-cdk-lib/aws-certificatemanager"; import { Distribution, PriceClass, ViewerProtocolPolicy } from "aws-cdk-lib/aws-cloudfront"; import { RestApiOrigin, S3Origin } from "aws-cdk-lib/aws-cloudfront-origins"; import { Effect, PolicyStatement, ServicePrincipal } from "aws-cdk-lib/aws-iam"; import { Bucket } from "aws-cdk-lib/aws-s3"; import {hostingDomain} from "./constants"; interface GraphiqlExplorerProps { appName: string; } export class GraphiqlExplorer extends Construct { constructor(scope: GuStack, id: string, props: GraphiqlExplorerProps) { super(scope, id); //We can't create the cert here, because it must live in us-east-1 for Cloudfront to use it. const hostingCertArn = new GuStringParameter(scope, "ExplorerCertArn", { fromSSM: true, default: `/${scope.stage}/${scope.stack}/${props.appName}/GlobalCertArn`, description: `Cert to use for graphiql ${scope.stage}. This must reside in us-east-1`, }); const certificate = Certificate.fromCertificateArn(this, "CapiExplorerCert", hostingCertArn.valueAsString); const staticBucketNameParam = new GuStringParameter(scope, "StaticBucketName", { fromSSM: true, default: `/account/services/static.serving.bucket`, description: "SSM parameter giving the name of a bucket which is to be used for static hosting" }); const hostingBucket = Bucket.fromBucketName(this, "StaticBucket", staticBucketNameParam.valueAsString); const distro = new Distribution(scope, "GraphiQLDistro", { defaultRootObject: "index.html", certificate, domainNames: [hostingDomain[scope.stage]], defaultBehavior: { viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS, origin: new S3Origin(hostingBucket, { originPath: `${scope.stage}/${props.appName}` }), }, enableIpv6: true, enabled: true, priceClass: PriceClass.PRICE_CLASS_100, //US & EU only /* we must tell Cloudfront to redirect 403 (forbidden/not present) exceptions from S3 into a 200 response from /index in order for react-router to work. */ errorResponses: [ { httpStatus: 403, responseHttpStatus: 200, responsePagePath: "/index.html", ttl: Duration.seconds(5), } ] }); hostingBucket.addToResourcePolicy(new PolicyStatement({ effect: Effect.ALLOW, principals: [ new ServicePrincipal("cloudfront.amazonaws.com"), ], actions: ["s3:GetObject"], resources: [`arn:aws:s3:::${hostingBucket.bucketName}/${scope.stage}/${props.appName}`], conditions: { "StringEquals": { "AWS:SourceArn": `arn:aws:cloudfront::${scope.account}:distribution/${distro.distributionId}` } } })); new CfnOutput(this, "DistroUrlOut", { value: distro.distributionDomainName, }); } }