Drupal connector
A Drupal 10/11 module that maps nodes, taxonomy terms, and Drupal Commerce products to bAInquet IngestItems, signs every request with the connector HMAC scheme, and POSTs idempotent batches.
The module lives under connectors/drupal/bainquet and sends X-Connector-Type: drupal.
What it maps
| Drupal entity | IngestItem type | Notes |
|---|---|---|
| Node (page-like bundle) | page | The body field's text-format-processed HTML becomes html; a deterministic plain-text rendition becomes text. |
| Node (article, blog, news) | post | Bundles named event or faq map to those types; everything else falls back to post. |
| Drupal Commerce product | product | Variations carry SKU and typed price { value, unit, currency } in json.product, never a concatenated string. |
| Taxonomy term | category | |
| User | profile | |
| Media | media |
- Custom fields.
field_*values are read structurally via the Field API intojson.fieldsas typed values, never scraped. Entity-reference fields become{ id, label }; link fields become{ uri, title }. - Stable ids. Built from the entity UUID (for example
post:<uuid>), so ids survive a site export or migration. - Language and canonical. Language is the entity's langcode normalized to BCP-47;
canonical_urlis the entity's absolutetoUrl().
Install
composer require drupal/bainquet
drush en bainquetOr copy the bainquet folder into web/modules/custom/ and enable it from Extend.
Configure
Go to Configuration, Web services, bAInquet Connector (/admin/config/services/bainquet).
- Paste the connector token (
connectorId.secret) from your dashboard. It is shown once; copy it immediately. - Enter the Website ID the token is scoped to.
- Confirm the Site domain (defaults to this site host) and the API base URL (defaults to
https://api.bainquet.online/v1/ingest). - Under Content to sync, tick the
entity_type:bundlepairs to ingest, for examplenode:article,node:page,commerce_product:default. - Save the configuration, then click Test connection to send a heartbeat.
- Run
drush bainquet:syncto backfill.
How incremental sync works
The module uses Drupal entity hooks.
- Upsert.
hook_entity_insertandhook_entity_updateon an in-scope, published entity map it and POST toPOST /v1/ingest/item. - Tombstone.
hook_entity_delete, and any unpublish transition, POST a tombstone toPOST /v1/ingest/deletewith the entity's stable id, so the public node drops it.
How backfill works
drush bainquet:syncIt walks every in-scope, published, access-granted entity in chunks of 50 per batch and batch-POSTs to POST /v1/ingest/batch, staying under the payload cap.
drush bainquet:sync --dry-runThe dry run builds and prints the batch it would send, locally, without posting.
Privacy and access
Only published entities are ingested, and drush bainquet:sync runs the entity query with accessCheck(TRUE) so access-restricted content is never loaded. An entity that becomes unpublished after ingest is tombstoned, so the public AI node drops it.
Out of scope
Single-item upserts are sent synchronously on the hook. A persistent Queue API plus cron outbox with debounce and coalesce is a later-phase enhancement. Planned
Uninstall
Uninstalling the module removes the bainquet.settings config (including the stored token) via Drupal's standard config-on-uninstall cleanup.
HMAC signing
Every request is signed exactly as the server verifies it, using the shared bq.connector.hmac.v1 scheme:
signingKey = HKDF-SHA256(secret, salt = websiteId, info = "bq.connector.hmac.v1", 32 bytes)
canonical = METHOD \n path \n sha256(body) \n timestamp \n nonce \n websiteId
X-Signature = hex HMAC-SHA256(signingKey, canonical)Headers sent: Authorization: Bearer <token>, X-Site-Domain, X-Connector-Type: drupal, X-Connector-Version, X-Signature, X-Timestamp, X-Nonce, X-Body-Sha256, and Idempotency-Key. Retries on 5xx and 429 reuse the same Idempotency-Key with a fresh nonce and timestamp.
The signing is parity-tested with a standalone test that needs no Drupal boot (php tests/SignerParityTest.php); it asserts X-Body-Sha256 and X-Signature match the frozen vector byte-for-byte. Full entity-hook, Commerce, and Drush behavior still requires a live Drupal install to validate. The full scheme is documented in Ingestion and signing.