Skip to content

Commit 25c98b5

Browse files
committed
feat: news resources
1 parent a5459b8 commit 25c98b5

File tree

17 files changed

+221
-96
lines changed

17 files changed

+221
-96
lines changed

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: help venv install start create-database
1+
.PHONY: help venv install start tests create-database
22

33
VENV_DIR = .venv
44
VENV_PYTHON = $(VENV_DIR)/bin/python
@@ -11,6 +11,7 @@ help:
1111
@echo " * venv -> Creates virtual environment (.venv)"
1212
@echo " * install -> Installs dependencies from src/requirements.txt"
1313
@echo " * start -> Starts the REST API (Python/Flask)"
14+
@echo " * tests -> Run all tests (PyTest)"
1415
@echo " * create-database -> Initializes the database"
1516
@echo ""
1617

@@ -23,5 +24,8 @@ install:
2324
start:
2425
cd src && ../$(VENV_FLASK) run
2526

27+
tests:
28+
$(VENV_PYTHON) -m pytest
29+
2630
create-database:
2731
PYTHONPATH=src $(VENV_PYTHON) -c "from app.settings.database.init_db import init_db; init_db()"

README.md

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ git push origin main
119119
| PUT | `/api/cards/<card_id>` | Update a specific card |
120120
| DELETE | `/api/cards/<card_id>` | Remove card |
121121

122+
122123
- Example Payload for Cards
123124

124125
```json
@@ -151,4 +152,42 @@ All API responses follow the following pattern:
151152
```
152153
> **Notes:**
153154
> - The `error` field contains the error object or list of errors.
154-
> - The `message` field describes the result of the operation or the reason for the error.
155+
> - The `message` field describes the result of the operation or the reason for the error.
156+
157+
## Project Progress
158+
159+
### Decks API
160+
- [ ] List all decks (GET /api/decks)
161+
- [ ] Create a new deck (POST /api/decks)
162+
- [ ] Validate title is required and not empty
163+
- [ ] Validate tags are list of strings (optional)
164+
- [ ] Validate description max length 500 chars
165+
- [ ] Get deck details (GET /api/decks/<id>)
166+
- [ ] Validate UUID format for `<id>`
167+
- [ ] Update deck information (PUT /api/decks/<id>)
168+
- [ ] Validate fields if present (partial update)
169+
- [ ] Validate title uniqueness (optional)
170+
- [ ] Delete a deck (DELETE /api/decks/<id>)
171+
- [ ] Validate UUID format for `<id>`
172+
173+
### Cards API
174+
- [ ] Add card to deck (POST /api/decks/<deck_id>/cards)
175+
- [ ] Validate question is required and not empty
176+
- [ ] Validate answer is required
177+
- [ ] Validate tags are list of strings (optional)
178+
- [ ] Validate deck_id is valid UUID and exists
179+
- [ ] Search all cards in a deck (GET /api/decks/<deck_id>/cards)
180+
- [ ] Validate UUID format for `<deck_id>`
181+
- [ ] Search for random card (GET /api/decks/<deck_id>/cards/random)
182+
- [ ] Validate UUID format for `<deck_id>`
183+
- [ ] Search specific card (GET /api/cards/<card_id>)
184+
- [ ] Validate UUID format for `<card_id>`
185+
- [ ] Update card (PUT /api/cards/<card_id>)
186+
- [ ] Validate fields if present
187+
- [ ] Validate question uniqueness (optional)
188+
- [ ] Remove card (DELETE /api/cards/<card_id>)
189+
- [ ] Validate UUID format for `<card_id>`
190+
191+
### Testing
192+
- [ ] Unit tests for Decks endpoints
193+
- [ ] Unit tests for Cards endpoints

pytest.ini

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[pytest]
2+
markers =
3+
unit: Testes de unidade — verificam funções ou classes isoladas, sem dependências externas.
4+
integration: Testes de integração — validam a interação entre múltiplos componentes ou camadas do sistema.
5+
controllers: Testes de controller — asseguram o formato e as chaves presentes no payload das respostas da API.
6+
services: Testes de serviço — confirmam se os campos retornados têm os tipos de dados esperados.

src/app/blueprints/api/cards.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def get_cards_by_deck(deck_uuid):
1616
def get_random_card(deck_uuid): pass
1717

1818
@cards.route('/cards/<string:card_uuid>', methods=['GET'])
19-
def get_cards(deck_uuid): pass
19+
def get_cards(card_uuid): return controller.searching_specific_card(card_uuid)
2020

2121
@cards.route('/cards/<string:card_uuid>', methods=['PUT'])
2222
def update_card(deck_uuid): pass

src/app/blueprints/api/decks.py

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,23 @@
22
from src.app.controllers.decks_controller import DecksController
33
from src.app.schemas.decks_schema import request_decks_schema, request_deck_id_schema
44
from src.app.services.decks_services import decks_services
5-
from ..constants.routes import DECKS as routes
5+
from ..constants.routes import DECKS as r
66

77
decks = Blueprint('decks', __name__)
8-
controller = DecksController(
9-
request_validator=request_decks_schema,
10-
deck_services=decks_services,
11-
id_validator=request_deck_id_schema
12-
)
13-
routes = routes.get("get_all_decks")
14-
@decks.route(routes.get("URI"), methods=[routes.get("method")])
15-
def get_all_decks():
16-
return controller.get_all_decks()
8+
controller = DecksController(request_validator=request_decks_schema, deck_services=decks_services,id_validator=request_deck_id_schema)
179

