Skip to content

API Reference

Complete reference for the Notification Server module APIs and services.

NotificationServerClientInterface

The main service interface for interacting with the notification server.

Service Access

PHP
<?php
// Dependency injection (recommended)
public function __construct(
  NotificationServerClientInterface $notification_client
) {
  $this->notificationClient = $notification_client;
}

// Service container
$notification_client = \Drupal::service('notification_server.client');

Publishing and Notifications

publishNotification()

Publishes a message to a specific channel.

Signature:

PHP
<?php
public function publishNotification(
  string $channel, 
  object|array|string|float|int|bool|null $message
): ?array

Parameters: - $channel (string): The channel name to publish to - $message (mixed): The message content (any JSON-serializable type)

Returns: - array|null: Notification data if successful, NULL on failure

Examples:

Publishing Different Message Types

PHP
<?php
// Simple string message
$result = $notification_client->publishNotification('alerts', 'Server maintenance in 5 minutes');

// Structured data
$result = $notification_client->publishNotification('user_updates', [
  'type' => 'profile_update',
  'user_id' => 123,
  'changes' => ['email', 'name'],
  'timestamp' => time()
]);

// Complex object
$notification_data = [
  'event' => 'content_published',
  'content' => [
    'id' => $node->id(),
    'title' => $node->getTitle(),
    'type' => $node->bundle(),
    'author' => [
      'uid' => $node->getOwner()->id(),
      'name' => $node->getOwner()->getDisplayName()
    ]
  ],
  'metadata' => [
    'published_at' => $node->getCreatedTime(),
    'workflow_state' => $node->get('moderation_state')->value
  ]
];

$result = $notification_client->publishNotification('content_updates', $notification_data);

getNotifications()

Retrieves notification history for a channel.

Signature:

PHP
<?php
public function getNotifications(string $channel, int $limit = 10): array

Parameters: - $channel (string): The channel name - $limit (int): Maximum number of notifications to return (1-100, default: 10)

Returns: - array: Array of notification objects

Examples:

Retrieving and Processing Notifications

PHP
<?php
// Get last 10 notifications
$notifications = $notification_client->getNotifications('user_alerts');

// Get last 25 notifications
$notifications = $notification_client->getNotifications('system_events', 25);

// Process notifications
foreach ($notifications as $notification) {
  $message = $notification['message'];
  $timestamp = $notification['timestamp'];
  $channel = $notification['channel'];

  // Process notification data
  processNotification($message, $timestamp, $channel);
}

Client Management

generateClientId()

Generates a new client ID for WebSocket connections.

Signature:

PHP
<?php
public function generateClientId(string $clientId = null, array $metadata = []): ?string

Parameters: - $clientId (string|null): Optional specific client ID to validate/register - $metadata (array): Optional metadata to associate with the client

Returns: - string|null: Generated client ID if successful, NULL on failure

Examples:

Client ID Generation Options

PHP
<?php
// Generate random client ID
$client_id = $notification_client->generateClientId();

// Generate with metadata
$client_id = $notification_client->generateClientId(null, [
  'user_id' => $user->id(),
  'session_id' => session_id(),
  'user_agent' => $_SERVER['HTTP_USER_AGENT'],
  'ip_address' => \Drupal::request()->getClientIp(),
  'created_at' => time()
]);

// Validate/register specific client ID
$client_id = $notification_client->generateClientId('user_123_session_abc', [
  'user_id' => 123,
  'session' => 'abc'
]);

validateClientId()

Validates whether a client ID is valid and active.

Signature:

PHP
<?php
public function validateClientId(string $clientId): bool

Parameters: - $clientId (string): The client ID to validate

Returns: - bool: TRUE if valid, FALSE otherwise

Examples:

Client ID Validation Pattern

PHP
<?php
// Check if client ID is valid before WebSocket connection
$client_id = $_SESSION['notification_client_id'] ?? null;

if ($client_id && $notification_client->validateClientId($client_id)) {
  // Use existing client ID
  $websocket_url = "ws://localhost:8080?clientId=" . urlencode($client_id);
} else {
  // Generate new client ID
  $client_id = $notification_client->generateClientId();
  $_SESSION['notification_client_id'] = $client_id;
  $websocket_url = "ws://localhost:8080?clientId=" . urlencode($client_id);
}

removeClient()

Removes a client and all associated data.

Signature:

PHP
<?php
public function removeClient(string $clientId): bool

Parameters: - $clientId (string): The client ID to remove

