Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(type-safe-api): generate lambda handler stubs based on model tra…
…it (#537) This change adds a new configuration option `handlers` which allows users to opt in to generating lambda handler projects for java, python, and/or typescript, ready-configured with packaging tasks for use in a lambda function. Handler stubs are generated for operations annotated with the @handler trait in Smithy (or the x-handler vendor extension in OpenAPI), only generating the handler stubs for the chosen language for each operation. Documentation to come once this feature has been further fleshed out.
- Loading branch information
Showing
43 changed files
with
11,269 additions
and
248 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
4 changes: 4 additions & 0 deletions
4
packages/type-safe-api/scripts/generators/java-lambda-handlers/config.yaml
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 @@ | ||
files: | ||
handlers.handlebars: | ||
destinationFilename: {{src}}/__all_handlers.java | ||
templateType: SupportingFiles |
46 changes: 46 additions & 0 deletions
46
packages/type-safe-api/scripts/generators/java-lambda-handlers/templates/handlers.handlebars
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,46 @@ | ||
###TSAPI_SPLIT_FILE### | ||
{{#apiInfo ~}} | ||
{{#apis ~}} | ||
{{#operations ~}} | ||
{{#operation ~}} | ||
{{#if vendorExtensions.x-handler}} | ||
{{#startsWith vendorExtensions.x-handler.language 'java'}} | ||
###TSAPI_WRITE_FILE### | ||
{ | ||
"dir": ".", | ||
"name": "{{operationIdCamelCase}}Handler", | ||
"ext": ".java", | ||
"overwrite": false, | ||
"kebabCaseFileName": false | ||
} | ||
###/TSAPI_WRITE_FILE###package {{#apiInfo}}{{#apis.0}}{{vendorExtensions.x-handlers-package}}{{/apis.0}}{{/apiInfo}}; | ||
|
||
import {{#apiInfo}}{{#apis.0}}{{vendorExtensions.x-runtime-package}}{{/apis.0}}{{/apiInfo}}.api.Handlers.{{operationIdCamelCase}}; | ||
import {{#apiInfo}}{{#apis.0}}{{vendorExtensions.x-runtime-package}}{{/apis.0}}{{/apiInfo}}.api.Handlers.{{operationIdCamelCase}}500Response; | ||
import {{#apiInfo}}{{#apis.0}}{{vendorExtensions.x-runtime-package}}{{/apis.0}}{{/apiInfo}}.api.Handlers.{{operationIdCamelCase}}RequestInput; | ||
import {{#apiInfo}}{{#apis.0}}{{vendorExtensions.x-runtime-package}}{{/apis.0}}{{/apiInfo}}.api.Handlers.{{operationIdCamelCase}}Response; | ||
import {{#apiInfo}}{{#apis.0}}{{vendorExtensions.x-runtime-package}}{{/apis.0}}{{/apiInfo}}.model.*; | ||
|
||
/** | ||
* Entry point for the AWS Lambda handler for the {{operationIdCamelCase}} operation. | ||
* The {{operationIdCamelCase}} class manages marshalling inputs and outputs. | ||
*/ | ||
public class {{operationIdCamelCase}}Handler extends {{operationIdCamelCase}} { | ||
/** | ||
* Type-safe handler for the {{operationIdCamelCase}} operation | ||
*/ | ||
@Override | ||
public {{operationIdCamelCase}}Response handle({{operationIdCamelCase}}RequestInput input) { | ||
// TODO: Implement {{operationIdCamelCase}} Operation | ||
return {{operationIdCamelCase}}500Response.of(InternalFailureErrorResponseContent.builder() | ||
.message("Not Implemented!") | ||
.build()); | ||
} | ||
} | ||
|
||
{{~/startsWith}} | ||
{{~/if}} | ||
{{~/operation}} | ||
{{~/operations}} | ||
{{~/apis}} | ||
{{~/apiInfo}} |
109 changes: 109 additions & 0 deletions
109
packages/type-safe-api/scripts/generators/post-process.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,109 @@ | ||
/*! Copyright [Amazon.com](http://amazon.com/), Inc. or its affiliates. All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 */ | ||
import * as fs from "fs"; | ||
import * as path from "path"; | ||
import kebabCase from "lodash/kebabCase"; | ||
import { parse } from "ts-command-line-args"; | ||
|
||
// Used to split OpenAPI generated files into multiple files in order to work around | ||
// the restrictions around file naming and splitting in OpenAPI generator | ||
const TSAPI_SPLIT_FILE_HEADER = "###TSAPI_SPLIT_FILE###"; | ||
const TSAPI_WRITE_FILE_START = "###TSAPI_WRITE_FILE###"; | ||
const TSAPI_WRITE_FILE_END = "###/TSAPI_WRITE_FILE###"; | ||
|
||
interface Arguments { | ||
/** | ||
* Path to the directory containing output files | ||
*/ | ||
readonly outputPath: string; | ||
/** | ||
* Path to the source directory relative to the output directory | ||
*/ | ||
readonly srcDir: string; | ||
} | ||
|
||
interface WriteFileConfig { | ||
readonly dir: string; | ||
readonly name: string; | ||
readonly ext: string; | ||
readonly overwrite?: boolean; | ||
readonly kebabCaseFileName?: boolean; | ||
} | ||
|
||
void (async () => { | ||
const args = parse<Arguments>({ | ||
outputPath: { type: String }, | ||
srcDir: { type: String }, | ||
}); | ||
|
||
// OpenAPI generator writes a manifest called FILES which lists the files it generated. | ||
const openApiGeneratedFilesManifestPath = path.join( | ||
args.outputPath, | ||
".openapi-generator", | ||
"FILES" | ||
); | ||
|
||
// Read the file paths from the manifest | ||
const generatedFiles = fs | ||
.readFileSync(openApiGeneratedFilesManifestPath, { encoding: "utf-8" }) | ||
.split("\n") | ||
.filter((x) => x); | ||
|
||
const additionalGeneratedFiles: string[] = []; | ||
|
||
// Loop over generated files | ||
generatedFiles.forEach((generatedFile) => { | ||
const filePath = path.join(args.outputPath, generatedFile); | ||
|
||
if (fs.existsSync(filePath)) { | ||
const contents = fs.readFileSync(filePath, "utf-8"); | ||
|
||
if (contents.startsWith(TSAPI_SPLIT_FILE_HEADER)) { | ||
// Split by the start template | ||
contents | ||
.split(TSAPI_WRITE_FILE_START) | ||
.filter((t) => t.includes(TSAPI_WRITE_FILE_END)) | ||
.forEach((destinationFileTemplate) => { | ||
// Split by the end template to receive the file path, and contents | ||
const [configString, newFileContents] = | ||
destinationFileTemplate.split(TSAPI_WRITE_FILE_END); | ||
const config = JSON.parse(configString) as WriteFileConfig; | ||
|
||
const newFileName = `${ | ||
config.kebabCaseFileName ? kebabCase(config.name) : config.name | ||
}${config.ext}`; | ||
const relativeNewFileDir = path.join(args.srcDir, config.dir); | ||
const relativeNewFilePath = path.join( | ||
relativeNewFileDir, | ||
newFileName | ||
); | ||
const newFilePath = path.join(args.outputPath, relativeNewFilePath); | ||
|
||
// Write to the instructed file path (relative to the src dir) | ||
if (!fs.existsSync(newFilePath) || config.overwrite) { | ||
// Create it's containing directory if needed | ||
fs.mkdirSync(path.join(args.outputPath, relativeNewFileDir), { | ||
recursive: true, | ||
}); | ||
fs.writeFileSync(newFilePath, newFileContents); | ||
|
||
// Overwritten files are added to the manifest so that they can be cleaned up | ||
// by clean-openapi-generated-code | ||
if (config.overwrite) { | ||
additionalGeneratedFiles.push(relativeNewFilePath); | ||
} | ||
} | ||
}); | ||
|
||
// Delete the original file | ||
fs.rmSync(filePath); | ||
} | ||
} | ||
}); | ||
|
||
// Update the manifest with any overwritten files | ||
fs.writeFileSync( | ||
openApiGeneratedFilesManifestPath, | ||
[...generatedFiles, ...additionalGeneratedFiles].join("\n") | ||
); | ||
})(); |
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
4 changes: 4 additions & 0 deletions
4
packages/type-safe-api/scripts/generators/python-lambda-handlers/config.yaml
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 @@ | ||
files: | ||
handlers.handlebars: | ||
destinationFilename: {{src}}/__all_handlers.py | ||
templateType: SupportingFiles |
43 changes: 43 additions & 0 deletions
43
...ges/type-safe-api/scripts/generators/python-lambda-handlers/templates/handlers.handlebars
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,43 @@ | ||
###TSAPI_SPLIT_FILE### | ||
{{#apiInfo ~}} | ||
{{#apis ~}} | ||
{{#operations ~}} | ||
{{#operation ~}} | ||
{{#if vendorExtensions.x-handler}} | ||
{{#startsWith vendorExtensions.x-handler.language 'python'}} | ||
###TSAPI_WRITE_FILE### | ||
{ | ||
"dir": ".", | ||
"name": "{{operationId}}", | ||
"ext": ".py", | ||
"overwrite": false | ||
} | ||
###/TSAPI_WRITE_FILE###from {{#apiInfo}}{{#apis.0}}{{vendorExtensions.x-runtime-module-name}}{{/apis.0}}{{/apiInfo}}.models import * | ||
from {{#apiInfo}}{{#apis.0}}{{vendorExtensions.x-runtime-module-name}}{{/apis.0}}{{/apiInfo}}.apis.tags.default_api_operation_config import ( | ||
{{operationId}}_handler, {{operationIdCamelCase}}Request, {{operationIdCamelCase}}OperationResponses, ApiResponse | ||
) | ||
|
||
|
||
def {{operationId}}(input: {{operationIdCamelCase}}Request, **kwargs) -> {{operationIdCamelCase}}OperationResponses: | ||
""" | ||
Type-safe handler for the {{operationIdCamelCase}} operation | ||
""" | ||
# TODO: Implement {{operationIdCamelCase}} Operation | ||
return ApiResponse( | ||
status_code=500, | ||
body=InternalFailureErrorResponseContent( | ||
message="Not Implemented!"), | ||
headers={} | ||
) | ||
|
||
|
||
# Entry point for the AWS Lambda handler for the {{operationIdCamelCase}} operation. | ||
# The {{operationId}}_handler method wraps the type-safe handler and manages marshalling inputs and outputs | ||
handler = {{operationId}}_handler({{operationId}}) | ||
|
||
{{~/startsWith}} | ||
{{~/if}} | ||
{{~/operation}} | ||
{{~/operations}} | ||
{{~/apis}} | ||
{{~/apiInfo}} |
4 changes: 4 additions & 0 deletions
4
packages/type-safe-api/scripts/generators/typescript-lambda-handlers/config.yaml
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 @@ | ||
files: | ||
handlers.handlebars: | ||
destinationFilename: {{src}}/__all_handlers.ts | ||
templateType: SupportingFiles |
45 changes: 45 additions & 0 deletions
45
...type-safe-api/scripts/generators/typescript-lambda-handlers/templates/handlers.handlebars
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,45 @@ | ||
###TSAPI_SPLIT_FILE### | ||
{{#apiInfo ~}} | ||
{{#apis ~}} | ||
{{#operations ~}} | ||
{{#operation ~}} | ||
{{#if vendorExtensions.x-handler}} | ||
{{#startsWith vendorExtensions.x-handler.language 'typescript'}} | ||
###TSAPI_WRITE_FILE### | ||
{ | ||
"dir": ".", | ||
"name": "{{nickname}}", | ||
"ext": ".ts", | ||
"overwrite": false, | ||
"kebabCaseFileName": true | ||
} | ||
###/TSAPI_WRITE_FILE###import { | ||
{{nickname}}Handler, | ||
{{operationIdCamelCase}}ChainedHandlerFunction, | ||
} from "{{#apiInfo}}{{#apis.0}}{{vendorExtensions.x-runtime-package-name}}{{/apis.0}}{{/apiInfo}}"; | ||
|
||
/** | ||
* Type-safe handler for the {{operationIdCamelCase}} operation | ||
*/ | ||
export const {{nickname}}: {{operationIdCamelCase}}ChainedHandlerFunction = async ({ input }) => { | ||
// TODO: Implement {{operationIdCamelCase}} Operation | ||
return { | ||
statusCode: 500, | ||
body: { | ||
message: "Not Implemented!", | ||
}, | ||
}; | ||
}; | ||
|
||
/** | ||
* Entry point for the AWS Lambda handler for the {{operationIdCamelCase}} operation. | ||
* The {{nickname}}Handler method wraps the type-safe handler and manages marshalling inputs and outputs | ||
*/ | ||
export const handler = {{nickname}}Handler({{nickname}}); | ||
|
||
{{~/startsWith}} | ||
{{~/if}} | ||
{{~/operation}} | ||
{{~/operations}} | ||
{{~/apis}} | ||
{{~/apiInfo}} |
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
Oops, something went wrong.