I am creating a custom rule for spectral for mapping the path and path parameters but having some difficulty with regex. I have the following rules:
1) URI MUST be in lowercase OR with combined words using the hyphen( - ). Aka kebab-case
Example:
https://api.com/payments/pending
https://api.com/payments/pending-payments
2) Path and Query string parameters MUST be in lowerCamelCase.
Example:
https://api.com/payments/payments/{paymentId}
https://api.com/payments/pending-payments/?paymentType=xxx
In the spectral rule set I have defined my custom rule like this:
paths-kebab-case:
description: Paths should be kebab-case.
message: "{{property}} should be kebab-case (lower-case and separated with hyphens)"
severity: error
given: $.paths[*]~
then:
function: pattern
functionOptions:
match: "^(\/([\da-z]+(-[\da-z]+)? |\{[a-z]+([A-Z][a-z]+)*}) )+$"
path-parameter-must-lower-camel-case:
message: Path parameters must be lowerCamelCase
description: MUST use lowerCamelCase
severity: error
given: $.paths.*~
then:
function: pattern
functionOptions:
match: '^[a-z][a-z]*(([A-Z][a-z]+)*[A-Z]?|([a-z]+[A-Z])*|[A-Z])$'
It seems to be not validating properly. like if my path contains only a single path, I am getting the error.
10:9 error path-parameter-must-lower-camel-case Path parameters must be lowerCamelCase
25:9 warning parser Mapping key must be a string scalar rather than number
48:9 warning parser Mapping key must be a string scalar rather than number
56:17 error path-parameter-must-lower-camel-case Path parameters must be lowerCamelCase
70:9 warning parser Mapping key must be a string scalar rather than number
Here is the swagger API:
openapi: "3.0.0"
info:
version: 1.0.0
title: Swagger Petstore
license:
name: MIT
servers:
- url: http://petstore.swagger.io/v1
paths:
/pets:
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
responses:
200:
description: An paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Create a pet
operationId: createPets
tags:
- pets
responses:
201:
description: Null response
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/pets/{petId}:
get:
summary: Info for a specific pet
operationId: showPetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
schema:
type: string
responses:
200:
description: Expected response to a valid request
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
components:
schemas:
Pet:
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Pets:
type: array
items:
$ref: "#/components/schemas/Pet"
Error:
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string
Related
I'm trying to create the following api config for GCP API Gateway (ommitted real backend URL):
swagger: '2.0'
info:
title: upload
description: upload
version: 1.0.0
schemes:
- https
produces:
- application/json
security:
- api_key: [ ]
paths:
/upload:
post:
summary: uploads a file.
consumes:
- multipart/form-data
operationId: uploadFile
parameters:
- in: formData
name: file
description: The file to upload.
required: true
type: file
responses:
'200':
description: upload successful
x-google-backend:
address: https://XXXXX.XXXXX
path_translation: APPEND_PATH_TO_ADDRESS
securityDefinitions:
api_key:
type: "apiKey"
name: "key"
in: "query"
Running gcloud (replaced real variables with placeholders)
gcloud api-gateway api-configs create uploadconfig --api=[API] --openapi-spec=openapi.yaml --project=[MYPROJECT] --backend-auth-service-account=[ACCOUNT]
This results in this error message:
ERROR: (gcloud.api-gateway.api-configs.create) INVALID_ARGUMENT: Cannot convert to service config.
'location: "unknown location"
kind: ERROR
message: "http: repeated message field 'google.protobuf.Struct.fields' referred to by message 'UploadFileRequest' cannot be mapped as an HTTP parameter."
location: "unknown location"
kind: ERROR
message: "http: cyclic message field 'google.protobuf.Struct.FieldsEntry.value' referred to by message 'UploadFileRequest' in method 'method 1.xxxxxxx.UploadFile' cannot be mapped as an HTTP parameter."
I verified the gcloud command with a different api configurations and backends.
The config itself seems fine, i.e. it validates with Swagger editor, still gcloud won't accept it.
How do I define a file upload via API Gateway?
As per file upload Endpoints does not accept the type: file for file upload parameters, type: string should be used instead.And I tried config with changed parameters and it validates with Swagger editor results are here.
With SwaggerHub and OpenAPI 2 spec, this is how I got this file upload to work. Let me know if you have any questions. This is written in YAML as opposed to JSON spec. It took some reading and some experimentation, but here's the Gcloud documentation: https://cloud.google.com/storage/docs/uploading-objects
And the SwaggerHub info as well: https://swagger.io/docs/specification/2-0/file-upload/
/upload/storage/v1/b/{bucket}/o?uploadType=media&name={objectName}:
post:
summary: Upload an object directly to a bucket
description: Upload an object directly to a bucket
consumes:
- multipart/form-data
produces:
- application/json
parameters:
- in: header
name: Authorization
type: string
required: true
- in: path
name: bucket
required: true
type: string
description: The name of the bucket to upload to.
- in: path
name: objectName
required: true
type: string
description: The name the object will receive in the bucket.
- in: header
name: Content-Type
type: string
required: true
description: The content type of the upload. Ex. text/plain
- in: formData
name: fileToUpload
type: file
description: The file to upload.
responses:
200:
description: OK
204:
description: Success - No Content
400:
description: Bad Request
401:
description: Insufficient Privileges
404:
description: Not Found
It DOES accept type: file in the formData request.
I'm struggling to pass the path params from my gateway to the actual endpoints and I use PUT method to do the update and DELETE method to do a delete action in API which is built with Google Cloud Functions.
Here is my API Gateway Config:
# openapi2-functions.yaml
swagger: '2.0'
info:
title: cpd-api
description: API Gateway Config
version: 1.0.0
schemes:
- https
produces:
- application/json
paths:
/users/{user_id}:
put:
summary: Update an existing user
operationId: update-user-cpd
parameters:
- name: user_id
in: path
description: User ID to be updated
required: true
type: string
x-google-backend:
address: https://asia-southeast2-project-id.cloudfunctions.net/cpd_user
path_translation: APPEND_PATH_TO_ADDRESS
responses:
'200':
description: User updated successfully
schema:
type: object
delete:
summary: Delete an existing user
operationId: delete-user-cpd
parameters:
- name: user_id
in: path
description: User ID to be deleted
required: true
type: string
x-google-backend:
address: https://asia-southeast2-project-id.cloudfunctions.net/cpd_user
path_translation: APPEND_PATH_TO_ADDRESS
responses:
'200':
description: User deleted successfully
schema:
type: object
/users:
get:
summary: Get Users data
operationId: get-users-cpd
x-google-backend:
address: https://asia-southeast2-project-id.cloudfunctions.net/cpd_user
responses:
'200':
description: User retrieved successfully
schema:
type: object
post:
summary: Create a new user
operationId: create-user-cpd
x-google-backend:
address: https://asia-southeast2-project-id.cloudfunctions.net/cpd_user
responses:
'200':
description: User created successfully
schema:
type: object
If I call the Functions URL directly with PUT/DELETE method it's working as expected.
e.g. PUT https://asia-southeast2-project-id.cloudfunctions.net/cpd_user/1
But, when I try to call it by using the API Gateway URL it gives me below errors with 404 HTTP Status:
Cannot PUT /users/1
Cannot DELETE /users/1
Here is the cpd_user index.js file.
I use express JS to define the routes
const _utils = require('./utils');
const _controller = require('./controllers');
const express = require('express');
const app = express();
const UserController = new _controller.UserController();
app.get('/', _utils.jwtFilter, UserController.getUserAction);
app.post('/', UserController.createUserAction);
app.delete('/{user_id}', _utils.jwtFilter, UserController.deleteUserAction);
app.put('/{user_id}', _utils.jwtFilter, UserController.updateUserAction);
exports.cpdUser = app;
I have checked the log but I didn't get any useful info there.
Please help me to fix this issue.
Thanks.
My setup contains google-endpoints with google-cloud-functions as my backend.
Google endpoints is defined with the following swagger v2 yaml:
swagger: "2.0"
info:
description: "yada..."
version: "0.0.1"
title: "yadada.."
termsOfService: "http://swagger.io/terms/"
contact:
name: "blah"
email: "email#mail.com"
url: "https://example.com"
host: "(generated service url by google when endpoints is deployed, i.e. 'api-gateway-xyz123123-ew.a.run.app')"
tags:
- name: "Documents"
description: "blah"
schemes:
- "https"
paths:
/api/documents:
post:
tags:
- "Documents"
summary: "Add a new document"
description: ""
security:
- firebase: []
operationId: "addDocument"
x-google-backend:
address: "(cloud functions http url)/documents"
consumes:
- "application/json"
produces:
- "application/json"
parameters:
- in: "body"
name: "body"
description: "Document supplied"
required: true
schema:
$ref: "#/definitions/Document"
responses:
201:
description: "The document was successfully created."
schema:
$ref: "#/definitions/Document"
400:
description: "Invalid input. See response for details"
schema:
items:
$ref: "#/definitions/Error"
/api/documents/{document_id}:
get:
tags:
- "Documents"
summary: "Get a document with the given ID"
description: ""
security:
- firebase: []
operationId: "getDocument"
x-google-backend:
address: "(cloud function http url)/documents/"
path_translation: APPEND_PATH_TO_ADDRESS
produces:
- "application/json"
parameters:
- in: "path"
name: "document_id"
description: "ID of the document to modify"
required: true
type: "string"
responses:
200:
description: "success."
schema:
type: "array"
items:
$ref: "#/definitions/Document"
404:
description: "Document not found"
schema:
items:
$ref: "#/definitions/Error"
securityDefinitions:
firebase:
authorizationUrl: ""
flow: "implicit"
type: "oauth2"
x-google-issuer: "https://securetoken.google.com/%%GOOGLE_PROJECT_ID%%"
x-google-jwks_uri: "https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken#system.gserviceaccount.com"
x-google-audiences: "%%GOOGLE_PROJECT_ID%%"
definitions:
(a lot of type definitions)
This works with the POST endpoint without any problems.
The problem is with the GET REST endpoint where the path variable is not passed correctly to the backend.
As in https://cloud.google.com/endpoints/docs/openapi/openapi-extensions I tried to add the x-google-backend parameter as in the swagger api above. (path_translation: APPEND_PATH_TO_ADDRESS).
However this does not work.
I get an Unauthorized Error (403) as the cloud function is not hit by the endpoints frontend.
Currently I use an ugly workaround without the path_translation parameter which translates the google endpoints path variable to a query parameter in the cloud function backend with the same name. I.e. in the backend the url /documents?document_id=xyz is called.
(What I try to achieve is to pass the call with the backend url /documents/{document_id})
Does anyone know how to configure path based parameters correctly so that they are passed correctly to the cloud function backend?
Thank you in advance.
Regards,
Sebastian
TL;DR:
I assume that your 403 error isn't the correct error. It should be a 404, but because the endpoint is unknown, I guess that 403 is answered.
Cloud Endpoint is frustrating about this behavior. With the path_translation: APPEND_PATH_TO_ADDRESS, you think that your final called address will be /documents/{document_id}, but NO. The full openAPI path is append to your backend address, in your case: /documents/api/documents/{document_id}
That's why the endpoint doesn't exist and you should have a 404 (and not a 403).
For more details, you can have a look to this page.
Note: I'm in relation with Google team on this topic, and it will take time before having an update on this behavior.
I have trouble with accessing S3 via Api Gataway. I subscribe the following template:
/s3:
get:
produces:
- application/json
parameters:
- name: "key"
in: "query"
required: false
type: "string"
responses:
"200":
description: 200 response
x-amazon-apigateway-integration:
credentials:
Fn::GetAtt:
- ApiRole
- Arn
requestParameters:
- integration.request.path.key: "method.request.querystring.key"
uri: "arn:aws:apigateway:eu-west-1:s3:path/{key}"
consumes:
- application/json
produces:
- application/json
responses:
default:
statusCode: '200'
passthroughBehavior: when_no_match
httpMethod: GET
type: aws
But when I check the work:
I get an error:
Execution failed due to configuration error: Illegal character in path at index 35: https://s3-eu-west-1.amazonaws.com/{key}
Thu Dec 13 22:46:03 UTC 2018 : Method completed with status: 500
Probably my querystring is not overridden in the integration request. But I can't figure out how to do it right.
I don't know for sure if this will work, but you are probably wanting to change that {s3} pathParam to a {proxy+} which transforms that pathParam into a /'*' wildcard.
See https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-method-settings-method-request.html
Good luck!
I'm trying to upload a file from SwaggerUI and receive it at Flask back end request.files is empty. I don't care about the original file name.
My Swagger configuration is :
parameters:
- name: file
in: formdata
required: false
type: file
responses:
'200':
description: Firmware updated
content:
application/json:
schema:
$ref: '#/definitions/UpdateResponse'
requestBody:
content:
multipart/form-data:
schema:
type: string
format: binary
description: log binary file
My app.py file look like that :
app.add_url_rule(
'/updates/update/',
view_func=Update.as_view('updates_update'),
methods=['POST']
)
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0', port=5000
And the implementation of POST method is the following:
class Update(MethodView):
def post(self):
"""
resets software and defaults
---
tags:
- system
summary: resets software and defaults
operationId: updateupdate
description: resets defaults
parameters:
- name: file
in: formdata
required: false
type: file
responses:
'200':
description: Firmware updated
content:
application/json:
schema:
$ref: '#/definitions/UpdateResponse'
requestBody:
content:
multipart/form-data:
schema:
type: string
format: binary
description: log binary file
'400':
description: 'update system not found'
"""
data = {
"id" : 0,
"status" : "refused",
"reason" : "battery_low"
}
print('Update Items')
print('request.method', request.method)
print('request.args', request.args)
print('request.form', request.form)
print('request.files', request.files)
All the prints are returning empty lists.
==> Any idea of what's going on?
I think, that Flask returning an empty answer in a case, when in your uploading form was not set a name.
I don't know how to change code in your case, I know, that in pure html you have to change (it is wrong code) <input type="file"> to <input type="file" name="upfile"> - it is right code.
Have you try
request.data
I see an answer here