Returns: - bool: TRUE if successful, FALSE otherwise

Examples:

PHP
<?php
// Clean up client on user logout
function cleanup_notification_client($user_id) {
  $client_id = get_user_client_id($user_id);

  if ($client_id) {
    $notification_client = \Drupal::service('notification_server.client');
    $success = $notification_client->removeClient($client_id);

    if ($success) {
      clear_user_client_id($user_id);
    }

    return $success;
  }

  return true;
}

Channel Management

createChannel()

Creates a new notification channel with access rules.

Signature:

PHP
<?php
public function createChannel(ChannelDTO $channel): bool

Parameters: - $channel (ChannelDTO): Channel configuration object

Returns: - bool: TRUE if successful, FALSE otherwise

Examples:

PHP
<?php
use Drupal\notification_server\DTO\ChannelDTO;
use Drupal\notification_server\DTO\ChannelRulesDTO;

// Public channel
$rules = new ChannelRulesDTO(isPublic: true);
$channel = new ChannelDTO('public_announcements', $rules);
$notification_client->createChannel($channel);

// Private channel with specific client access
$rules = new ChannelRulesDTO(
  allowedClientIds: ['client_123', 'client_456'],
  maxSubscribers: 50
);
$channel = new ChannelDTO('private_updates', $rules);
$notification_client->createChannel($channel);

// Pattern-based access
$rules = new ChannelRulesDTO(
  allowedPatterns: ['admin_.*', 'moderator_.*'],
  maxSubscribers: 100
);
$channel = new ChannelDTO('staff_channel', $rules);
$notification_client->createChannel($channel);

// Using array data
$channel_data = [
  'channel' => 'dynamic_channel',
  'rules' => [
    'isPublic' => false,
    'allowedClientIds' => [$client_id],
    'maxSubscribers' => 25
  ]
];
$channel = ChannelDTO::fromArray($channel_data);
$notification_client->createChannel($channel);

grantChannelAccess()

Grants a specific client access to a channel.

Signature:

PHP
<?php
public function grantChannelAccess(string $channel, string $clientId): bool

Parameters: - $channel (string): The channel name - $clientId (string): The client ID to grant access to

Returns: - bool: TRUE if successful, FALSE otherwise

Examples:

PHP
<?php
// Grant user access to private channel
$user_client_id = get_user_client_id($user->id());
$success = $notification_client->grantChannelAccess('vip_updates', $user_client_id);

// Grant access based on user role
if ($user->hasRole('premium_member')) {
  $notification_client->grantChannelAccess('premium_channel', $user_client_id);
}

// Grant temporary access
$notification_client->grantChannelAccess('event_live_updates', $client_id);
// Set up task to revoke access after event

revokeChannelAccess()

Revokes a client's access to a channel.

Signature:

PHP
<?php
public function revokeChannelAccess(string $channel, string $clientId): bool

Parameters: - $channel (string): The channel name - $clientId (string): The client ID to revoke access from

Returns: - bool: TRUE if successful, FALSE otherwise

Examples:

PHP
<?php
// Revoke access when user role changes
$user_client_id = get_user_client_id($user->id());
$notification_client->revokeChannelAccess('admin_channel', $user_client_id);

// Revoke access on user suspension
if ($user->isBlocked()) {
  $channels = ['general', 'updates', 'announcements'];
  foreach ($channels as $channel) {
    $notification_client->revokeChannelAccess($channel, $user_client_id);
  }
}

Subscription Management

subscribeToChannel()

Subscribes a client to a channel (server-side subscription).

Signature:

PHP
<?php
public function subscribeToChannel(string $channel, string $clientId): bool

Parameters: - $channel (string): The channel name to subscribe to - $clientId (string): The WebSocket client ID

Returns: - bool: TRUE if successful, FALSE otherwise

Examples:

PHP
<?php
// Auto-subscribe user to relevant channels
function auto_subscribe_user_channels($user, $client_id) {
  $notification_client = \Drupal::service('notification_server.client');

  // Subscribe to user-specific channel
  $user_channel = "user_{$user->id()}";
  $notification_client->subscribeToChannel($user_channel, $client_id);

  // Subscribe based on user roles
  foreach ($user->getRoles() as $role) {
    $role_channel = "role_{$role}";
    $notification_client->subscribeToChannel($role_channel, $client_id);
  }

  // Subscribe to general announcements
  $notification_client->subscribeToChannel('announcements', $client_id);
}

