Skip to content

Add a Posit Connect API client - #252

Draft
jat255 wants to merge 1 commit into
mainfrom
jat255/4d5a-connect-client
Draft

Add a Posit Connect API client#252
jat255 wants to merge 1 commit into
mainfrom
jat255/4d5a-connect-client

Conversation

@jat255

@jat255 jat255 commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Second piece of M7 tracing (kata 4d5a). The write side has to act on Connect in two ways, turning on content observability and granting collaborators access to traces, and both go through this client. Neither of those tasks is in this PR; this is the client they share.

ConnectClient resolves credentials from the environment, normalises the server URL, and builds authenticated requests against /__api__/v1/. Connect hands running content an ephemeral owner-scoped CONNECT_API_KEY, which is what lets content turn on its own observability without a publisher configuring anything. That key must not escape, so ConnectError names the request by method, path and status instead of carrying the response or the request headers, and a test pins that.

What a publisher can observe is now shared with the R package rather than written twice. tests/shared/connect.json holds the environment variables read, the server URL forms accepted, and the URLs requests land on. R already had hand-written tests for the first two, so they read the fixture now. Error wording stays per-language, because neither package can match the other's error idiom and R snapshots its messages.

httpx moves from the tracing extra to a plain dependency, because this module imports it directly. It arrived transitively through chatlas before.

Not here, and deliberately: the trace-reading endpoints, their paging, and the 401/403 message about editor access. Those have no Python caller until kata 88cg.

R changes

No R source changed. R/connect.R is untouched, so no R behaviour differs; the change is entirely in what the R suite checks and where those expectations live.

Three of the behaviours in test-connect.R were specified only in prose-equivalent hand-written assertions on the R side, and the new Python client re-specified the same ones. That is the duplication tests/shared/ exists to prevent, so the expectations moved into tests/shared/connect.json and both suites read it. The R copy at pkg-r/tests/testthat/fixtures/shared/connect.json is generated by scripts/sync-shared-fixtures.sh; treat it as generated and skip it in review. The existing verify-shared-fixtures workflow fails on a stale copy, so it cannot drift.

What was deleted and what replaced it. The normalisation test asserted two forms inline; it now loops six fixture cases, including two R was not previously checked against, a server with repeated trailing slashes and a server hosted under a path. The runtime-detection test asserted three combinations inline; it now loops five, adding a non-Connect POSIT_PRODUCT value and an empty CONNECT_CONTENT_GUID. Both new cases pass against the existing regexes with no source change. The credentials snapshot test is unchanged, because message wording is not shared.

Two tests are new. connect_req had no direct test before; it now asserts the URL for three path shapes, which is the contract the Python client had to match. A guard test asserts the fixture actually contains both detection outcomes and a minimum number of cases, so a truncated fixture cannot pass by collecting nothing.

Blast radius is limited to the test file. connect_client(), is_connect_runtime() and connect_req() are all internal and their call sites in R/tracing.R and R/trajectory-read.R are unaffected.

Evidence rather than assertion that this holds: testthat::test_file("test-connect.R") gives 24 passing and 3 skipped, the skips being the snapshot tests that skip non-interactively, and the full test_dir run gives 6565 passing and none failing. The fixture is authoritative rather than decorative, checked by mutating three cases in the synced copy and confirming three R failures, and separately nine Python failures against the source copy.

Worth your scrutiny: whether the withr::with_envvar loop handles an unset variable the way you would expect, since the fixture encodes "unset" as JSON null and the test maps it to NA. And whether the path-hosted server case reflects a deployment shape you have actually seen, since I added it from reading the regex rather than from a real server.

Covers what the write side of tracing needs before it can act on Connect:
detecting that this process is Connect content, resolving the content GUID and
credentials from the environment, building authenticated requests against
`/__api__/v1/`, and raising on a failed one.

Connect gives running content an ephemeral owner-scoped `CONNECT_API_KEY`, so
content can turn on its own observability and grant collaborators access
without a publisher configuring anything. That key must not escape: the error
names the request by method, path and status rather than carrying the response
or the request headers, and a test pins that.

What a publisher can observe is shared with the R package rather than
specified twice: `tests/shared/connect.json` holds the variables read, the
server URL forms accepted and the URLs requests land on. R already had
hand-written tests for the first two, so those now read the fixture, and a new
R test covers the request URLs. Checked by mutating the fixture and watching
each suite fail. Error wording stays per-language, since neither package can
match the other's error idiom.

httpx moves from the `tracing` extra to a plain dependency, because this
module imports it directly. It arrived transitively through chatlas before,
which is not something commons should rest on.

Requests are exercised through an injected `httpx.Client`, so the tests build
and inspect real requests rather than asserting against a stubbed client.

Part of kata bvcv (M7). Closes kata 4d5a.
@jat255
jat255 force-pushed the jat255/4d5a-connect-client branch from b3b7012 to 0c1ba95 Compare September 2, 2026 00:34
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

Preview deployed to Connect (dogfood.team.pct.posit.it): https://dogfood.team.pct.posit.it/connect/#/apps/d7a36cae-8f27-448b-a478-61b81fbe3942/draft/366796

Deployed from commit 0c1ba95.

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

Preview deployed to Connect (connect.staging.pct.posit.it): https://connect.staging.pct.posit.it/connect/#/apps/ad662e1b-5048-4acc-9ad7-f9478c92274e/draft/2457

Deployed from commit 0c1ba95.

@jat255
jat255 marked this pull request as draft September 2, 2026 00:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant