Repository navigation
Releases: river-creative/populi-api
Release list
1.1.0 — what has actually met a Populi server
No code changed. This release exists because the previous one claimed coverage the evidence did not support.
Most of the surface that shipped in 1.0.0 as contract-only has now been driven against a live instance. The routes behaved as written — but two undocumented behaviours turned up that callers need.
Documented, each measured rather than inferred
- A course enrollment's
student_idis a PERSON id. A row reportingstudent_id: 24564256resolves atGET /people/24564256;by_student_id(24564256)finds nobody. The rows carry noperson_idkey at all, so the only identifier present is named after the scheme it does not belong to. Correct for the grade routes, wrong for anything resolving a visible student id — and wrong silently. - An honoured
expandcan still be null.expand: ["student"]adds the key to every/peoplerow and sets itnullfor non-students. That is a different thing from the key being absent because the expand was dropped, and only checking for the key separates them. It also defeatsrow.get('student', {}).get(...), which still raises. people.with_role()walks every page. On a role the size of Student that is thousands of people against a budget shared with everything else using the key.
Verified live
academic_terms, courses (offerings/assignments/enrollments), data_slicer, files.download_link, people.online_payment_link, people.list with expands, custom_fields.definitions_for_scope and definition() including the options expand, and PopuliFilter — the last confirmed by partition: a negated condition and its positive returned 2,843 and 27,596 against a directory of 30,439, which a dropped filter cannot produce.
Still unproven, marked ○ in the README: grade writes, file upload, term-scoped writes, people.update, by_student_ids. Every one is a write or a bulk walk.
1.0.1 — one route, one spelling
Fixed
custom_fields.definition()requested.../{field_id}/with a trailing slash. It was the only call in the client carrying one, and the only spelling of that route never driven against a live instance — the unslashed form has been. Nothing asserted the URL, which is how two spellings of one route coexisted unnoticed; there is now a test that fails on either one changing.
Changed
- The README and CHANGELOG now say which half of this client has actually met a Populi server. The core lifted out of the original application — people, tags, custom fields, leads, communication plans, notes — was exercised live through real webhook traffic. The surface added afterwards for parity with the .NET client —
courses,files,data_slicer,academic_terms,PopuliFilter, and the definition and term-scoped helpers — was written from the documented contract and unit-tested, but never sent to a real server.
On an API that answers an unreadable request with 200, that distinction is the whole basis for how much to trust a route, so the coverage table now marks each group live, contract-only, or mixed. Reports of what the untested paths actually do are the most useful contribution available.
1.0.0 — first release
First public release of the Populi API2 client extracted from an in-house Django application.
Built around the ways Populi fails open: a request the server cannot read is answered with HTTP 200 and quietly ignored, so most of the defensive code here exists because a silent failure got to production first.
Highlights
- Bearer-auth transport with error-object detection regardless of HTTP status
- Paged reads that verify the page echo and refuse a short read
PopuliFilter, which refuses an empty filter or group — both of which Populi ignores, and an ignored filter matches everyone- Per-caller rate pacing against a budget re-read per request, so a long run widens itself at the Pacific boundary
- Framework-free: importing the client loads zero Django modules
See the CHANGELOG for the undocumented behaviours this client works around, each of which was a defect before it was a paragraph.