If you’re reading this article then you already have said this to yourself : I need to quickly build an API, with the least amount of code possible, but how ? And I’m guessing you know a bit of Python too.As DevOps Engineers we’re often faced with the need to build some automation tool to trigger this or to do that and have it exposed using an API to be used later by other apps or services ( Jira, Jenkins, services, .. )So in this tutorial we’re going to try and build us an simple automation API, and have it automate few tasks for us. And In a second article we’ll try to add HTTPS support some authentications mechanisms so it can pass security requirements of enterprises and organisations.
To build our API we will be using Python 3 programming language and we’ll use the Connexion framework to handle our HTTP requests and interactions.
if you haven't installed connexion yet you can do so by running:
$ pip install connexionlet's start with an old fashioned Hello World :
In our app folder let's create a new file helloworld.py:
import connexion
def greeting(name: str) -> str:
return 'Hello {name}'.format(name=name)
if __name__ == '__main__':
app = connexion.FlaskApp(__name__, port=8080, specification_dir='swagger/')
app.add_api('helloworld.yaml', arguments={'title': 'Hello World'})
app.run()In the code above, we have the main code that will render our specifications for the API and load the arguments, and the operation method greeting which will be executed when we run the api server.
Next, we will write the specifications of the API, for this we will create a new folder called swagger and we will add a new file swagger/helloworld.yaml:
swagger: "2.0"
info:
title: "{{title}}"
version: "1.0"
basePath: /v1.0
paths:
/greeting/{name}:
post:
summary: Generate greeting
description: Generates a greeting message.
operationId: helloworld.greeting
produces:
- text/plain;
responses:
200:
description: greeting response
schema:
type: string
examples:
"text/plain": "Hello John"
parameters:
- name: name
in: path
description: Name of the person to greet.
required: true
type: stringAnd that's it, we are ready to go :) :
To run the API server:
$ python helloworld.pyYou should be faced with a warning message saying that "The swagger_ui directory could not be found." this library is used to generate the swagger documention for the defined specifications. to fix this you can run:
$ pip install 'connexion[swagger-ui]'This UI is accessible at : http://localhost:8080/v1.0/ui
To test the endpoint that we just created you can run:
$ curl --request POST 'http://localhost:8080/v1.0/greeting/john'So what just happened ?
the connexion framework rendered our specifictions defined in our yaml file to create the API server and linked the greeting endpoint with the greeting method in the helloworld.py using the object operationId: helloworld.greeting where we passed a parameter called name to the method.
Now that we got around the basic let's do somehting that is not a Hello World, We're going to build an API that will receive a POST request with a json body and write the text in a file and then return the lines count in this file, so let's dive right to it:
In a new app folder, let's call it logmymessages, once again we'll create main file called app.py
import connexion
if __name__ == '__main__':
app = connexion.FlaskApp(__name__, port=8080, specification_dir='swagger/')
app.add_api('swagger.yaml', arguments={'title': 'Log My Messages'})
app.run()And then let's create the swagger specifications for this API:
swagger: "2.0"
info:
title: "{{title}}"
version: "1.0"
basePath: /v1.0
paths:
/log:
post:
summary: Log the messages
description: Write the recieved message to a log file.
operationId: api.log.write
produces:
- application/json;
responses:
200:
description: number of received messages
schema:
type: object
parameters:
- name: message
in: body
description: the JSON text object.
schema:
type: object
required:
- sender
- message
properties:
sender:
type: string
message:
type: stringTo define the behaviour of the end point we are going to create a new file called log.py where we will implement the operation api.log.write, to do so, we'll create a folder called api and inside this folder we will create a file log.py:
api/log.py:
from datetime import datetime
def write(message):
# GET JSON object attributes
sender = message['sender']
message = message['message']
# Write attributes to file
with open("log.txt", "a") as logfile:
logfile.write("[%s][%s] : %s\n" % (
datetime.now().strftime('%Y%m%d-%H%M%S'), sender, message))
# Count line if file
num_lines = sum(1 for line in open('log.txt'))
# Return a JSON object with the count of messages in the log file
return {
"count_messages": num_lines
}Let's run the API server and test our new app:
$ python app.pyWe will send a POST request with the content of our message to the API server:
$ curl --request POST 'http://localhost:8080/v1.0/log' \
--header 'Content-Type: application/json' \
-d '{
"message": "Hi, I am Matt. I am a radar technician",
"sender": "Matt"
}'Output:
{
"count_messages": 1
}We will send a second POST request with another message:
$ curl --request POST 'http://localhost:8080/v1.0/log' \
--header 'Content-Type: application/json' \
-d '{
"message": "I am 90% sure Matt is Kylo Ren!",
"sender": "Stormtrooper"
}'
Output:
{
"count_messages": 2
}If everything is working fine we should be able to see a new file created in our app folder called log.txt with two lines in it:
$ cat log.txt
[20210101-101001][Matt] : Hi, I am Matt. I am a radar technician
[20210101-101010][Stormtrooper] : I am 90% sure Matt is Kylo Ren!For our second operation we will try the read this file and get back the messages logged in this file by send a GET request to our /log endpoint:
In the swagger/swagger.yaml file we will add:
get:
summary: Read the received messages
description: Read the recieved message to a log file.
operationId: api.log.read
produces:
- application/json;
responses:
200:
description: number of received messages
schema:
type: objectAnd in the api/log.py file we will add:
def read():
messages = []
with open('log.txt', "r") as file:
for line in file.read().splitlines():
timestamp = [x.split(']')[0]
for x in line.split('[') if ']' in x][0]
sender = [x.split(']')[0]
for x in line.split('[') if ']' in x][1]
message = line.split(':')[1]
messages.append({
"timestamp": timestamp,
"sender": sender,
"message": message
})
return messagesAnd that's it!
we just created a second endpoint for our api and implemented it's operation method:
Let's re-run the api server now by executing:
$ python app.py$ curl --request GET 'http://localhost:8080/v1.0/log'Output:
[
{
"message": " Hi, I am Matt. I am a radar technician",
"sender": "Matt",
"timestamp": "20210101-101001"
},
{
"message": " I am 90% sure Matt is Kylo Ren!",
"sender": "Stormtrooper",
"timestamp": "20210101-101010"
}
]