Test assignment for Backend developer
Simple product inventory management system API built with Python and FastAPI
Deploying and testing made easy with use of Docker containerization.
Create .env file
cp .env.example .env
and set own environment variables.
Build containers defined in Dockerfile and docker-compose.yaml:
docker compose up -d --buildAll tests are integrational and require DB connection.
Tests will run on postgres default database instead of app database.
Defined in .env:
TEST_DATABASE_URL=postgresql://app:app@app_db:5432/postgresdocker compose exec api pytestAll test cases are defined in src/tests/test_inventory.py
# Destroy running containers
docker compose down
# and rebuild them
docker compose up -d --build├── src
│ ├── inventory
│ │ ├── crud.py # CRUD functions
│ │ ├── models.py # SQLAlchemy models
│ │ ├── schemas.py # Pydantic models
│ │ └── router.py # router
| |
│ ├── tests
│ │ └── test_inventory.py # unit tests
│ │
│ ├── main.py # projects main file
│ ├── database.py # db connection and session
│ └── config.py # global configs
│
├── .env # Environment variables
├── Dockerfile
└── docker-compose.yaml
Documentation with Swagger UI is located in http://localhost:8000/docs
alternatively ReDoc http://localhost:8000/redoc
POST : http://localhost:8000/inventory/vendors
Body
{
"name": "Galata headquarters",
"description": "Ottoman Brotherhood main quarters",
"location": "Constantinople",
"opening_hours": "09:30:00",
}Response
{
"name": "Galata headquarters",
"description": "Ottoman Brotherhoods main quarters",
"location": "Constantinople",
"opening_hours": "09:30:00",
"id": 1
}GET : /inventory/vendors/?skip=0&limit=100
Use path parameters for pagination:
skip : number of vendors to skip, starting from the beginning
limit : number of vendors to retrieve
Response
[
{
"name": "Galata headquarters",
"description": "Isntanbul Brotherhood main quarters",
"location": "Istanbul city",
"opening_hours": "09:30:00",
"id": 1,
"items": []
},
{
"name": "Alamut",
"description": "Assasin's Global HQ",
"location": "Qazvin Plain",
"opening_hours": "09:00:00",
"id": 2,
"items": []
}
]GET : /inventory/vendors/{vendor_id}/
Response
{
"name": "Galata headquarters",
"description": "Isntanbul Brotherhood main quarters",
"location": "Istanbul city",
"opening_hours": "10:00:00",
"id": 1,
"items": [
{
"name": "Chai",
"description": "Turkish style tea",
"price": 250.0,
"quantity": 420,
"id": 1,
"vendor_id": 6
},
{
"name": "Kahve",
"description": "Turkish style coffee",
"price": 420.0,
"quantity": 69,
"id": 2,
"vendor_id": 6
}
]
}PUT : /inventory/vendors/{vendor_id}/
Body
{
"location": "Constantinople",
"opening_hours": "11:30:00"
}Response
{
"name": "Galata headquarters",
"description": "Isntanbul Brotherhood main quarters",
"location": "Constantinople",
"opening_hours": "11:30:00",
"id": 1
}DELETE : /inventory/vendors/{vendor_id}/
204 No ContentPOST : http://localhost:8000/inventory/vendors/{vendor_id}/items/
Body
{
"name": "Kahve",
"description": "Turkish style coffee",
"price": 420.0,
"quantity": 69
}Response
{
"name": "Kahve",
"description": "Turkish style coffee",
"price": 420.0,
"quantity": 69,
"id": 1,
"vendor_id": 1
}GET : /inventory/items/?skip=0&limit=100
Use path parameters for pagination:
skip : number of items to skip, starting from the beginning
limit : number of items to retrieve
Response:
[
{
"name": "Kahve",
"description": "Turkish style coffee",
"price": 420.0,
"quantity": 69,
"id": 1,
"vendor_id": 1
},
{
"name": "Chai",
"description": "Turkish style tea",
"price": 150.0,
"quantity": 69,
"id": 2,
"vendor_id": 1
}
]GET : /inventory/items/{item_id}/
Response
{
"name": "Chai",
"description": "Turkish style tea",
"price": 150.0,
"quantity": 69,
"id": 2,
"vendor_id": 1
}PUT : /inventory/items/{item_id}/
Body
{
"price": 430.0,
"description": "Kyrgyz style tea"
}Response
{
"name": "Chai",
"description": "Kyrgyz style tea",
"price": 430.0,
"quantity": 69,
"id": 2,
"vendor_id": 1
}DELETE : /inventory/items/{item_id}/
204 No Content