Created
November 2, 2020 23:28
-
-
Save Sleavely/174dbf5d0642ef86b3f7263b2f792c3d to your computer and use it in GitHub Desktop.
A Serverless extension for orchestrating API->Lambda setups from your Swagger/OpenAPI definitions. This workflow encourages that the documentation is kept up-to-date.
This file contains hidden or 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
const ServerlessAWSCloudFormationSubVariables = require('serverless-cloudformation-sub-variables') | |
class ApiOrchestrator { | |
constructor(serverless) { | |
this.serverless = serverless | |
// Register ${lambda:myFunctionKey} so that we can | |
// refer to it from our Swagger/OpenAPI definition | |
this.variableResolvers = { | |
lambda: this.referenceLambda.bind(this), | |
} | |
// Somewhere to store our referenced lambdas across the lifecycle | |
this.referencedKeys = [] | |
this.hooks = { | |
// Prior to packaging we want to inject a fake HTTP event on the referenced function(s) | |
// to piggyback on Serverless' logic for automatically creating API Gateway and their | |
// deployments and allowing SLS to think that the method can be invoked by events. | |
'after:package:setupProviderConfiguration': () => { | |
// Lets loop through the lambdas we referenced earlier with our | |
// ${lambda:something} syntax and add an HTTP event on them | |
const functions = this.serverless.service.functions | |
for (const functionKey of this.referencedKeys) { | |
const func = functions[functionKey] | |
const hasHttp = this.serverless.utils.isEventUsed([func], 'http') | |
if (hasHttp) continue; | |
functions[functionKey].events.push({ http: { | |
path: 'sample/path', | |
method: 'post', | |
}}) | |
} | |
}, | |
// After Serverless has generated the CloudFormation template we want to | |
// clean it up from APIGateway-methods and -resources since the API definition | |
// will create its own. Also clean up the artificial event. | |
'aws:package:finalize:mergeCustomProviderResources': () => { | |
const cfnTemplate = this.serverless.service.provider.compiledCloudFormationTemplate | |
const cfnResourcesToBeRemoved = ['AWS::ApiGateway::Method', 'AWS::ApiGateway::Resource'] | |
for (const [key, resource] of Object.entries(cfnTemplate.Resources)) { | |
// Filter unwanted resources | |
if (cfnResourcesToBeRemoved.includes(resource.Type)) { | |
delete cfnTemplate.Resources[key] | |
} | |
// Clean up refs to those resources | |
if(resource.Type === 'AWS::ApiGateway::Deployment') { | |
delete resource.DependsOn | |
} | |
} | |
// Remove the artifical http event to avoid confusion in stored state | |
// (manifests in e.g. post-deploy endpoint list) | |
for (const functionKey of this.referencedKeys) { | |
const func = this.serverless.service.functions[functionKey] | |
func.events = func.events.filter((event) => { | |
if (event.http) return false | |
return true | |
}) | |
} | |
// Now before finishing up, lets replace all the fake Lambda references we added | |
this.convertCfnVariables() | |
}, | |
// Finally, if someone for some reason uses `serverless info`.. | |
'before:aws:info:displayEndpoints': () => { | |
//TODO: For display purposes we should show the paths from Swagger | |
const functions = this.serverless.service.functions | |
for (const functionKey of this.referencedKeys) { | |
functions[functionKey].events.push({ http: { | |
path: '{?}', | |
method: 'POTATO', | |
}}) | |
} | |
} | |
} | |
} | |
async referenceLambda(src) { | |
const functionKey = src.slice('lambda:'.length) | |
const logicalId = this.serverless.getProvider('aws').naming.getLambdaLogicalId(functionKey) | |
this.referencedKeys.push(functionKey) | |
// this relies on another plugin to magically turn this string into | |
// an actual reference in CloudFormation: serverless-cloudformation-sub-variables | |
return `#{${logicalId}}` | |
} | |
convertCfnVariables() { | |
const cfnVarConverter = new ServerlessAWSCloudFormationSubVariables(this.serverless) | |
cfnVarConverter.convertSubVariables() | |
} | |
} | |
module.exports = exports = ApiOrchestrator |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment