I'm coding a photo web application using mongoengine, flask, connexion and swagger 2.0 to define my API. The idea is to have an API about the photographers who post the photos and an other API about the photos.
I'm defining my yaml file for the photo API. We have two methods post( to upload a photo) and get using the id of the photo to retrieve it.
swagger: '2.0'
info:
title: Photo service API
version: "0.1"
paths:
/photo:
post:
tags:
- "Photo"
operationId: photo_service.post_photo
summary: Add a new photo
consumes:
- multipart/form-data
parameters:
- name: photo_name
in: formData
description: "file to upload"
required: true
type: string
- name: photo_file
in: formData
description: "file to upload"
required: true
type: file
responses:
201:
description: New photo added
schema:
properties:
photo_file:
type: object
description: Information about the photo
409:
description: Photo already exists
/photo/{photo_id}:
get:
tags:
- "Specific photo"
operationId: photo_service.get_photo
summary: Get specific photo.
produces:
- image/png
parameters:
- $ref: '#/parameters/photo_id'
responses:
200:
description: Return the requested photo.
schema:
type: file
404:
description: Photo does not exist.
The Photo class is pretty simple for test purposes:
class Photo(Document):
photo_name = StringField(max_length=120, required=True)
photo_file = ImageField()
I've coded the following functions which corresponds to the functions called in Operation Id field of the Yaml file:
def get_photo(photo_id):
try:
ph = mongo_get_photo(photo_id)
except (Photo.DoesNotExist, InvalidId) as e:
return 'Not Found', 404
except pymongo.errors.ServerSelectionTimeoutError as sste:
return 'Mongo unavailable', 503
return ph
def post_photo(photo_name,photo_file):
try:
ph = mongo_add (photo_name,photo_file)
return 'Created', 201, {'location': '/photo/' + str(ph.id)}
except pymongo.errors.ServerSelectionTimeoutError as sste:
return 'Mongo unavailable', 503
And since i'm using mongodb as a data base. I'm using a wrapper to make request to my db:
#robustify.retry_mongo
def mongo_add(photo_name,photo_file):
ph = Photo(photo_name = photo_name)
ph.photo_file.put(photo_file)
ph.save()
return ph
#robustify.retry_mongo
def mongo_get_photo(photo_id):
ph = Photo.objects(id=ObjectId(photo_id)).first()
photo = ph.photo_file.read()
return photo
My problem is that the POST method seems to be working. I'm creating a 200 code response whenever i try to post a png picture. But the get method doesn't work. It seems to be a problem with the way mongodb handles the file. Is it a problem with the way i'm reading the image file in the function mongo_get_photo ? Does anyone have a clue please ?
Would it be better to store the image as a string by converting it in base 64 before ?
Thanks !
Related
I am working on a REST API defined by an OpenAPI specification document; one thing that is currently missing is the use of Links to define the connection options between endpoints, and I can't figure out how to specify what I want.
For example, imagine a POST /widgets endpoint that returns a 201 Created response, and sets the URI of the created widget in the Location response header. My definition is currently something like this:
paths:
/widgets:
post:
operationId: addWidget
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AddWidget'
responses:
'201':
headers:
Location:
$ref: '#/components/headers/Location'
'400':
$ref: '#/components/responses/BadRequest'
/widgets/{widgetId}:
get:
operationId: getWidgetById
parameters:
- $ref: '#/components/parameters/WidgetId'
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/Widget'
The OpenAPI documentation (https://swagger.io/docs/specification/links/) shows how to get the raw value of the Location header, but that's a URI, and the body of the 201 response is empty e.g.
links:
getWidgetById:
operationId: getWidgetById
parameters:
employeeId: '$response.headers.location'
So how can I extract e.g. a GUID from Location: https://my.api.com/widgets/3a32d587-56ca-4dea-81b4-01ed9cdf41c0/ to insert that into the employeeId parameter for the link?
I'm trying to post a request through AWS API gateway with postman.
I have aws IAM on and it appears to be working. I can send a request with nothing in the body I get a 200 response back from my lambda function.
I would like to send a binary file in the body however whenever I add anything, form-data, raw or binary data to the body of the request I get this message:
{
"message": "The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.\n\nThe Canonical String for this request should have been\n'POST\ ..."
}
I have tried adding
image/png and application/octect-stream to Binary Media Types
and also tried specifying a model with just one field in it and that doesn't work either.
Any help would be much appreciated. I tried posting on AWS forums but there is no "post", "ask a question" or "create" button to actually ask a question, so back to old trusty stackoverflow.
Cheers. Mitch.
Edit API gateway setup:
openapi: 3.0.1
info:
title: AlarmEndpoints
version: '2019-03-02T03:22:39Z'
servers:
- url: https://602wer34n1.execute-api.ap-southeast-2.amazonaws.com/{basePath}
variables:
basePath:
default: /dev
paths:
/alarm/message:
post:
responses:
'200':
description: 200 response
content:
application/json:
schema:
$ref: '#/components/schemas/Empty'
security:
- sigv4: [
]
x-amazon-apigateway-integration:
uri: arn:aws:apigateway:ap-southeast-2:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-southeast-2:962000000000:function:alarm_message/invocations
responses:
default:
statusCode: '200'
passthroughBehavior: when_no_match
httpMethod: POST
contentHandling: CONVERT_TO_TEXT
type: aws
components:
schemas:
Empty:
title: Empty Schema
type: object
securitySchemes:
sigv4:
type: apiKey
name: Authorization
in: header
x-amazon-apigateway-authtype: awsSigv4
x-amazon-apigateway-policy:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
AWS: arn:aws:iam::962000000000:user/alarm_user
Action: execute-api:Invoke
Resource: arn:aws:execute-api:ap-southeast-2:962000000000:*/*/*
Lambda Function:
import json
import os
import boto3
def lambda_handler(event, context):
var = os.environ.get("a_variable")
return {
'statusCode': 200,
'body': json.dumps({'message': 'Hello from Lambda 2!',
'echo': event,
'data': var})
}
I have an OpenAPI v3 specification file with the following (showing just fragments):
paths:
/global/name:
get:
description: Some description
tags:
- Global settings
operationId: getGlobalSettingsName
responses:
# Response code
'200':
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/globalSettingsName'
components:
schemas:
globalSettingsName:
type: object
properties:
name:
type: integer
description: 'ID'
example: 1
required:
- name
but the server response is:
{
"name": "somestring"
}
Note the name property type is integer and in the server response, it is a string (on purpose) but dredd request passes (success).
Doesn't dredd check for response property types?
I redefined the response as string (not JSON):
responses:
# Response code
'200':
description: Successful response
content:
application/json:
schema:
type: string
and dredd doesn't complain about either.
I even changed the property of the schema:
globalSettingsName:
type: object
properties:
www:
type: string
description: 'some description'
example: 'somestring'
required:
- www
And same (success) result when it is expected to fail.
Aren't these validation supported by dredd? Am I using specification wrong?
It results that the current version (8.0.5) only supports example value in content: https://github.com/apiaryio/dredd/issues/1281
So I'm having problem with how I pass my data.
This is my WCF rest method
[WebInvoke(UriTemplate = "/GetCustomFieldValues",
Method = "POST",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Wrapped)]
Dictionary<string, Object> GetCustomFieldValues(string pwaUrl, string projectUid, List<string> customFieldNames);
On Swagger this is my declaration for it
/GetCustomFieldValues:
post:
parameters:
- name: Param
in: body
description: The custom field value you want to get
required: true
schema:
$ref: "#/definitions/GetCustomFieldValues"
responses:
200:
description: Getting custom field value using name
summary: GetCustomFieldValues
description: GetCustomFieldValues
operationId: GetCustomFieldValues
definitions:
GetCustomFieldValues:
type: object
properties:
pwaUrl:
type: string
projectUid:
type: string
customFieldNames:
type: array
items:
type: string
When I used my Advanced Rest Client or Powershell the method works, the only difference is how I assemble the body.
For Swagger I need to use a definition since I can't do multiple parameters on body.
I'm also putting this at the top of the swagger
schemes:
- http
consumes:
- application/json
produces:
- application/json
Weird thing about WCF,
CORS needs to be both Global.asx and web.config, this works on mine
CORS enabled but response for preflight has invalid HTTP status code 404 when POSTing JSON
This question already has answers here:
In Swagger, how to define an API that consumes a file along with a schema parameter?
(3 answers)
Closed 2 years ago.
I'm using Swagger 2.0 and I have a problem to send multiple post parameters. I have a swagger error Operation cannot have a body parameter and a formData parameter and I don't know how to fix it. In my definition I have a body parameter and this parameter need a JSON format but a side I have other parameter like files to upload and filename.
How can I do to send body and formData parameters both ?
Here is the web service definition :
/updateDatas:
post:
summary: Upadate datas
description: |
Update datas
consumes:
- multipart/form-data
produces:
- application/json
parameters:
- name: firstFileName
in: formData
description: First file name.
required: true
type: string
- name: secondFileName
in: formData
description: Second file name.
required: true
type: string
- name: datas
in: body
description: Json object informations.
required: true
schema:
$ref: '#/definitions/Datas'
- name: firstFile
in: formData
description: First file .jpg
required: true
type: file
- name: clientFile
in: formData
description: Second file .jpg
required: true
type: file
tags:
- Application
responses:
'200':
description: Uploaded
schema:
$ref: '#/definitions/Upload'
'401':
description: Unauthorized Bad Token
According to the swagger specifications see, type:body and type:formData cannot exist together for the same operation.
One way to resolve the problem is to set "datas" as form parameter with the type "file". Here is an example:
parameters:
- name: petId
in: path
description: ID of pet to update
required: true
type: integer
format: int64
- name: additionalMetadata
in: formData
description: Additional data to pass to server
required: false
type: string
- name: file
in: formData
description: file to upload
required: false
type: file
Ref: https://github.com/swagger-api/swagger-codegen/blob/master/modules/swagger-codegen/src/test/resources/2_0/petstore.yaml#L257
UPDATE: body parameters and form parameters cannot co-exist: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#parameterObject
Body - The payload that's appended to the HTTP request. Since there can only be one payload, there can only be one body parameter. The name of the body parameter has no effect on the parameter itself and is used for documentation purposes only. Since Form parameters are also in the payload, body and form parameters cannot exist together for the same operation.