18-
@decks.route(routes.get("URI"), methods=[routes.get("method")])
19-
def create_deck():
20-
return controller.create_deck(request.get_json())
2110

22-
@decks.route(routes.get("URI"), methods=[routes.get("method")])
23-
def get_deck(deck_id):
24-
return controller.get_deck_by_id(deck_id)
11+
@decks.route(r.get("get_all_decks").get("URI"), methods=[r.get("get_all_decks").get("method")])
12+
def get_all_decks(): return controller.get_all_decks()
2513

26-
@decks.route(routes.get("URI"), methods=[routes.get("method")])
27-
def update_deck(deck_id):
28-
return controller.change_existing_deck(request.get_json(), deck_id)
14+
@decks.route(r.get("create_deck").get("URI"), methods=[r.get("create_deck").get("method")])
15+
def create_deck(): return controller.create_deck(request.get_json())
2916

30-
@decks.route(routes.get("URI"), methods=[routes.get("method")])
31-
def delete_deck(deck_id):
32-
return controller.delete_existing_deck(deck_id)
17+
@decks.route(r.get("get_deck").get("URI"), methods=[r.get("get_deck").get("method")])
18+
def get_deck(deck_id): return controller.get_deck_by_id(deck_id)
19+
20+
@decks.route(r.get("update_deck").get("URI"), methods=[r.get("update_deck").get("method")])
21+
def update_deck(deck_id): return controller.change_existing_deck(request.get_json(), deck_id)
22+
23+
@decks.route(r.get("delete_deck").get("URI"), methods=[r.get("delete_deck").get("method")])
24+
def delete_deck(deck_id): return controller.delete_existing_deck(deck_id)

src/app/controllers/cards_controller.py

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from src.app.utils.responses.response import APIResponse
2-
from werkzeug.exceptions import BadRequest
2+
from werkzeug.exceptions import BadRequest, NotFound
33
from src.app.utils.mocks.decks_mocks import mocks_responses_decks as mock
44
from src.app.services.cards_services import CardsService
55
from src.app.validators.cards_validator import CardsValidator
66
from src.app.schemas.decks_schema import request_deck_id_schema
77

8+
89
class CardsController:
910
def __init__(self):
1011
self.service = CardsService()
@@ -18,11 +19,23 @@ def get_cards_by_deck(self, id):
1819
error="Formato do UUID não é válido.",
1920
status_code=BadRequest.code
2021
)
22+
23+
verify_id_exists = self.service.verify_deck_id_exists(uuid_value)
24+
if not verify_id_exists:
25+
return APIResponse.error(
26+
message="Deck não encontrado.",
27+
error="ID inexistente na base de dados.",
28+
status_code=NotFound.code
29+
)
2130

22-
cards = self.service.get_cards_by_deck_id(uuid_value)
31+
cards, deck_name = self.service.get_cards_by_deck_id(uuid_value)
32+
2333
return APIResponse.success(
24-
message="Cards encontrados com sucesso.",
25-
data=cards,
34+
message=f"Cards encontrados com sucesso.",
35+
data={
36+
"deckName": deck_name,
37+
"cards": cards,
38+
},
2639
status_code=200
2740
)
2841

@@ -42,4 +55,27 @@ def post_cards_by_deck(self, id, body):
4255
status_code=201,
4356
data=card_added.to_dict()
4457
)
45-
58+
59+
def searching_specific_card(self, id):
60+
is_uuid, uuid_value = self.validator.check_uuid(id)
61+
if not is_uuid:
62+
return APIResponse.error(
63+
message="UUID Inválido.",
64+
error="Formato do UUID não é válido.",
65+
status_code=BadRequest.code
66+
)
67+
verify_id_exists = self.service.verify_card_id_exists(id)
68+
if not verify_id_exists:
69+
return APIResponse.error(
70+
message="ID inexistente na base de dados.",
71+
status_code=NotFound.code,
72+
error="Nenhum dado encontrado."
73+
)
74+
75+
card = self.service.get_card_by_id(uuid_value)
76+
77+
return APIResponse.success(
78+
message="Detalhes do card encontrado com sucesso.",
79+
status_code=200,
80+
data=card,
81+
)

src/app/services/cards_services.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,31 @@ def post_cards_by_deck_id(self, uuid, body):
3434
self.session.refresh(new_card)
3535
return new_card
3636

37-
3837
@handle_db_errors
3938
def get_cards_by_deck_id(self, uuid):
4039
deck = self.session.query(Deck).options(
4140
joinedload(Deck.cards)
4241
).filter(Deck.id == uuid).first()
43-
4442
if not deck:
43+
return None, None
44+
deck_infos = deck.to_dict()
45+
return deck_infos.get("cards"), deck_infos.get("title")
46+
47+
@handle_db_errors
48+
def verify_card_id_exists(self, id):
49+
result = self.session.query(Cards).filter(Cards.id == id).first()
50+
if not result:
51+
return False
52+
return True
53+
54+
@handle_db_errors
55+
def verify_deck_id_exists(self, id):
56+
result = self.session.query(Deck).filter(Deck.id == id).first()
57+
return bool(result)
58+
59+
@handle_db_errors
60+
def get_card_by_id(self, card_id):
61+
finding_card = self.session.query(Cards).filter(Cards.id == card_id).first()
62+
if not finding_card:
4563
return None
46-
47-
return deck.to_dict().get("cards")
64+
return finding_card.to_dict()
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)