-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 72f56ef
Showing
30 changed files
with
579 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
.venv | ||
.vscode | ||
|
||
**/__pycache__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
APIS_DIR = $(CURDIR)/apis | ||
CRUD_DIR = $(CURDIR)/crud | ||
|
||
.PHONY: help | ||
help: ## Show a list of available commands | ||
@fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/\\$$//' | sed -e 's/##//' | ||
|
||
.PHONY: build-apis | ||
build-apis: ## build apis testing | ||
docker compose -f $(APIS_DIR)/docker-compose.yml build | ||
|
||
.PHONY: run-apis | ||
run-apis: ## run apis testing | ||
docker compose -f $(APIS_DIR)/docker-compose.yml up | ||
|
||
.PHONY: clean-apis | ||
clean-apis: ## clean apis | ||
docker compose -f $(APIS_DIR)/docker-compose.yml down | ||
|
||
.PHONY: build-crud | ||
build-crud: ## build crud | ||
docker compose -f $(CRUD_DIR)/docker-compose.yml build | ||
|
||
.PHONY: run-crud | ||
run-crud: ## run crud | ||
docker compose -f $(CRUD_DIR)/docker-compose.yml run backend | ||
|
||
.PHONY: run-crud-tests | ||
run-crud-tests: ## run crud tests | ||
docker compose -f $(CRUD_DIR)/docker-compose.yml down | ||
docker compose -f $(CRUD_DIR)/docker-compose.yml run test | ||
|
||
.PHONY: run-crud-clean | ||
clean-crud: ## build run crud | ||
docker compose -f $(CRUD_DIR)/docker-compose.yml down |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# Prueba Técnica - Desarrollador Backend | ||
|
||
## Descripción | ||
|
||
El código de este repositorio es una prueba técnica con el perfil de backend. El lenguaje elegido para realizar la prueba ha sido Python. | ||
|
||
## Pruebas | ||
|
||
Para facilitar la ejecución de las pruebas se creado un [Makefile](./Makefile) con los comandos necesarios. | ||
Se puede comprobar el catálogo de comandos con: | ||
|
||
```bash | ||
make help | ||
``` | ||
|
||
Como pre-requisito se necesita tener instalado [Docker](https://docs.docker.com/get-docker/) y [Docker Compose](https://docs.docker.com/compose/install/). | ||
|
||
### Tratamiento de datos en API | ||
|
||
El código Python que se ha desarrollado para la prueba de la `petstore` se encuentra en la carpeta [apis](./apis/). | ||
|
||
- En la carpeta de [petstore](./apis/petstore) se encuentra el código para hacer las llamadas a la api de `petstore` con las distintas clases y métodos necesarios. | ||
- El fichero [test.py](./apis/test.py) contiene el código con el flujo de llamadas requerido en el ejercicio. | ||
|
||
Para ejecutar el código se puede hacer con los siguientes comandos: | ||
|
||
```bash | ||
make build-apis | ||
make run-apis | ||
``` | ||
|
||
Para terminar se puede limpiar el entorno con: | ||
|
||
```bash | ||
make clean-apis | ||
``` | ||
|
||
### Desarrollo de una API (CRUD) | ||
|
||
En la carpeta [crud](./crud/) se encuentra el código para el desarrollo de una API con las operaciones CRUD. | ||
Concretamente, el código está en el fichero [backend.py](./crud/backend.py). | ||
Para crear la API se ha utilizar [Flask](https://flask.palletsprojects.com/en/2.0.x/) y como base de datos se ha utilizado [MongoDB](https://www.mongodb.com/). | ||
|
||
Para levantar el servicio se ha montado un entorno con [Docker Compose](https://docs.docker.com/compose/) que tiene los siguientes servicios: | ||
|
||
- backend: servicio con la API desarrollada en Python. | ||
- mongo: base de datos MongoDB. | ||
- mongo-express: interfaz web para la base de datos MongoDB. | ||
- test: servicio para ejecutar los tests de aceptación de la API con [Behave](https://behave.readthedocs.io/en/stable/). | ||
|
||
Para levantar el entorno se puede hacer con los siguientes comandos: | ||
|
||
```bash | ||
make build-crud | ||
make run-crud | ||
``` | ||
|
||
Con docker-compose se expone la api en el puerto `8888` de localhost: | ||
|
||
- `http://localhost:8888` | ||
|
||
Los endpoints asociados son: | ||
|
||
- GET /health | ||
- GET /products | ||
- GET /products/{id} | ||
- POST /products | ||
- PUT /products/{id} | ||
- DELETE /products/{id} | ||
|
||
Si se quiere ejecutar los tests de aceptación se puede hacer con: | ||
|
||
```bash | ||
make run-crud-tests | ||
``` | ||
|
||
Finalmente, si se quiere limpiar el entorno se puede hacer con: | ||
|
||
```bash | ||
make clean-crud | ||
``` | ||
|
||
De esta manera se ejecuta la feature [product.feature](./crud/features/product.feature) que contiene un set básico (happy path) de tests de aceptación de la API. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
**/__pycache__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
FROM python:3.10.13-slim-bullseye | ||
|
||
COPY ./requirements.txt /root/requirements.txt | ||
RUN pip install -r /root/requirements.txt | ||
|
||
COPY ./petstore /root/petstore | ||
COPY ./test.py /root/test.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
version: '3.8' | ||
|
||
services: | ||
api: | ||
image: api-test-image | ||
build: | ||
context: . | ||
dockerfile: Dockerfile | ||
command: ["python", "/root/test.py"] |
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
URL="https://petstore.swagger.io/" | ||
API_VERSION="v2" |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import requests | ||
from ..config.api import URL, API_VERSION | ||
from os import path | ||
|
||
|
||
|
||
PET_ENDPOINT="pet" | ||
|
||
def get_pet_by_status(status): | ||
response = requests.get(path.join(URL,API_VERSION,PET_ENDPOINT,"findByStatus"), params={"status": status}) | ||
return response | ||
|
||
def list_sold_pets(): | ||
response = get_pet_by_status("sold") | ||
pet_tuple = {} | ||
for pet in response.json(): | ||
try: | ||
pet_tuple[pet["id"]] = pet["name"] | ||
except KeyError: | ||
print(f"Pet {pet['id']} does not have a name") | ||
return pet_tuple | ||
|
||
class SoldPets: | ||
def __init__(self, sold_pets): | ||
self.sold_pets = sold_pets | ||
|
||
def count_repeated_pet_names(self): | ||
pet_names = [] | ||
for pet in self.sold_pets: | ||
pet_names.append(self.sold_pets[pet]) | ||
return {pet_name: pet_names.count(pet_name) for pet_name in pet_names} | ||
|
||
class Pet: | ||
def __init__(self, id, category, name, photoUrls, tags, status): | ||
self.id = id | ||
self.category = category | ||
self.name = name | ||
self.photoUrls = photoUrls | ||
self.tags = tags | ||
self.status = status | ||
|
||
def __str__(self): | ||
return f"Pet: {self.name}" | ||
|
||
def __repr__(self): | ||
return f"Pet: {self.name}" | ||
|
||
def create_pet(self): | ||
payload = { | ||
"id": self.id, | ||
"category": self.category, | ||
"name": self.name, | ||
"photoUrls": self.photoUrls, | ||
"tags": self.tags, | ||
"status": self.status | ||
} | ||
response = requests.post(path.join(URL,API_VERSION,PET_ENDPOINT), json=payload) | ||
return response | ||
|
||
def get_pet(self): | ||
response = requests.get(path.join(URL,API_VERSION,PET_ENDPOINT,self.id)) | ||
return response | ||
|
||
|
||
|
||
def update_pet(self): | ||
payload = { | ||
"id": self.id, | ||
"category": self.category, | ||
"name": self.name, | ||
"photoUrls": self.photoUrls, | ||
"tags": self.tags, | ||
"status": self.status | ||
} | ||
response = requests.put(path.join(URL,API_VERSION,PET_ENDPOINT), json=payload) | ||
return response | ||
|
||
def delete_pet(self): | ||
response = requests.delete(path.join(URL,API_VERSION,PET_ENDPOINT,self.id)) | ||
|
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import requests | ||
from ..config.api import URL, API_VERSION | ||
from os import path | ||
|
||
USER_ENDPOINT="user" | ||
|
||
|
||
class User: | ||
def __init__(self, username, password, email, firstName, lastName, phone, userStatus): | ||
self.username = username | ||
self.password = password | ||
self.email = email | ||
self.firstName = firstName | ||
self.lastName = lastName | ||
self.phone = phone | ||
self.userStatus = userStatus | ||
|
||
|
||
def __str__(self): | ||
return f"User: {self.username}" | ||
|
||
def __repr__(self): | ||
return f"User: {self.username}" | ||
|
||
def create_user(self): | ||
payload = { | ||
"id": 0, | ||
"username": self.username, | ||
"firstName": self.firstName, | ||
"lastName": self.lastName, | ||
"email": self.email, | ||
"password": self.password, | ||
"phone": self.phone, | ||
"userStatus": self.userStatus | ||
} | ||
# join paths URL, API_VERSION, USER_ENDPOINT | ||
request_path = path.join(URL,API_VERSION,USER_ENDPOINT) | ||
|
||
response = requests.post(request_path, json=payload) | ||
return response | ||
|
||
def get_user(self): | ||
response = requests.get(path.join(URL,API_VERSION,USER_ENDPOINT,self.username)) | ||
return response | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
requests | ||
pytest |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
from petstore.user.user import User | ||
from petstore.pet.pet import Pet, SoldPets, list_sold_pets | ||
|
||
|
||
print(">>>>>>>> Create User >>>>>>>>") | ||
|
||
user = User(username="test_user", password="password", email="[email protected]", | ||
firstName="Test", lastName="User", phone="1234567890", userStatus=0) | ||
response = user.create_user() | ||
print(f"Create user response: {response.json()}") | ||
assert response.status_code == 200 | ||
|
||
print(">>>>>>>> Get created user info >>>>>>>>") | ||
|
||
response = user.get_user() | ||
assert response.status_code == 200 | ||
assert response.json()["username"] == "test_user" | ||
assert response.json()["firstName"] == "Test" | ||
assert response.json()["lastName"] == "User" | ||
|
||
print(f"User info: {response.json()}") | ||
|
||
|
||
print(">>>>>>>> Create sold pets >>>>>>>>") | ||
pet_a = Pet(id=0, category={"id": 0, "name": "TestJordi"}, name="TestJordi", photoUrls=["string"], tags=[{"id": 0, "name": "TestJordi"}], status="sold") | ||
pet_b = Pet(id=0, category={"id": 0, "name": "TestJordi"}, name="TestJordi", photoUrls=["string"], tags=[{"id": 0, "name": "TestJordi"}], status="sold") | ||
response = pet_a.create_pet() | ||
assert response.status_code == 200 | ||
print(f"Create pet response: {response.json()}") | ||
response = pet_b.create_pet() | ||
assert response.status_code == 200 | ||
print(f"Create pet response: {response.json()}") | ||
|
||
print(">>>>>>>> List sold pets >>>>>>>>") | ||
sold_pets = list_sold_pets() | ||
|
||
for pet in sold_pets.items(): | ||
print(f"Sold pet: {pet}") | ||
|
||
print(">>>>>>>> Count pet repeated names >>>>>>>>") | ||
|
||
sold_pets_object = SoldPets(sold_pets) | ||
pet_repeated_names = sold_pets_object.count_repeated_pet_names() | ||
|
||
print(f"Count of pet names: {pet_repeated_names}") | ||
|
||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
**/__pycache__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
FROM python:3.10.13-slim-bullseye | ||
|
||
COPY ./requirements.txt /root/requirements.txt | ||
RUN pip install -r /root/requirements.txt | ||
|
||
COPY ./backend.py /root/backend.py | ||
COPY ./endpoints /root/endpoints | ||
WORKDIR /root | ||
|
||
EXPOSE 8888 |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
|
||
from flask import Flask | ||
from flask_restful import Api | ||
from endpoints.products import Products | ||
from endpoints.health import Health | ||
|
||
app = Flask(__name__) | ||
api = Api(app) | ||
|
||
api.add_resource(Products, '/products', '/products/<id>') | ||
api.add_resource(Health, '/health') | ||
|
||
if __name__ == '__main__': | ||
app.run(host='0.0.0.0', port=8888, debug=True) |
Oops, something went wrong.