Problem/Motivation

The PA module currently only tracks page views via the client-side JS SDK. There are scenarios where the JS SDK never executes — server-side redirects (301/302), API endpoints, cron jobs — and events for these are lost entirely.

The PA Collection API supports server-side HTTP event submission, but there is no reusable service in the module to leverage it.

Proposed resolution

Add a new ServerSideEventSender service that POSTs events to the PA Collection API using the site ID and collect domain already configured in pianoanalytics.settings.

The service:

  • POSTs JSON to https://{collect_domain}/event?s={site}&idclient={visitor_id} with Content-Type: text/plain (per PA Collection API spec)
  • Auto-enriches event properties with device_timestamp_utc and event_collection_platform: api
  • Forwards browser context headers (User-Agent, Referer, X-Forwarded-For) so PA can perform device and geo analysis on server-side events
  • Provides resolveVisitorId(Request) to extract the visitor ID from PA cookies (_pcid, pa_vid, atuserid) with fallback to a sha256 hash of the client IP
  • Provides extractBrowserHeaders(Request) to extract the relevant headers from a Symfony request
  • Uses a 2-second timeout and catches all exceptions — callers are never broken by PA failures
  • Errors are logged to the pianoanalytics logger channel

New files

  • pianoanalytics.services.yml — declares the pianoanalytics logger channel and the autowired ServerSideEventSender service
  • src/ServerSideEventSender.php — the service class
  • tests/src/Kernel/ServerSideEventSenderTest.php — 6 kernel tests covering request construction, Content-Type, auto-enriched properties, browser header forwarding, missing config guard, HTTP error handling, cookie-based visitor ID resolution, and header extraction

API usage example

$sender = \Drupal::service(ServerSideEventSender::class);
$visitor_id = ServerSideEventSender::resolveVisitorId($request);
$headers = ServerSideEventSender::extractBrowserHeaders($request);

$sender->sendEvent('page.display', [
  'page' => '/my-page',
  'page_chapter1' => 'section',
], $visitor_id, $headers);

Remaining tasks

  • Review
  • Test
Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

mably created an issue. See original summary.

mably’s picture

Status: Active » Needs review

  • mably committed f2dba186 on 2.x
    feat: #3579450 Add server-side Collection API service (...
mably’s picture

Status: Needs review » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.