← All articles

Data Sync

SharePoint List & Webhook Event Engine

Subscribe to SharePoint list changes with webhooks, trigger Drupal workflows or Power Automate flows, and enable two-way list editing.

Published February 25, 2024
drupalsharepoint-listswebhooksreal-time-syncautomationoffice365power-automate

Overview

The SharePoint List & Webhook Event Engine provides real-time synchronization between Drupal and SharePoint Lists. This integration enables automated workflows triggered by list changes, bi-directional editing capabilities, and seamless data flow between platforms using webhooks and the Microsoft Graph API.

Features

  • Real-time webhook subscriptions for list changes
  • Bi-directional list editing from Drupal
  • Automated workflow triggers
  • Power Automate flow integration
  • Custom list views in Drupal
  • Field mapping and transformation
  • Batch operations support
  • Conflict resolution and versioning

Technical Details

Technologies: Microsoft Graph API, SharePoint REST API, Webhooks, Drupal Entity API, Drupal Views, Power Automate

Requirements:

  • Drupal 9.x or 10.x
  • SharePoint Online
  • Azure AD app with Sites.ReadWrite.All permission
  • Public HTTPS endpoint for webhooks
  • Valid SSL certificate
  • Drupal Views module

API Endpoints:

Create subscription: POST /sites/{site-id}/lists/{list-id}/subscriptions
List items: GET /sites/{site-id}/lists/{list-id}/items?$expand=fields
Create item: POST /sites/{site-id}/lists/{list-id}/items
Update item: PATCH /sites/{site-id}/lists/{list-id}/items/{item-id}/fields

Implementation

  1. Configure Azure AD app with SharePoint permissions
  2. Create Drupal entity type for SharePoint list items
  3. Implement Graph API client for list operations
  4. Set up webhook subscription endpoint
  5. Implement webhook validation and renewal
  6. Create field mapping configuration
  7. Build bi-directional sync service
  8. Implement conflict resolution logic
  9. Add Views integration for list data
  10. Configure Power Automate connectors
<?php
// Example: SharePoint webhook subscription manager
namespace Drupal\sharepoint_lists\Service;

use Microsoft\Graph\Graph;

class WebhookSubscriptionManager {
  
  protected $graphClient;
  protected $siteId;
  
  public function __construct($graph_client, $config) {
    $this->graphClient = $graph_client;
    $this->siteId = $config->get('site_id');
  }
  
  public function createSubscription($list_id) {
    $notification_url = Url::fromRoute(
      'sharepoint_lists.webhook',
      ['list_id' => $list_id],
      ['absolute' => TRUE]
    )->toString();
    
    // Subscription expires in 6 months (max allowed)
    $expiration = new \DateTime('+6 months');
    
    $subscription_data = [
      'resource' => "/sites/{$this->siteId}/lists/{$list_id}",
      'notificationUrl' => $notification_url,
      'expirationDateTime' => $expiration->format('Y-m-d\TH:i:s.000\Z'),
      'clientState' => bin2hex(random_bytes(16)),
    ];
    
    try {
      $endpoint = "/sites/{$this->siteId}/lists/{$list_id}/subscriptions";
      $subscription = $this->graphClient
        ->createRequest('POST', $endpoint)
        ->attachBody($subscription_data)
        ->execute();
      
      // Store subscription details
      \Drupal::state()->set(
        "sharepoint_lists.subscription.{$list_id}",
        [
          'id' => $subscription->getId(),
          'client_state' => $subscription_data['clientState'],
          'expiration' => $subscription->getExpirationDateTime(),
        ]
      );
      
      \Drupal::logger('sharepoint_lists')->info(
        'Created webhook subscription for list: @list_id',
        ['@list_id' => $list_id]
      );
      
      return $subscription;
      
    } catch (\Exception $e) {
      \Drupal::logger('sharepoint_lists')->error(
        'Failed to create webhook subscription: @message',
        ['@message' => $e->getMessage()]
      );
      throw $e;
    }
  }
  
  public function handleWebhookNotification($list_id, $notification) {
    // Validate client state
    $subscription = \Drupal::state()->get("sharepoint_lists.subscription.{$list_id}");
    if ($notification['clientState'] !== $subscription['client_state']) {
      throw new \Exception('Invalid client state');
    }
    
    // Process changes
    foreach ($notification['value'] as $change) {
      $this->processChange($list_id, $change);
    }
  }
  
  protected function processChange($list_id, $change) {
    $sync_service = \Drupal::service('sharepoint_lists.sync');
    
    // Fetch the changed item
    $item = $this->graphClient
      ->createRequest('GET', $change['resource'])
      ->setReturnType(Model\ListItem::class)
      ->execute();
    
    // Sync to Drupal
    $sync_service->syncItemToDrupal($list_id, $item);
  }
  
  public function renewSubscription($list_id) {
    $subscription = \Drupal::state()->get("sharepoint_lists.subscription.{$list_id}");
    $new_expiration = new \DateTime('+6 months');
    
    $endpoint = "/sites/{$this->siteId}/lists/{$list_id}/subscriptions/{$subscription['id']}";
    $this->graphClient
      ->createRequest('PATCH', $endpoint)
      ->attachBody([
        'expirationDateTime' => $new_expiration->format('Y-m-d\TH:i:s.000\Z'),
      ])
      ->execute();
  }
}

Use Cases

  • Inventory management synchronization
  • Project tracking and status updates
  • Customer data management
  • Product catalog synchronization
  • Multi-system data consistency

Benefits

  • Real-time data synchronization across platforms
  • Automated workflow triggers from list changes
  • Reduced manual data entry and errors
  • Centralized data management in SharePoint
  • Powerful Drupal views and reporting
  • Integration with Power Automate workflows
  • Improved data consistency and reliability