Skip to content

Commit

Permalink
beta v.1.0.0 - DB config, list terms and GitHub Actions (#13)
Browse files Browse the repository at this point in the history
* support: add command RUN to Makefile

* support: mkdocs config and define new docs pages

* test: config of TestClient interface to handle integration tests of the service

* support: ignoring .env files

* support: Make command to run loading produciton variables

* support: reformat logs for uvicorn server

* support: database connection & initial config

* support: pydantic schemas for glossary Terms

* support: env file loading module

* feat: Enum for exceptions of business rules

* feat: serializer to normalize API responses

* feat: endpoint that lists & search Test Terms from glossary database

* docs: list & filter test Terms

* docs: add link to docs on GH Page

* docs: remove unsed config from mkdocs

* Update execute-python-tests.yml

* support: update dependencies inside requirements files

* support: creates an env file

from GitHub secrets, creates an env file

* fix: removed extra colons

* fix: double-step to use gh secret

* support: make help command skips a line before printing results

* support: using market GH action to create env file

* fix: GHA secret typo error
  • Loading branch information
thiagojacinto authored May 6, 2022
1 parent 8550131 commit b484702
Show file tree
Hide file tree
Showing 25 changed files with 507 additions and 27 deletions.
14 changes: 13 additions & 1 deletion .github/workflows/execute-python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,28 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }} # See docs: https://github.com/actions/checkout#checkout-pull-request-head-commit-instead-of-merge-commit

- name: Set up Python 3.10
uses: actions/setup-python@v2
with:
python-version: "3.10"

- name: Creates .staging.env file
uses: SpicyPizza/[email protected]
with:
envkey_DATABASE_CONNECTION_STRING: ${{ secrets.DB_CONNECTION_STRING }}
file_name: .staging.env
fail_on_empty: true

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Execute tests with pytest
run: |
python -m pytest
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,6 @@ cython_debug/
#.idea/

# End of https://www.toptal.com/developers/gitignore/api/python

# .env files
*.env
27 changes: 22 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,37 @@ install: # install project dependencies from requirements.txt file

test: # execute all tests
@echo "$(COLOUR_GREEN)Executing tests ...$(COLOUR_END)"
python -m pytest
python -m pytest --verbose

lint: # use linter
@echo "$(COLOUR_GREEN)Running lint process ...$(COLOUR_END)"
python -m black $(APP_SOURCE_CODE_DIR) ; \
python -m isort --profile black $(APP_SOURCE_CODE_DIR) ; \
python -m autopep8 --in-place --recursive --verbose $(APP_SOURCE_CODE_DIR)

help: # list all Makefile commands
@echo "$(COLOUR_BLUE)These are all the avalaible commands ...$(COLOUR_END)"
grep ':' Makefile
run: # starts uvicorn server with auto reload @ port 8880
@echo "$(COLOUR_GREEN)Starting server ...$(COLOUR_END)"
uvicorn testglossary.main:app --reload --port=8880

go-prod: # Run in Production environment. Starts uvicorn server with auto reload
@echo "$(COLOUR_GREEN)Starting server ...$(COLOUR_END)"
PRODUCTION_READY=true uvicorn testglossary.main:app --reload --log-config=log_conf.yaml

gh-deploy: # builds and deploy MkDocs documentation style to GitHub Pages
mkdocs gh-deploy --verbose --strict --remote-branch="support/gh-pages"

docker-build: GET_NOW := $(shell date +%s)
docker-build: # builds a new container image
@echo "$(COLOUR_RED)Building a Docker image ...$(COLOUR_END)"
TAG_NAME="$(DOCKER_HUB_USERNAME)/$(DOCKER_HUB_REPOSITORY):$(GET_NOW)" ; \
docker build -t $${TAG_NAME} .
docker build -t $${TAG_NAME} .

#: #########################################
#: #### Help - Makefile for TestGlossary API
#: #########################################

help: # list all Makefile commands
@echo "$(COLOUR_BLUE)These are all the avalaible commands ...$(COLOUR_END)"
@echo ""
@grep ': #' Makefile

2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
# test-glossary-project
Building a glossary of testing terms based on ISTQB's glossary

Documentation is available at https://thiagojacinto.github.io/test-glossary-project/
41 changes: 36 additions & 5 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
# Welcome to TestGlossary projects
# Welcome to TestGlossary

