ReviewOS

stacks/reportshq

Laravel integration package: auto-instrumented events

#8
Open chris opened this 8 days ago ·
8 days ago

Context

Same idea as #7 for Laravel apps that follow framework best practices: a composer package whose service provider listens to the standard Laravel surface (auth events, Eloquent model observers, commerce-shaped models) and forwards the standard taxonomy (docs/events.md, #6). This is our own package; its only dependency is laravel/framework (illuminate components) - no third-party HTTP clients or queues.

Scope

  • Composer package reportshq/laravel (repo stacksjs/reportshq-laravel): service provider with auto-discovery, config/reportshq.php publishable (key, endpoint, per-domain toggles, sampling, queue connection).
  • Listeners for built-in events: Illuminate\Auth\Events\{Registered, Login} to user.registered / user.login; a small observer-registration API for the app's own models (ReportsHQ::observe(Order::class, OrderMapper::class)) plus shipped mappers for conventional commerce models (order with total/currency, refund, customer).
  • Transport: batched, queueable (dispatch to the app's queue when configured, sync-with-terminating-callback fallback), Laravel's HTTP client, bounded retry, never throws into the host app's request path.
  • Taxonomy fidelity: payloads byte-compatible with docs/events.md; a shared JSON fixture set in this repo asserts both SDKs (#7 and this one) produce identical shapes for the same logical event.
  • Tests with Laravel's package test tooling (phpunit, orchestral-free if possible - keep dependencies to the illuminate surface).
  • README quickstart: composer require, env vars, one observer registration, screenshot-free.
  • Versioned release (tagged; packagist submission can wait for launch).

Acceptance

  • A demo Laravel app (script or CI job) fires Registered + an order-model create and both arrive through /ingest (#6) with taxonomy-correct payloads; the cross-SDK fixture test passes.

Depends on #6. Feeds #14. Related to #7. Part of #1 (M5 Integrations and auto-reports). Ground rules: see #1.

8 days ago

Most of this is built and verified, in 6513975 and 10b05b0. Leaving the issue open, because one acceptance item genuinely is not met and I would rather say so than quietly redefine it.

The cross-SDK contract came first, since two SDKs mean two chances to disagree about what an event is, and the disagreement is invisible: a Laravel app and a Stacks app doing the same thing would produce reports that quietly differ, and nobody finds out until somebody compares them.

docs/fixtures/sdk-events.json states, per logical event, the one taxonomy payload every SDK must produce. The Stacks package asserts it from TypeScript and this one asserts the same file from PHP. Equality is exact, not a subset match, because a field one SDK adds and the other does not is drift too. Both pass, first run.

The package. reportshq/laravel, requiring nothing but PHP 8.2. Everything worth testing is deliberately free of Illuminate - mapper, sampler, config, transport - which is what lets it run without booting an application. The service provider is the only file that knows Laravel exists.

Delivery happens on the terminating callback, after the response has already reached the browser, or on the queue when one is named. PHP has no long-lived process to flush from, so the buffer would otherwise die with the request. The queued batch travels as plain arrays rather than models, so what is queued is exactly what is sent and cannot change in between.

The sampler is the same FNV-1a over the same subject as the Stacks SDK, asserted against known values, so both agree about who is in a sample. An application migrating between them keeps a continuous history rather than a step change at the switchover.

36 tests, run in CI with the php the runners already ship: no composer, no vendor directory, no network. Two of them are worth calling out. Every other transport test injects a fake sender, which is the right way to test retry and back pressure and the wrong way to learn whether the header is actually named X-ReportsHQ-Key on the wire - a fake agrees with whatever it is handed. So two tests post to a PHP built-in server and read back what arrived: that the batch turns up with the key header and the payload intact, and that a 401 is reported as a refusal rather than mistaken for a failure to connect, which is the difference between not retrying a bad key and hammering the endpoint with it.

What is not done, and why.

The acceptance asks for a demo Laravel app firing Registered plus an order-model create, arriving through /ingest. That is not built. The payload shapes are proven by the shared fixtures, and delivery over real HTTP is proven by the wire tests, but the service provider's listener registration has never been executed - it needs a booted application with Illuminate installed, which means a composer install and a small harness. That is the honest remaining 10% of this issue.

I also could not run any of this locally: php is not installed on this machine and a pantry install php did not finish in half an hour. Everything above was verified by CI, which is a legitimate path but worth stating plainly rather than implying I watched it pass on my own machine.

Not published to Packagist either, which the issue already scopes as a launch task.

For #7's sibling note: buddy test does not walk packages/, and the app's tsconfig does not typecheck it. Both now have their own CI steps, and this package has a third.

Sign in to comment on this issue.