Skip to content

Commit ed6b87f

Browse files
authored
Merge pull request #26 from andreagrandi/api-docs-parity-contract-validation
Fix API/docs parity and add contract validation
2 parents a7bf201 + 8689851 commit ed6b87f

17 files changed

Lines changed: 327 additions & 33 deletions

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ jobs:
6060
- name: Run test suite
6161
run: uvx nox -s tests
6262

63+
- name: Validate OpenAPI schema
64+
run: uvx nox -s validate_openapi
65+
6366
deploy:
6467
needs: tests
6568
if: github.ref == 'refs/heads/master' && github.event_name == 'push'

book-corners-plan.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,22 +1239,22 @@ preparing the project for a larger dataset before adding another major surface a
12391239

12401240
##### 7.2.3 — API/docs parity and contract validation
12411241

1242-
- [ ] Fix docs drift where prose docs and actual behavior disagree
1243-
- [ ] Align search docs with the real search implementation (or expand implementation to match the docs)
1244-
- [ ] Update upload docs to reflect all accepted image formats
1245-
- [ ] Correct statistics docs and changelog entries to use the actual routed API paths
1246-
- [ ] Add a prose docs page for `POST /api/v1/libraries/{slug}/photo`
1247-
- [ ] Add OpenAPI schema validation in CI
1248-
- [ ] Add an end-to-end API integration test covering:
1249-
- [ ] register
1250-
- [ ] login
1251-
- [ ] submit library
1252-
- [ ] list
1253-
- [ ] search
1254-
- [ ] detail
1255-
- [ ] report
1256-
- [ ] community photo submission
1257-
- [ ] Regenerate `docs/openapi.json` whenever API code or API docs change
1242+
- [x] Fix docs drift where prose docs and actual behavior disagree
1243+
- [x] Align search docs with the real search implementation (or expand implementation to match the docs)
1244+
- [x] Update upload docs to reflect all accepted image formats
1245+
- [x] Correct statistics docs and changelog entries to use the actual routed API paths
1246+
- [x] Add a prose docs page for `POST /api/v1/libraries/{slug}/photo`
1247+
- [x] Add OpenAPI schema validation in CI
1248+
- [x] Add an end-to-end API integration test covering:
1249+
- [x] register
1250+
- [x] login
1251+
- [x] submit library
1252+
- [x] list
1253+
- [x] search
1254+
- [x] detail
1255+
- [x] report
1256+
- [x] community photo submission
1257+
- [x] Regenerate `docs/openapi.json` whenever API code or API docs change
12581258

12591259
##### 7.2.4 — Search quality and indexing
12601260

docs/changelog.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
# Changelog
22

3+
## v1.3.0
4+
5+
- Community photo endpoint (`POST /api/v1/libraries/{slug}/photo`) is now documented
6+
- Fixed `q` search parameter description: searches name and description (not address)
7+
- Fixed image format documentation: JPEG/PNG/WEBP accepted (not just JPEG/PNG)
8+
- Fixed `GET /api/v1/statistics/` path in docs (was missing `/api/v1/` prefix)
9+
- Added `POST /libraries/{slug}/photo` and `GET /statistics/` to rate-limiting documentation
10+
311
## v1.2.0
412

513
- Login endpoint (`POST /auth/login`) now accepts email address in the `username` field, matching the web login flow. Email lookup is case-insensitive and the identifier is trimmed before authentication.
614

715
## v1.1.0
816

9-
- Public statistics endpoint (`GET /statistics/`) with totals, top countries, and cumulative growth series
17+
- Public statistics endpoint (`GET /api/v1/statistics/`) with totals, top countries, and cumulative growth series
1018
- Community photo submissions count towards the "libraries with photos" statistic
1119

1220
## v1.0.0

docs/errors.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ All API errors return a consistent JSON structure, making it straightforward to
2323
| `400` | Bad Request | Invalid input (bad email, duplicate username, unsupported photo format) |
2424
| `401` | Unauthorized | Missing/invalid JWT token, or bad credentials on login |
2525
| `404` | Not Found | Library slug doesn't exist or isn't visible to the current user |
26-
| `413` | Payload Too Large | Uploaded photo exceeds the size limit (8 MB for libraries, 5 MB for reports) |
26+
| `413` | Payload Too Large | Uploaded photo exceeds the size limit (8 MB for libraries and community photos, 5 MB for reports) |
2727
| `422` | Unprocessable Entity | Request validation failed (missing required fields, out-of-range values) |
2828
| `429` | Too Many Requests | Rate limit exceeded (see [Rate Limiting](rate-limiting.md)) |
2929
| `500` | Internal Server Error | Unexpected server error |

docs/getting-started.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,4 @@ Public endpoints like listing libraries don't require authentication:
101101
- [Authentication](authentication.md) — full token lifecycle details
102102
- [List & Search](libraries/list-and-search.md) — all search and filter options
103103
- [Submit a Library](libraries/submit.md) — add a new library with a photo
104+
- [Submit a Community Photo](libraries/submit-photo.md) — add a photo to an existing library

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ https://bookcorners.org/api/v1/
2525
| [List & Search](libraries/list-and-search.md) | Browse and search the library catalogue |
2626
| [Submit a Library](libraries/submit.md) | Add a new library with a photo |
2727
| [Report an Issue](libraries/report.md) | Flag problems with a library |
28+
| [Submit a Community Photo](libraries/submit-photo.md) | Add a photo to an existing library |
2829
| [Statistics](statistics.md) | Platform-wide aggregate statistics |
2930
| [Errors](errors.md) | Error response format and status codes |
3031
| [Rate Limiting](rate-limiting.md) | Request limits and 429 handling |

