DatasheetNotifier
in package
Tells the Sharp Datasheet Generator that TypeSense has changed.
The contract is deliberately dumb: one authenticated POST, no durable outbox, no cursor state. A missed notification is not lost data — duplicate or over-broad pushes are free (the Generator hashes render input, so an unchanged Model x Locale is a no-op), and the Generator's staleness guard alerts an operator when nothing has arrived in N days.
POST <WSC_DATASHEET_INTAKE_URL> X-Datasheet-Signature: <WSC_DATASHEET_INTAKE_SECRET> incremental: {"changed": [{"model_id": "75JP7765E", "languages": ["en","de"]}]} full rebuild: {"full_rebuild": true}
The two paths are discriminated by which key is present. model_id is the TypeSense
products document id; omitted languages means "all locales the Generator has configured".
Call sites live in Collection, i.e. at the point TS is actually
written, NOT in an orchestrator — so BufferChecker, WP-CLI and any future flush queue all
notify without knowing this class exists, and a notification can never outrun the write.
Records accumulate in-process and one POST is sent from the shutdown hook, because a
single buffer run can repopulate a hundred documents.
Table of Contents
Constants
- LOG_CHANNEL : mixed = 'wsc-datasheet-notify'
- ATTEMPTS : mixed = 3
- ATTRIBUTES_ALIAS : mixed = 'attributes'
- MAX_CHANGED : mixed = 500
- Above this, stop naming Models and just ask for a rebuild — keeps the payload bounded.
- MODELS_ALIAS : mixed = 'products'
- The TS aliases the Generator reads (ADR-0001). Writes to anything else are ignored.
- RETRY_DELAY_US : mixed = 250000
- SIGNATURE_HEADER : mixed = 'X-Datasheet-Signature'
- TIMEOUT : mixed = 5
Properties
- $changed : array<string, true>
- $fullRebuildReason : string|null
- Non-null once anything happened that the Connector cannot express as a per-Model delta.
- $languageScope : array<int, string>|null
Methods
- buildPayload() : array<string, mixed>|null
- Builds the wire payload for everything recorded so far, or null when there is nothing to say. Pure — no WordPress, no HTTP — so the contract is unit-testable.
- flush() : void
- Sends everything recorded during this request as one notification.
- fullRebuildReason() : string|null
- isConfigured() : bool
- recordCollectionRebuild() : void
- A whole collection was regenerated and its alias swapped.
- recordDocumentWrite() : void
- A single document was (re)written.
- recordFullRebuild() : void
- recordModelChange() : void
- reset() : void
- setLanguageScope() : void
- Declares which languages the current sync run covers.
- describeJob() : string
- The Generator answers 202 with the Render Job it enqueued; carrying that id into the Connector log is what makes "the Connector fired at 14:02" traceable across the two systems.
- intakeSecret() : string
- intakeUrl() : string
- send() : bool
Constants
LOG_CHANNEL
public
mixed
LOG_CHANNEL
= 'wsc-datasheet-notify'
ATTEMPTS
private
mixed
ATTEMPTS
= 3
ATTRIBUTES_ALIAS
private
mixed
ATTRIBUTES_ALIAS
= 'attributes'
MAX_CHANGED
Above this, stop naming Models and just ask for a rebuild — keeps the payload bounded.
private
mixed
MAX_CHANGED
= 500
MODELS_ALIAS
The TS aliases the Generator reads (ADR-0001). Writes to anything else are ignored.
private
mixed
MODELS_ALIAS
= 'products'
RETRY_DELAY_US
private
mixed
RETRY_DELAY_US
= 250000
SIGNATURE_HEADER
private
mixed
SIGNATURE_HEADER
= 'X-Datasheet-Signature'
TIMEOUT
private
mixed
TIMEOUT
= 5
Properties
$changed
private
static array<string, true>
$changed
= []
changed Model ids, keyed to dedupe repeated writes
$fullRebuildReason
Non-null once anything happened that the Connector cannot express as a per-Model delta.
private
static string|null
$fullRebuildReason
= null
$languageScope
private
static array<int, string>|null
$languageScope
= null
null = no caller declared a scope => every configured locale
Methods
buildPayload()
Builds the wire payload for everything recorded so far, or null when there is nothing to say. Pure — no WordPress, no HTTP — so the contract is unit-testable.
public
static buildPayload() : array<string, mixed>|null
Return values
array<string, mixed>|nullflush()
Sends everything recorded during this request as one notification.
public
static flush() : void
Wired to shutdown, so delivery happens after the sync has already finished and can never
block or break it — hence the blanket catch. Clearing state before sending makes a repeat
flush a no-op even if the send dies.
fullRebuildReason()
public
static fullRebuildReason() : string|null
Return values
string|nullisConfigured()
public
static isConfigured() : bool
Return values
boolrecordCollectionRebuild()
A whole collection was regenerated and its alias swapped.
public
static recordCollectionRebuild(string $alias) : void
There is no per-Model delta to report, so this is a full-rebuild signal — the Generator's hash-diff scan works out what actually changed.
Parameters
- $alias : string
recordDocumentWrite()
A single document was (re)written.
public
static recordDocumentWrite(string $alias, string $documentId) : void
A product maps 1:1 to a Model. An attribute does not map to anything: attribute metadata (labels, units, group names) feeds every datasheet and there is no attribute -> Model index, so it escalates to a full rebuild — the same call BufferChecker already makes for its own localized collections when attributes move.
Parameters
- $alias : string
- $documentId : string
recordFullRebuild()
public
static recordFullRebuild(string $reason) : void
Parameters
- $reason : string
recordModelChange()
public
static recordModelChange(string $modelId) : void
Parameters
- $modelId : string
reset()
public
static reset() : void
setLanguageScope()
Declares which languages the current sync run covers.
public
static setLanguageScope(array<int, string>|null $langs) : void
Only an orchestrator can know this: products is a single GLOBAL document holding every
locale, and repopulating it refetches PIM with all, so the collection layer has no
honest way to narrow. Callers that stay silent get the full locale list, which is safe —
over-broad is free, too narrow silently drops a datasheet.
Unions rather than overwrites, so two runs in one request cannot shrink each other's scope.
Parameters
- $langs : array<int, string>|null
describeJob()
The Generator answers 202 with the Render Job it enqueued; carrying that id into the Connector log is what makes "the Connector fired at 14:02" traceable across the two systems.
private
static describeJob(string $responseBody) : string
Parameters
- $responseBody : string
Return values
stringintakeSecret()
private
static intakeSecret() : string
Return values
stringintakeUrl()
private
static intakeUrl() : string
Return values
stringsend()
private
static send(array<string, mixed> $payload) : bool
Parameters
- $payload : array<string, mixed>