To jest działające rozwiązanie przykładu wspólnego do analizy podczas LAB1. Pokazuje technikę, regułę biznesową i testy. Własny projekt zbuduj ze startera, używając własnej domeny. Wymagania zaliczenia określa LAB1.
Kurier potrzebuje sprawdzić dostępność skrytki i zmienić jej stan. Locker Service jest właścicielem danych paczkomatów i skrytek. Nie prowadzi historii przesyłek, płatności, kont ani wysyłki powiadomień.
| Usługa | Odpowiedzialność | Własne dane | Operacje | Potrzebne informacje |
|---|---|---|---|---|
| Locker Service — zaimplementowana | Dostępność paczkomatów i skrytek | Paczkomaty, rozmiary i statusy skrytek | Odczyt, dodanie, zmiana statusu | Na LAB1 nie wymaga drugiej usługi |
| Parcel Service — planowana | Obsługa paczek | Paczki i ich statusy | Umieszczenie paczki, odczyt jej statusu | Dostępność skrytki i potwierdzenie zmiany jej stanu |
Pojęcia domenowe: paczkomat to lokalizacja zawierająca skrytki; skrytka to miejsce na paczkę; rezerwacja oznacza czasowe przeznaczenie skrytki dla operacji; zajętość oznacza stan po umieszczeniu paczki. Skrytki pozostają razem z paczkomatami, bo tworzą jeden obszar zarządzania dostępnością.
Przyszły kontrakt: po wcześniejszej rezerwacji Parcel Service wywoła PATCH /compartments/{id}/status z {"status":"occupied"}. Locker Service zwróci 200 i obiekt skrytki albo 404/409/422. Przy niedostępności odbiorcy operacja umieszczenia paczki pozostaje niepotwierdzona; nie wolno zgłosić sukcesu ani opublikować ParcelPlaced. Sposób rozstrzygania nieznanego wyniku po timeout i niezawodną publikację zdarzeń projektujemy później. Komunikacja między usługami nie jest zaimplementowana na LAB1.
Locker zawiera id, name, address, city, status (online/offline). Compartment zawiera id, locker_id, size (S/M/L) i status. Identyfikatory podaje klient. Identyfikator skrytki jest unikalny w całej usłudze. Przy uruchomieniu powstaje paczkomat KRK-1024 oraz skrytki KRK-1024-L1 i KRK-1024-M1 w stanie free.
Reguła biznesowa: można przechodzić tylko free → reserved → occupied → free lub free ↔ out_of_service. Ponowne ustawienie aktualnego statusu jest dozwolone. To uproszczony model dydaktyczny, bez anulowania i wygasania rezerwacji. Przykładowo free → reserved jest poprawne, a reserved → out_of_service zwraca 409 bez zmiany danych.
| Metoda i ścieżka | Sukces | Błędy |
|---|---|---|
GET /health |
200, status i nazwa usługi | — |
GET /lockers |
200, lista paczkomatów | — |
GET /lockers/{locker_id} |
200, obiekt paczkomatu | 404 |
POST /lockers |
201, utworzony paczkomat | 409 duplikat, 422 dane |
GET /lockers/{locker_id}/compartments |
200, lista skrytek (może być pusta) | 404 brak paczkomatu |
POST /lockers/{locker_id}/compartments |
201, skrytka w stanie free | 404, 409 duplikat, 422 dane |
PATCH /compartments/{compartment_id}/status |
200, skrytka po zmianie | 404, 409 przejście, 422 dane |
Status 404 oznacza brak zasobu, 409 konflikt z aktualnym stanem, a 422 błędne dane wejściowe. Błędy 404/409 mają {"detail":"opis"}. Błędy walidacji 422 mają listę obiektów w detail, wskazujących m.in. pole (loc) i przyczynę (msg). Ograniczenia pól i przykłady modeli są dostępne w /docs.
Wymagany Python 3.12 lub nowszy. Polecenia wykonaj w PowerShellu w katalogu locker-service, zawierającym requirements.txt:
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
.\.venv\Scripts\python.exe -m uvicorn app.main:app --reload --port 8000
Dokumentacja API:
.\.venv\Scripts\python.exe -m pytest -q
Nie trzeba uruchamiać Uvicorna do testów TestClient. Fixture tworzy świeżą aplikację i magazyn dla każdego testu. Testy sprawdzają zapis i odczyt, dozwolone przejścia, błędne dane, duplikat, niedozwolone przejście, brak zasobu i niezależność stanu aplikacji. Po odrzuceniu zapisu porównują dane z poprzednim stanem. Test nowej aplikacji nie zastępuje ręcznego eksperymentu z rzeczywistym restartem procesu.
Invoke-RestMethod http://localhost:8000/health
Invoke-RestMethod http://localhost:8000/lockers
Invoke-RestMethod http://localhost:8000/lockers/KRK-1024/compartments
Dodanie paczkomatu:
$lockerBody = @{ id = 'WAW-0001'; name = 'Warszawa Centrum'; address = 'ul. Testowa 1'; city = 'Warszawa' } | ConvertTo-Json
Invoke-RestMethod -Method Post -Uri http://localhost:8000/lockers -ContentType 'application/json' -Body $lockerBody
Invoke-RestMethod http://localhost:8000/lockers/WAW-0001
Obie odpowiedzi zawierają dane paczkomatu oraz "status":"online". Statusy HTTP to odpowiednio 201 i 200; można je obejrzeć w /docs (PowerShell powyżej pokazuje treść). Powtórny POST zwraca 409 i nie zmienia istniejącego paczkomatu.
Zmiana statusu początkowo wolnej skrytki i osobny odczyt:
Invoke-RestMethod -Method Patch -Uri http://localhost:8000/compartments/KRK-1024-L1/status -ContentType 'application/json' -Body '{"status":"reserved"}'
Invoke-RestMethod http://localhost:8000/lockers/KRK-1024/compartments
Odpowiedź PATCH (200):
{"id":"KRK-1024-L1","size":"L","locker_id":"KRK-1024","status":"reserved"}
Następnie w /docs spróbuj dla tej skrytki {"status":"out_of_service"}. Oczekuj 409 z {"detail":"Cannot change compartment status from 'reserved' to 'out_of_service'"}. Ponowny GET powinien nadal pokazać reserved.
Zacznij od restartu Uvicorna. Podczas scenariusza nie zapisuj zmian w kodzie, żeby --reload nie resetował danych. Wszystkie operacje wykonasz w /docs; zawsze sprawdzaj status HTTP i treść odpowiedzi.
| Krok | Żądanie / działanie | Oczekiwany wynik |
|---|---|---|
| 1 | GET /health |
200, {"status":"ok","service":"locker-service"} |
| 2 | GET /lockers i GET /lockers/KRK-1024 |
200, lista i szczegóły paczkomatu KRK-1024 |
| 3 | POST /lockers z JSON-em paczkomatu WAW-0001 z przykładu powyżej |
201, obiekt z tym id |
| 4 | GET /lockers/WAW-0001 |
200, dane zapisane w kroku 3 |
| 5 | Powtórz krok 3 ze zmienionym city, potem GET /lockers/WAW-0001 |
409, a następnie 200 z niezmienionymi danymi |
| 6 | GET /lockers/UNKNOWN |
404, detail opisuje brak paczkomatu |
| 7 | PATCH /compartments/KRK-1024-L1/status, JSON {"status":"broken-value"}; potem GET /lockers/KRK-1024/compartments |
422, detail wskazuje status; skrytka nadal free |
| 8 | Ta sama ścieżka PATCH, JSON {"status":"reserved"}; potem GET skrytek |
200, w obu odpowiedziach skrytka L1 ma reserved |
| 9 | Ta sama ścieżka PATCH, JSON {"status":"out_of_service"}; potem GET skrytek |
409; skrytka L1 nadal reserved |
| 10 | Zatrzymaj serwer przez Ctrl+C i uruchom ponownie | Nowy magazyn z danymi początkowymi |
| 11 | GET /lockers/WAW-0001 i GET /lockers/KRK-1024/compartments |
404 dla WAW-0001, skrytki KRK-1024 ponownie free |
Zapisz własny wynik rzeczywisty dla każdego kroku. Restart resetuje cały scenariusz. Dane żyją w magazynie aplikacji między żądaniami, ale nie są zapisywane poza pamięcią procesu.
Monolit: klient → [jeden proces: paczki + skrytki + powiadomienia] → wspólne dane
Modularny monolit: klient → [jeden proces: moduł paczek → moduł skrytek]
wywołanie w kodzie; logiczne granice danych
Mikrousługi: klient → [Parcel Service, dane paczek] → HTTP → [Locker Service, dane skrytek]
osobny proces osobny proces
W modularnym monolicie moduły i własność danych są wyraźnie rozdzielone, ale wdrożenie obejmuje całą aplikację. Locker Service jest samodzielną aplikacją z własnym magazynem i kontraktem, więc może stać się częścią systemu mikrousługowego. Sam działający endpoint nie dowodzi jeszcze niezależności całego systemu. Dla małej aplikacji paczkomatów modularny monolit może być prostszy; mikrousługi umożliwiają niezależne wdrażanie, ale wymagają obsługi opóźnień, niedostępności i częściowych błędów komunikacji. Awaria jednej usługi może wpłynąć na zależne operacje.
| Fragment dokumentacji | Zastosowanie | Weryfikacja |
|---|---|---|
| FastAPI: Request Body | Modele żądań Pydantic i walidacja statusu | Test błędnego statusu oczekuje 422, wskazania pola i niezmienionego stanu |
| FastAPI: Handling Errors | Tłumaczenie NotFoundError i ConflictError na HTTPException | Testy braku zasobu, duplikatu i niedozwolonego przejścia |
| FastAPI: Testing | TestClient i asercje odpowiedzi | Testy API bez uruchamiania Uvicorna |
W swoim README opisz własne wykorzystanie dokumentacji, autorów oraz użycie AI. Ten przykład nie jest gotowym sprawozdaniem studenta.
--reload przywracają dane początkowe.