2. Qui sommes nous ?
● Matthieu Guillermin - Multitask
Consultant chez
@mguillermin
● Nicolas Chambrier - Nodefag Troll
Développeur chez
@naholyr
3. Pourquoi intégrer Drupal / NodeJS ?
● On va de plus en plus vers des architectures
hétérogènes
● Utiliser chaque techno pour ce qu'elle sait
bien faire
● Les besoins de "temps-réels" sont de plus
en plus présents
5. Le Cas d'Utilisation
● Notre but était de :
○ Utiliser Drupal pour ce qu'il fait de mieux
○ Utiliser NodeJS pour ce qu'il fait de mieux
○ Trouver un cas d'utilisation réellement utile
● En utilisant des fonctionnalités classiques à
la fois pour Drupal et pour NodeJS
6. Le Cas d'Utilisation
● Drupal :
○ Gestion de contenu
○ Contribution
○ Workflow
● NodeJS :
○ Mise en place d'une API REST
○ Application "temps réel"
7. Le Cas d'Utilisation
● Système de notification
○ Messages affichés directement dans le navigateur
○ En réponse à des événements Drupal
○ Envoi en "temps-réel"
● Paramétrable
○ Définition de règles d'envoi de messages
○ Définition des destinataires des message
9. Comment le réaliser ?
● Utilisation de standards Web
● Communication depuis Drupal
○ Web Services
○ REST
● Communication depuis le client
○ Accès direct au serveur NodeJS
10. Comment le réaliser ?
Appel Web Service Serveur
Drupal
NodeJS
Dispatch &
Action
Notification
Navigateur Navigateur
Navigateur
client client
client
12. Comment on a travaillé
● Pas de super-héros !
○ Je n'y connais rien à Drupal
○ Il n'y connait pas grand chose à Node
● Il faut:
○ Savoir installer et faire tourner le travail de l'autre
■ Drupal: LAMP + dump MySQL
■ Node: NodeJS → `node app.js`
○ Définir l'API avant tout
■ Sur un coin de table 3 jours avant
○ C'est tout !
13. Un serveur HTTP + WebSocket
● On veut que ce soit simple
○ Toute la stack dans une seule application
○ Notifier = une requête HTTP simple
○ Afficher les notifications = une inclusion de script +
une ligne de config
● On veut que ce soit sécurisé, performant,
et scalable
○ Oui mais c'est un tout autre sujet ;)
14. One app to rule them all
var app = http.createServer(handler)
Notifications
function handler (req, res) {
if (req.url === "/" &&
req.method === "POST") {
newNotification(req, res)
}
else {
Fichiers statiques serveStatic(req, res);
}
}
WebSockets var io = require('socket.io')
io.listen(app)
15. Modèle de données
● Identifier les paramètres requis:
○ On veut un message
→ "message": String
○ On veut cibler les utilisateurs par rôle
→ "roles": [String]
○ On veut éventuellement cibler par utilisateur
→ "users": [String]
● JSON: { "roles": ["administrator"], "users": [1],
"message": "hello, world" }
16. API REST
function newNotification (req, res)
● JSON {
if (!isJSON(req)) {
● Création = POST error400()
}
● Réponse adaptée:
201 - Created var notif = JSON.parse(req.body)
400 - Bad Request validateNotif(notif)
500 - Server Error
notif.id = uuid()
● Broadcast via broadcastViaWebSocket(notif)
WebSocket
respond(res,
201, "application/json",
JSON.stringify(notif))
}
17. function display (id, roles) {
API Client addJS("socket.io.js")
addCSS("notifications.css")
● Script servi par addDOMContainer()
l'application
var socket = io.connect()
● Une seule fonction socket.on('connect', function ()
{
socket.emit('roles', roles)
● Ajoute seul les socket.emit('user_id', id)
dépendances })
JS & CSS socket.on('error', function (e) {
notifyError(e)
})
● Enrichit le DOM socket.on('notif', function (n) {
notifyInfo(n)
})
● Gère le WebSocket
}
18. API : Utilisation
Client
<script src="http://localhost:8080/notifications.js"
></script>
<script>notifications.display(1, ["administrator"])</script>
Serveur
POST http://localhost:8080
Content-Type: application/json
{"roles":["administrator"],"message":"Show me your big
power"}
Simple enough ?
19. Socket.IO
● Pourquoi Socket.IO ?
○ Puissant, simple, efficace, hyper-compatible.
● Comment distribuer les messages ?
○ Filtrer côté client ? Pas efficace
○ Côté serveur: comment router les message ?
● Socket.IO à la rescousse: les "rooms":
○ Client: socket.join("room")
○ Serveur: io.sockets.in("room").emit(data)
○ Nos salons: "USER:$id" & "ROLE:$role"
● Problème des notifications en double
23. Appel de Web Service
● Utilisation du module wsclient
○ Permet de définir des "services"
○ Et sur chaque services, des "opérations"
● Différents types de services :
○ SOAP
○ REST
24. Appel de Web Service
● Les opérations sont définies avec
○ une URL
○ une Method HTTP
○ des paramètres
○ un type de retour
● Paramètres
○ Type, Required, Multiple, Default value
25. Appel de Web Service
● Paramétrage possible
○ depuis l'interface
○ via l'API
● L'utilisation de l'API offre plus de possibilités
○ Ex : HTTP Method de l'opération
○ Ex : Format à utiliser (Json, Form,...)
26. Appel de Web Service
● Une fois définis, les opérations peuvent être
appelées via l'API
$serviceName = "notification";
$operationName = "send_message";
$service = wsclient_service_load($serviceName);
$service->invoke($operationName,
array("message" => "Ceci est mon message"));
27. Déclenchement des envois de
message
● La solution :
○ Utiliser l'API de wsclient directement en
l'implémentant dans un module
● Problème :
○ Tout doit être prévu dans le code (quand envoyer le
message ? à qui ? ...)
28. Déclenchement des envois de
message
● Utilisation de Rules
○ wsclient expose les opérations sous forme d'actions
Rules
○ Avec possibilité de renseigner les paramètres
● Avantages
○ Tout est configurable depuis le backoffice
○ Les valeurs des paramètres peuvent contenir des
tokens
29. Intégration javascript "client"
● Pour communiquer avec NodeJS, il faut
inclure un javascript côté client
○ Ce script doit être chargé systématiquement sur
chaque page
● On doit aussi initialiser en js l'Id et les rôles
de l' utilisateur
○ Utilisation de drupal_add_js()
33. Pistes d'amélioration
● Ajout de nouveaux attributs aux messages
○ Type
○ Priorité / "Sticky"
○ Emetteur
○ ...
● A ajouter à l'API REST, dans la définition de
service wsclient et à gérer dans le javascript
client
34. Pistes d'amélioration
● Gérer l'authentification utilisateur côté
NodeJS
○ Pour s'assurer que l'ID utilisateur envoyé est bien
celui de l'utilisateur courant
● Mécanisme de jeton envoyé par le client et
vérifié par NodeJS auprès de Drupal (SSO)
○ Cette vérification renverrait l'ID et les rôles
utilisateurs
35. Pistes d'amélioration
● Sécuriser l'API REST
○ Eviter qu'un tiers n'utilise l'API REST pour envoyer
des messages en n'autorisant les envois de
message que depuis Drupal
● Utilisation d'une clé d'API
○ la clé serait validée à chaque appel d'API
36. Pistes d'amélioration
● Améliorer l'UI des notifications
○ Afficher/cacher
○ Persister les notifications entre les pages (statut
lu/non lu)
● Utiliser un vrai pub/sub pour la transmission
○ Redis, RabbitMQ, ØMQ…
● Les améliorations de performance du serveur