Quick Reference¶
Fast reference guide for common tasks and code snippets.
Installation¶
Via DDEV (Recommended)¶
Easiest Installation Method
Via Docker Compose¶
Docker Compose Setup
Configuration¶
Module Configuration¶
Quick Configuration Steps
- Visit:
/admin/config/system/notification-server - Set URLs:
- DDEV: Server URL:
http://notification-server:3000, WebSocket:ws://notification-server:8080 - Local: Server URL:
http://localhost:3000, WebSocket:ws://localhost:8080 - Save configuration
Permissions¶
Grant these permissions as needed:
- administer notification_server configuration - Admin access
- access notification_server http demo - HTTP demo access
- access notification_server websocket demo - WebSocket demo access
Basic Usage¶
Publishing Notifications¶
Basic Notification Publishing
PHP
<?php
// Get service
$client = \Drupal::service('notification_server.client');
// Simple message
$client->publishNotification('alerts', 'Server maintenance starting');
// Structured data
$client->publishNotification('updates', [
'type' => 'content_update',
'title' => 'New Article Published',
'data' => ['id' => 123, 'author' => 'John']
]);
Client Management¶
Client ID Operations
Channel Management¶
Channel Creation and Access Control
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);
$client->createChannel($channel);
// Private channel
$rules = new ChannelRulesDTO(
allowedClientIds: [$client_id],
maxSubscribers: 50
);
$channel = new ChannelDTO('private_updates', $rules);
$client->createChannel($channel);
// Grant/revoke access
$client->grantChannelAccess('channel_name', $client_id);
$client->revokeChannelAccess('channel_name', $client_id);
WebSocket Integration¶
JavaScript Setup¶
WebSocket Client Implementation
JavaScript
// Get WebSocket URL from Drupal settings
const websocketUrl = drupalSettings.notificationServer.websocketUrl;
const clientId = drupalSettings.notificationServer.clientId;
// Connect
const socket = new WebSocket(`${websocketUrl}?clientId=${clientId}`);
// Event handlers
socket.onopen = () => console.log('Connected');
socket.onclose = () => console.log('Disconnected');
socket.onerror = (error) => console.error('Error:', error);
// Handle messages
socket.onmessage = function(event) {
const data = JSON.parse(event.data);
if (data.type === 'notification') {
handleNotification(data.data);
}
};
// Subscribe to channel
socket.send(JSON.stringify({
type: 'subscribe',
clientId: clientId,
channel: 'channel_name'
}));
Passing Data to JavaScript¶
PHP
<?php
// In controller or form
$build['#attached']['drupalSettings']['notificationServer'] = [
'websocketUrl' => $client->getWebsocketEndpoint(),
'clientId' => $client_id
];
// In hook_preprocess_page()
$variables['#attached']['drupalSettings']['notificationServer'] = [
'websocketUrl' => \Drupal::service('notification_server.client')->getWebsocketEndpoint(),
'clientId' => $_SESSION['notification_client_id'] ?? null
];
Common Integration Patterns¶
Entity Hooks¶
PHP
<?php
// In .module file
use Drupal\node\NodeInterface;
/**
* Implements hook_entity_insert().
*/
function mymodule_entity_insert(EntityInterface $entity) {
if ($entity instanceof NodeInterface) {
$client = \Drupal::service('notification_server.client');
$client->publishNotification('content_updates', [
'action' => 'created',
'type' => $entity->bundle(),
'title' => $entity->getTitle(),
'id' => $entity->id(),
'author' => $entity->getOwner()->getDisplayName()
]);
}
}
/**
* Implements hook_entity_update().
*/
function mymodule_entity_update(EntityInterface $entity) {
if ($entity instanceof NodeInterface) {
$client = \Drupal::service('notification_server.client');
$client->publishNotification('content_updates', [
'action' => 'updated',
'type' => $entity->bundle(),
'title' => $entity->getTitle(),
'id' => $entity->id(),
'changed' => $entity->getChangedTime()
]);
}
}
User Activity Tracking¶
PHP
<?php
/**
* Implements hook_user_login().
*/
function mymodule_user_login($account) {
$client = \Drupal::service('notification_server.client');
// Generate client ID for this session
$client_id = $client->generateClientId(null, [
'user_id' => $account->id(),
'login_time' => time()
]);
// Store in session
$_SESSION['notification_client_id'] = $client_id;
// Auto-subscribe to user channels
$client->subscribeToChannel("user_{$account->id()}", $client_id);
$client->subscribeToChannel('announcements', $client_id);
// Subscribe based on roles
foreach ($account->getRoles() as $role) {
$client->subscribeToChannel("role_{$role}", $client_id);
}
}
/**
* Implements hook_user_logout().
*/
function mymodule_user_logout($account) {
$client_id = $_SESSION['notification_client_id'] ?? null;
if ($client_id) {
$client = \Drupal::service('notification_server.client');
$client->removeClient($client_id);
unset($_SESSION['notification_client_id']);
}
}
Form Integration¶
PHP
<?php
// In form submit handler
public function submitForm(array &$form, FormStateInterface $form_state) {
// Process form data
$data = $form_state->getValues();
// Send notification
$client = \Drupal::service('notification_server.client');
$client->publishNotification('form_submissions', [
'form_id' => $form['#id'],
'user_id' => \Drupal::currentUser()->id(),
'data' => $data,
'timestamp' => time()
]);
$this->messenger()->addStatus($this->t('Form submitted and notification sent.'));
}
Queue Integration¶
PHP
<?php
// Queue worker for sending notifications
class NotificationQueueWorker extends QueueWorkerBase {
/**
* {@inheritdoc}
*/
public function processItem($data) {
$client = \Drupal::service('notification_server.client');
try {
$result = $client->publishNotification($data['channel'], $data['message']);
if ($result === null) {
throw new SuspendQueueException('Failed to send notification');
}
} catch (Exception $e) {
\Drupal::logger('mymodule')->error('Queue notification failed: @error', [
'@error' => $e->getMessage()
]);
throw new SuspendQueueException($e->getMessage());
}
}
}
// Adding items to queue
```php
<?php
$queue = \Drupal::queue('notification_queue');
$queue->createItem([
'channel' => 'batch_updates',
'message' => [
'type' => 'batch_complete',
'items_processed' => 100,
'completed_at' => time()
]
]);
Drupal 10/11 Compatibility¶
Service Definition¶
YAML
# mymodule.services.yml
services:
mymodule.notification_handler:
class: Drupal\mymodule\Service\NotificationHandler
arguments: ['@notification_server.client', '@current_user', '@logger.factory']
Service Class¶
PHP
<?php
namespace Drupal\mymodule\Service;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\notification_server\Service\NotificationServerClientInterface;
class NotificationHandler {
public function __construct(
private readonly NotificationServerClientInterface $notificationClient,
private readonly AccountInterface $currentUser,
LoggerChannelFactoryInterface $loggerFactory
) {
$this->logger = $loggerFactory->get('mymodule');
}
public function sendUserNotification(string $message, int $userId = null): bool {
$userId = $userId ?? $this->currentUser->id();
try {
$result = $this->notificationClient->publishNotification(
"user_{$userId}",
[
'message' => $message,
'timestamp' => time(),
'user_id' => $userId
]
);
return $result !== null;
} catch (\Exception $e) {
$this->logger->error('Failed to send notification: @error', [
'@error' => $e->getMessage()
]);
return false;
}
}
}
Testing¶
PHPUnit Tests¶
PHP
<?php
namespace Drupal\Tests\mymodule\Unit;
use Drupal\Tests\UnitTestCase;
use Drupal\notification_server\Service\NotificationServerClientInterface;
use Drupal\mymodule\Service\NotificationHandler;
class NotificationHandlerTest extends UnitTestCase {
public function testSendUserNotification() {
// Mock the notification client
$client = $this->createMock(NotificationServerClientInterface::class);
$client->expects($this->once())
->method('publishNotification')
->willReturn(['success' => true]);
// Test the handler
$handler = new NotificationHandler($client, $this->createMock(AccountInterface::class), $this->createMock(LoggerChannelFactoryInterface::class));
$result = $handler->sendUserNotification('Test message', 123);
$this->assertTrue($result);
}
}
Functional Tests¶
PHP
<?php
namespace Drupal\Tests\mymodule\Functional;
use Drupal\Tests\BrowserTestBase;
class NotificationIntegrationTest extends BrowserTestBase {
protected static $modules = ['notification_server', 'mymodule'];
public function testNotificationConfiguration() {
// Create admin user
$admin = $this->drupalCreateUser(['administer notification_server configuration']);
$this->drupalLogin($admin);
// Visit configuration page
$this->drupalGet('/admin/config/system/notification-server');
$this->assertSession()->statusCodeEquals(200);
// Test configuration form
$this->submitForm([
'server_url' => 'http://localhost:3000',
'websocket_url' => 'ws://localhost:8080'
], 'Save configuration');
$this->assertSession()->pageTextContains('The configuration options have been saved.');
}
}
Debugging¶
Enable Debug Logging¶
PHP
<?php
// In settings.php
$config['system.logging']['error_level'] = 'verbose';
// Check Drupal logs
tail -f /var/log/drupal/drupal.log | grep notification_server
Test Connectivity¶
Bash
# Test HTTP API
curl http://localhost:3000/health
# Test WebSocket (install wscat: npm install -g wscat)
wscat -c ws://localhost:8080?clientId=test123
# Test Redis
redis-cli ping
Debug WebSocket in Browser¶
JavaScript
// Enable debug logging
socket.onmessage = function(event) {
console.log('WebSocket message received:', event.data);
try {
const data = JSON.parse(event.data);
console.log('Parsed data:', data);
} catch (e) {
console.error('Failed to parse message:', e);
}
};
// Debug connection state
setInterval(() => {
console.log('WebSocket state:', socket.readyState);
}, 5000);
Troubleshooting Quick Fixes¶
Common Issues and Quick Solutions
Bash
# Service not running
docker ps | grep notification-server
docker-compose up -d
# Permission denied
drush upwd admin --password=admin
drush user-login
# Configuration not saved
drush cr
drush cex -y
# WebSocket connection fails
# Check CORS settings in notification server
# Verify WebSocket URL format (ws:// not http://)
# Redis connection issues
docker logs redis
redis-cli ping
Reset Configuration¶
Bash
# Reset module configuration
drush config-delete notification_server.settings
# Reinstall module
drush pmu notification_server
drush en notification_server
# Clear all caches
drush cr
Production Checklist¶
Production Deployment Checklist
Security¶
- Use
wss://for WebSocket connections - Set appropriate CORS origins
- Implement rate limiting
- Monitor for suspicious activity
- Use secure Redis configuration
Performance¶
- Configure Redis memory limits
- Set appropriate message TTL
- Monitor connection counts
- Implement connection pooling
- Use load balancer for multiple servers
Monitoring¶
- Set up health checks
- Monitor server logs
- Track WebSocket connection metrics
- Monitor Redis performance
- Set up alerts for failures
Backup¶
- Backup Redis data
- Backup module configuration
- Document server settings
- Test recovery procedures