Search by

as-cornell / as_webhook_entities

markewilson

Manage Drupal entities like people records and articles via webhook notifications.

Package info

github.com/as-cornell/as_webhook_entities

Homepage

Type:drupal-custom-module

pkg:composer/as-cornell/as_webhook_entities

Statistics

Installs: 1 216

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v2.16.1 2026-10-07 15:41 UTC

README

Latest Stable Version

AS WEBHOOK ENTITIES (as_webhook_entities)

INTRODUCTION

Touchdown!

Receives webhook notifications from remote systems and creates, updates, or deletes local Drupal entities (people, articles, taxonomy terms) accordingly. Uses a Strategy pattern with per-type handler classes for maintainability and extensibility.

REQUIREMENTS

System Requirements

  • Drupal 9.5+ or Drupal 10+
  • PHP 8.0+

INSTALLATION

New Installation

  1. Enable the module:

    drush en as_webhook_entities -y
  2. Configure the module settings:

    • Navigate to /admin/config/as_webhook_entities/settings
    • Configure the authorization token and whether to process notifications as they arrive
  3. Verify the queue worker is registered:

    drush queue:list

    You should see webhook_entities_processor in the list.

Upgrading from 1.x to 2.0

  1. Pull the updated code and clear cache:

    drush cr
  2. Verify the module is functioning:

    drush watchdog:show --type=as_webhook_entities --count=10

CONFIGURATION

  • Settings UI: /admin/config/as_webhook_entities/settings
  • Notifications are queued (webhook_entities_processor) and the listener answers at once
  • With "Process notifications as soon as they arrive" on (config key crontrigger), the queue is drained in batches right after the listener responds, and the batch's nodes are indexed in Search API; see WebhookQueueDrainer
  • A drain stops claiming after 40 seconds and, if items remain, posts an empty body to its own listener to start the next one, so nothing waits for cron
  • Cron still drains the queue (30 seconds per run), which is the only processing when the setting is off
  • Logs create/update/delete operations as as_webhook_entities

ARCHITECTURE

Strategy Pattern (v2.0+)

Per-type field logic is handled by dedicated handler classes. WebhookCrudManager is a thin dispatcher that handles shared logic (title, status, departments/domain access, save) and delegates to the appropriate handler.

as_webhook_entities/
├── src/
│   ├── WebhookCrudManager.php              - Thin dispatcher, shared CRUD logic
│   ├── WebhookUuidLookup.php               - Looks up existing entities by UUID
│   ├── WebhookHandler/
│   │   ├── WebhookHandlerInterface.php     - applyCreateFields / applyUpdateFields
│   │   ├── WebhookHandlerBase.php          - Shared lookup helpers
│   │   ├── PersonWebhookHandler.php
│   │   ├── ArticleWebhookHandler.php
│   │   ├── MediaReportEntryWebhookHandler.php
│   │   ├── MediaReportPersonWebhookHandler.php
│   │   └── TermWebhookHandler.php
│   └── Plugin/QueueWorker/
│       └── WebhookEntitiesQueue.php        - Queue processor, event dispatcher

Supported Entity Types

Payload type Drupal entity Handler
person node:person PersonWebhookHandler
article node:article ArticleWebhookHandler
media_report_entry node:media_report_entry MediaReportEntryWebhookHandler
media_report_person node:person MediaReportPersonWebhookHandler
term taxonomy_term TermWebhookHandler

Adding a New Entity Type

  1. Create a class in src/WebhookHandler/ extending WebhookHandlerBase
  2. Implement getType(), applyCreateFields(), and applyUpdateFields()
  3. Register it in WebhookCrudManager::__construct():
    'my_type' => new MyTypeWebhookHandler($entity_type_manager),

MAINTAINERS

Current maintainers for Drupal 10:

  • Mark Wilson (markewilson)

TROUBLESHOOTING

Queue items not processing

  1. Check the queue size:

    drush queue:list
  2. Run the queue manually:

    drush queue:run webhook_entities_processor
  3. Check recent logs:

    drush watchdog:show --type=as_webhook_entities --count=20

Entity not created or updated

  1. Verify the UUID lookup is finding the entity:

    lando drush php-eval "
    \$lookup = \Drupal::service('as_webhook_entities.uuid_lookup');
    \$entity = \$lookup->findEntity('YOUR-UUID-HERE', 'person');
    echo \$entity ? 'Found: ' . \$entity->id() : 'Not found';
    "
  2. Push a test payload directly into the queue:

    lando drush php-eval "
    \$payload = json_encode([
      'event' => 'create',
      'type' => 'person',
      'uuid' => 'test-uuid-001',
      'status' => '1',
      'uid' => '1',
      'title' => 'Test Person',
      'field_departments_programs' => [],
      'field_overview_research' => [],
      'field_links' => [],
    ]);
    \Drupal::queue('webhook_entities_processor')->createItem(\$payload);
    echo 'Queued.' . PHP_EOL;
    "
    lando drush queue:run webhook_entities_processor
  3. Check for field errors — if a field in the payload doesn't exist on the destination bundle, the update will fail. Verify field existence:

    lando drush php-eval "
    \$fields = \Drupal::service('entity_field.manager')->getFieldDefinitions('node', 'article');
    echo implode(', ', array_keys(\$fields));
    "

CHANGELOG

2.0

  • Refactored WebhookCrudManager using the Strategy pattern — per-type field logic extracted into dedicated handler classes under src/WebhookHandler/
  • Added WebhookHandlerInterface and WebhookHandlerBase with shared entity lookup helpers (lookupTermTidsByName, lookupTermTidsByProperty, lookupNodeNidsByRemoteUuid)
  • Added handler classes: PersonWebhookHandler, ArticleWebhookHandler, MediaReportEntryWebhookHandler, MediaReportPersonWebhookHandler, TermWebhookHandler
  • WebhookCrudManager is now a thin dispatcher; shared logic (departments/domain access, node creation, save) remains centralised
  • Simplified WebhookUuidLookup::findEntity() to a type-keyed lookup map, eliminating duplicate if blocks
  • Cleaned up WebhookEntitiesQueue: removed redundant UUID ternary, extracted dispatchCreate() helper, removed dead code
  • Fixed two bugs in MediaReportEntryWebhookHandler: field_news_date was incorrectly read from field_outlet_name, and field_media_report_public_cat had a variable typo
  • Moved field_portrait_image_alt out of shared update logic into ArticleWebhookHandler where it belongs