serverless-aws-documentation model definitions with optional fields? - amazon-web-services

I want to define request and response models. I use the Serverless Framework with AWS and everything I'm seeing recommends using serverless-aws-documentation
The README says that I need to have this line in custom.documentation.models.MODELNAME
schema: ${file(models/error.json)}
But they don't have an example file of models/error.json to use as a baseline.
In the actual example serverless.yml they have a definition like this:
-
name: DoSomethingRequest
contentType: "application/json"
schema:
type: array
items:
type: string
This doesn't provide enough detail for what I'm trying to do.
My goal is to have a schema defined for an array of string objects, a message and a status code. The message and status code, though, are optional. These could also be part of other models and if possible I'd like to not repeat their definition for each model.
My current attempt is:
-
name: ReturnArrayResponse
contentType: "application/json"
schema:
type: array
itemsArray:
type: string
message:
type: string
statusCode:
type: number
I think this is going to do what I want, but how can I have message and statusCode be optional and repeat these two items in my other models?
I'd be happy with either a yml solution I can put in my serverless.yml file or a json file that I can reference.

Including a file
In the example given, error.json can contain any valid schema. So something as simple as this is fine:
{"type":"object","properties":{"message":{"type":"string"}}}
It's also fine to include attributes like $schema and title:
{
"$schema" : "http://json-schema.org/draft-04/schema#",
"title" : "Error Schema",
"type" : "object",
"properties" : {
"message" : { "type" : "string" },
"statusCode": { "type": "number" },
"itemsArray": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
This is especially handy when you have models already defined in AWS, but you don't have the serverless yaml to build them. You can simply copy the schema out of the AWS console, paste the json into a file, and use the schema: ${file()} syntax mentioned in the question. As far as I can tell anything that you can get the AWS console to accept will work.
DRY
I don't know of a way to reference models from within other models in a serverless file, but you could use the same approach as the plugin authors, and just put anything you need to reuse outside of the models and somewhere it's easier to reuse. The plugin authors use commonModelSchemaFragments.
So if you have some fragments like so:
commonModelSchemaFragments:
# defining common fragments means you can reference them with a single line
StringArrayFragment:
type: array
items:
type: string
HttpResponse:
type: object
properties:
message:
type: string
statusCode:
type: number
You can reference those fragments in models like this:
-
name: HttpStatusResponse
contentType: "application/json"
schema:
type: object
properties:
serverResponse:
${self:custom.commonModelSchemaFragments.HttpResponse}
messageArray:
${self:custom.commonModelSchemaFragments.StringArrayFragment}
Marking attributes optional
You can accomplish this by marking attributes as required. Simply provide a list of all attributes, except the ones you want to be optional. The json schema for that looks like this:
{
"type": "object",
"required": ["message"],
"properties": {
"optionalMessage": {
"type": "string"
},
"message": {
"type": "string"
}
}
}
which you would build by using yaml like this in your serverless file:
-
name: OptionalResponse
contentType: "application/json"
schema:
type: object
required:
- "message"
properties:
message:
type: string
optionalMessage:
type: string
Note on request validation
Marking attributes required or optional only matters if request body validation is turned on:
I don't know of a way to turn on request validation using any special serverless syntax. It looks like you can do this in the resources section, but I haven't tried it. Source.

Just a guess (posting it as an answer to preserve formatting) - your top-level entity in the schema should be an object, not an array, something like this:
schema:
type: object
properties:
items:
type: array
items:
type: string
message:
type: string
statusCode:
type: number

Related

How put Array in API DEFINITION WSO2

Can someone help me please ?
"RefeItem": [
{
"Key": "dev",
"Value": "dev234"
}
]
how can i set this in API Definition publisher wso2
Latest versions of WSO2 API Manager supports Swagger 3.0.0 OpenAPI definitions. So any swagger definition adhering to that standard is supported.
You requirement can be achieved using the following snippet,
responses:
200:
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/RefeItem'
components:
schemas:
RefeItem:
type: array
items:
$ref: '#/components/schemas/keyValueObj'
keyValueObj:
type: object
properties:
Key:
type: string
Value:
type: string
You can find more information on using array data types in swagger here

AWS::ApiGatewayV2::Integration how to use MessageAttributes in CloudFormation

I have tried several variations of using MessageAttributes in my CloudFormation document but I can't get it to work. This is what I've got:
HttpApiSqsIntegration:
Type: AWS::ApiGatewayV2::Integration
Properties:
ApiId: !Ref HttpApiRef
CredentialsArn: !GetAtt MyHttpApiRole.Arn
IntegrationType: AWS_PROXY
IntegrationSubtype: SQS-SendMessage
PayloadFormatVersion: "1.0"
RequestParameters:
QueueUrl: !Ref InputMessageSqs
MessageBody: $request.body
MessageAttributes.entry.1.Name: foo
MessageAttributes.entry.1.Value.StringValue: bar
MessageAttributes.entry.1.Value.DataType: String
I'm following the format specified here, here, and here but it gives the following error message:
Parameter: MessageAttributes.entry.1.Name does not fit schema for Operation:
SQS-SendMessage. (Service: AmazonApiGatewayV2; Status Code: 400;
Error Code: BadRequestException; Request ID: ...; Proxy: null)
Any help is appreciated. Thank you.
You are following the wrong specification. Here is the right one.
RequestParameters For WebSocket APIs, a key-value map specifying
request parameters that are passed from the method request to the
backend. The key is an integration request parameter name and the
associated value is a method request parameter value or static value
that must be enclosed within single quotes and pre-encoded as required
by the backend. The method request parameter value must match the
pattern of method.request.{location}.{name} , where {location} is
querystring, path, or header; and {name} must be a valid and unique
method request parameter name.
For HTTP API integrations with a specified integrationSubtype, request
parameters are a key-value map specifying parameters that are passed
to AWS_PROXY integrations. You can provide static values, or map
request data, stage variables, or context variables that are evaluated
at runtime. To learn more, see Working with AWS service integrations
for HTTP APIs.
For HTTP API integrations without a specified integrationSubtype
request parameters are a key-value map specifying how to transform
HTTP requests before sending them to the backend. The key should
follow the pattern :<header|querystring|path>. where
action can be append, overwrite or remove. For values, you can provide
static values, or map request data, stage variables, or context
variables that are evaluated at runtime. To learn more, see
Transforming API requests and responses.
I haven't done this in CloudFormation but managed to get it working from the web console. I imagine the syntax is equivalent to the one you would use in CloudFormation.
For example, suppose you want to add an attribute called timestamp with value equal to the request header x-timestamp. You would use this syntax in the MessageAttributes field:
{ "timestamp": { "DataType": "String", "StringValue": "${request.header.x-timestamp}" } }
This is (very badly) documented in the SQS API reference.
See the SendMessage and MessageAttributeValue pages.
The correct part of the documentation is where it says that MessageAttributes has to be a "String to MessageAttributeValue object map".
The rest of the documentation where it mentions MessageAttribute.N.Name seems to be wrong.
Here is how you can do within a CloudFormation template:
HttpApiSqsIntegration:
Type: AWS::ApiGatewayV2::Integration
Properties:
ApiId: !Ref HttpApiRef
CredentialsArn: !GetAtt MyHttpApiRole.Arn
IntegrationType: AWS_PROXY
IntegrationSubtype: SQS-SendMessage
PayloadFormatVersion: "1.0"
RequestParameters:
QueueUrl: !Ref InputMessageSqs
MessageBody: $request.body
MessageAttributes: >-
{
"foo": {
"DataType": "String",
"StringValue": "bar"
}
}
If you need a dynamic value you can do:
RequestParameters:
QueueUrl: !Ref InputMessageSqs
MessageBody: $request.body
MessageAttributes: >-
{
"foo": {
"DataType": "String",
"StringValue": "${context.path}"
}
}
Note that MessageAttributes must be a valid JSON string.
See: https://awsteele.com/blog/2021/09/06/api-gateway-http-apis-and-sqs-messageattributes.html

Writing reusable CloudFormation snippets with AWS::Include and Nested Stacks

I have been using nested stacks in CloudFormation for several months and they are very useful. So I thought I should spend sometime to make each nested stack reusable to other teams in the org.
I saw the use case of AWS::Include in several places like here and here and it makes good sense to me.
One approach I have in mind is one snippet for each resource, like an AWS::EC2::Subnet or AWS::EC2::InternetGateway which can be included zero or more times into a vpc.json template, which itself can be used as a nested stack in a larger application.
The snippet does not take any parameters, but can reference a parameter that exists in the parent template.
At first glance this doesn't seem enough to me. Consider this example:
"PublicSubnet": {
"Type": "AWS::EC2::Subnet",
"Properties": {
"VpcId": {"Ref": "VPC"},
"AvailabilityZone": {
"Fn::Select" : [ "0", { "Fn::GetAZs" : {"Ref": "AWS::Region"} }]
},
"CidrBlock": {
"Fn::FindInMap": ["AZSubnetMap", {
"Fn::Select" : [ "0", { "Fn::GetAZs" : {"Ref": "AWS::Region"} }]},
"PublicSubnet"]},
"MapPublicIpOnLaunch": "true",
"Tags": [..]
}
}
How can I avoid hard coding that "0" for the AZ in a Subnet snippet for example?
Unfortunately, AWS doesn't provide a way to dynamically update the template as per the requirement.
I have solved a similar problem using Mustache Templates using Java Library Handle Bars. Using this library you can generate template on the fly based on the requirements.
Hope this helps.
You will have to use two AWS::Include files located in:
s3://yourname/PublicSubnetA.yaml
s3://yourname/PublicSubnetB.yaml
And call them from your MAIN Template:
Fn::Transform:
Name: AWS::Include
Parameters:
Location : "s3://yourname/PublicSubnetA.yaml"
Fn::Transform:
Name: AWS::Include
Parameters:
Location : "s3://yourname/PublicSubnetB.yaml"
I 'am trying to find way to send additional parameters or override parameter to AWS::include, as you see it has
Parameters:
Location:
Why ti's not understanding more parameters and not just Location, I would be glad to have something like this:
Fn::Transform:
Name: AWS::Include
Parameters:
MySubnetIndex: 0
Location : "s3://yourname/PublicSubnetB.yaml"
I tried this way to send additional parameters:
Fn::Transform:
Name: AWS::Include
Parameters:
Location : "s3://my.test/create-ec2.yaml"
EC2Size :
Type: String
Default: "t2.micro"
And got interesting error:
The value of parameter EC2Size under transform Include must resolve to a string, number, boolean or a list of any of these
Looks like it understand what this is additional parameters, probably it need to be configured little bit different. I was not able to find way to fix this error yet.

Use timestamp in AWS API Gateway in the Path Override of the Integration Request

I've been trying to create a method in API Gateway that drops the body of the incoming request into a file/object in an S3 bucket. But I want the method to create a new file each time (so a file with a new name) instead of overwriting the previous one. But I've been struggling to find a way to do it. Anyone has any suggestions/ideas? Something like a timestamp, or a sequence number (or both) to use as a variable in the Path override so that it would become the name of the s3 file. I looked at suggestions to use the X-Amzn-Trace-Id but it doesn't seem to be available in Path override. Anything else I could try? Maybe something in Swagger? I want to achieve it using API Gateway (avoid using a lambda as an extra step) to keep our architecture from getting too complex. Thanks in advance!
You can set the X-Amzn-Trace-Id as the method parameter, then map the parameter to the integration parameter on the path to S3 as the object name .
Example:
---
swagger: "2.0"
info:
version: "2017-12-04T23:03:26Z"
title: "API"
host: "xxxx.execute-api.us-east-1.amazonaws.com"
basePath: "/dev"
schemes:
- "https"
paths:
/:
post:
produces:
- "application/json"
parameters:
- name: "X-Amzn-Trace-Id"
in: "header"
required: false
type: "string"
responses:
200:
description: "200 response"
schema:
$ref: "#/definitions/Empty"
x-amazon-apigateway-integration:
credentials: "arn:aws:iam::178779171625:role/api-gate-way"
responses:
default:
statusCode: "200"
requestParameters:
integration.request.path.traceid: "method.request.header.X-Amzn-Trace-Id"
uri: "arn:aws:apigateway:us-east-1:bucketName.s3:path/{traceid}"
passthroughBehavior: "when_no_match"
httpMethod: "PUT"
type: "aws"
definitions:
Empty:
type: "object"
title: "Empty Schema"

loopback remote method: parameter validation

Is there a form to make loopback automatically validate input parameters in a remote method?
Let's assume we have the following definition of a remote method:
WebuserModel.remoteMethod('overLogin', {
description: "Performs a Webuser's login to the system",
accepts: [
{
arg: 'credentials', type: {
"username": { type: "string", required:true },
"password": { type: "string", required: true }
},
http: {source: 'body'},
required: true
},
],
returns: {arg: 'accesToken', type: "object", root: true},
http: {path: '/login', verb: 'post'}
}
I would here expect from loopback to perform validation of the input parameter on each request and to raise an error if the passed object does not comply with the defined schema (mandatory object with two mandatory properties).
Apparently it does not happen.
Any clue?
Disclaimer: I am a core developer of LoopBack and the author of argument validation in strong-remoting#3.x.
LoopBack does not support validation of nested object properties provided by the clients when invoking remote methods. Right now, we check only that the value is an object, see lib/types/object.js in strong-remoting.
In the upcoming LoopBack 4 version, we are planning to support full OpenAPI and/or JSON Schema validation for input arguments, see https://github.com/strongloop/loopback-next/issues/118
Based on the comments in that GitHub issue, it should be relatively easy to add JSONSchema-based validations to LoopBack 3.x too.