-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0d5226b
commit 8fc6767
Showing
17 changed files
with
759 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
src | ||
tests | ||
.eslintrc.js | ||
tsconfig.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# @reapit-cdk/active-ruleset | ||
|
||
|
||
![npm version](https://img.shields.io/npm/v/@reapit-cdk/active-ruleset) | ||
![npm downloads](https://img.shields.io/npm/dm/@reapit-cdk/active-ruleset) | ||
![coverage: 99.02%25](https://img.shields.io/badge/coverage-99.02%25-green) | ||
![Integ Tests: ✔](https://img.shields.io/badge/Integ%20Tests-%E2%9C%94-green) | ||
|
||
This construct returns the currently active SES receipt RuleSet, or creates one. This enables you to add rules to it. | ||
|
||
## Package Installation: | ||
|
||
```sh | ||
yarn add --dev @reapit-cdk/active-ruleset | ||
# or | ||
npm install @reapit-cdk/active-ruleset --save-dev | ||
``` | ||
|
||
## Usage | ||
```ts | ||
import { CfnOutput, Stack, App } from 'aws-cdk-lib' | ||
import { ActiveRuleset } from '@reapit-cdk/active-ruleset' | ||
|
||
const app = new App() | ||
const stack = new Stack(app, 'stack-name') | ||
const activeRuleset = new ActiveRuleset(stack, 'active-ruleset') | ||
new CfnOutput(stack, 'activeRulesetName', { | ||
value: activeRuleset.receiptRuleSet.receiptRuleSetName, | ||
}) | ||
|
||
``` |
114 changes: 114 additions & 0 deletions
114
packages/constructs/edge-api-swagger/src/edge-api-swagger-endpoint.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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', | ||
] | ||
} | ||
} |
145 changes: 145 additions & 0 deletions
145
packages/constructs/edge-api-swagger/src/generation/endpoint-handlers-to-openapi.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import { | ||
ContentObject, | ||
InfoObject, | ||
OpenApiBuilder, | ||
PathItemObject, | ||
RequestBodyObject, | ||
ResponseObject, | ||
SchemaObject, | ||
SchemaObjectType, | ||
} from 'openapi3-ts/oas30' | ||
import { EndpointHandlerInfo, ResolvedProperty, ReturnType } from './types' | ||
|
||
export const endpointHandlersToOpenApi = ( | ||
endpointHandlersInfo: EndpointHandlerInfo[], | ||
url: string, | ||
info?: InfoObject, | ||
) => { | ||
const openApi = new OpenApiBuilder() | ||
openApi.addInfo({ | ||
title: 'Edge API', | ||
version: '1.0.0', | ||
...(info ?? {}), | ||
}) | ||
openApi.addServer({ | ||
url, | ||
}) | ||
|
||
endpointHandlersInfo.map(transformEndpointHandler).forEach(({ path, pathItem }) => openApi.addPath(path, pathItem)) | ||
|
||
return openApi | ||
} | ||
|
||
const stringIsSchemaObjectType = (str: string): str is SchemaObjectType => { | ||
return ['integer', 'number', 'string', 'boolean', 'array'].includes(str) | ||
} | ||
|
||
const propertyToSchema = (property: ResolvedProperty): SchemaObject => { | ||
const type = property.typeName && stringIsSchemaObjectType(property.typeName) ? property.typeName : 'object' | ||
const properties = property.properties?.reduce( | ||
(pv, cv) => { | ||
if (!cv.name) { | ||
return pv | ||
} | ||
return { | ||
...pv, | ||
[cv.name]: propertyToSchema(cv), | ||
} | ||
}, | ||
{} as Record<string, SchemaObject>, | ||
) | ||
|
||
return { | ||
type, | ||
required: property.properties | ||
? (property.properties | ||
.filter((rp) => !rp.isOptional) | ||
.map((rp) => rp.name) | ||
.filter(Boolean) as string[]) | ||
: undefined, | ||
properties, | ||
} | ||
} | ||
|
||
const transformBody = (body: ResolvedProperty, isForm?: boolean): RequestBodyObject => { | ||
const contentType = isForm ? 'application/x-www-form-urlencoded' : 'application/json' | ||
const content: ContentObject = { | ||
[contentType]: { | ||
schema: propertyToSchema(body), | ||
}, | ||
} | ||
return { content } | ||
} | ||
|
||
const returnTypeToResponse = (returnType?: ReturnType): ResponseObject => { | ||
if (!returnType) { | ||
return { | ||
description: 'not typed', | ||
} | ||
} | ||
|
||
if (returnType.isRedirection) { | ||
return { | ||
description: 'redirection', | ||
headers: { | ||
location: { | ||
schema: { | ||
type: 'string', | ||
example: 'https://google.com', | ||
}, | ||
}, | ||
status: { | ||
schema: { | ||
type: 'number', | ||
example: 302, | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
if (returnType.isJSONResponse && returnType.response) { | ||
return { | ||
description: 'default', | ||
content: { | ||
'application/json': { | ||
schema: propertyToSchema(returnType.response), | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
return { | ||
description: 'invalid return type', | ||
} | ||
} | ||
|
||
const transformEndpointHandler = (endpointInfo: EndpointHandlerInfo): { path: string; pathItem: PathItemObject } => { | ||
const { bodyType, returnType, isFormRequestHandler, description } = endpointInfo | ||
if (bodyType) { | ||
return { | ||
path: endpointInfo.pathPattern, | ||
pathItem: { | ||
post: { | ||
description, | ||
responses: { | ||
default: returnTypeToResponse(returnType), | ||
}, | ||
requestBody: transformBody(bodyType, isFormRequestHandler), | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
return { | ||
path: endpointInfo.pathPattern, | ||
pathItem: { | ||
get: { | ||
description, | ||
responses: { | ||
default: returnTypeToResponse(returnType), | ||
}, | ||
}, | ||
}, | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
packages/constructs/edge-api-swagger/src/generation/generate-openapi-docs.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { InfoObject } from 'openapi3-ts/oas30' | ||
import { getEndpointHandlerInfo, endpointHandlersToOpenApi, EndpointsInput } from '.' | ||
|
||
export const generateOpenAPIDocs = ({ | ||
endpointsInput, | ||
url, | ||
info, | ||
}: { | ||
endpointsInput: EndpointsInput | ||
url: string | ||
info?: InfoObject | ||
}) => { | ||
const endpointHandlerInfo = getEndpointHandlerInfo(endpointsInput) | ||
return endpointHandlersToOpenApi(endpointHandlerInfo, url, info).getSpec() | ||
} |
Oops, something went wrong.