unsubscribeFromChannel()

Unsubscribes a client from a channel (server-side unsubscription).

Signature:

PHP
<?php
public function unsubscribeFromChannel(string $channel, string $clientId): bool

Parameters: - $channel (string): The channel name to unsubscribe from - $clientId (string): The WebSocket client ID

Returns: - bool: TRUE if successful, FALSE otherwise

Examples:

PHP
<?php
// Unsubscribe from role-specific channels when role changes
function update_user_subscriptions($user, $old_roles, $new_roles) {
  $notification_client = \Drupal::service('notification_server.client');
  $client_id = get_user_client_id($user->id());

  // Unsubscribe from removed roles
  $removed_roles = array_diff($old_roles, $new_roles);
  foreach ($removed_roles as $role) {
    $role_channel = "role_{$role}";
    $notification_client->unsubscribeFromChannel($role_channel, $client_id);
  }

  // Subscribe to new roles
  $added_roles = array_diff($new_roles, $old_roles);
  foreach ($added_roles as $role) {
    $role_channel = "role_{$role}";
    $notification_client->subscribeToChannel($role_channel, $client_id);
  }
}

getWebsocketEndpoint()

Gets the configured WebSocket server URL.

Signature:

PHP
<?php
public function getWebsocketEndpoint(): string

Returns: - string: The WebSocket server URL

Examples:

PHP
<?php
// Get WebSocket URL for JavaScript
$websocket_url = $notification_client->getWebsocketEndpoint();

// Add to Drupal settings
$build['#attached']['drupalSettings']['notificationServer'] = [
  'websocketUrl' => $websocket_url,
  'clientId' => $client_id
];

// Use in Twig template
$variables['websocket_url'] = $websocket_url;

Data Transfer Objects (DTOs)

ChannelDTO

Represents a notification channel configuration.

Constructor:

PHP
<?php
public function __construct(
  string $channel,
  ?ChannelRulesDTO $rules = null
)

Methods: - getChannel(): string - Get the channel name - getRules(): ?ChannelRulesDTO - Get the channel rules - toArray(): array - Convert to array representation - static fromArray(array $data): self - Create from array data

Examples:

PHP
<?php
// Basic channel
$channel = new ChannelDTO('simple_channel');

// Channel with rules
$rules = new ChannelRulesDTO(isPublic: true);
$channel = new ChannelDTO('public_channel', $rules);

// From array data
$data = [
  'channel' => 'array_channel',
  'rules' => [
    'isPublic' => false,
    'allowedClientIds' => ['client1', 'client2']
  ]
];
$channel = ChannelDTO::fromArray($data);

// Convert to array for API calls
$array_data = $channel->toArray();

ChannelRulesDTO

Represents access control rules for a channel.

Constructor:

PHP
<?php
public function __construct(
  ?array $allowedClientIds = null,
  ?bool $isPublic = null,
  ?array $allowedPatterns = null,
  ?int $maxSubscribers = null
)

Methods: - getAllowedClientIds(): ?array - Get allowed client IDs - isPublic(): ?bool - Check if channel is public - getAllowedPatterns(): ?array - Get allowed patterns - getMaxSubscribers(): ?int - Get maximum subscribers - toArray(): array - Convert to array representation - static fromArray(array $data): self - Create from array data

Examples:

PHP
<?php
// Public channel
$rules = new ChannelRulesDTO(isPublic: true);

// Private channel with specific clients
$rules = new ChannelRulesDTO(
  allowedClientIds: ['client1', 'client2', 'client3'],
  maxSubscribers: 50
);

// Pattern-based access
$rules = new ChannelRulesDTO(
  allowedPatterns: ['admin_.*', 'user_\d+'],
  maxSubscribers: 100
);

// Complex rules
$rules = new ChannelRulesDTO(
  allowedClientIds: ['special_client'],
  allowedPatterns: ['premium_.*'],
  isPublic: false,
  maxSubscribers: 25
);

// From array
$rules = ChannelRulesDTO::fromArray([
  'allowedClientIds' => ['client1'],
  'isPublic' => false,
  'maxSubscribers' => 10
]);

WebSocket Protocol

Client-Side JavaScript API

Connection

JavaScript
// Establish connection
const websocketUrl = drupalSettings.notificationServer.websocketUrl;
const clientId = drupalSettings.notificationServer.clientId;
const socket = new WebSocket(`${websocketUrl}?clientId=${clientId}`);

