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

API Requirements

Assumptions

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

Image for post
Image for post

Enhanced Infrastructure

Image for post
Image for post

Overview of Activities Covered

  • 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

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.

Image for post
Image for post

Using Swagger Editor to Create OpenAPI Spec

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

Image for post
Image for post

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

Image for post
Image for post

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

Click on the “PUT” method to expand it.

Image for post
Image for post

Click on “Authorise” and enter any API Key.

Image for post
Image for post

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”.

Image for post
Image for post

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,

  • connexionis instantiated. It manages requests to/from the Server based on the API spec file
  • specification_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 the swagger.yaml file is relative to
    C:\Apps\deact_api\python-flask-server\swagger_server, so we would expect to find the generated spec at
    C:\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:

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 six
from 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

Back to our Swagger Editor:

Select Generate Client → python.

Image for post
Image for post

A zip file will be generated.

Image for post
Image for post

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

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?

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/.

Written by

Primarily a Learner/Coder with interests in Python, Cloud Technologies, Security and Automation. Pandas munching on Bamboo sticks give me the “Giggles” :))

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store