Skip to content

Releases: river-creative/populi-api

1.1.0 — what has actually met a Populi server

Choose a tag to compare

@rmi-wilhelm rmi-wilhelm released this 14 Sep 20:24

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_id is a PERSON id. A row reporting student_id: 24564256 resolves at GET /people/24564256; by_student_id(24564256) finds nobody. The rows carry no person_id key 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 expand can still be null. expand: ["student"] adds the key to every /people row and sets it null for 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 defeats row.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

Choose a tag to compare

@rmi-wilhelm rmi-wilhelm released this 14 Sep 16:43

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

Choose a tag to compare

@rmi-wilhelm rmi-wilhelm released this 14 Sep 16:43

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.