feat(datasource-pylon): all collections + relations (EXT-8) - #354
Open
christophebrun-forest wants to merge 5 commits into
Open
feat(datasource-pylon): all collections + relations (EXT-8)#354christophebrun-forest wants to merge 5 commits into
christophebrun-forest wants to merge 5 commits into
Conversation
Adds search/list/fetch methods for the four new resources, routed through shared private helpers; search_issues and fetch_issue now delegate to the same helpers with unchanged behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Extracts the cursor-walk search flow, sort warning and operator maps into BaseCollection and shared modules; declares the four ManyToOne relations on PylonIssue and adds the schema-driven RelationEmbedder. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cursor-paginated read-only collections with server-side filters, free-text search, a single-id fast path on the record endpoint and their relations to issues, contacts and account. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fetch-all read-only collections: the endpoints return the complete dataset, so filtering, sorting and pagination run in memory over operators proven evaluable by the toolkit equivalence machinery. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Registers the five collections on the datasource and covers the relation embedding end to end: bulk id search per foreign collection, chunking, dedup and missing-record handling. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
12 new issues
|
| fetch_resource('issues', id) | ||
| end | ||
|
|
||
| def search_accounts(limit:, cursor: nil, filter: nil, search_text: nil) |
| fetch_resource('accounts', id) | ||
| end | ||
|
|
||
| def search_contacts(limit:, cursor: nil, filter: nil, search_text: nil) |
|
|
||
| private | ||
|
|
||
| def search_resource(path, limit:, cursor: nil, filter: nil, search_text: nil) |
| end | ||
| end | ||
| end | ||
| end |
| 'nor POST /accounts/search takes a sort parameter, so accounts come back in the order the API imposes.' | ||
| end | ||
|
|
||
| def search_page(limit:, cursor:, filter:, search_text:) |
| 'nor POST /contacts/search takes a sort parameter, so contacts come back in the order the API imposes.' | ||
| end | ||
|
|
||
| def search_page(limit:, cursor:, filter:, search_text:) |
| return ascending ? comparison : -comparison | ||
| end | ||
|
|
||
| 0 |
| return 1 if left.nil? | ||
| return -1 if right.nil? | ||
|
|
||
| (left <=> right) || (left.to_s <=> right.to_s) |
| 'POST /issues/search always returns issues from the most recent to the oldest.' | ||
| end | ||
|
|
||
| def search_page(limit:, cursor:, filter:, search_text:) |
| # not it resolved: a null foreign key, and a record the operator can no | ||
| # longer reach, both read as "no related record" rather than as a row | ||
| # missing the field. | ||
| def embed_foreign(foreign_collection, relations, records, rows) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Story
EXT-8 — Story 4: All collections + relations
Adds the four remaining read-only collections and wires the relational graph between issues, accounts, contacts, users and teams.
What's inside
Client — search/list/fetch endpoints for the four resources, mutualized in generic private helpers (
search_issues/fetch_issuenow delegate to them, behavior unchanged).Collections
PylonAccount,PylonContact(CursorCollection): cursor-paginated listing (GET, 60 req/min) when browsing,POST /search(20 req/min) as soon as a filter or free-text search is present, and a single-id fast path on the record endpoint for detail views. Their search endpoints filteridserver-side, so unlike issues they need no primary-key short-circuit:idsits inapi_filtersand translates like any other field,orincluded.PylonUser,PylonTeam(FetchAllCollection):GET /users/GET /teamsreturn the complete dataset, so filtering, sorting and pagination run in memory — correct because the snapshot is complete, and every advertised operator is proven in-memory-evaluable through the toolkit's equivalence machinery (guarded by a programmatic spec). Deactivated agents are included so historical assignees stay resolvable.Relations —
issue → account / requester (contact) / assignee (user) / team(ManyToOne) with their OneToMany inverses, pluscontact → accountandaccount → contacts. All reverse sides ride the existing server-side filters (account_id,requester_id,assignee_id,team_id).RelationEmbedder — schema-driven port of the Zendesk pattern: ManyToOne projections are resolved in bulk at read time (one
id in [...]search per foreign collection per page for accounts/contacts, chunked at 100; one indexed fetch-all for users/teams), reusing each foreign collection's own serializer instead of duplicating field lists. A relation the projection does not ask for costs zero requests.Filter allow-lists are transcribed from the Pylon API reference (
static.usepylon.com/openapi.json); fields the API cannot filter advertise no operator, andexternal_idsadvertises none because the API matches bare id strings while the column displays{external_id, label}objects.Tests
🤖 Generated with Claude Code
Note
Add PylonAccount, PylonContact, PylonUser, and PylonTeam collections to the Pylon datasource
Account,Contact,User,Team) alongside the existingIssuecollection indatasource.rb.CursorCollectionas a base for cursor-paginated collections (Account,Contact,Issue), routing list requests to the optimal Pylon endpoint (GET list, POST search, or GET by id) and batching related-record lookups by id.FetchAllCollectionas a base for unpaginated collections (User,Team), fetching the full dataset once and applying filtering, sorting, and pagination in memory.RelationEmbedderto bulk-fetch and embed ManyToOne related records into list results;Issuenow embedsaccount,requester,assignee, andteamrelations.Query::OperatorMapsand shared serialization helpers intoRecordSerialization, used by all collection schemas.client.rbwith search, list, fetch-all, and fetch-one methods for accounts, contacts, users, and teams via private shared helpers.Macroscope summarized e3a0366.