Le module de gestion de la connexion de Salesforce vers Mercedes Cloud (2/3) : La connexion
Dans les Posts précédents, nous avons examiné comment appeler le serveur Mercedes Cloud ‘à la main’ dans la console de programmation Salesforce. Le but de ce Post est d’expliquer comment implémenter le code de manière à ce qu’il soit utilisable par un utilisateur normal.
1. Un nouvel objet pour stocker les infos de connexion
On vient de travailler ‘à la main’ dans la console. Mais si on veut que ce soit facile de travailler avec tous ces codes, ce serait bien de les enregistrer quelque part.
Pour cela on va créer un objet SF qui sert à mémoriser les informations de la connexion vers Mercedes, on a besoin de :
- client id =txt
- client secret=txt
- authorisation code=txt
- access token=txt
- expiration date = date/time
- scope = texte
- refresh token = texte
On va créer les champs de cet objet connexion avec le menu setup, comme on avait fait pour l’objet voiture.
On peut du coup saisir les informations de connexion dans Salesforce
Et aussi faire les requêtes avec du code SOQL comme on l’a fait avant pour les voitures :
SELECT Id, Mercedes_API_Client_ID__c, Mercedes_API_Client_Secret__c FROM Mercedes_API_Connection__c
2. Comment savoir pour quelle connexion on a demandé l’autorisation ?
Quand le serveur Mercedes renvoie vers la page SF, on ne peut pas passer de paramètre. Il n’est pas possible de savoir quelle Mercedes Connection a été autorisée s’il y en a plusieurs configurées dans SF.
On va donc devoir « marquer » dans la base SF quelle connexion est concernée avant l’appel de la page d’autorisation Mercedes, pour que quand l’utilisateur revient dans SF sur la page d’après-autorisation on retrouve la connexion concernée.
On a ajouté deux champs à l’objet Mercedes Connection :
- Last_Autorisation_Request_Time__c : la dernière fois qu’un utilisateur a demandé l’autorisation
- Last_Autorisation_Request_User__c : quel utilisateur a demandé l’autorisation
Avant d’aller vers la page d’autorisation Mercedes, on va marquer la connexion ainsi en mémorisant que la connexion vient d’être demandée par l’utilisateur et que cela vient de se faire.
3. Page pour demander la connexion
On va créer un composant visuel qui sera placé sur les objets Mercedes Api Connection.
Ce composant va permettre de cliquer sur un bouton pour aller sur le serveur Mercedes pour activer la connexion.
a) Le composant visuel
C’est un simple composant qui affiche l’Id de la connexion et un bouton pour appeler la page de connexion
<template>
<lightning-card title="Connection Information" icon-name="standard:people">
<div class="slds-m-around_medium">
<p>The connection is </p>
<ul>
<li>SF ID : {recordId}</li>
</ul>
</div>
<lightning-button label="Activate Connection" onclick={handleActivateConnection} variant="brand"></lightning-button>
</lightning-card>
</template>
b) Le code javascript du composant
Dans le code javascript du composant, ce qu’il y a de particulier est que :
- Le code handleActivateConnection appelle d’abord une méthode memorizeAutorisationRequestContext du serveur (Apex) pour stocker le fait qu’on vient de lancer le processus pour cette connexion (voir paragraphe suivant)
memorizeAutorisationRequestContext( {aConnectionId : this.recordId })
- Quand cela a marché, dit d’aller vers la page de connexion Mercedes (l’URL est calculée par le code coté serveur) :
this[NavigationMixin.Navigate]({
"type" : "standard__webPage",
attributes: {
"url" : this.autorisationPageURL.data
}
}
Cela donne le code suivant pour le javascript du composant :
import { LightningElement, api, wire } from 'lwc';
import { getRecord /*, getFieldValue */ } from 'lightning/uiRecordApi';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { NavigationMixin } from 'lightning/navigation';
import memorizeAutorisationRequestContext from '@salesforce/apex/MercedesAPI.memorizeAutorisationRequestContext';
import getAuthorizationPageURL from '@salesforce/apex/MercedesAPI.getAuthorizationPageURL';
import CONNECTION_SALEFORCE_ID from '@salesforce/schema/Mercedes_API_Connection__c.Id';
import CONNECTION_CLIENT_ID from '@salesforce/schema/Mercedes_API_Connection__c.Mercedes_API_Client_ID__c';
import CONNECTION_CLIENT_SECRET from '@salesforce/schema/Mercedes_API_Connection__c.Mercedes_API_Client_Secret__c';
import CONNECTION_ACCESS_TOKEN from '@salesforce/schema/Mercedes_API_Connection__c.Mercedes_API_Acces_Token__c';
const connectionFields = [
CONNECTION_SALEFORCE_ID,
CONNECTION_CLIENT_ID,
CONNECTION_CLIENT_SECRET,
CONNECTION_ACCESS_TOKEN
];
export default class MercedesConnectionManager extends NavigationMixin(LightningElement)
{
@api recordId; // Connection Id
@wire(getRecord, { recordId: '$recordId', fields: connectionFields })
mercedesConnection;
@wire(getAuthorizationPageURL, { aConnectionId: '$recordId' })
autorisationPageURL;
get urlInfos() {
return JSON.stringify(this.autorisationPageURL);
}
handleActivateConnection() {
memorizeAutorisationRequestContext( {aConnectionId : this.recordId })
.then(result => {
// small event message
const evt = new ShowToastEvent({
title: 'Connection Context Memorized',
message: 'The connection context has been successfully memorized for '+result,
variant: 'success',
});
this.dispatchEvent(evt);
// go to page
this[NavigationMixin.Navigate]({
"type" : "standard__webPage",
attributes: {
"url" : this.autorisationPageURL.data
}
},
{
replace: true
}
);
})
.catch(error => {
const evt = new ShowToastEvent({
title: 'Connection Context Not Memorized',
message: 'An error occured '+JSON.stringify(error),
variant: 'error',
});
this.dispatchEvent(evt);
});
}
}
c) Le code Apex
C’est le code du côté Salesforce qui fait les calculs et les sauvegarde (dans la classe MercedesAPI).
Avant d’aller vers la page d’autorisation Mercedes, on marque la connexion ainsi :
@AuraEnabled(cacheable=false)
public static id memorizeAutorisationRequestContext(Id aConnectionId) {
// recupere la connection
Mercedes_API_Connection__c theConnection = [
SELECT
Id,
Name,
Last_Autorisation_Request_User__c,
Last_Autorisation_Request_Time__c
FROM Mercedes_API_Connection__c
where id = :aConnectionId
];
// memorise qui et quand
theConnection.Last_Autorisation_Request_User__c = UserInfo.getUserId();
theConnection.Last_Autorisation_Request_Time__c = System.now();
// sauve le resultat
update theConnection;
return aConnectionId;
}
Et le code qui calcule la page pour demander l’autorisation d’accès à Mercedes
@AuraEnabled(cacheable=true)
public static String getAuthorizationPageURL(Id aConnectionId) {
Mercedes_API_Connection__c myConnection = [
SELECT
Id,
Mercedes_API_Client_ID__c
FROM Mercedes_API_Connection__c
WHERE id = :aConnectionId
];
return API_URL_Authorization
+ '?response_type=code'
+ '&client_id=' + myConnection.Mercedes_API_Client_ID__c
+ '&redirect_uri='+redirectURI
+ '&scope='+requestedScope;
}
d) Les informations du composant pour indiquer qu’il est affichable sur les pages ‘Mercedes Api Connection’
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="mercedesConnectionManager">
<apiVersion>45.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__RecordPage</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__RecordPage">
<objects>
<object>Mercedes_API_Connection__c</object>
</objects>
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
e) Le composant ajouté à la page de l’objet API Connection
On ajoute le composant à la page comme on avait fait avant pour le composant qui affichait la couleur d’une voiture.
4. Page qui récupère les informations de demande d’autorisation
Quand on clique sur le lien, on arrive chez Mercedes où on peut valider qu’on autorise l’accès, puis Mercedes redirige sur la page Mercedes API Authorization (ce qu’on avait fait dans la partie d’avant)
Il faut maintenant faire que cette page utilise le code d’autorisation qu’elle a récupéré
a) La page
Pour cela on ajoute un bouton sur la page
<apex:page controller="Mercedes_API_Authorization_Ctrl" >
Authorization : {! authorizationCode}
<apex:form >
<apex:commandButton value="validate connection request" action="{!saveAuthorizationCode}" />
</apex:form>
</apex:page>
Le bouton appelle une action saveAuthorizationCode dans le contrôleur de la page (Mercedes_API_Authorization_Ctrl )
b) Le code Apex
Du coup, quand on revient dans l’environnement SF de l’utilisateur on peut retrouver la bonne connexion pour y sauver les informations de session (token, etc.) : on prend la dernière connexion modifiée par cet utilisateur
public PageReference saveAuthorizationCode() {
mercedesConnection = [
SELECT
Id,
Name,
Mercedes_API_Client_ID__c,
Mercedes_API_Client_Secret__c,
Mercedes_API_Autorization_Code__c
FROM Mercedes_API_Connection__c
where
Last_Autorisation_Request_User__c= : UserInfo.getUserId()
order by Last_Autorisation_Request_Time__c desc
limit 1
];
….
}
- c) On utilise le code de connexion qu’on avait auparavant recopié dans une classe MercedesAPI pour pouvoir le réutiliser.
MercedesAPI api = new MercedesAPI(
theConnection.Mercedes_API_Client_ID__c,
theConnection.Mercedes_API_Client_Secret__c
);
api.initSession(authorizationCode);
- d) On stocke l’autorisation et les tokens obtenus
theConnection.Mercedes_API_Autorization_Code__c = authorizationCode;
theConnection.Mercedes_API_Acces_Token__c = api.access_token;
// sauver les autres champs ici
update theConnection;
Et voilà la connexion est établie !
Voici le code avec tous les morceaux mis ensemble.
public PageReference saveAuthorizationCode() {
Mercedes_API_Connection__c theConnection = [
SELECT
Id,
Mercedes_API_Client_ID__c,
Mercedes_API_Client_Secret__c,
Mercedes_API_Autorization_Code__c
FROM Mercedes_API_Connection__c
// where id = :monID
limit 1
];
MercedesAPI api = new MercedesAPI(
theConnection.Mercedes_API_Client_ID__c,
theConnection.Mercedes_API_Client_Secret__c
);
api.initSession(authorizationCode);
api.getVehicules();
theConnection.Mercedes_API_Autorization_Code__c = authorizationCode;
theConnection.Mercedes_API_Acces_Token__c = api.access_token;
// sauver les autres champs ici
update theConnection;
vehiculeTexte = api.vehiculesJson;
return null;
}
Et on peut demander à rafraîchir cette partie de page après le clic sur le bouton
<apex:commandButton value="validate connection request" action="{!saveAuthorizationCode}" reRender="vehiculesPanel"/>
Voici ce qu’on voit quand on arrive sur la page ‘redirect url’ après être passé sur le serveur Mercedes :
Puis ce qu’on voit quand on clique sur ‘Validate’
La connexion est maintenant active et le token de session est enregistré.
5. Rafraichissement automatique du token de connexion
Normalement, le jeton de session n’est valide qu’une heure. Il faudrait faire un code qui appel l’API pour rafraîchir automatiquement le token toutes les heures.
Je ne l’ai pas fait dans le temps de mon Trape.
C’est une amélioration à faire. Pour le moment, il faut recliquer sur ces écrans toutes les heures.