-
Notifications
You must be signed in to change notification settings - Fork 214
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #573 from paolorossig/master
feat: add request validator schema for http events
- Loading branch information
Showing
5 changed files
with
342 additions
and
1 deletion.
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,160 @@ | ||
'use strict'; | ||
|
||
const BbPromise = require('bluebird'); | ||
const _ = require('lodash'); | ||
|
||
module.exports = { | ||
compileRequestValidators() { | ||
const apiGatewayConfig = this.serverless.service.provider.apiGateway || {}; | ||
|
||
this.pluginhttpValidated.events.forEach((event) => { | ||
const resourceName = this.getResourceName(event.http.path); | ||
const methodLogicalId = this.provider.naming | ||
.getMethodLogicalId(resourceName, event.http.method); | ||
const template = this.serverless.service.provider | ||
.compiledCloudFormationTemplate.Resources[methodLogicalId]; | ||
let validatorLogicalId; | ||
|
||
if (event.http.request && event.http.request.schemas) { | ||
for (const [contentType, schemaConfig] of Object.entries(event.http.request.schemas)) { | ||
let modelLogicalId; | ||
|
||
const referencedDefinitionFromProvider = !_.isObject(schemaConfig) && _.get(apiGatewayConfig, `request.schemas.${schemaConfig}`); | ||
|
||
if (referencedDefinitionFromProvider) { | ||
modelLogicalId = this.createProviderModel( | ||
schemaConfig, | ||
apiGatewayConfig.request.schemas[schemaConfig], | ||
); | ||
} else { | ||
// In this situation, we have two options - schema is defined as | ||
// string that does not reference model from provider or as an object | ||
let modelName; | ||
let description; | ||
let definition; | ||
|
||
if (_.isObject(schemaConfig)) { | ||
if (schemaConfig.schema) { | ||
// In this case, schema is defined as an object with explicit properties | ||
modelName = schemaConfig.name; | ||
description = schemaConfig.description; | ||
definition = schemaConfig.schema; | ||
} else { | ||
// In this case, schema is defined as an implicit object that | ||
// stores whole schema definition | ||
definition = schemaConfig; | ||
} | ||
} else { | ||
// In this case, schema is defined as an implicit string | ||
definition = schemaConfig; | ||
} | ||
|
||
modelLogicalId = this.provider.naming.getEndpointModelLogicalId( | ||
resourceName, | ||
event.http.method, | ||
contentType, | ||
); | ||
|
||
Object.assign( | ||
this.serverless.service.provider.compiledCloudFormationTemplate.Resources, | ||
{ | ||
[modelLogicalId]: { | ||
Type: 'AWS::ApiGateway::Model', | ||
Properties: { | ||
RestApiId: this.provider.getApiGatewayRestApiId(), | ||
ContentType: contentType, | ||
Schema: definition, | ||
Name: modelName, | ||
Description: description, | ||
}, | ||
}, | ||
}, | ||
); | ||
} | ||
|
||
if (!validatorLogicalId) { | ||
const requestValidator = this.createRequestValidator(); | ||
validatorLogicalId = requestValidator.validatorLogicalId; | ||
} | ||
|
||
template.Properties.RequestValidatorId = { Ref: validatorLogicalId }; | ||
template.Properties.RequestModels = template.Properties.RequestModels || {}; | ||
template.Properties.RequestModels[contentType] = { Ref: modelLogicalId }; | ||
} | ||
} | ||
}); | ||
|
||
return BbPromise.resolve(); | ||
}, | ||
|
||
createProviderModel(schemaId, schemaConfig) { | ||
let modelName; | ||
let description; | ||
let definition; | ||
|
||
// If schema is not defined this will try to map resourceDefinition as the schema | ||
if (!schemaConfig.schema) { | ||
definition = schemaConfig; | ||
} else { | ||
definition = schemaConfig.schema; | ||
} | ||
|
||
const modelLogicalId = this.provider.naming.getModelLogicalId(schemaId); | ||
|
||
if (schemaConfig.name) { | ||
modelName = schemaConfig.name; | ||
} | ||
|
||
if (schemaConfig.description) { | ||
description = schemaConfig.description; | ||
} | ||
|
||
Object.assign(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, { | ||
[modelLogicalId]: { | ||
Type: 'AWS::ApiGateway::Model', | ||
Properties: { | ||
RestApiId: this.provider.getApiGatewayRestApiId(), | ||
Schema: definition, | ||
ContentType: 'application/json', | ||
}, | ||
}, | ||
}); | ||
|
||
if (modelName) { | ||
this.serverless.service.provider.compiledCloudFormationTemplate.Resources[ | ||
modelLogicalId | ||
].Properties.Name = modelName; | ||
} | ||
|
||
if (description) { | ||
this.serverless.service.provider.compiledCloudFormationTemplate.Resources[ | ||
modelLogicalId | ||
].Properties.Description = description; | ||
} | ||
|
||
return modelLogicalId; | ||
}, | ||
|
||
createRequestValidator() { | ||
const validatorLogicalId = this.provider.naming.getValidatorLogicalId(); | ||
const validatorName = `${ | ||
this.serverless.service.service | ||
}-${this.provider.getStage()} | Validate request body and querystring parameters`; | ||
|
||
this.serverless.service.provider.compiledCloudFormationTemplate | ||
.Resources[validatorLogicalId] = { | ||
Type: 'AWS::ApiGateway::RequestValidator', | ||
Properties: { | ||
RestApiId: this.provider.getApiGatewayRestApiId(), | ||
ValidateRequestBody: true, | ||
ValidateRequestParameters: true, | ||
Name: validatorName, | ||
}, | ||
}; | ||
|
||
return { | ||
validatorLogicalId, | ||
validatorName, | ||
}; | ||
}, | ||
}; |
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,91 @@ | ||
'use strict'; | ||
|
||
const expect = require('chai').expect; | ||
const Serverless = require('serverless/lib/Serverless'); | ||
const AwsProvider = require('serverless/lib/plugins/aws/provider'); | ||
const ServerlessStepFunctions = require('../../../index'); | ||
|
||
describe('#requestValidator()', () => { | ||
let serverless; | ||
let serverlessStepFunctions; | ||
|
||
beforeEach(() => { | ||
const options = { | ||
stage: 'dev', | ||
region: 'us-east-1', | ||
}; | ||
|
||
serverless = new Serverless(); | ||
serverless.service.service = 'step-functions'; | ||
serverless.setProvider('aws', new AwsProvider(serverless)); | ||
serverless.service.provider.compiledCloudFormationTemplate = { | ||
Resources: { | ||
ApiGatewayMethodFirstPost: { | ||
Properties: {}, | ||
}, | ||
}, | ||
}; | ||
serverless.configSchemaHandler = { | ||
// eslint-disable-next-line no-unused-vars | ||
defineTopLevelProperty: (propertyName, propertySchema) => {}, | ||
}; | ||
|
||
serverlessStepFunctions = new ServerlessStepFunctions(serverless, options); | ||
serverlessStepFunctions.apiGatewayRestApiLogicalId = 'ApiGatewayRestApi'; | ||
serverlessStepFunctions.apiGatewayResourceNames = { | ||
'foo/bar1': 'apiGatewayResourceNamesFirst', | ||
'foo/bar2': 'apiGatewayResourceNamesSecond', | ||
}; | ||
serverlessStepFunctions.pluginhttpValidated = { | ||
events: [ | ||
{ | ||
stateMachineName: 'first', | ||
http: { | ||
path: 'foo/bar1', | ||
method: 'post', | ||
request: { | ||
schemas: { | ||
'application/json': { | ||
name: 'StartExecutionSchema', | ||
schema: {}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
{ | ||
stateMachineName: 'second', | ||
http: { | ||
path: 'foo/bar2', | ||
method: 'post', | ||
private: true, | ||
}, | ||
}, | ||
], | ||
}; | ||
serverlessStepFunctions.apiGatewayResources = { | ||
'foo/bar1': { | ||
name: 'First', | ||
resourceLogicalId: 'ApiGatewayResourceFirst', | ||
}, | ||
|
||
'foo/bar2': { | ||
name: 'Second', | ||
resourceLogicalId: 'ApiGatewayResourceSecond', | ||
}, | ||
}; | ||
}); | ||
|
||
describe('#compileRequestValidators()', () => { | ||
it('should process schema from http event request schemas', () => serverlessStepFunctions | ||
.compileRequestValidators().then(() => { | ||
expect(serverlessStepFunctions.serverless.service.provider.compiledCloudFormationTemplate | ||
.Resources) | ||
.to.have.property('ApiGatewayMethodFirstPostApplicationJsonModel'); | ||
|
||
expect(serverlessStepFunctions.serverless.service.provider.compiledCloudFormationTemplate | ||
.Resources) | ||
.to.have.property('ApiGatewayStepfunctionsRequestValidator'); | ||
})); | ||
}); | ||
}); |
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.