docs/libraries/list-and-search.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Return a paginated list of approved libraries with optional search filters.
1212

1313
| Parameter | Type | Default | Description |
1414
|-----------|------|---------|-------------|
15-
| `q` | string || Free-text search across name, description, and address (max 200 chars) |
15+
| `q` | string || Free-text search across name and description (max 200 chars) |
1616
| `city` | string || Filter by city name (case-insensitive, max 100 chars) |
1717
| `country` | string || Filter by ISO 3166-1 alpha-2 country code (max 2 chars) |
1818
| `postal_code` | string || Filter by postal / ZIP code (max 20 chars) |

docs/libraries/report.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Report a problem with an approved library. Reports are reviewed by moderators.
2020
|-------|------|----------|-------------|
2121
| `reason` | string | Yes | Issue category (see values below) |
2222
| `details` | string | No | Free-text description of the issue (max 2000 chars) |
23-
| `photo` | file | No | Photo showing the issue (JPEG/PNG, max 5 MB) |
23+
| `photo` | file | No | Photo showing the issue (JPEG/PNG/WEBP, max 5 MB) |
2424

2525
### Reason values
2626

docs/libraries/submit-photo.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Submit a Community Photo
2+
3+
`POST /api/v1/libraries/{slug}/photo`
4+
5+
Submit a community photo for an approved library. The photo starts in **pending** status and must be approved by a moderator before it appears publicly. Each user can submit up to 3 photos per library.
6+
7+
**Auth required:** Yes (`Bearer` token)
8+
9+
**Content type:** `multipart/form-data`
10+
11+
## Path parameters
12+
13+
| Parameter | Type | Description |
14+
|-----------|------|-------------|
15+
| `slug` | string | URL slug of the library to add a photo to |
16+
17+
## Fields
18+
19+
| Field | Type | Required | Description |
20+
|-------|------|----------|-------------|
21+
| `photo` | file | Yes | Photo of the library (JPEG/PNG/WEBP, max 8 MB) |
22+
| `caption` | string | No | Optional caption for the photo (max 200 chars) |
23+
24+
## Examples
25+
26+
=== "curl"
27+
28+
```bash
29+
curl -X POST https://bookcorners.org/api/v1/libraries/berlin-friedrichstr-12-corner-books/photo \
30+
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
31+
-F "photo=@community-photo.jpg" \
32+
-F "caption=Summer view from the park side"
33+
```
34+
35+
=== "Python"
36+
37+
```python
38+
import requests
39+
40+
resp = requests.post(
41+
"https://bookcorners.org/api/v1/libraries/berlin-friedrichstr-12-corner-books/photo",
42+
headers={"Authorization": f"Bearer {access_token}"},
43+
data={"caption": "Summer view from the park side"},
44+
files={"photo": open("community-photo.jpg", "rb")},
45+
)
46+
print(resp.json())
47+
```
48+
49+
### Submit without a caption
50+
51+
=== "curl"
52+
53+
```bash
54+
curl -X POST https://bookcorners.org/api/v1/libraries/berlin-friedrichstr-12-corner-books/photo \
55+
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
56+
-F "photo=@community-photo.jpg"
57+
```
58+
59+
=== "Python"
60+
61+
```python
62+
resp = requests.post(
63+
"https://bookcorners.org/api/v1/libraries/berlin-friedrichstr-12-corner-books/photo",
64+
headers={"Authorization": f"Bearer {access_token}"},
65+
files={"photo": open("community-photo.jpg", "rb")},
66+
)
67+
```
68+
69+
## Response (`201 Created`)
70+
71+
```json
72+
{
73+
"id": 15,
74+
"caption": "Summer view from the park side",
75+
"status": "pending",
76+
"created_at": "2025-06-15T14:30:00Z"
77+
}
78+
```
79+
80+
!!! note
81+
The photo will have **pending** status. It won't appear on the library detail page until approved by a moderator. Each user can submit up to 3 photos per library (rejected photos do not count towards this limit).
82+
83+
## Errors
84+
85+
| Status | Cause |
86+
|--------|-------|
87+
| `400` | Invalid photo format, or per-user limit of 3 photos reached for this library |
88+
| `404` | Library not found or not in approved status |
89+
| `413` | Photo exceeds 8 MB size limit |
90+
| `422` | Request validation error |
91+
| `429` | Rate limit exceeded (see [Rate Limiting](../rate-limiting.md)) |

docs/libraries/submit.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Submit a new library location with a photo. The library starts in **pending** st
2828
| `brand` | string | No | Network or brand name (max 255 chars) |
2929
| `latitude` | float | Yes | Latitude (-90 to 90, WGS 84) |
3030
| `longitude` | float | Yes | Longitude (-180 to 180, WGS 84) |
31-
| `photo` | file | Yes | Photo of the library (JPEG/PNG, max 8 MB) |
31+
| `photo` | file | Yes | Photo of the library (JPEG/PNG/WEBP, max 8 MB) |
3232

3333
## Examples
3434

0 commit comments

Comments
 (0)