Closed scarlier closed 2 years ago
Thanks for the feature request! We have passed this along to our product team for possible prioritization. Please +1 on the feature to help with prioritization.
@praneetap this has been raised before. I opened issue #1232 and it was quickly closed in favor of #931
+1
A temporary solution could be to add a partial DefinitionBody in your ApiGateway resource :
DefinitionBody:
swagger: 2.0
x-amazon-apigateway-request-validators:
basic:
validateRequestBody: true
validateRequestParameters: true
x-amazon-apigateway-request-validator: basic
paths:
/request-path:
post:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri:
Fn::Sub: 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${byIamcToken.Arn}/invocations'
parameters:
- required: true
in: body
name: somemodel
schema:
$ref: '#/definitions/somemodel'
This will add a validator named basic and will add it to every function in your apigateway, thus will enforce validation of the body and parameters (tweak it as you need)
~@g-pelletier as I understand you can't do a partial swagger doc. You'll need to define the full API and the links to the Lambda functions as SAM will either generate the swagger doc or it will rely on the one provided if you've set DefinitionBody
.~
~I believe in the case of the new HttpApi
resource they have designed that to be able to merge source and auto-generated definitions. @praneetap can you verify?~
You can generate partial swagger doc. I just did in our templates that needed request body verification. What I posted is exactly what I used combined with something similiar as what is posted in the original post.
@g-pelletier disregard that last comment. I wasn't thinking. What you posted is what we've had to do for similar workarounds as well. Though I don't think you could really call that a partial swagger doc.
Sorry, by partial, I meant that you don't need to write the full API code to have it working. SAM will append the other parts, such as Models and Authorizers.
@praneetap @keetonian could you provide some info on how this relates to #931 and #1232? Those issues seem to suggest that there is work underway yet this issue seems to be still in triage? I'd avoid the swagger workaround if validation support for AWS::Serverless::Api-Models
is in the pipeline.
Hi!
I was a little bit annoyed with the lack of this feature, so I started hacking away a possible solution. I ended up with a version that works locally using cloud formation packaging/deploy, but I am not sure if it would be eligible for a Pull Request. I would appreciate if anyone would give me a feedback whether this would be useful to the general development of the specification.
Basically, I've added properties to the SAM specification
GenerateValidators
inside AWS::Serverless::APIGeneratedValidator
inside AWS::Serverless::Function implicit API event source.The first one basically injects the following on the generated template:
"x-amazon-apigateway-request-validators": {
"BODY": {
"validateRequestParameters": false,
"validateRequestBody": true
},
"FULL": {
"validateRequestParameters": true,
"validateRequestBody": false
},
"PARAMS": {
"validateRequestParameters": true,
"validateRequestBody": false
}
}
Then the second would inject this to the API Method:
"x-amazon-apigateway-request-validator": "BODY",
A sample template would be:
MyApi:
Type: AWS::Serverless::Api
Properties:
GenerateValidators: true # New property
StageName: Dev
Models:
Book:
required:
- name
- author
type: object
properties:
name:
type: string
author:
type: string
description: author
CreateBookFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: dynamo/
Handler: handlers.put_item_handler
Events:
HelloWorld:
Type: Api
Properties:
RestApiId: !Ref MyApi
Path: /book
Method: post
RequestModel:
Model: Book
Required: true
GeneratedValidator: BODY # New property
Still need "Request Validator" parameter in SAM.
+1 for "Request Validator"
Still need "Request Validator" parameter in SAM.
+1
Still need "Request Validator" parameter in SAM.
+++
Still need "Request Validator" parameter in SAM.
+1
Still need "Request Validator" parameter in SAM.
+1
++
+1
+1
Hi! Is there any ETA?
+1
+1
This is really needed.
Up
+1
+
+1
+1
+1
++++++1
+1
+1
+1
+1
Any updates?
+1
+1
+1
+1
+1
+1
+1
+1
+1
++++++1
+1 !
A temporary solution could be to add a partial DefinitionBody in your ApiGateway resource :
DefinitionBody: swagger: 2.0 x-amazon-apigateway-request-validators: basic: validateRequestBody: true validateRequestParameters: true x-amazon-apigateway-request-validator: basic paths: /request-path: post: x-amazon-apigateway-integration: httpMethod: POST type: aws_proxy uri: Fn::Sub: 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${byIamcToken.Arn}/invocations' parameters: - required: true in: body name: somemodel schema: $ref: '#/definitions/somemodel'
This will add a validator named basic and will add it to every function in your apigateway, thus will enforce validation of the body and parameters (tweak it as you need)
Hi @g-pelletier, I did try as you mentioned with openAPI-3.0.0
. API deployed successfully but i get this error
Execution failed due to configuration error: Invalid permissions on Lambda function
My CFN looks as this DefinitionBoyd
TestServiceApi:
Type: AWS::Serverless::Api
Properties:
Name: TestServiceApi
EndpointConfiguration: EDGE
StageName: Prod
TracingEnabled: true
Auth:
DefaultAuthorizer: CustomAuthorizer
AddDefaultAuthorizerToCorsPreflight: false
Authorizers:
CustomAuthorizer:
FunctionPayloadType: TOKEN
FunctionArn: !GetAtt TestServiceCustomAuthorizerLambdaFunction.Arn
Identity:
ValidationExpression: Bearer.*
ReauthorizeEvery: 0
GatewayResponses:
DEFAULT_4xx:
ResponseParameters:
Headers:
Access-Control-Allow-Origin: "'*'"
DEFAULT_5XX:
ResponseParameters:
Headers:
Access-Control-Allow-Origin: "'*'"
Cors:
AllowMethods: "'*'"
AllowHeaders: "'*'"
AllowOrigin:
Fn::Join:
- ''
- - "'https://"
- Fn::ImportValue: !Sub "${ProjectPrefix}-${Stage}-DomainName"
- "'"
DefinitionBody:
openapi: "3.0.0"
info:
description: "This is a Test management service API specification."
version: "1.0.0"
title: "Test Management Service API"
termsOfService: "http://aws.com/terms/"
contact:
email: "aws-bots-grimsby@amazon.com"
license:
name: "Amazon"
url: "http://www.amazon.com"
servers:
- url: "https://{stage}.aws.com/test-management"
variables:
stage:
default: api # Production server
enum:
- api # Production server
- api.gamma # Pre-Prod server
- api.beta # Beta server
x-amazon-apigateway-request-validators:
basic:
validateRequestBody: true
validateRequestParameters: true
x-amazon-apigateway-request-validator: basic
paths:
/instructors:
get:
summary: "Finds Instructors"
operationId: "GetInstructorsLambdaFunction"
x-amazon-apigateway-integration:
uri:
Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetInstructorsLambdaFunction.Arn}/invocations"
httpMethod: POST # Keep "POST" when the API definition method is not POST. This "httpMethod" is used to call Lambda.
type: aws_proxy
parameters:
- name: "search_text"
in: "query"
description: "Filter instructors based on partial search"
required: false
schema:
type: "string"
example: "pra"
- name: "from"
in: "query"
description: "from field for pagination"
required: false
schema:
type: "integer"
example: 0
- name: "size"
in: "query"
description: "size field for pagination"
required: false
schema:
type: "integer"
example: 10
- name: "sort_fields"
in: "query"
description: "Sort instructors based on given fields"
required: false
schema:
type: "array"
items:
type: "object"
example: [{"email": {"order": "asc"}},{"instrctor_status": {"order": "desc"}}]
- name: "source_fields"
in: "query"
description: "Source fields to return in reponse"
required: false
schema:
type: "array"
items:
type: "string"
example: ["first_name", "last_name", "programs", "regions"]
- name: "saved_filter"
in: "query"
description: "Addtional filters to apply"
required: false
schema:
type: "object"
example: {"postcal_code": "97124", "active": True}
responses:
"200":
description: "successful operation"
content:
application/json:
schema:
type: "array"
items:
type: "object"
Any help on this please.
@paolorechia any thoughts from your end too??
Hi, @karthikvadla.
As far as I remember, you really just need these two pieces in the generated CFN.
"x-amazon-apigateway-request-validators": {
"SomeValidatorName": {
"validateRequestParameters": true,
"validateRequestBody": true
},
And
x-amazon-apigateway-request-validator: SomeValidatorName
I am not sure why your CFN did not work with OpenAPI 3.0. What I can do to help you is clone my old forked repo and try to generate a CFN that works so we can compare.
Also, for the final the solution, I believe it would be ideal if we could automatically generate the boilerplate by simply defining:
For instance:
(...)
Models:
Book:
required:
- name
- author
type: object
properties:
name:
type: string
author:
type: string
description: author
(...)
RequestModel:
Model: Book
Required: true
I think the developer using SAM CLI would not really care about which actual validator is used, as long as it actually validates both body and query params.
It seems like I added the validator to each API Method and not to the root level, like your CFN. I'll confirm this once I have the develop environment installed again.
Here's the full diff from a stale version of develop
to my local branch
, so you can inspect what I did:
diff --git a/samtranslator/model/api/api_generator.py b/samtranslator/model/api/api_generator.py
index 1dc2caa..73c05ae 100644
--- a/samtranslator/model/api/api_generator.py
+++ b/samtranslator/model/api/api_generator.py
@@ -85,6 +85,7 @@ class ApiGenerator(object):
open_api_version=None,
models=None,
domain=None,
+ generate_validators=False
):
"""Constructs an API Generator class that generates API Gateway resources
@@ -104,6 +105,7 @@ class ApiGenerator(object):
:param resource_attributes: Resource attributes to add to API resources
:param passthrough_resource_attributes: Attributes such as `Condition` that are added to derived resources
:param models: Model definitions to be used by API methods
+ :param generate_validators: Uses auto generated validators
"""
self.logical_id = logical_id
self.cache_cluster_enabled = cache_cluster_enabled
@@ -131,6 +133,7 @@ class ApiGenerator(object):
self.remove_extra_stage = open_api_version
self.models = models
self.domain = domain
+ self.generate_validators = generate_validators
def _construct_rest_api(self):
"""Constructs and returns the ApiGateway RestApi.
@@ -144,6 +147,8 @@ class ApiGenerator(object):
rest_api.BinaryMediaTypes = self.binary_media
rest_api.MinimumCompressionSize = self.minimum_compression_size
+ # Defines default request validators for the API
+
if self.endpoint_configuration:
self._set_endpoint_configuration(rest_api, self.endpoint_configuration)
@@ -181,6 +186,17 @@ class ApiGenerator(object):
if self.name:
rest_api.Name = self.name
+ print(self.generate_validators)
+ if self.generate_validators:
+ print( "Generating...")
+ self._set_generated_validators()
+ _X_APIGW_REQUEST_VALIDATORS = "x-amazon-apigateway-request-validators"
+ rest_api.Body[_X_APIGW_REQUEST_VALIDATORS] = {
+ "BODY": {"validateRequestBody": True, "validateRequestParameters": False},
+ "PARAMS": {"validateRequestBody": False, "validateRequestParameters": True},
+ "FULL": {"validateRequestBody": False, "validateRequestParameters": True}
+ }
+
return rest_api
def _construct_body_s3_dict(self):
@@ -723,6 +739,17 @@ class ApiGenerator(object):
# Assign the Swagger back to template
self.definition_body = swagger_editor.swagger
+ def _set_generated_validators(self):
+ swagger_editor = SwaggerEditor(self.definition_body)
+ swagger_editor.set_request_validators({
+ "BODY": {"validateRequestBody": True, "validateRequestParameters": False},
+ "PARAMS": {"validateRequestBody": False, "validateRequestParameters": True},
+ "FULL": {"validateRequestBody": False, "validateRequestParameters": True}
+ })
+
+ self.definition_body = swagger_editor.swagger
+ # print(self.definition_body)
+
def _add_models(self):
"""
Add Model definitions to the Swagger file, if necessary
diff --git a/samtranslator/model/eventsources/push.py b/samtranslator/model/eventsources/push.py
index 03ec6a8..258d037 100644
--- a/samtranslator/model/eventsources/push.py
+++ b/samtranslator/model/eventsources/push.py
@@ -483,6 +483,8 @@ class Api(PushEventSource):
"Auth": PropertyType(False, is_type(dict)),
"RequestModel": PropertyType(False, is_type(dict)),
"RequestParameters": PropertyType(False, is_type(list)),
+ "GeneratedValidator": PropertyType(False, is_str()),
+ "x-amazon-apigateway-request-validator": PropertyType(False, is_str())
}
def resources_to_link(self, resources):
@@ -491,6 +493,7 @@ class Api(PushEventSource):
necessary data from the explicit API
"""
+
rest_api_id = self.RestApiId
if isinstance(rest_api_id, dict) and "Ref" in rest_api_id:
rest_api_id = rest_api_id["Ref"]
@@ -504,6 +507,10 @@ class Api(PushEventSource):
permitted_stage = "*"
stage_suffix = "AllStages"
explicit_api = None
+
+ if self.GeneratedValidator:
+ setattr(self, 'x-amazon-apigateway-request-validator', self.GeneratedValidator)
+
if isinstance(rest_api_id, string_types):
if (
@@ -788,6 +795,10 @@ class Api(PushEventSource):
path=self.Path, method_name=self.Method, request_parameters=parameters
)
+ if self.GeneratedValidator:
+ editor.add_request_validator_to_method(
+ path=self.Path, method_name=self.Method, request_validator=self.GeneratedValidator)
+
api["DefinitionBody"] = editor.swagger
diff --git a/samtranslator/model/sam_resources.py b/samtranslator/model/sam_resources.py
index b1ed7f6..c78f076 100644
--- a/samtranslator/model/sam_resources.py
+++ b/samtranslator/model/sam_resources.py
@@ -760,6 +760,7 @@ class SamApi(SamResourceMacro):
"Tags": PropertyType(False, is_type(dict)),
"DefinitionBody": PropertyType(False, is_type(dict)),
"DefinitionUri": PropertyType(False, one_of(is_str(), is_type(dict))),
+ "GenerateValidators": PropertyType(False, is_type(bool)),
"CacheClusterEnabled": PropertyType(False, is_type(bool)),
"CacheClusterSize": PropertyType(False, is_str()),
"Variables": PropertyType(False, is_type(dict)),
@@ -829,6 +830,7 @@ class SamApi(SamResourceMacro):
open_api_version=self.OpenApiVersion,
models=self.Models,
domain=self.Domain,
+ generate_validators=self.GenerateValidators
)
(
diff --git a/samtranslator/swagger/swagger.py b/samtranslator/swagger/swagger.py
index abd15b9..6aa4db5 100644
--- a/samtranslator/swagger/swagger.py
+++ b/samtranslator/swagger/swagger.py
@@ -23,6 +23,7 @@ class SwaggerEditor(object):
_X_APIGW_GATEWAY_RESPONSES = "x-amazon-apigateway-gateway-responses"
_X_APIGW_POLICY = "x-amazon-apigateway-policy"
_X_ANY_METHOD = "x-amazon-apigateway-any-method"
+ _X_APIGW_REQUEST_VALIDATORS = "x-amazon-apigateway-request-validators"
_CACHE_KEY_PARAMETERS = "cacheKeyParameters"
# https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
_ALL_HTTP_METHODS = ["OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "PATCH"]
@@ -44,6 +45,7 @@ class SwaggerEditor(object):
self._doc = copy.deepcopy(doc)
self.paths = self._doc["paths"]
+ self._X_APIGW_REQUEST_VALIDATORS = {}
self.security_definitions = self._doc.get("securityDefinitions", {})
self.gateway_responses = self._doc.get(self._X_APIGW_GATEWAY_RESPONSES, {})
self.resource_policy = self._doc.get(self._X_APIGW_POLICY, {})
@@ -152,6 +154,12 @@ class SwaggerEditor(object):
path_dict.setdefault(method, {})
+
+ def set_request_validators(self, validators):
+ self._X_APIGW_REQUEST_VALIDATORS = validators
+ print(self._X_APIGW_REQUEST_VALIDATORS)
+
+
def add_lambda_integration(
self, path, method, integration_uri, method_auth_config=None, api_auth_config=None, condition=None
):
@@ -1004,6 +1012,19 @@ class SwaggerEditor(object):
statement.append(s)
self.resource_policy["Statement"] = statement
+
+ def add_request_validator_to_method(self, path, method_name, request_validator):
+ normalized_method_name = self._normalize_method_name(method_name)
+
+ for method_definition in self.get_method_contents(self.get_path(path)[normalized_method_name]):
+
+ # If no integration given, then we don't need to process this definition (could be AWS::NoValue)
+ if not self.method_definition_has_integration(method_definition):
+ continue
+
+ method_definition["x-amazon-apigateway-request-validator"] = request_validator
+
+
def add_request_parameters_to_method(self, path, method_name, request_parameters):
"""
Add Parameters to Swagger.
diff --git a/samtranslator/validator/sam_schema/schema.json b/samtranslator/validator/sam_schema/schema.json
index c18f106..af54c10 100644
--- a/samtranslator/validator/sam_schema/schema.json
+++ b/samtranslator/validator/sam_schema/schema.json
@@ -5,6 +5,9 @@
"AWS::Serverless::Api": {
"additionalProperties": false,
"properties": {
+ "GenerateValidators": {
+ "type": "boolean"
+ },
"DeletionPolicy": {
"enum": [
"Delete",
@@ -86,6 +89,9 @@
}
},
"type": "object"
+ },
+ "x-amazon-apigateway-request-validators": {
+ "type:": "object"
}
},
"required": [
@@ -310,6 +316,9 @@
"AWS::Serverless::Function.ApiEvent": {
"additionalProperties": false,
"properties": {
+ "GeneratedValidator": {
+ "type": "string"
+ },
"Method": {
"type": "string"
},
I've found an old transformed template lying around (swagger 2.0) in my local file system. You can find the full file here
In API Method:
"/book": {
"post": {
"x-amazon-apigateway-integration": {
"httpMethod": "POST",
"type": "aws_proxy",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${CreateBookFunction.Arn}/invocations"
}
},
"x-amazon-apigateway-request-validator": "BODY",
"security": [
{
"MyCognitoAuthorizer": [
"api/generic"
]
}
],
"parameters": [
{
"required": true,
"in": "body",
"name": "book",
"schema": {
"$ref": "#/definitions/book"
}
}
],
"responses": {}
},
Somewhere in the top level:
"swagger": "2.0",
"x-amazon-apigateway-request-validators": {
"BODY": {
"validateRequestParameters": false,
"validateRequestBody": true
},
"FULL": {
"validateRequestParameters": true,
"validateRequestBody": false
},
"PARAMS": {
"validateRequestParameters": true,
"validateRequestBody": false
}
}
Description: Defining api model to required=true will not add the Request Validator to the method.
with following sam template and sam cli version 0.40
Observed result: Request Validator in method settings has value "NONE"
Expected result: Request Validator in method settings has value "Validate body..."