Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions _contract/MOBILE_API_SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ All FKs respect existing `utenti`/`libri` schema. Follow the soft-delete rule on
- `GET /me/devices` — list devices. `DELETE /me/devices/{id}` — revoke a device.
- `GET /catalog/search` — filters: `q`, `author`, `publisher`, `genre` (cascade id), `language`, `available` (bool); cursor pagination.
- `GET /catalog/books/{id}` — full detail + personal history.
- `GET /catalog/books/{id}/availability` — per-day availability calendar for the loan/reservation date picker.
- `GET /catalog/genres` — genre cascade tree (for filter UI).
- `GET /me/loans` — own loans (active + history). `GET /me/reservations`.
- `POST /reservations` — request a loan/reservation (honor existing overlap/availability rules). `DELETE /reservations/{id}` — cancel own pending reservation.
Expand Down
7 changes: 4 additions & 3 deletions _contract/endpoint-manifest.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ const ENDPOINTS = [
{ name: 'POST /auth/forgot-password', method: 'POST', path: '/auth/forgot-password', auth: false, kind: 'write2xx',
body: () => ({ email: USER_EMAIL }) },
{ name: 'POST /auth/register', method: 'POST', path: '/auth/register', auth: false, kind: 'conflict2',
body: () => ({ email: USER_EMAIL, password: USER_PASS, password_confirm: USER_PASS, nome: 'Idem', cognome: 'Test', telefono: '0', indirizzo: 'x', privacy_acceptance: '1' }),
body: () => ({ email: USER_EMAIL, password: USER_PASS, password_confirm: USER_PASS, nome: 'Idem', cognome: 'Test', telefono: '0', indirizzo: 'x', privacy_acceptance: true }),
firstAny: true /* registration may be disabled → 1st is 4xx; still asserts 2nd >= 400 */ },
{ name: 'POST /auth/logout', method: 'POST', path: '/auth/logout', auth: 'throwaway', kind: 'revoked2' },
{ name: 'GET /me', method: 'GET', path: '/me', auth: true, kind: 'safeGet' },
Expand All @@ -140,6 +140,7 @@ const ENDPOINTS = [
{ name: 'DELETE /me/devices/{deviceId}', method: 'DELETE', path: '/me/devices/{deviceId}', auth: true, kind: 'gone2' },
{ name: 'GET /catalog/search', method: 'GET', path: '/catalog/search', auth: true, kind: 'etag' },
{ name: 'GET /catalog/books/{bookId}', method: 'GET', path: '/catalog/books/{bookId}', auth: true, kind: 'etag' },
{ name: 'GET /catalog/books/{bookId}/availability', method: 'GET', path: '/catalog/books/{bookId}/availability', auth: true, kind: 'safeGet' },
{ name: 'GET /catalog/genres', method: 'GET', path: '/catalog/genres', auth: true, kind: 'etag' },
{ name: 'GET /me/loans', method: 'GET', path: '/me/loans', auth: true, kind: 'safeGet' },
{ name: 'GET /me/reservations', method: 'GET', path: '/me/reservations', auth: true, kind: 'safeGet' },
Expand All @@ -151,7 +152,7 @@ const ENDPOINTS = [
body: (ctx) => ({ book_id: ctx.bookId }) /* adding twice must not duplicate; both 2xx */ },
{ name: 'DELETE /me/wishlist/{bookId}', method: 'DELETE', path: '/me/wishlist/{bookId}', auth: true, kind: 'gone2' },
{ name: 'POST /messages', method: 'POST', path: '/messages', auth: true, kind: 'write2xx',
body: () => ({ messaggio: 'idem test message', oggetto: 'idem' }) },
body: () => ({ subject: 'idem', body: 'idem test message' }) },
{ name: 'GET /me/notifications', method: 'GET', path: '/me/notifications', auth: true, kind: 'safeGet' },
{ name: 'GET /me/push/prefs', method: 'GET', path: '/me/push/prefs', auth: true, kind: 'safeGet' },
{ name: 'PUT /me/push/prefs', method: 'PUT', path: '/me/push/prefs', auth: true, kind: 'write2xx',
Expand Down Expand Up @@ -297,7 +298,7 @@ test.describe('Mobile API — two calls per endpoint (idempotency + ETag/304)',
} catch { ctx.reservationId = 0; }

// Pre-add the wishlist book so DELETE /me/wishlist/{bookId} has something to remove on call #1.
try { dbExec(`INSERT IGNORE INTO wishlist (utente_id, libro_id, created_at) VALUES (${ctx.userId}, ${ctx.bookId}, NOW())`); } catch {}
try { dbExec(`INSERT IGNORE INTO wishlist (utente_id, libro_id) VALUES (${ctx.userId}, ${ctx.bookId})`); } catch {}
});

test.afterAll(async () => {
Expand Down
2 changes: 1 addition & 1 deletion _contract/health-sample.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"data":{"name":"Pinakes","logo":null,"version":"0.7.20.2","api_version":"v1","features":{"catalog":true,"loans":true,"reservations":true,"wishlist":true,"messages":true,"notifications":true,"push":true},"app_access_enabled":true,"registration_enabled":true,"private_mode":false,"vapid_public_key":"BAI9Ljyok0x691trnj9VsLYnPPGG651bXV9UDXM-TwKViUOWm9l3HPfRfXodc8-YaR7rMCc6fjrZKJv9ZBZBunE"},"meta":{"https":false,"warning":"insecure_transport"},"error":null}
{"data":{"name":"Pinakes","logo":null,"version":"0.7.20.2","api_version":"v1","features":{"catalog":true,"loans":true,"reservations":true,"wishlist":true,"messages":true,"notifications":true,"push":true},"catalogue_mode":false,"app_access_enabled":true,"registration_enabled":true,"private_mode":false,"vapid_public_key":"BAI9Ljyok0x691trnj9VsLYnPPGG651bXV9UDXM-TwKViUOWm9l3HPfRfXodc8-YaR7rMCc6fjrZKJv9ZBZBunE"},"meta":{"https":false,"warning":"insecure_transport"},"error":null}
Loading