Exemples d'Utilisation
Ce guide fournit des exemples complets et concrets démontrant comment utiliser le SDK Optave dans différents scénarios et frameworks. Chaque exemple inclut du code complet et prêt pour la production avec gestion des erreurs, meilleures pratiques et explications.
Configuration Client de Base (Navigateur)
Exemple complet pour les applications navigateur utilisant le pattern token provider:
import OptaveJavaScriptSDK from '@optave/client-sdk';
// Initialiser le SDK
const client = new OptaveJavaScriptSDK({
websocketUrl: 'wss://ws-{{tenant}}.oco.optave.{{tld}}',
authTransport: 'subprotocol',
// Token provider - récupère les tokens depuis votre backend sécurisé
tokenProvider: async () => {
// Remplacez '/api/optave-token' par votre véritable endpoint backend
const response = await fetch('/api/optave-token', {
method: 'POST',
credentials: 'include', // Inclure les cookies de session
headers: {
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`Échec de récupération du token: ${response.status}`);
}
const { token } = await response.json();
return token;
},
// Optionnel: Configurer les timeouts
connectionTimeoutMs: 30000,
requestTimeoutMs: 45000
});
// Configurer les écouteurs d'événements
client.on('open', () => {
console.log('✓ Connecté à Optave');
});
client.on('close', (event) => {
console.log('✗ Déconnecté:', event.code);
});
client.on('error', (error) => {
console.error('Erreur SDK:', error.message);
});
// Se connecter
try {
await client.openConnection();
console.log('Prêt à envoyer des messages');
} catch (error) {
console.error('Échec de connexion:', error);
}
Configuration Client de Base (Serveur)
Exemple complet pour les applications serveur Node.js utilisant les identifiants client:
import OptaveJavaScriptSDK from '@optave/client-sdk/server';
import dotenv from 'dotenv';
dotenv.config();
// Initialiser le SDK
const client = new OptaveJavaScriptSDK({
websocketUrl: process.env.OPTAVE_WEBSOCKET_URL,
authenticationUrl: process.env.OPTAVE_AUTHENTICATION_URL,
clientId: process.env.OPTAVE_CLIENT_ID,
clientSecret: process.env.OPTAVE_CLIENT_SECRET
});
// Configurer les écouteurs d'événements
client.on('open', () => {
console.log('✓ Serveur connecté à Optave');
});
client.on('error', (error) => {
console.error('Erreur SDK:', error);
});
// Authentifier et se connecter
try {
const token = await client.authenticate();
await client.openConnection(token);
console.log('Prêt à traiter les requêtes');
} catch (error) {
console.error('Échec d\'initialisation:', error);
process.exit(1);
}
Intégration React
Intégration React complète avec hooks et gestion d'état:
// hooks/useOptaveClient.js
import { useState, useEffect, useCallback, useRef } from 'react';
import OptaveJavaScriptSDK from '@optave/client-sdk';
export function useOptaveClient() {
const [isConnected, setIsConnected] = useState(false);
const [isConnecting, setIsConnecting] = useState(false);
const [error, setError] = useState(null);
const clientRef = useRef(null);
// Initialiser le client au montage
useEffect(() => {
const client = new OptaveJavaScriptSDK({
websocketUrl: import.meta.env.VITE_OPTAVE_WEBSOCKET_URL,
authTransport: 'subprotocol',
tokenProvider: async () => {
// Remplacez '/api/optave-token' par votre véritable endpoint backend
const response = await fetch('/api/optave-token', {
method: 'POST',
credentials: 'include'
});
if (!response.ok) {
throw new Error('Échec de récupération du token');
}
const { token } = await response.json();
return token;
}
});
// Gestionnaires d'événements
client.on('open', () => {
setIsConnected(true);
setIsConnecting(false);
setError(null);
});
client.on('close', () => {
setIsConnected(false);
});
client.on('error', (err) => {
setError(err);
setIsConnecting(false);
});
clientRef.current = client;
// Se connecter
setIsConnecting(true);
client.openConnection().catch((err) => {
setError(err);
setIsConnecting(false);
});
// Nettoyage au démontage
return () => {
if (clientRef.current) {
clientRef.current.cleanup();
}
};
}, []);
// Fonction d'aide pour envoyer un message
const sendMessage = useCallback(async (action, params) => {
if (!clientRef.current || !isConnected) {
throw new Error('Client non connecté');
}
try {
const response = await clientRef.current[action](params);
return response;
} catch (error) {
console.error(`${action} a échoué:`, error);
throw error;
}
}, [isConnected]);
return {
client: clientRef.current,
isConnected,
isConnecting,
error,
sendMessage
};
}
// components/ChatInterface.jsx
import React, { useState } from 'react';
import { useOptaveClient } from '../hooks/useOptaveClient';
function ChatInterface() {
const { isConnected, isConnecting, error, sendMessage } = useOptaveClient();
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [loading, setLoading] = useState(false);
const handleSend = async () => {
if (!input.trim() || !isConnected) return;
const userMessage = input;
setInput('');
setMessages(prev => [...prev, { role: 'user', content: userMessage }]);
try {
setLoading(true);
const response = await sendMessage('interaction', {
request: {
scope: {
conversations: [{
messages: [
...messages,
{ content: userMessage, participantId: 'user-1', timestamp: new Date().toISOString() }
]
}]
}
}
});
setMessages(prev => [
...prev,
{ role: 'assistant', content: response.data.message }
]);
} catch (err) {
console.error('Échec d\'envoi du message:', err);
setMessages(prev => [
...prev,
{ role: 'error', content: 'Échec d\'envoi du message. Veuillez réessayer.' }
]);
} finally {
setLoading(false);
}
};
if (isConnecting) {
return <div>Connexion à Optave...</div>;
}
if (error) {
return <div>Erreur: {error.message}</div>;
}
return (
<div className="chat-interface">
<div className="status">
{isConnected ? '✓ Connecté' : '✗ Déconnecté'}
</div>
<div className="messages">
{messages.map((msg, index) => (
<div key={index} className={`message ${msg.role}`}>
{msg.content}
</div>
))}
</div>
<div className="input-area">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSend()}
disabled={!isConnected || loading}
placeholder="Tapez un message..."
/>
<button onClick={handleSend} disabled={!isConnected || loading}>
{loading ? 'Envoi...' : 'Envoyer'}
</button>
</div>
</div>
);
}
export default ChatInterface;
Intégration Lightning Web Component Salesforce
Intégration LWC Salesforce complète avec le SDK Optave utilisant les Ressources Statiques:
remarque
Téléchargez le bundle UMD du SDK Optave (browser.umd.js) comme Ressource Statique nommée OptaveSDK dans Salesforce.
// optaveChatComponent.js - Lightning Web Component
import { LightningElement, track, api } from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import OPTAVE_SDK from '@salesforce/resourceUrl/OptaveSDK'; // Ressource statique
import getOptaveToken from '@salesforce/apex/OptaveController.getToken';
export default class OptaveChatComponent extends LightningElement {
@track connectionStatus = 'Déconnecté';
@track messages = [];
@api recordId; // Contexte d'enregistrement actuel
sdk;
async connectedCallback() {
try {
// Charger le SDK Optave depuis la ressource statique
await loadScript(this, OPTAVE_SDK);
// Initialiser le SDK
await this.initializeOptaveSDK();
} catch (error) {
console.error('Échec de chargement du SDK Optave:', error);
this.connectionStatus = 'Échec de chargement';
}
}
async initializeOptaveSDK() {
// Accéder au SDK depuis le scope global - Lightning Locker utilise globalThis
const OptaveSDK = globalThis.OptaveJavaScriptSDK || window.OptaveJavaScriptSDK;
if (!OptaveSDK) {
throw new Error('SDK Optave introuvable dans le scope global');
}
// Initialiser avec une configuration spécifique LWC
this.sdk = new OptaveSDK({
websocketUrl: 'wss://ws-{{tenant}}.oco.optave.{{tld}}',
authTransport: 'subprotocol',
tokenProvider: async () => {
// Appeler la méthode Apex via appel impératif
const result = await getOptaveToken();
return result;
},
strictValidation: false
});
// Configurer les gestionnaires d'événements
this.setupEventHandlers();
// Se connecter au WebSocket Optave
await this.sdk.openConnection();
console.log('✅ LWC: SDK Optave initialisé');
}
setupEventHandlers() {
this.sdk.on('open', () => {
this.connectionStatus = 'Connecté';
console.log('✅ LWC: WebSocket connecté');
});
this.sdk.on('message', (payload) => {
this.handleMessage(JSON.parse(payload));
});
this.sdk.on('error', (error) => {
console.error('❌ LWC: Erreur SDK:', error);
this.connectionStatus = `Erreur: ${error.message}`;
});
this.sdk.on('close', () => {
this.connectionStatus = 'Déconnecté';
console.log('🔌 LWC: Connexion fermée');
});
}
handleMessage(message) {
// Mettre à jour les propriétés réactives
this.messages = [
...this.messages,
{
id: message.headers?.correlationId || Date.now(),
content: message.payload?.content || 'Message reçu',
timestamp: new Date().toLocaleTimeString(),
type: 'ai'
}
];
}
handleSendMessage(event) {
const messageText = event.target.value;
if (this.sdk && messageText) {
this.sdk.interaction({
request: {
connections: {
threadId: `lwc-${this.recordId}-${Date.now()}`
},
scope: {
conversations: [
{
conversationId: 'conv_' + Date.now(),
participants: [
{
participantId: 'salesforce_user_' + Date.now(),
role: 'user',
displayName: 'Utilisateur Salesforce'
}
],
messages: [
{
participantId: 'salesforce_user_' + Date.now(),
content: messageText,
timestamp: new Date().toISOString()
}
],
metadata: {}
}
]
}
}
});
// Vider l'entrée
event.target.value = '';
}
}
disconnectedCallback() {
// Nettoyage à la destruction du composant
if (this.sdk) {
this.sdk.closeConnection();
}
}
get isConnected() {
return this.connectionStatus === 'Connecté';
}
}
Patterns Courants
Vérifier la Connexion Avant d'Envoyer
if (!client.wss || client.wss.readyState !== 1) {
throw new Error('Non connecté au WebSocket');
}
const response = await client.interaction(params);
Réessayer en Cas d'Erreur
async function sendWithRetry(action, params, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await client[action](params);
} catch (error) {
if (attempt === maxRetries) throw error;
const delay = Math.pow(2, attempt) * 1000;
console.log(`Nouvelle tentative ${attempt} après ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
const response = await sendWithRetry('interaction', params);