This project uses mkdocs. For full documentation how-to visit [mkdocs.org](https://www.mkdocs.org).
This is the official documentation for the TestGlossary project.

If you have ideas, feel free to get in touch or open an Issue to the project [repository](https://github.com/thiagojacinto/test-glossary-project/issues).

## Commands

* `make run` - start uvicorn server at port `8880`
* `make install` - install project dependencies from requirements.txt file
* `make test` - execute all tests
* `make test` - execute all tests
* `make lint` - use linter
* `make lint` - use linter
* `make help` - lists all Makefile commands

### Auxiliary commands
* `make go-prod` - start uvicorn server with production environment configuraiton
* `make gh-deploy` - uses the built-in tool of MkDocs (docs [here](https://www.mkdocs.org/user-guide/deploying-your-docs/)) to deploy documenation to the specific branch that is responsible to handle the GitHub page: [thiagojacinto.github.io/test-glossary-project/](https://thiagojacinto.github.io/test-glossary-project/)
* `make docker-build` - build a new Docker image and tag it with timestamp.

## Project layout

Makefile # Makefile with simplifications for project maintenance
mkdocs.yml # MkDocs configuration file.
Makefile # Makefile with simplifications for project maintenance
mkdocs.yml # MkDocs configuration file.
log_conf.yaml # Logging configuraiton file.
.env # Important environment variables like DATABASE_CONNECTION_STRING
docs/
index.md # The documentation homepage.
... # Other markdown pages, images and other documentation related files.
Expand All @@ -21,3 +32,23 @@ This project uses mkdocs. For full documentation how-to visit [mkdocs.org](https
testglossary/
... # Main application files must be placed under this directory, following futher structure.

### .env files

The development was done considering two environments: production and staging. So, at root directory must exist at least **two files** named:
```
- .env
- .staging.env
```

The content of these files must contain the following variables and its values:
```
- DATABASE_CONNECTION_STRING # desired database connection string.
# An example could be a DB-as-a-Service implementation like:
# Heroku Postgres DBaaS, ElephantSQL
```

## Why documentation?

Documentation matters.

To help with this project goal, TestGlossary uses MkDocs. For full documentation how-to visit [mkdocs.org](https://www.mkdocs.org). We `highly` recommend you to.
9 changes: 9 additions & 0 deletions docs/service/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Service

TestGlossary offers an API Service to be used, and documentation for that is also available in well stablished formats - once the server is up and running:

- `OpenAPI v3`: accessing `/api/openapi.json`
- `swagger`: accessing `/api/docs`
- `redocs`: accessing `/api/redocs`

While you may find some discussion here about the funcionalities, feel free to visit the the links above.
28 changes: 28 additions & 0 deletions docs/service/v1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# API: Version 1

This page lists the features implemented in this version of the Test-Glossary service API.

## `api/v1/healthcheck`

Endpoint that returns an OK status response just to give an overall status feedback.

### Allowed Methods

```
GET api/v1/healthcheck
```

## `api/v1/terms`

Service that concentrate interactions between user and test Terms database.

- returns a paginated list of all registered glossary test Terms;
- allow filtering of specific test Terms, returning a paginated list as well, if any match.

### Allowed Methods
```
GET api/v1/terms
GET api/v1/terms?page=2&terms_per_page=5
GET api/v1/terms/search/{specific-term}
GET api/v1/terms/search/{specific-term}?page=1&terms_per_page=5
```
29 changes: 29 additions & 0 deletions log_conf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
version: 1
disable_existing_loggers: False
formatters:
default:
"()": uvicorn.logging.DefaultFormatter
format: '%(asctime)s %(levelname)s %(name)s - %(message)s'
access:
"()": uvicorn.logging.AccessFormatter
format: '%(asctime)s %(levelname)s %(name)s - %(message)s'
handlers:
default:
formatter: default
class: logging.StreamHandler
stream: ext://sys.stderr
access:
formatter: access
class: logging.StreamHandler
stream: ext://sys.stdout
loggers:
uvicorn.error:
level: INFO
handlers:
- default
propagate: no
uvicorn.access:
level: INFO
handlers:
- access
propagate: yes
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
site_name: TestGlossary
theme: readthedocs
theme: readthedocs
4 changes: 4 additions & 0 deletions prod-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ uvloop==0.16.0
watchgod==0.7
websockets==10.2
zipp==3.7.0

SQLAlchemy==1.4.34
psycopg2==2.9.3
greenlet==1.1.2
7 changes: 7 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ asgiref==3.5.0
attrs==21.4.0
autopep8==1.6.0
black==22.1.0
certifi==2021.10.8
charset-normalizer==2.0.12
click==8.0.4
fastapi==0.75.0
ghp-import==2.0.2
greenlet==1.1.2
h11==0.13.0
httptools==0.3.0
idna==3.3
Expand All @@ -22,6 +25,7 @@ packaging==21.3
pathspec==0.9.0
platformdirs==2.5.1
pluggy==1.0.0
psycopg2==2.9.3
py==1.11.0
pycodestyle==2.8.0
pydantic==1.9.0
Expand All @@ -31,12 +35,15 @@ python-dateutil==2.8.2
python-dotenv==0.19.2
PyYAML==6.0
pyyaml_env_tag==0.1
requests==2.27.1
six==1.16.0
sniffio==1.2.0
SQLAlchemy==1.4.34
starlette==0.17.1
toml==0.10.2
tomli==2.0.1
typing_extensions==4.1.1
urllib3==1.26.9
uvicorn==0.17.5
uvloop==0.16.0
watchdog==2.1.6
Expand Down
Empty file.
15 changes: 15 additions & 0 deletions testglossary/database/connection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

from testglossary.internal.config import configuration

# from FastAPI docs' https://fastapi.tiangolo.com/tutorial/sql-databases/#create-the-sqlalchemy-parts

engine = create_engine(
url=configuration.DATABASE_CONNECTION_STRING, pool_pre_ping=True, echo=True
)

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()
25 changes: 25 additions & 0 deletions testglossary/database/entities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from sqlalchemy import Column, ForeignKey, Integer, String

from testglossary.database.connection import Base


class Term(Base):
"""Test term to be explained"""

__tablename__ = "terms"

id = Column(Integer, primary_key=True, index=True)
name = Column(String)
acronym = Column(String)
definition = Column(String)
version = Column(Integer)
language_id = Column(Integer, ForeignKey("languages.id"), index=True)


class Language(Base):
"""Choose language of test term translation"""

__tablename__ = "languages"

id = Column(Integer, primary_key=True, index=True)
language = Column(String, unique=True)
25 changes: 25 additions & 0 deletions testglossary/database/interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from sqlalchemy.orm import Session

from testglossary.database.entities import Term


def get_terms(db: Session, page: int = 0, results_per_page: int = 30):
"""
Return all test terms, paginated
"""
return db.query(Term).offset(page).limit(results_per_page).all()


def get_term_by_name(
db: Session, term_name: str, page: int = 0, results_per_page: int = 5
):
"""
Search for a test term by its name
"""
return (
db.query(Term)
.where(Term.name.ilike("%{}%".format(term_name)))
.offset(page)
.limit(results_per_page)
.all()
)
26 changes: 26 additions & 0 deletions testglossary/internal/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from os import getenv

from pydantic import BaseSettings


class Configuration(BaseSettings):
"""
Configuration & settings wrapper using Pydantic's BaseSettings
"""

APP_NAME: str = "TestGlossary API"
DATABASE_CONNECTION_STRING: str = "PROVIDE A VALID DB CONNECTION STRING"

class Config:
"""
Sub class for handling .env file reading
"""

env_file = ".staging.env"
env_file_encoding = "utf-8"


if getenv("PRODUCTION_READY") == "true":
configuration = Configuration(_env_file=".env")
else:
configuration = Configuration()
4 changes: 4 additions & 0 deletions testglossary/internal/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
terms: dict = {
"NOT_FOUND": "It was not possible to find any test term with that filter.",
"EMPTY_LIST": "The test term list is currently empty.",
}
21 changes: 21 additions & 0 deletions testglossary/internal/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from pydantic import BaseModel

from testglossary.models.schemas import Term


class Paginated_ouput(BaseModel):
"""
Paginated response output serializer.
"""

result: list | dict | None
page: int = 0
offset: int = 0


class Paginated_terms_list(Paginated_ouput):
"""
Specialized paginated response for a Test Terms list
"""

result: list[Term]
Loading

0 comments on commit b484702

Please sign in to comment.