diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index eace507..2ca74d1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -24,8 +24,8 @@ preparation: - echo "CLIENT_PROD_IMAGE_NAME=${IMAGE_BASE}/client:prod-${BUILD_ID}" >> context.env - echo "CLIENT_TEST_IMAGE_NAME=${IMAGE_BASE}/client:test-${BUILD_ID}" >> context.env - - echo "DOCKER_HUB_API_IMAGE=$DOCKER_HUB_USER/foodtruckers-api:${BUILD_ID}" >> context.env - - echo "DOCKER_HUB_CLIENT_IMAGE=$DOCKER_HUB_USER/foodtruckers-client:${BUILD_ID}" >> context.env + - echo "DOCKER_HUB_API_IMAGE=$DOCKER_HUB_USER/fids-api:${BUILD_ID}" >> context.env + - echo "DOCKER_HUB_CLIENT_IMAGE=$DOCKER_HUB_USER/fids-client:${BUILD_ID}" >> context.env - echo "ENV_DEV_FILE=$(echo $ENV_DEV)" >> context.env - echo "ENV_PROD_FILE=$(echo $ENV_PROD)" >> context.env @@ -83,8 +83,8 @@ test-api: - docker compose -f docker-compose.yml --env-file $ENV_DEV_FILE --profile api pull - docker compose -f docker-compose.yml --env-file $ENV_DEV_FILE --profile api up --abort-on-container-exit --renew-anon-volumes - - docker cp foodtruckers_api:/usr/src/app/coverage.xml . - - docker cp foodtruckers_api:/usr/src/app/report.xml . + - docker cp fids_api:/usr/src/app/coverage.xml . + - docker cp fids_api:/usr/src/app/report.xml . artifacts: when: always paths: @@ -152,8 +152,6 @@ deploy-prod: - docker compose -f docker-compose.yml --profile all --env-file $ENV_PROD_FILE rm -f - docker compose -f docker-compose.yml --profile all --env-file $ENV_PROD_FILE pull - docker compose -f docker-compose.yml --profile all --env-file $ENV_PROD_FILE up -d - - docker compose -f docker-compose.yml --profile all --env-file $ENV_PROD_FILE exec api python manage.py recreate_db - - docker compose -f docker-compose.yml --profile all --env-file $ENV_PROD_FILE exec api python manage.py seed_db needs: - job: deliver-dockerhub - job: preparation diff --git a/docker-compose.yml b/docker-compose.yml index 5cbe7b9..f2ca73c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ version: '3.8' services: api: - container_name: foodtruckers_api + container_name: fids_api image: ${API_IMAGE} profiles: - api @@ -26,7 +26,7 @@ services: condition: service_healthy api-db: - container_name: foodtruckers_api_db + container_name: fids_api_db build: context: ./db dockerfile: Dockerfile @@ -46,7 +46,7 @@ services: - POSTGRES_PASSWORD=${POSTGRES_PASS} client: - container_name: foodtruckers_client + container_name: fids_client image: ${CLIENT_IMAGE} profiles: - client @@ -58,4 +58,4 @@ services: api: condition: service_healthy environment: - - API_HOST=api \ No newline at end of file + - API_HOST=api diff --git a/sample-api-users/.bandit.yml b/flights-domain/.bandit.yml similarity index 100% rename from sample-api-users/.bandit.yml rename to flights-domain/.bandit.yml diff --git a/sample-api-users/.coveragerc b/flights-domain/.coveragerc similarity index 100% rename from sample-api-users/.coveragerc rename to flights-domain/.coveragerc diff --git a/sample-api-users/.gitignore b/flights-domain/.gitignore similarity index 100% rename from sample-api-users/.gitignore rename to flights-domain/.gitignore diff --git a/flights-domain/Dockerfile.prod b/flights-domain/Dockerfile.prod new file mode 100644 index 0000000..9e8ef04 --- /dev/null +++ b/flights-domain/Dockerfile.prod @@ -0,0 +1,32 @@ +# pull official base image +FROM python:3.11.2-slim-buster AS prod + +# set working directory +WORKDIR /usr/src/app + +# set environment variables +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 +ARG SECRET_KEY +ENV SECRET_KEY $SECRET_KEY + +RUN apt-get update \ + && apt-get -y install netcat gcc curl \ + && apt-get clean \ + && groupadd -g 999 python \ + && useradd -r -u 999 -g python python \ + && python -m venv /usr/src/app/.venv \ + && chown -R python:python /usr/src/app + +ENV PATH="/usr/src/app/.venv/bin:$PATH" +ENV PIP_NO_CACHE_DIR=off +USER 999 + +COPY --chown=python:python requirements.txt requirements.txt +RUN python -m pip install --upgrade pip && \ + python -m pip install -r requirements.txt + +COPY --chown=python:python . . + +# run gunicorn +CMD ["/usr/src/app/.venv/bin/gunicorn", "src.api.main:app", "--worker-class", "uvicorn.workers.UvicornWorker"] diff --git a/sample-api-users/Dockerfile.prod.dockerignore b/flights-domain/Dockerfile.prod.dockerignore similarity index 100% rename from sample-api-users/Dockerfile.prod.dockerignore rename to flights-domain/Dockerfile.prod.dockerignore diff --git a/flights-domain/Dockerfile.test b/flights-domain/Dockerfile.test new file mode 100644 index 0000000..acb5288 --- /dev/null +++ b/flights-domain/Dockerfile.test @@ -0,0 +1,18 @@ +# pull official base image +ARG BASE_IMAGE +FROM ${BASE_IMAGE} + +ENV DATABASE_TEST_URL=postgresql://postgres:postgres@api-db:5432/api_test + +# add and install requirements +COPY --chown=python:python ./requirements.test.txt . +RUN python -m pip install -r requirements.test.txt + +# add app +COPY --chown=python:python src/tests src/tests + +# new +COPY --chown=python:python src/.cicd/test.sh . +RUN chmod +x /usr/src/app/test.sh + +CMD ["/usr/src/app/test.sh"] diff --git a/flights-domain/Pipfile b/flights-domain/Pipfile new file mode 100644 index 0000000..2c45070 --- /dev/null +++ b/flights-domain/Pipfile @@ -0,0 +1,12 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +fastapi = "==0.103.2" + +[dev-packages] + +[requires] +python_version = "3.11" diff --git a/sample-api-users/entrypoint.sh b/flights-domain/entrypoint.sh similarity index 100% rename from sample-api-users/entrypoint.sh rename to flights-domain/entrypoint.sh diff --git a/flights-domain/requirements.test.txt b/flights-domain/requirements.test.txt new file mode 100644 index 0000000..b7b4a79 --- /dev/null +++ b/flights-domain/requirements.test.txt @@ -0,0 +1,10 @@ +## Testing +pytest==7.2.2 +pytest-cov==4.0.0 +pytest-xdist==3.2.0 +pytest-watch==4.2.0 +flake8==6.0.0 +black==23.1.0 +isort==5.12.0 +bandit==1.7.5 +pactman==2.3.0 \ No newline at end of file diff --git a/flights-domain/requirements.txt b/flights-domain/requirements.txt new file mode 100644 index 0000000..30a2eec --- /dev/null +++ b/flights-domain/requirements.txt @@ -0,0 +1,5 @@ +## Prod +fastapi[all]==0.103.2 +psycopg2-binary==2.9.5 +pyjwt==2.6.0 +gunicorn==20.1.0 \ No newline at end of file diff --git a/sample-api-users/setup.cfg b/flights-domain/setup.cfg similarity index 100% rename from sample-api-users/setup.cfg rename to flights-domain/setup.cfg diff --git a/flights-domain/src/.cicd/test.sh b/flights-domain/src/.cicd/test.sh new file mode 100755 index 0000000..c6205ed --- /dev/null +++ b/flights-domain/src/.cicd/test.sh @@ -0,0 +1,22 @@ +#!/bin/bash -e + + +if [ "${TEST_TARGET:-}" = "INTEGRATION" ]; then + # Execute your command here + /usr/src/app/.venv/bin/gunicorn manage:app +else + ## pytest + python -m pytest "src/tests" --junitxml=report.xml + + ## Coverage + python -m pytest "src/tests" -p no:warnings --cov="src" --cov-report xml + + + ## Linting + flake8 src --extend-ignore E221 + black src --check + isort src --check + + ## Security + # bandit -c .bandit.yml -r . +fi diff --git a/flights-domain/src/api/main.py b/flights-domain/src/api/main.py new file mode 100644 index 0000000..804fb62 --- /dev/null +++ b/flights-domain/src/api/main.py @@ -0,0 +1,22 @@ +from fastapi import FastAPI + +from src.api.models.Flight import Flight + +app = FastAPI() + +# Crear una instancia de la clase Flight +flight_instance = Flight( + id="1", + flight_code="ABC123", + status="En ruta", + origin="Ciudad A", + destination="Ciudad B", + departure_time="2023-10-09 10:00 AM", + arrival_time="2023-10-09 12:00 PM", + gate="A1", +) + + +@app.get("/flights/{id}") +async def get_flight_by_id(id: int): + return flight_instance diff --git a/flights-domain/src/api/models/Flight.py b/flights-domain/src/api/models/Flight.py new file mode 100644 index 0000000..731dd27 --- /dev/null +++ b/flights-domain/src/api/models/Flight.py @@ -0,0 +1,17 @@ +from pydantic import BaseModel + + +class Flight(BaseModel): + id: str + flight_code: str + status: str + origin: str + destination: str + departure_time: str + arrival_time: str + gate: str + + def __eq__(self, other): + if not isinstance(other, Flight): + return False + return self.id == other.id diff --git a/sample-api-users/src/config.py b/flights-domain/src/config.py similarity index 100% rename from sample-api-users/src/config.py rename to flights-domain/src/config.py diff --git a/flights-domain/src/tests/dummy_test.py b/flights-domain/src/tests/dummy_test.py new file mode 100644 index 0000000..454fab8 --- /dev/null +++ b/flights-domain/src/tests/dummy_test.py @@ -0,0 +1,13 @@ +from fastapi.testclient import TestClient + +from src.api.main import Flight, app, flight_instance + +client = TestClient(app) + + +def test_get_flight(): + response = client.get("/flights/3") + assert response.status_code == 200 + retrieved_fligt = Flight(**response.json()) + assert retrieved_fligt.id == flight_instance.id + assert retrieved_fligt.__eq__(flight_instance)