Working with OpenAPI, Swagger Tools & Python
Recently I was involved in the enhancement of a Customer Account deactivation process.
Existing processes would identify accounts that qualify for deactivation based on specific business triggers/logic built into application batch jobs. Once batch processing completes for a given run, a file of account numbers would be manually sent to the respective team to action the deactivation requests through the Customer Management online/live system.
To enable for closer to real-time deactivations, and eliminate the need for manual intervention, the process was targeted for enhancement, with the aim of eliminating manual processing. Development of an API, which supports an endpoint call to deactivate accounts, had just been released by an internal team and it made sense to plug into the API to perform these deactivations programmatically.
I wasn’t involved in the development of the API, but had to understand how to implement the integration in the existing process and ensure automated deactivations worked as expected.
The application team that developed the API had published the “swagger.yaml
” (OpenAPI 2.0
) spec, which was readily accessible via the Intranet. From here on, the learning journey began.
By gaining a basic understanding of the OpenAPI spec and Swagger tools, I was able to generate a Python Client that would allow programmatic access and integration within existing batch infrastructure.
The aim of this article is to cover some learnings by taking a step back and starting from the creation of very basic OpenAPI 2.0 spec “swagger.yaml
” that mimics the “real” API Spec. After creation of the spec, details of the procedures/tools used throughout the different stages are also explained.
API Solution Requirements and Assumptions
To cover off on what we’re trying to achieve, we start by defining the requirements and scoping the solution.
API Requirements
Primary Function: Deactivate Customer Account
Endpoint: localhost:8080
Protocol: http
Inputs: Customer Account Identifier (cust_id)
Response Codes: [200: Successfully deactivated, 400: Error]
API Specification Type: OpenAPI 2.0
Authentication Method: API Key
Server: python-flask (requests managed by connexion)
Client: python
Assumptions
Endpoint Protocol (HTTP)
The python-flask test server that will run on our local machine is setup for http. In a real production environment, https must be used. If you chose to run the local test server using https, generation of OpenSSL certificates/private key would be required.
Further details re Flask and https, can be found here.
API Authentication
Setting up an API Authentication Server is out of scope. In other words, for examples used, there is no Authentication server to handle authentication requests, so any keys/credentials transmitted from Client to the API Endpoint will be accepted by the Endpoint.
Backend Databases Objects
The code required by the API python-flask server to physically apply backend database operations is out of scope.
Solution Architecture
Existing Infrastructure
Enhanced Infrastructure
Overview of Activities Covered
- Install Swagger Editor
- Using Swagger Editor
- Create OpenAPI 2.0 API Spec (.yaml)
- Generate python-flask-server module
- Generate python-client modules for testing on your local machine - Test the python-flask-server using Web UI Server
- Test the python-client with python source code
Install Swagger Editor
Swagger Editor is an interactive Web-based tool that allows us to build an API in conformance with the OpenAPI spec, and also provides the ability to generate respective Client/Server modules.
Start by creating a directory on your local machine to store components required for the whole project, I’m using C:\Apps\deact_api
A docker version of the Swagger Editor and can be installed as outlined here:
docker pull swaggerapi/swagger-editor
docker run -d -p 8085:8080 swaggerapi/swagger-editor
If you are not familiar with using Docker, you can use the “live” version hosted at https://editor.swagger.io/
Examples that follow will assume that you are using Docker.
After running the above Docker commands, a Docker container hosting the Editor can be accessed at:
http://localhost:8085
Accessing this URL brings up the Web interface with a default “Petstore” sample API spec as shown below.
Using Swagger Editor to Create OpenAPI Spec
With the Editor now open:
Select → File → Clear Editor
By referencing the OpenAPI spec syntax guidelines (https://swagger.io/docs/specification/basic-structure/), and our requirements in mind, we can go about creating the API Spec as per below.
Copy and paste the following spec into the Swagger Editor.
swagger: "2.0"
info:
description: API for Deactivating Customer Accounts
version: "0.1"
title: Deactivate Customer Account
host: localhost:8080
basePath: "/"
schemes:
- http
consumes:
- application/json
produces:
- application/json
security:
- Authorization-Header: []
paths:
/customer/deactivate/{cust_id}:
put:
tags:
- "Deactivate Customer"
summary: "Deactivate Customer Account"
operationId: deactivate
parameters:
- name: cust_id
in: path
required: true
type: string
responses:
200:
description: "Customer Deactivated"
400:
description: Request Failed.
securityDefinitions:
Authorization-Header:
type: apiKey
name: Authorization
in: header
Swagger Editor parses the spec (with each edit made) and will return syntax errors/warnings (if any).
Select File → Save as YAML and save the output file to your local machine. I’ve saved to the location previously mentioned, as C:\Apps\deact_api\swagger.yaml
.
Generate python-flask-server Module
To generate our Python Flask Server, select → Generate Server → python-flask
Swagger will generate a zip file which contains the python-flask server components. Save the zip file locally, I’ve saved to C:\Apps\deact_api\python-flask-server-generated.zip
Extract the zip file to the same folder it resides in. Once the unzip is done, you should have the following sub-directory, which holds the server module source code:C:\Apps\deact_api\python-flask-server
Open a Windows Cmd prompt and enter the following:
C:\Users>cd c:\Apps\deact_api\python-flask-serverc:\Apps\deact_api\python-flask-server> pip3 install -r requirements.txtc:\Apps\deact_api\python-flask-server> python3 -m swagger_server
The following output should be displayed, which tells us that our flask server is running locally (http://localhost:8080
) and listening on port 8080
:
* Serving Flask app “__main__” (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)
Test the python-flask-server Using Web UI
Logon to the server WebUI at http://localhost:8080/ui
.
Click on the “PUT
” method to expand it.
Click on “Authorise
” and enter any API Key.
We have no authentication servers setup, so anything is fine. I’ve entered dummy key 11111222222
.
Click “Authorise
”.
Click on “try it out
”.
Enter a cust_id
of 12345
as shown below and click on “Execute
”.
The API Server should return the following:
Response Code : 200
Response Body : Response body “do some magic”
Let’s take a look at the python-flask server module code and see if we can validate this is correct.
Source code for the python-flask-server module (swagger_server), resides at:
C:\Apps\deact_api\python-flask-server\swagger_server
When we execute the command to run the Server
(python3 -m swagger_server
), the module’s __main__ .py
is invoked (source code shown below).
C:\Apps\deact_api\python-flask-server\swagger_server\__main__.py
----------------------------------------------------------------#!/usr/bin/env python3import connexionfrom swagger_server import encoderdef main():
app = connexion.App(__name__, specification_dir='./swagger/')
app.app.json_encoder = encoder.JSONEncoder
app.add_api('swagger.yaml', arguments={'title': 'Deactivate Customer Account'})
app.run(port=8080)if __name__ == '__main__':
main()
Some of the key elements of the code are highlighted in bold.
The above code shows us that when main()
is triggered,
connexion
is instantiated. It manages requests to/from the Server based on the API spec filespecification_dir=’./swagger/’
this tells us the folder location where the spec file for the API server resides, as generated by Swagger Editor. This is a relative location, i.e,./swagger
tells us that the sub-directory hosting theswagger.yaml
file is relative toC:\Apps\deact_api\python-flask-server\swagger_server
, so we would expect to find the generated spec atC:\Apps\deact_api\python-flask-server\swagger_server\swagger\swagger.yaml
Let’s take a look at the code in the .yaml
file.
swagger.yaml
--------------------------------------------------------------paths:
/customer/deactivate/{cust_id}:
put:
tags:
- "Deactivate Customer"
summary: "Deactivate Customer Account"
operationId: "deactivate"
parameters:
- name: "cust_id"
in: "path"
required: true
type: "string"
responses:
"200":
description: "Customer Deactivated"
"400":
description: "Request Failed"
x-swagger-router-controller: "swagger_server.controllers.deactivate_customer_controller"
With reference to the connexion
documentation links:
https://connexion.readthedocs.io/en/latest/routing.html
https://connexion.readthedocs.io/en/latest/routing.html#endpoint-routing-to-your-python-views
and the spec above, we learn that that an invocation of
http://localhost:8080/customer/deactivate/{cust_id}
maps to:
operationId
deactivate
at the API Endpoint.
We also learn that,
x-swagger-router-controller
with a value of:
swagger_server.controllers.deactivate_customer_controller
refers to the “python view
”, (aka controller), located at:
C:\Apps\deact_api\python-flask-server\swagger_server\controllers\deactivate_customer_controller.py
.
The controller code is shown below.
deactivate_customer_controller.py
-----------------------------------------------------------------import connexion
import sixfrom swagger_server import utildef deactivate(cust_id): # noqa: E501
"""Deactivate Customer Account# noqa: E501:param cust_id:
:type cust_id: str:rtype: None
"""
return 'do some magic!'
By looking at the deactivate
function, we see that this is where you would “plugin” code for the server to handle the deactivations. At the moment, it’s basically an empty function that returns ‘do some magic!
’. This is the same message we received in our response body earlier on, so this indicates the server is working as expected.
Generate our Python Client
With the API server now successfully up and running, we can start to generate our python client so that we can programmatically make the necessary call to API server.
Back to our Swagger Editor:
Select Generate Client → python.
A zip file will be generated.
Save the zip file locally, I’ve saved it to my project folder:C:\Apps\deact_api\python-client-generated.zip
Extract contents and once done, the code modules can be found at: C:\Apps\deact_api\python-client
Open a Command Prompt
and change directory to root of the extracted folder:cd C:\Apps\deact_api\python-client
Then run:python3 setup.py install
Test Python Client
The following Python code can be used to do basic testing. Please note the API server credentials are hard-coded within the code and this is just for demonstration purposes only. Do not do this in Production.
from __future__ import print_function
import time
import swagger_client
from swagger_client.rest import ApiException
from pprint import pprint
configuration = swagger_client.Configuration()
configuration.api_key['Authorization'] = '11111222222'
configuration.host = "http://localhost:8080"
api_instance = swagger_client.DeactivateCustomerApi(swagger_client.ApiClient(configuration))
cust_id = '12345'
try:
# Deactivate Customer Account
req = api_instance.deactivate(cust_id)
status_code = api_instance.api_client.last_response.urllib3_response.status
status_msg = api_instance.api_client.last_response.data
print(status_code, status_msg)
except ApiException as e:
print("Exception when calling DeactivateCustomerApi->deactivate: %s\n" % e)
If we execute the above code, we get:
200 “do some magic!”
This response is as expected, and proves the client successfully made the call to the API Dectivation Endpoint.
Where to from Here?
Now that we have a mock server and a working client, we can start putting together the logic we need to manage the deactivations. You can also be confident that your Client will work correctly when you’re ready to make calls to the “real/Production” endpoint.
You may be interested in using the spec with Postman. There are tools available that allow you to convert from the OpenAPI Swagger format to a Postman Collection.
For further reading on development with Swagger tools, check out the link at https://swagger.io/docs/.