Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

construct: add edge-api-swagger #16

Merged
merged 3 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@reapit-cdk/generate-readme": "workspace:^",
"@reapit-cdk/integration-tests": "workspace:^",
"@types/jest": "^29.5.5",
"@types/swagger-ui-dist": "^3.30.3",
"aws-cdk": "2.100.0",
"aws-sdk-client-mock": "^3.0.0",
"aws-sdk-client-mock-jest": "^3.0.0",
Expand Down
59 changes: 59 additions & 0 deletions packages/constructs/cross-region-stack-export/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# @reapit-cdk/cross-region-stack-export


![npm version](https://img.shields.io/npm/v/@reapit-cdk/cross-region-stack-export)
![npm downloads](https://img.shields.io/npm/dm/@reapit-cdk/cross-region-stack-export)
![coverage: 74.02%25](https://img.shields.io/badge/coverage-74.02%25-orange)
![Integ Tests: X](https://img.shields.io/badge/Integ%20Tests-X-red)

Allows you to share values between stack across regions and accounts.

## Package Installation:

```sh
yarn add --dev @reapit-cdk/cross-region-stack-export
# or
npm install @reapit-cdk/cross-region-stack-export --save-dev
```

## Usage
```ts
import { CfnOutput, Stack, App } from 'aws-cdk-lib'
import { CrossRegionStackExport } from '@reapit-cdk/cross-region-stack-export'
import { Bucket } from 'aws-cdk-lib/aws-s3'

const app = new App()
const euStack = new Stack(app, 'stack-eu', {
env: {
account: '11111111',
region: 'eu-west-1',
},
})

const exporter = new CrossRegionStackExport(euStack, 'exporter')
exporter.setValue('thing', 'avalue')

const bucket = new Bucket(euStack, 'bucket')
exporter.setValue('bucketArn', bucket.bucketArn)

const usStack = new Stack(app, 'stack-us', {
env: {
account: '2222222222',
region: 'us-east-1',
},
})

const importer = exporter.getImporter(usStack, 'eu-importer')

const euThing = importer.getValue('thing')
const euBucket = Bucket.fromBucketArn(usStack, 'eu-bucket', importer.getValue('bucketArn'))

new CfnOutput(usStack, 'euThing', {
value: euThing,
})

new CfnOutput(usStack, 'euBucketName', {
value: euBucket.bucketName,
})

```
4 changes: 4 additions & 0 deletions packages/constructs/edge-api-swagger/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
src
tests
.eslintrc.js
tsconfig.json
53 changes: 53 additions & 0 deletions packages/constructs/edge-api-swagger/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "@reapit-cdk/edge-api-swagger",
"version": "0.0.0",
"description": "Add a swagger endpoint to your EdgeAPI",
"homepage": "https://github.com/reapit/ts-cdk-constructs/blob/main/packages/constructs/edge-api-swagger",
"readme": "https://github.com/reapit/ts-cdk-constructs/blob/main/packages/constructs/edge-api-swagger/readme.md",
"bugs": {
"url": "https://github.com/reapit/ts-cdk-constructs/issues"
},
"license": "MIT",
"author": {
"name": "Josh Balfour",
"email": "jbalfour@reapit.com"
},
"repository": {
"url": "https://github.com/reapit/ts-cdk-constructs.git"
},
"scripts": {
"build": "reapit-cdk-tsup",
"check": "yarn run root:check -p $(pwd)",
"lint": "reapit-cdk-eslint",
"test": "yarn run root:test -- $(pwd)",
"prepack": "reapit-version-package && yarn build",
"integ": "yarn run root:integ -- $(pwd)",
"jsii:build": "rpt-cdk-jsii",
"jsii:publish": "rpt-cdk-jsii --publish"
},
"main": "src/index.ts",
"types": "src/index.ts",
"publishConfig": {
"main": "dist/index.js",
"types": "dist/index.d.ts"
},
"dependencies": {
"openapi3-ts": "^4.1.2",
"swagger-ui-dist": "^5.9.0",
"typescript": "^5.1.3"
},
"peerDependencies": {
"@reapit-cdk/edge-api": "workspace:^",
"aws-cdk-lib": "^2.96.2",
"constructs": "^10.2.70"
},
"devDependencies": {
"@reapit-cdk/eslint-config": "workspace:^",
"@reapit-cdk/integration-tests": "workspace:^",
"@reapit-cdk/jsii": "workspace:^",
"@reapit-cdk/tsup": "workspace:^",
"@reapit-cdk/version-package": "workspace:^",
"aws-cdk-lib": "^2.96.2",
"constructs": "^10.2.70"
}
}
73 changes: 73 additions & 0 deletions packages/constructs/edge-api-swagger/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# @reapit-cdk/edge-api-swagger


![npm version](https://img.shields.io/npm/v/@reapit-cdk/edge-api-swagger)
![npm downloads](https://img.shields.io/npm/dm/@reapit-cdk/edge-api-swagger)
![coverage: 0%25](https://img.shields.io/badge/coverage-0%25-red)
![Integ Tests: X](https://img.shields.io/badge/Integ%20Tests-X-red)

Add a swagger endpoint to your EdgeAPI

## Package Installation:

```sh
yarn add --dev @reapit-cdk/edge-api-swagger
# or
npm install @reapit-cdk/edge-api-swagger --save-dev
```

## Usage
```ts
import { Stack, App } from 'aws-cdk-lib'
import { EdgeAPI, EdgeAPILambda } from '@reapit-cdk/edge-api'
import { Code, Runtime } from 'aws-cdk-lib/aws-lambda'
import { EdgeAPISwaggerEndpoint } from '@reapit-cdk/edge-api-swagger'
import { Certificate } from 'aws-cdk-lib/aws-certificatemanager'
import * as path from 'path'

const app = new App()
const stack = new Stack(app, 'stack-name')

const certificate = new Certificate(stack, 'certificate', {
domainName: 'example.org',
})
const api = new EdgeAPI(stack, 'api', {
certificate,
domains: ['example.org', 'example.com'],
devMode: false,
defaultEndpoint: {
destination: 'example.com',
},
})

const lambda = new EdgeAPILambda(stack, 'lambda', {
code: Code.fromAsset(path.resolve('../lambda/dist')),
codePath: path.resolve('../lambda/src/index.ts'), // gets added to the docs
handler: 'index.handler',
runtime: Runtime.NODEJS_18_X,
environment: {
aVariable: 'contents',
},
})

api.addEndpoint({
pathPattern: '/api/lambda',
lambda,
})

api.addEndpoint(
new EdgeAPISwaggerEndpoint(stack, 'docs', {
api,
url: 'https://example.org',

pathPattern: '/swagger', // optional, defaults to /swagger

// optional
info: {
title: '', // defaults to Edge API
version: '', // defaults to 1.0.0
},
}),
)

```
114 changes: 114 additions & 0 deletions packages/constructs/edge-api-swagger/src/edge-api-swagger-endpoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { EdgeAPI, FrontendEndpoint, endpointIsLambdaEndpoint, endpointIsProxyEndpoint } from '@reapit-cdk/edge-api'
import { Construct } from 'constructs'

import { Bucket } from 'aws-cdk-lib/aws-s3'
import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment'
import { RemovalPolicy } from 'aws-cdk-lib'
import { generateOpenAPIDocs } from './generation/generate-openapi-docs'
import { EndpointInputItem } from './generation'
import { getAbsoluteFSPath } from 'swagger-ui-dist'
import { InfoObject } from 'openapi3-ts/oas30'

interface EdgeAPISwaggerEndpointProps {
api: EdgeAPI
url: string
pathPattern?: string
info?: InfoObject
}

const swaggerHtml = (urlPrefix: string) => `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta
name="description"
content="SwaggerUI"
/>
<title>SwaggerUI</title>
<link rel="stylesheet" href="${urlPrefix}/swagger-ui.css" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="${urlPrefix}/swagger-ui-bundle.js" crossorigin></script>
<script src="${urlPrefix}/swagger-ui-standalone-preset.js" crossorigin></script>
<script>
window.onload = () => {
window.ui = SwaggerUIBundle({
url: '${urlPrefix}/openapi.json',
dom_id: '#swagger-ui',
});
};
</script>
</body>
</html>`

export class EdgeAPISwaggerEndpoint extends Construct implements FrontendEndpoint {
bucket: Bucket
invalidationItems: string[]
pathPattern: string

constructor(scope: Construct, id: string, props: EdgeAPISwaggerEndpointProps) {
super(scope, id)
this.pathPattern = props.pathPattern ?? '/swagger'

const destinationKeyPrefix = this.pathPattern.replace(/^\/+/g, '')

this.bucket = new Bucket(this, 'bucket', {
websiteIndexDocument: 'index.html',
websiteErrorDocument: destinationKeyPrefix + '/index.html',
removalPolicy: RemovalPolicy.RETAIN, // otherwise deletion will fail on stack destroy due to non-empty bucket
publicReadAccess: true,
blockPublicAccess: {
blockPublicAcls: false,
blockPublicPolicy: false,
ignorePublicAcls: false,
restrictPublicBuckets: false,
},
})

const openapiJson = generateOpenAPIDocs({
url: props.url,
info: props.info,
endpointsInput: props.api._endpoints.map((endpoint): EndpointInputItem => {
if (endpointIsLambdaEndpoint(endpoint)) {
return {
codePath: endpoint.lambda.codePath,
pathPattern: endpoint.pathPattern,
isFrontend: false,
}
}
if (endpointIsProxyEndpoint(endpoint)) {
return {
isProxy: true,
pathPattern: endpoint.pathPattern,
proxyDestination: endpoint.destination,
}
}
return {
pathPattern: endpoint.pathPattern,
isFrontend: true,
}
}),
})

new BucketDeployment(this, 'deployment', {
sources: [
Source.data('index.html', swaggerHtml(`${props.url}/${destinationKeyPrefix}`)),
Source.jsonData('openapi.json', openapiJson),
Source.asset(getAbsoluteFSPath()),
],
destinationBucket: this.bucket,
destinationKeyPrefix,
retainOnDelete: false,
})

this.invalidationItems = [
'/index.html',
'/openapi.json',
'/swagger-ui-bundle.js',
'/swagger-ui-standalone-preset.js',
'/swagger-ui.css',
]
}
}
Loading
Loading