I'm trying to build a simple API with Swagger/Flask/Connexion, however the example on their GitHub does not work, even after installing connexion[swagger-ui] as specified here
pip3 install 'connexion[swagger-ui]'
Here is my helloworld-api.yaml:
openapi: "3.0.0"
info:
title: Hello World
version: "1.0"
servers:
- url: http://localhost:9090/swagger
paths:
/greeting:
get:
summary: Generate greeting
description: Generates a greeting message.
operationId: hello.get_greeting
responses:
200:
description: greeting response
content:
text/plain:
schema:
type: string
example: "hello dave!"
parameters:
- in: query
name: name
required: true
schema:
type: string
example: "dave"
description: Name of the person to greet.
and my hello.py
#!/usr/bin/env python3
import connexion
def get_greeting(name: str) -> str:
return f'Hello {name}'
if __name__ == '__main__':
app = connexion.FlaskApp(__name__, specification_dir='openapi/')
app.add_api('helloworld-api.yaml', arguments={'title': 'Hello World Example'})
app.run(port=8080)
hello.py is in the root directory and helloworld-api.yaml is in a folder called openapi/. Navigating to a 0.0.0.0:8080/swagger in a browser returns this:
{
"detail": "The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.",
"status": 404,
"title": "Not Found",
"type": "about:blank"
}
Same goes for the get method I've specified. Any ideas? I've been banging my head against a brick wall over this.
You are using the wrong patch , to access Swagger UI you must access {base_path}/ui/ in your case 0.0.0.0:8080/ui/
You can check the docs https://connexion.readthedocs.io/en/latest/quickstart.html#the-swagger-ui-console
Related
I'm trying to use GCP API Gateway to create a single endpoint for a couple of my backend services (A,B,C,D), each with their own path structure. I have the Gateway configured for one of the services as follows:
swagger: '2.0'
info:
title: <TITLE>
description: <DESC>
version: 1.0.0
schemes:
- https
produces:
- application/json
paths:
/service_a/match/{id_}:
get:
summary: <SUMMARY>
description: <DESC>
operationId: match_id_
parameters:
- required: true
type: string
name: id_
in: path
- required: true
type: boolean
default: false
name: bool_first
in: query
- required: false
type: boolean
default: false
name: bool_Second
in: query
x-google-backend:
address: <cloud_run_url>/match/{id_}
deadline: 60.0
responses:
'200':
description: Successful Response
'422':
description: Validation
This deploys just fine. But when I hit the endpoint gateway_url/service_a/match/123, it gets routed to cloud_run_url/match/%7Bid_%7D?id_=123 instead of cloud_run_url/match/123.
How can I fix this?
Editing my answer as I misunderstood the issue.
It seems like the { are getting leaked from your configuration as ASCII code, so when you call
x-google-backend:
address: <cloud_run_url>/match/{id_}
deadline: 60.0
it doesn't show the correct ID.
So this should be a leak issue from your yaml file and you can approach this the same way as in this thread about using path params
After implementing an api gateway in front of my app engine instances I got a problem stating that the request was blocked because of the CORS header. After searching online I found out that API gateway doesn't provide a way to set the CORS policy, however it also "overwrite" the header sent by my single back-end application. Does I need to implement a load balancer to set an additional Header or there is a way to avoid the overwrite?
Example of API:
paths:
"/login":
post:
description: "Login into the service"
operationId: "login"
x-google-backend:
address: https://project-id.oa.r.appspot.com/api/v1/login
produces:
- "application/json"
responses:
200:
description: "Projects retrieved successfully"
schema:
$ref: "#/definitions/access_token"
401:
description: "Wrong password"
schema:
type: "string"
404:
description: "User not exists"
schema:
type: "string"
parameters:
- in: body
name: user
description: The user to create.
schema:
type: object
required:
- userName
properties:
userName:
type: string
firstName:
type: string
lastName:
type: string
After a lot of trials, I found a simpler solution than implementing a load balancer in front of the gateway:
To use the CORS headers provided by the back-end application it is enough to add a OPTIONS request to the API to avoid headers being overwritten. So, given the login API I just need to add the request like this:
paths:
"/login":
post:
description: "Login into the service"
operationId: "login"
x-google-backend:
address: https://project-id.oa.r.appspot.com/api/v1/login
produces:
- "application/json"
responses:
200:
description: "Projects retrieved successfully"
schema:
$ref: "#/definitions/access_token"
401:
description: "Wrong password"
schema:
type: "string"
404:
description: "User not exists"
schema:
type: "string"
parameters:
- in: body
name: user
description: The user to create.
schema:
type: object
required:
- userName
properties:
userName:
type: string
firstName:
type: string
lastName:
type: string
options:
description: "Cors associated request to login"
operationId: "login cors"
x-google-backend:
address: https://project-id.oa.r.appspot.com/api/v1/login
responses:
200:
description: "Allow"
401:
description: "Cors not allowed"
I am writing a python service with connexion. I have access to the swagger gui of the service via localhost:<port>/ui. However, when I enter localhost:<port> in the browser without the /ui (automatically added by connexion), I get the following message:
{
"detail": "The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.",
"status": 404,
"title": "Not Found",
"type": "about:blank"
}
Is it possible to customize this message ? In my case, I want localhost:<port> to return the following message instead:
{"message": "check /ui to have access to the Swagger UI"}
I think I found a solution. Just add the following to the openspec api:
/:
get:
operationId: path.to.function
summary: "my personlized message"
responses:
......
i have created 2 apis: system and experience for a project. The system had already been deployed into the cloudhub and running successfully. The experience api needs to invoke the system api through a router using the URL:
http://demo-insurance-system-api.us-e2.cloudhub.io
and uriparam is :customer
and queryparams are:?fname=James&lname=Butt
Its working perfectly fine.
but when i want to hit the same url from experience api's requester it gives me
ERROR 2020-05-18 01:58:15,217 [[muleinsurance-exp-api].http.requester.requestConfig.04 SelectorRunner] [event: ] org.mule.runtime.core.internal.exception.OnErrorPropagateHandler:
********************************************************************************
Message : HTTP OPTIONS on resource 'http://demo-insurance-system-api.us-e2.cloudhub.io' failed: not found (404).
Error type : HTTP:NOT_FOUND
Element : muleinsurance-experience-api-main/processors/0 # muleinsurance-exp-api:muleinsurance-experience-api.xml:17
Element XML : <apikit:router config-ref="muleinsurance-experience-api-config"></apikit:router>
(set debug level logging or '-Dmule.verbose.exceptions=true' for everything)
the onpremise testing is done in using postman and the experience url is:
http://localhost:8081/demoapi/customer?fname=James
the experience api raml is:
#%RAML 1.0
title: muleinsurance-experience-api
version: 1.0.0
traits:
client-id-required:
headers:
client_id:
type: string
client_secret:
type: string
responses:
401:
description: Unauthorized, The client_id or client_secret are not valid or the client does not have access.
404:
description: No Record found.
429:
description: The client used all of it's request quota for the current period.
500:
description: Server error ocurred
503:
description: Contracts Information Unreachable.
/demoapi:
/{searchString}:
get:
description: invokes either customer or policy request
queryParameters:
fname:
description: first name
example: Sumitra
required: false
type: string
lname:
description: Last name
example: Ojha
required: false
type: string
dob:
description: Date of Birth
example: 1/2/2003
required: false
type: string
customerID:
description: CustomerID
example: BU79786
required: false
type: string
policytype:
description: type of policy taken
example: Personal Auto
required: false
type: string
responses:
200:
body:
application/json:
example:
{"message": "connecting to System API"}
here i am adding the HTTP request xml snippet:
<choice doc:name="Choice" doc:id="444a5cd6-4aee-441a-8736-2d2fff681e2e" >
<when expression="#[attributes.uriParams.searchString == 'customer']">
<http:request method="GET" doc:name="Request" doc:id="30958d05-467a-41b1-bef3-83426359f2aa" url="http://demo-insurance-system-api.us-e2.cloudhub.io"><http:uri-params ><![CDATA[#[output application/java
---
{ customer : attributes.uriParams.searchString}]]]></http:uri-params>
<http:query-params ><![CDATA[#[output application/java
---
{ fname : vars.fname,
lname : vars.lname,
dob : vars.dob}]]]>
kindly point out where i need to improve. thanks in advance.
The problem is indicated in the error message: HTTP OPTIONS on resource 'http://demo-insurance-system-api.us-e2.cloudhub.io' failed: not found (404).
It looks like your HTTP Request in the experience API is using the HTTP Options method. The RAML indicates that the API only accepts the HTTP GET method. In the XML it should look like: <http:request method="GET" ...
I tried to check if a specific URL doesn't specified an ID
This are what I've got so far:
http://localhost:8082/orders/1234 ---- Success!
http://localhost:8082/orders/ ---- URL not found!
I want to throw and error something like "Id not specified!" not a URL not found.
Look at Validation Module
Look at the "Matches Regex" section down.
I have not tried it
Hope it may help
the believe the best approach for you is to start using RAML. you can design the endpoint in RAML and specify what error to be thrown if the request is not a match.
/{order_id}:
get:
description: Get a Order by id
responses:
200:
body:
application/json:
type: Foo
example: !include examples/Order.json
400:
body:
application/json:
type: Error
example: !include examples/Error.json
in the above RAML definition if there is no orders/{order_id} you will get a http 400 returned.
Define a custom error handling and that will return error description alongside other required data
set status code 400 in a variable, map fields as per below example.
ex:
{
"error": {
"errorCode": "NOT_FOUND",
"errorDateTime": "2019-10-10T14:00:46",
"errorMessage": "The requested API cannot be found, ID is not specified",
"errorDescription": "Description: /orders"
}
}