- Description
- Database models and design
- REST API
- Authentication and Authorization
- SMS Notifications
- Tests
- API Documentation
- Contributions and License
This project is about building a Python REST API service with very minimalistic databases for customers and users.
The REST API implementation uses the awesome Django REST framework. It should support CRUD operations on the database models instances.
Authentication and authorization of users is done using OpenID Connect.
The underlying database server used for this service is PostgreSQL. The database models are designed to be very simple.
The customers have very few details.
- first_name: customer's first name.
- second_name: customer's second name.
- code: a 5-digit alphanumeric code unique to each customer.
- phone: customer's phone number, which is used for sending updates when an order is placed in their name. Phone number should be entered in international format, e.g. +254712345678
- id: autogenerated and used as the Primary Key.
Similarly the orders model is kept simple.
- item: name of the item to be bought. An order can only have one item in it.
- quantity: the number of units of the item being bought.
- price_per_unit: the price of each unit of the item.
- status: the status of the order. This is an enum with the following values;
Pending
,Closed
,Cancelled
. The default isPending
. - customer_code: the code of the customer to whom the order belongs. The code is used internally for bookkeeping practices like relating an order to a customer.
- time_placed: hold the time the order was placed. It is autofilled when an order is created.
- customer: a Foreign Key that associates a specific customer to an order instance. The relationship between an order and a customer is many-to-one. Many orders can belong to a single customer.
- id: autogenrated and used as Primary Key
This was supposed to be a model for items that can be ordered. Out of the interes of time I did not pursue this idea for long. In due time I will revisit it. The idea is that there should be a dropdown field of items for a user to select from when placing an order.
The API serive is built using Django REST framework.
The browsable API can be accessed here.
For detailed description of supported HTTP methods, how to invoke them, and expected responses, check out the API docs
.
/api/v1/
: the API root, has links to the customers and orders endpoints.
/api/v1/customers/
: list all customer instances belonging to the user./api/v1/customers/
: create a new customer instance./api/v1/customers/<pk>/
: perform an action on the specified customer instance.
/api/v1/orders/
: list all order instances belonging to the user./api/v1/orders/
: create a new order instance./api/v1/orders/<pk>/
: perform an action on the specified order instance
/api/v1/api-auth/authenticate/
: login to the API using OpenID Connect and go to the home page./api/v1/api-auth/logout/
: logout from the API and return to the home page.
/api/schema/
: download the schema./api/v1/docs/
: view the redoc./api/v1/swagger-docs/
: view the Swagger formatted docs.
OpenID Connect is used to provide authentication to API. The OpenID Connect provider used is Google.
To implement this feature I used a third party library, Mozilla OIDC, which makes the task remarkably easy.
To enforce authorization, I used the permission_classes
of Django REST to ensure only authenticated users can create, read, update or delete the models.
Having the IsAuthenticated
as the only permission class is security flaw. That means any authenticated user can perform any kind of operation on the customer/order instances even if they did not create them. This will be problematic in a setting where the API is used by many parties.
I implemented the object level permissions
, but there is a subtle bug that is causing that mechanism to fail. And so I cannot use it yet in production but the code is there. Until I figure out where the bug is, let's assume the API will be used by only one person.
When an order is placed, an SMS notification is sent to the related customer using the Africa's Talking gateway.
For testing purposes I am using the Sandbox app, therefore the messages are delivered to the sandbox app.
The screenshot below shows it works as expected.
Unittests followed the usual Django testing procedures. For coverage tests I used the coverage
module in combination with django-coverage-plugin
.
This API's schema and documentation has been generated in accordance to the OpenAPI specifications.
Please visit these links for in-depth details:
- Redocs: View Redocs
- Swagger Docs: View Swagger docs
- API Schema: Download Schema
This work was done as part of the screening test for the backend developer role at Savannah Informatics, and is protected by the MIT License, all rights reserved.