// Connection event handlers
socket.onopen = function(event) {
  console.log('WebSocket connected');
  // Send initial subscriptions
};

socket.onclose = function(event) {
  console.log('WebSocket disconnected', event.code, event.reason);
  // Implement reconnection logic
};

socket.onerror = function(error) {
  console.error('WebSocket error:', error);
};

Message Handling

JavaScript
socket.onmessage = function(event) {
  try {
    const data = JSON.parse(event.data);

    switch (data.type) {
      case 'notification':
        handleNotification(data.data);
        break;
      case 'subscription_success':
        handleSubscriptionSuccess(data.channel);
        break;
      case 'subscription_error':
        handleSubscriptionError(data.channel, data.error);
        break;
      case 'error':
        handleError(data.message);
        break;
    }
  } catch (error) {
    console.error('Error parsing WebSocket message:', error);
  }
};

function handleNotification(notificationData) {
  // Process incoming notification
  console.log('Received notification:', notificationData);

  // Update UI
  displayNotification(notificationData.message);

  // Trigger custom events
  document.dispatchEvent(new CustomEvent('notification-received', {
    detail: notificationData
  }));
}

Channel Subscription

JavaScript
// Subscribe to channel
function subscribeToChannel(channel) {
  const message = {
    type: 'subscribe',
    clientId: clientId,
    channel: channel
  };

  socket.send(JSON.stringify(message));
}

// Unsubscribe from channel
function unsubscribeFromChannel(channel) {
  const message = {
    type: 'unsubscribe',
    clientId: clientId,
    channel: channel
  };

  socket.send(JSON.stringify(message));
}

// Bulk subscribe
function subscribeToChannels(channels) {
  channels.forEach(channel => {
    subscribeToChannel(channel);
  });
}

Message Formats

Subscription Messages

JavaScript
// Subscribe
{
  "type": "subscribe",
  "clientId": "client_123",
  "channel": "channel_name"
}

// Unsubscribe
{
  "type": "unsubscribe",
  "clientId": "client_123", 
  "channel": "channel_name"
}

Notification Messages

JavaScript
// Incoming notification
{
  "type": "notification",
  "data": {
    "channel": "channel_name",
    "message": "Notification content",
    "timestamp": "2023-12-01T10:00:00Z",
    "metadata": {
      "priority": "high",
      "sender": "system"
    }
  }
}

Response Messages

JavaScript
// Subscription success
{
  "type": "subscription_success",
  "channel": "channel_name"
}

// Subscription error
{
  "type": "subscription_error",
  "channel": "channel_name",
  "error": "Access denied"
}

// General error
{
  "type": "error",
  "message": "Error description"
}

Error Handling

Error Handling Best Practice

Always check return values from notification server methods. A null return value indicates failure, and you should implement appropriate fallback behavior.

Common Error Codes

HTTP Status Description Common Causes
400 Bad Request Invalid JSON, missing required fields
401 Unauthorized Invalid client ID
403 Forbidden No access to channel
404 Not Found Channel doesn't exist
409 Conflict Channel already exists
500 Internal Server Error Server or Redis connection issues

Error Handling Examples

Proper Error Handling

PHP
<?php
try {
  $result = $notification_client->publishNotification('channel', 'message');

  if ($result === null) {
    // Handle failure
    \Drupal::logger('notification_server')->error('Failed to publish notification to channel: @channel', [
      '@channel' => 'channel'
    ]);
  } else {
    // Handle success
    \Drupal::logger('notification_server')->info('Successfully published notification to channel: @channel', [
      '@channel' => 'channel'
    ]);
  }
} catch (Exception $e) {
  // Handle exceptions
  \Drupal::logger('notification_server')->error('Exception publishing notification: @message', [
    '@message' => $e->getMessage()
  ]);
}

Best Practices

Performance Best Practices

  1. Batch operations when possible
  2. Cache client IDs to avoid regeneration
  3. Use appropriate message TTL settings
  4. Monitor channel subscriber counts

Security Best Practices

  1. Validate all input data
  2. Use pattern-based access for scalability
  3. Implement rate limiting at application level
  4. Monitor for suspicious patterns

Error Handling Best Practices

  1. Always check return values
  2. Implement proper logging
  3. Handle network failures gracefully
  4. Provide user feedback

Code Organization Best Practices

  1. Use dependency injection for services
  2. Create helper functions for common operations
  3. Document complex channel rules
  4. Implement proper error recovery