This project allows users to view and create news posts with pictures and comments. See detailed OpenAPI documentation of NewsFeed project without starting the project.
Features:
- FastAPI (Python 3.10) - framework for API
- OAuth2 - for authentication with JSON Web Token
- PostgreSQL - database with indexes for post and user id to improve the speed of operations
- pgAdmin for management
- SQLite - database for tests
- SqlAlchemy - for ORM
- MinIO - for S3 storage
- Pytest - for tests (Reusable Pytest fixtures and new test database for each test)
- Docker Compose - for running application (containers for application and MinIO storage with volumes)
- Uvicorn - for ASGI web server
- CI/CD pipeline: Github action for pytest and docker image build before pull request in master
- Poetry - for packaging and dependency management
- Fastapi-pagination - for pagination in endpoint for getting all posts
- Python-dotenv - for reading configuration variables and secret data
- Loguru - for logging errors
- Pre-commit - black, flake8 and isort formate code before each commit
Clone github repository
git clone https://github.com/KatyaLubyankina/NewsFeed.git
Change directory to NewsFeed
cd NewsFeed
Start Docker Desktop and run this command to start project in docker containers
docker compose up --build
You can change secret data (secret keys for encryption and s3 storage) in src/secrets folder. The API documentation will be at http://localhost/docs. API endpoints will be available at http://localhost/. MinIO object storage - http://localhost:9000 - use minioadmin as login and password. You can change these values with 'MINIO_ROOT_USER' and 'MINIO_ROOT_PASSWORD' environment variables (use .env_vars file).
This project comes with Pytest and a few Pytest fixtures for new user, login, post and comment. Tests are located in /tests directory and the fixtures are available in tests/conftest.py. To run all tests use:
pytest
To check coverage use:
pytest --cov
Some examples of API endpoints.
Request to create new user
curl -X 'POST' \
'http://localhost/user' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"username": "test",
"email": "[email protected]",
"password": "test",
"avatar_url": ""
}'
Response:
{
"username": "test",
"email": "[email protected]"
}
Authentication is required for this endpoint. Request:
curl -X 'POST' \
'http://localhost/post' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"image_url": "test url",
"caption": "test caption",
"creator_id": 1
}'
Response:
{
"id": 1,
"image_url": "test url",
"caption": "test caption",
"timestamp": "2023-06-29T11:02:39.927456",
"user": {
"username": "test"
},
"comments": []
}
Authentication is required for this endpoint. Request:
curl -X 'POST' \
'http://localhost/comment' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QiLCJleHAiOjE2ODgwMjY2NDF9.0BlUSHzdzR4lOdghXxuwxijc1E1aZQYJ_lOOUFWbNwY' \
-H 'Content-Type: application/json' \
-d '{
"username": "test",
"text": "test comment",
"post_id": 1
}'
Response:
{
"text": "test comment",
"username": "test",
"post_id": 1,
"id": 1,
"timestamp": "2023-06-29T11:04:21.418987"
}
Request:
curl -X 'GET' \
'http://localhost/post/all' \
-H 'accept: application/json'
Response:
[
{
"id": 1,
"image_url": "test url",
"caption": "test caption",
"timestamp": "2023-06-29T11:02:39.927456",
"user": {
"username": "test"
},
"comments": [
{
"text": "test comment",
"username": "test",
"timestamp": "2023-06-29T11:04:21.418987"
}
]
}
]
Request:
curl -X 'GET' \
'http://localhost/comment/all/1' \
-H 'accept: application/json'
Response:
[
{
"text": "test comment",
"username": "test",
"post_id": 1,
"id": 1,
"timestamp": "2023-06-29T11:04:21.418987"
}
]
Authenticated user can delete post only if user created this post. Request:
curl -X 'GET' \
'http://localhost/post/delete/1' \
-H 'accept: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QiLCJleHAiOjE2ODgwMjY2NDF9.0BlUSHzdzR4lOdghXxuwxijc1E1aZQYJ_lOOUFWbNwY'
Response:
"ok"