Archives de catégorie : Mercedes

Raspberry Cars : RC 2.0 !

Pour mon travail personnel de l’année 2019-2020 au Lycée Ermesinde, je vais étendre mon travail personnel de l’année dernière, avec de nouvelles technologies pour le rendre plus performant et avancé.

Le travail personnel de l’année dernière consistait à connecter le site Salesforce avec une voiture Mercedes pour recevoir des donnés sur la voiture en fonctionnement. Par exemple la position de la voiture, sa vitesse, le niveau de carburant. Avec ces informations, on a eu l’idée de faire une course virtuelle où des personnes peuvent aller d’un point de départ à un point d’arrivée, à des dates différentes, et par la suite on peut comparer les trajets entre eux.

Le projet de l’année dernière avait comme limitations que les données passaient par les serveurs de Mercedes : Les données de la voiture étaient lues par une application Mercedes sur le téléphone mobile puis envoyée sur le serveur Mercedes ; Ensuite, un programme exécuté chez Salesforce allait se connecter automatiquement au serveur Mercedes pour demander les données de la voiture et les enregistrer dans la base de données de Salesforce.

En résumé, les limitations étaient les suivantes :

  • On avait besoin d’avoir le téléphone dans la voiture
  • Ceci fonctionnait juste sur les voitures Mercedes
  • Pour que Salesforce se connecte aux API du serveur cloud de Mercedes il fallait un contrat avec Mercedes et une fois la phase de développement terminée il faut payer un abonnement
  • On ne pouvait pas facilement se connecter très souvent du serveur Salesforce au serveur Mercedes (on a fait toutes les 10min.)
  • L’application Mercedes n’enregistrait pas les donnés à une haute fréquence, on voudrait enregistrer toute les 2-10 secondes par exemple pour avoir une course plus dynamique

Ce que je veux réaliser cette année c’est :

  • Un petit ordinateur autonome dans la voiture,
  • Fonctionnant avec toutes les marques de voiture,
  • Ne nécessitant pas d’abonnement particulier,
  • Enregistrant de la même manière la position de la voiture, la vitesse, le niveau de carburant, l’état des pneus et d’autres paramètres,
  • Les envoyant à Salesforce le plus souvent possible.

Ce travail personnel va donc nécessiter différents groupes de tâches :

Voici la suite d’articles décrivant les différentes étapes du projet  :

 

Le module de gestion de la connexion de Salesforce vers Mercedes Cloud (3/3) : La récupération des informations des voitures

1.         Utilisation pour stocker / mettre à jour la liste des cars dans SF

a)              Un objet en mémoire pour récupérer le contenu de la réponse du serveur

Quand on demandait la voiture à la main avec Curl, on obtenait la réponse suivante

[
{
"id":                "00338353A46599799B",
"licenseplate":     "S-GG-9041",
"finorvin":         "1HM8CE2A6H6CEC802"
}
]

C’est un texte qui décrit la liste de voitures. Dans ce cas une seule voiture.

Pour pouvoir la manipuler, il faut définir un objet en mémoire dans Salesforce pour Apex.

class Vehicule {
public String salesforceId        { get; set; }
public String vehiculeId { get; set; }
public String licenseplate       { get; set; }
public String finorvin           { get; set; }
}

Et on va transformer chaque voiture de la liste dans un de ses objets.

b)              Le code qui analyse le contenu de la réponse du serveur et crée les voitures en mémoire

Du coup on va utiliser l’outil de Salesforce qui sert à lire un message JSON

JSON.deserializeUntyped( leMessage );

Et stocker en mémoire les voitures dans une liste

public List<Vehicule> vehicules { get; set; }

Comme pour l’appel à l’API précédente (session), on peut maintenant écrire le code qui appelle Mercedes, analyse la réponse et mémorise les voitures

public void loadVehiculesFromServer() {
  • Préparation de la requête
//Set HTTPRequest Method
HttpRequest req = new HttpRequest();
req.setMethod('GET');
req.setHeader('authorization', 'Bearer '+access_token );
req.setHeader('accept', 'application/json');
req.setEndpoint(API_URL_Vehicules);
  • Appel de la requête
//Execute web service call here
Http http = new Http();
HTTPResponse res = http.send(req);
  • Récupération de la réponse
vehiculesJson = res.getBody();
System.debug('response:' + vehiculesJson);
  • Décodage de la réponse
List<Object> results =
(List<Object>)JSON.deserializeUntyped(vehiculesJson);

vehicules = new List<Vehicule>();

for(Object o:results)  {
Map<String,Object> m = (Map<String,Object>)o;
Vehicule v = new Vehicule();
v.vehiculeId = (String) m.get('id');
v.licenseplate = (String)  m.get('licenseplate');
v.finorvin =   (String) m.get('finorvin');
vehicules.add(v);
}
}

c)              Le code qui met à jour / crée les voitures dans la base SF à partir des voitures en mémoire

 

Ce qu’on veut maintenant c’est d’enregistrer les voitures qu’on vient d’obtenir dans Salesforce (mais sans créer des double à chaque appel). Pour ça on doit vérifier si la voiture est déjà là ou pas.

Cela se fait avec un programme qui  :

  1. lit les id Mercedes des voitures retournées par l’api Mercedes (ce qu’on vient de coder)
  2. charge les voitures de la base SF avec ces ID Mercedes pour cette connexion, et les place dans une carte
  3. pour chaque voiture retournée par l’API Mercedes
    1. regarde si elle est déjà dans la base salesforce
    2. sinon ; il prépare la création
    3. si oui ; il prépare la mise à jour des données dans la base salesforce
  4. il met à jour toutes les voitures qui existaient déjà
  5. il crée toutes celles qui n’existaient pas
public void storeVehiculesInSalesforce() {

//  Etape 1
Set<String> vehiculeMBIds = new Set<String>();
for (Vehicule v:vehicules) {
vehiculeMBIds.add(v.vehiculeId);
}

//  Etape 2
List<Car__c> listSFCars = [
SELECT Id, Mercedes_ID__c, Mercedes_API_Connection__c, Car_number__c
FROM Car__c
WHERE   Mercedes_API_Connection__c = :salesforceConnectionId
AND Mercedes_ID__c in :vehiculeMBIds
];

Map<String,Car__c> mapSFCars = new Map<String,Car__c>();
for (Car__c c:listSFCars) {
mapSFCars.put(c.Mercedes_ID__c, c);
}

//  Etape 3
List<Car__c> carsToCreate = new List<Car__c>();
List<Car__c> carsToUpdate = new List<Car__c>();

for (Vehicule v:vehicules) {
//  Etape 3a
Car__c theSFCar =  mapSFCars.get(v.vehiculeId);

if (theSFCar == null) {

//  Etape 3b
carsToCreate.add(
new Car__c(
Mercedes_API_Connection__c = salesforceConnectionId,
Mercedes_ID__c = v.vehiculeId,
Car_number__c = v.licenseplate
)
);

} else {

//  Etape 3c
theSFCar.Car_number__c = v.licenseplate;
carsToUpdate.add(theSFCar);
}

}

insert carsToCreate;
update carsToUpdate;

}

Maintenant à chaque fois qu’on appelle ce code, la liste des voitures dans Salesforce est mise à jour avec la réponse de Mercedes.

2.         Utilisation pour demander la position de toutes les voitures et les stocker dans la base SF

On va faire la même chose pour l’appel qui demande à Salesforce où se trouve chaque voiture.

a)              La réponse du serveur

En Curl, on obtenait la réponse suivante

{
"longitude": {"value":11.372609,"retrievalstatus":"VALID","timestamp":1548425864},
"latitude":  {"value":47.90321,"retrievalstatus":"VALID","timestamp":1548425864},
"heading":   {"value":52.520008,"retrievalstatus":"VALID","timestamp":1548425864}
}

b)              Un objet en mémoire pour récupérer le contenu de la réponse du serveur

On va aussi créer un objet Apex pour récupérer le résultat

class VehiculeLocation {
DateTime requestedOn { get; set; }
Decimal longitude    { get; set; }
Decimal latitude     { get; set; }
Decimal heading { get; set; }
}

c)              Un objet dans SF pour stocker la position

A chaque fois qu’on appelle la demande de position, on veut stocker cela comme enregistrement en plus. On a besoin de créer un nouvel objet dans Salesforce.

On l’appelle « Car Status », et il a les champs Position, Direction et date d’observation.

Voici ce que cela donne dans l’Object Manager.

d)              Des champs sur la voiture pour mémoriser la dernière position

On va en profiter aussi pour enregistrer la dernière position de la voiture dans l’objet voiture lui-même : un champ « Last Position ».

e)              Le code qui analyse le contenu de la réponse du serveur

Comme auparavant, on va appeler la requête et analyser le résultat. En rouge ce qui est particulier pour ce cas.

public VehiculeLocation getVehiculeLocation(String aVehiculeMbId) {

//Set HTTPRequest Method
HttpRequest req = new HttpRequest();
req.setMethod('GET');
req.setHeader('authorization', 'Bearer '+access_token );
req.setHeader('accept', 'application/json');
req.setEndpoint(API_URL_Vehicules+'/'+aVehiculeMbId+'/location');

//Execute web service call here
Http http = new Http();
HTTPResponse res = http.send(req);

//Helpful debug messages
String jsonResponse = res.getBody();
System.debug('response:' + jsonResponse);

Map<String, Object> results =(Map<String, Object>)JSON.deserializeUntyped(jsonResponse);

VehiculeLocation vl = new VehiculeLocation();
vl.requestedOn = System.now();
vl.longitude= (Decimal) ((Map<String, Object>) results.get('longitude')).get('value');
vl.latitude= (Decimal) ((Map<String, Object>) results.get('latitude')).get('value');
vl.heading=(Decimal) ((Map<String, Object>) results.get('heading')).get('value');
System.debug('pos:'+vl);

return vl;
}

f)               Le code qui stocke dans la base SF les positions des voitures récupérées

Comme pour la liste des voitures, on va enregistrer les positions obtenues.

On va donc écrire un programme qui

  1. pour chaque véhicule
    1. appelle le serveur pour obtenir la position (le code du paragraphe précèdent)
    2. retrouve la voiture dans la base SF
    3. crée en mémoire l’objet Car_Status__c à stocker dans SF
    4. mémorise sur la voiture sa dernière position
  2. enregistre tous les objets Car_Status__c en une fois
  3. met à jour toutes les voitures (nouvelles positions) en une fois

public void getAndStoreVehiculeLocations() {

list<Car_Status__c> statusToInsert = new list<Car_Status__c>();

//  Etape 1
for (Vehicule v:vehicules) {

//  Etape 1a
VehiculeLocation vl = getVehiculeLocation(v.vehiculeId);

// Etape 1b
Car__c theCar = [
SELECT
Id,
Mercedes_ID__c,
Mercedes_API_Connection__c,
Car_number__c
FROM Car__c
WHERE Mercedes_API_Connection__c = :salesforceConnectionId
AND Mercedes_ID__c = :v.vehiculeId
];

// Etape 1c
statusToInsert.add(
new Car_Status__c(
Car__c = theCar.id,
Heading__c = vl.heading,
Location__longitude__s = vl.longitude,
Location__latitude__s = vl.latitude,
Requested_On__c = vl.requestedOn
)
);

// Etape 1d
theCar.Last_Position__longitude__s = vl.longitude;
theCar.Last_Position__latitude__s = vl.latitude;
carsToUpdate.add(theCar);
}

// Etape 2
insert statusToInsert;

// Etape 3
update(carsToUpdate);

}

3.         Enregistrer automatiquement toutes les 5 ‘ la position de la voiture

Si on veut suivre les voitures en permanence, il faut répéter l’enregistrement de la position régulièrement.

Ceci est le code pour une seule connexion qu’on veut exécuter toutes les 5 minutes (le code Apex qu’on a écrit avant, dans la classe Mercedes API, avec ‘a011t00000ByCf5AAF’ qui est l’identifiant de l’objet pour la connexion à Mercedes) :

MercedesAPI api = new MercedesAPI('a011t00000ByCf5AAF');
api.loadVehiculesFromServer();
api.getAndStoreVehiculeLocations();

Pour cela, on doit le placer dans une classe Apex programmable qui :

  1. Charge la liste des connexions, et pour chacune
  2. Fait les appels pour la liste des positions
global class MercedesStatusRequestScheduler implements Schedulable {

global void execute(SchedulableContext SC) {
//  Etape 1
for (Mercedes_API_Connection__c c : [SELECT Id FROM Mercedes_API_Connection__c]) {
//  Etape 2
doRequest(c.id);
}

}

    @future(callout=true)
    public static void doRequest(String theConnectionId) {
       MercedesAPI api = new MercedesAPI(theid);
       api.loadVehiculesFromServer();
       api.getAndStoreVehiculeLocations();
   }

}

Pour chaque exécution souhaitée (toutes les 5 ‘ d’une heure, donc 12 fois), on doit enregistrer l’exécution dans le tableau d’exécution SF.

Voici le code qui permet d’enregistrer une exécution

public static void schedule(String name, String cron) {
MercedesStatusRequestScheduler leJob =
new MercedesStatusRequestScheduler();
String jobID = system.schedule(name,cron,leJob);
}

Pour le programmer toutes les 5’ on doit enregistrer 12 programmations ainsi

schedule('Mercedes-Status xh00m', '0 0 * * * ? *');
schedule('Mercedes-Status xh05m', '0 5 * * * ? *');
schedule('Mercedes-Status xh10m', '0 10 * * * ? *');
schedule('Mercedes-Status xh15m', '0 15 * * * ? *');
schedule('Mercedes-Status xh20m', '0 20 * * * ? *');
schedule('Mercedes-Status xh25m', '0 25 * * * ? *');
schedule('Mercedes-Status xh30m', '0 30 * * * ? *');
schedule('Mercedes-Status xh35m', '0 35 * * * ? *');
schedule('Mercedes-Status xh40m', '0 40 * * * ? *');
schedule('Mercedes-Status xh45m', '0 45 * * * ? *');
schedule('Mercedes-Status xh50m', '0 50 * * * ? *');
schedule('Mercedes-Status xh55m', '0 55 * * * ? *');

4.         Amélioration : indiquer quelle connexion ou quelle voiture on veut suivre

Pour le moment, on ne peut pas activer désactiver l’enregistrement des positions.

Pour décider à chaque exécution :

  • si on veut charger les voitures d’une connexion
  • quelle voiture on veut interroger

On ajoute une checkbox :

  • à l’objet custom connection : Scheduled_Job_Execution__c
  • à l’objet custom car : Record_Status__c

Et on modifie le code ainsi

  • Quand on charge l’objet de connexion (Mercedes API connection) en mémoire, on mémorise si on doit faire l’enregistrement des positions
public boolean scheduledJobsExecution {get; set;  }
public MercedesAPI(Id aSalesforceConnectionId) {
…

scheduledJobsExecution = theConnection.Scheduled_Job_Execution__c;
}
  • Dans le code programmé toutes les 5 minutes, on vérifie si la connexion doit être enregistrée
@future(callout=true)
public static void doRequest(String theid) {
MercedesAPI api = new MercedesAPI(theid);
if (api.scheduledJobsExecution) {
api.loadVehiculesFromServer();
api.getAndStoreVehiculeLocations();
}
}

Dans le code d’interrogation et de stockage des statuts, on vérifie si la voiture doit être suivie pour sa position

public void getAndStoreVehiculeLocations() {
list<Car_Status__c> statusToInsert = new list<Car_Status__c>();
for (Vehicule v:vehicules) {
Car__c theCar = [
SELECT
Id,
Mercedes_ID__c,
Mercedes_API_Connection__c,
Car_number__c,
Record_Status__c
FROM Car__c
WHERE Mercedes_API_Connection__c = :salesforceConnectionId
AND Mercedes_ID__c = :v.vehiculeId
];
 
             if (theCar.Record_Status__c==true) {
VehiculeLocation vl = getVehiculeLocation(v.vehiculeId);
statusToInsert.add(new Car_Status__c(
Car__c = theCar.id,
Heading__c = vl.heading,
Location__longitude__s = vl.longitude,
Location__latitude__s = vl.latitude,
Requested_On__c = vl.requestedOn
)); 
            }

}

insert statusToInsert;
}

 

Et voilà maintenant, quand le code s’exécute, on n’accède à la connexion et on n’appelle l’enregistrement des voitures que si les cases sont cochées pour cette connexion et cette voiture.

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
];

….

}
  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);
  1. 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.

Le module de gestion de la connexion de Salesforce vers Mercedes Cloud (1/3) : Techniques utilisées

Dans les deux premières parties nous avons parlé de l’API Mercedes et on l’a utilisée ‘à la main’ avec CuRL en ligne de commande, puis on a regardé comment utiliser et configurer Salesforce,  puis  programmer des composants visuels avec Salesforce et enfin comment stocker et lire en mode programmation des données dans Salesforce.

Maintenant, on regarde comment programmer ces API Mercedes que l’on a manipulé en CuRL avec les outils de programmation de Salesforce.

1.         La page qui permet de recevoir l’autorisation

Quand on appelé le mécanisme de Mercedes pour obtenir le code d’autorisation, on allait sur une URL Mercedes avec nos données de l’application (dont le client id) , c’était l’étape A ; puis l’utilisateur tapait ses données de connexion pour autoriser l’application (étape B) ; et enfin Mercedes renvoyait sur une page de l’application en passant le code d’autorisation en paramètre (étape C).

L’URL de l’étape C ressemblait à ceci, avec le code obtenu en rouge :

https://localhost/?code=6e9d89c4-a374-4bb9-8c88-673034f87342

Parce qu’on avait dit que la redirect URL de l’application était https://localhost/.

En Salesforce on a besoin de faire une page web qui va savoir lire le paramètre code pour qu’on puisse l’utiliser par la suite.

Le code de la page est simple. Ce sera une page Visual Force, vide pour le moment

<apex:page controller="Mercedes_API_Authorization_Ctrl">
</apex:page>

Avec un controller Mercedes_API_Authorization_Ctrl qui :

  • Stocke le code d’autorisation (en bleu)
  • Lit le paramètre passé quand la page se charge. (en orange).
public class Mercedes_API_Authorization_Ctrl {
    public String authorizationCode { get; set; }
 
public Mercedes_API_Authorization_Ctrl(){
   authorizationCode =
apexpages.currentpage().getparameters().get('code');
}
}

Pour faire plus visible, on peut aussi modifier la page pour qu’elle affiche le paramètre obtenu

<apex:page controller= »Mercedes_API_Authorization_Ctrl »>
  Authorization : {! authorizationCode}
</apex:page>

 

On doit ajouter cette redirect URL à l’application déclarée chez Mercedes :

Essayons d’ouvrir la première URL (etape A), avec la bonne redirect URL (en vert), puis suivons les étapes, on arrive à :

https://api.secure.mercedes-benz.com/oidc10/auth/oauth/v2/authorize?response_type=code&client_id=36f504aa-f141-41ad-93ae-09603cddf507&redirect_uri=https%3A%2F%2Fc.eu16.visual.force.com%2Fapex%2FMercedes_API_Authorization&scope=mb:user:pool:reader%20mb:vehicle:status:general

 

On sait récupérer dans SF le code d’autorisation !

 

2.         Le code pour demander le jeton de session

 

Après avoir reçu le code d’autorisation, quand on travaillait à la main :

  • on devait récupérer les jetons de session
  • on pouvait récupérer les données avec l’API

Maintenant on doit faire la même chose qu’on faisait en Curl, mais en SF, avec le langage APEX.

Avec CuRL, à chaque fois :

  • on créait la requête (la commande tapée)
  • on l’exécutait (appel du serveur Mercedes par le serveur Salesforce), en lançant la commande
  • on traitait le résultat (en le lisant)

On va faire la même chose en programmant.

L’appel CuRL pour l’obtention de la session était

curl -X POST "https://api.secure.mercedes-benz.com/oidc10/auth/oauth/v2/token" \
-H "authorization: Basic ZGY4YTczYmUtYTY4Zi00.....hMWQwLWRjMmM0ZWI4MTU0ZA==" \
-H "content-type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code&code=297ded07-43ea-4fcc-ab73-6b6376d787ca&redirect_uri=https%3A%2F%2Flocalhost.com"

Le Code correspondant en Apex est

  • Préparation de la requête
// creation d’une requête HTTP
HttpRequest req = new HttpRequest();
req.setMethod('POST');

// vers le serveur Mercedes
req.setEndpoint('https://api.secure.mercedes-benz.com/oidc10/auth/oauth/v2/token');

// avec le bon code d’auorisation
req.setHeader('authorization', 'Basic ZGY4YTczYmUtYTY4Zi0.....2NiZTMxYmUxMDM5OjBlYjRmMGE0LTM1YzgtNDc0Ny1hMWQwLWRjMmM0ZWI4MTU0ZA==');
req.setHeader('content-type', 'application/x-www-form-urlencoded');

// avec le message de demande de session
String messageBody =
'grant_type=authorization_code'
+'&code=297ded07-43ea-4fcc-ab73-6b6376d787ca'    +'&redirect_uri=https%3A%2F%2Fc.eu16.visual.force.com%2Fapex%2FMercedes_API_Authorization';
req.setBody(messageBody);
  • Exécution de la requête
//Execute de la requête HTTP
Http http = new Http();
HTTPResponse res = http.send(req);
  • Lecture de la réponse et traitement (juste un affichage)
String jsonResponse = res.getBody();

Map<String, Object> results = (Map<String, Object>)

JSON.deserializeUntyped(jsonResponse);

String access_token = (String)  results.get('access_token');
String token_type   = (String)  results.get('token_type');
Integer expires_in = (Integer) results.get('expires_in');
String refresh_token=  (String)  results.get('refresh_token');
String scope         = (String)  results.get('scope');

System.debug('access_token:' + access_token);
System.debug('token_type:' + token_type);
System.debug('expires_in:' + expires_in);
System.debug('refresh_token:' + refresh_token);
System.debug('scope:' + scope); 

Voici ce qu’on obtient en debug quand on lance le code dans la Console

3.         Le code pour faire un appel API : liste des voitures

De même, pour obtenir la liste des véhicules, on appelait avec CuRL

curl -X GET "https://api.mercedes-benz.com/experimental/connectedvehicle/v1/vehicles"  -H "accept: application/json"  -H "authorization: Bearer f9da01aa-0c07-412b-bb0c-f31a7b413b7b"

On fait les mêmes étapes en Apex :

  • Préparation de la requête (on passe le token obtenu comme avec CuRL)
HttpRequest req = new HttpRequest();
req.setMethod('GET');
req.setHeader('authorization', 'Bearer '+access_token );
req.setHeader('accept', 'application/json');
req.setEndpoint('https://api.mercedes-benz.com/experimental/connectedvehicle/v1/vehicles');
  • Exécution de la requête
Http http = new Http();
HTTPResponse res = http.send(req);
  • Lecture de la réponse et traitement (ici on imprime juste la liste)
String jsonResponse = res.getBody();
System.debug('response:' + jsonResponse);

Voici ce qu’on obtient dans le Debug :

Et voici ! Reste à intégrer ce code manipulé à la main dans une application… Ce sera l’objet des travaux du prochain post !

Connectons-nous au serveur Cloud de Mercedes (2/2) – Utiliser les API avec CuRL

Pour appeler le serveur Mercedes il faut envoyer des commandes REST en https .

J’ai trouvé une explication de l’utilisation de ces commandes sur le site de SCIP : CAR HACKING ANALYSIS OF THE MERCEDES CONNECTED VEHICLE API: www.scip.ch/en/?labs.20180405 .

Nota : Https est un Protocole de transmission permettant à l’utilisateur d’accéder à des pages web par l’intermédiaire d’un navigateur. Rest est une logique d’appel d’API de serveurs.

1.         CURL

CURL est une interface en ligne de commande, destinée à récupérer le contenu d’une ressource accessible par un réseau informatique. La ressource est désignée à l’aide d’une URL et doit être d’un type supporté par le logiciel.

Normalement on ne peut pas utiliser CURL sur Windows mais des personnes ont reprogrammé CURL pour qu’on puisse utiliser CURL sur Windows(https://o7planning.org/fr/11617/installation-de-curl-sous-windows). Donc j’ai téléchargé la version Windows de CURL sur mon PC et j’ai fait un test avec mon Blog et ceci a marché (j’ai bien obtenu le contenu de la page de mon blog dans le fichier mapage.html).

curl » http://wollef.org/fr/mieux-que-pokemon-go-pixelmon-mod/ –output mapage.html

2.         Connexion sécurisée et Redirect URL

a)              Le principe

Avant d’accéder aux données avec l’API il faut montrer qu’on est le propriétaire du compte Mercedes. Ce n’est malheureusement pas aussi simple que de taper un nom d’utilisateur et un mot de passe.

En effet le propriétaire du compte (la personne) doit approuver l’utilisation des données du compte par l’application que l’on a déclarée juste avant.

Le mécanisme est décrit dans l’article de SCIP dont je parle juste avant, et aussi dans la documentation Mercedes  ( https://developer.mercedes-benz.com/content-page/oauth-documentation ).

Pour simplifier cela donne les étapes suivantes :

  • Etape A : L’application qui veut utiliser les données doit envoyer l’utilisateur vers une page Mercedes pour y taper son utilisateur et mot de passe
  • Etape B : L’utilisateur va pouvoir saisir ces informations et dire qu’il approuve l’utilisation des données par l’application
  • Etape C : Le serveur Mercedes renvoie alors l’utilisateur vers l’application en donnant une clé d’accès à celle—ci.

Cette dernière action (le retour vers l’application) se fait avec la ‘redirect url’.

b)              Configuration de la redirect URL

On doit indiquer dans la page de configuration la redirect URL. Pour les premiers tests en CURL, ce sera une url ‘localhost’ (car on n’a pas encore notre serveur avec le jeu en place).

Note : Sur l’impression d’écran, il y a un autre redirect URL, c’est celle de l’application finie (celle intégrée avec salesforce). Nous n’en avons pas besoin pour le moment.

3.         Mise en place de la connexion

La première étape est de connaître les informations qui permettent à l’application d’accéder au serveur Mercedes avec les API.

Ce sont les client id et client secret qui ont été créés quand on a déclaré l’application (ils sont sur dans le texte de ce blog tronqués pour des raison de sécurité, les id réels obtenus et à saisir sont plus longs).

Ensuite, on simule l‘étape A à la main, en tapant l’URL d’authentification dans le navigateur avec le seul client-id:

https://api.secure.mercedes-benz.com/oidc10/auth/oauth/v2/authorize?response_type=code&client_id=36f504aa-f141-41ad-……&redirect_uri=https%3A%2F%2Flocalhost&scope=mb:user:pool:reader%20mb:vehicle:status:general

On arrive sur une page pour se connecter, c’est l’étape B faite par l’utilisateur.

Le serveur Mercedes demande d’approuver la connexion aux données

Ensuite le serveur Mercedes redirige vers une page web (return URL), en donnant un code d‘autorisation unique : 6e9d89c4-a374-4bb9-8c88-673034f87342. C’est l’étape C.

4.         Obtenir le jeton de session

Maintenant on va demander avec l’API (donc en utilisant CURL) d’obtenir le jeton de session qui sera utilisé pour tous les appels suivant à l’API.

Il y a une astuce : c’est que l’appel demande les client Id et client secret dans une forme spéciale qui est l’encodage base 64.

Pour l’obtenir j’ai utilisé une application disponible sur le web : Base 64 Encode https://www.base64encode.net/

Pour encoder, on passe à cette application une chaine de caractère composée du client id et du secret id sépararés par un : (double point).

On obtient la forme encodée des informations suivantes (nota : la partie du milieu est tronquée pour des raisons de sécurité) :

MzZmNTA0YWEtZjE0MS00MWFkLTkz …… QxNjUtNDUwOS04MTdhLTE2YTRkZjViMTM3ZA==

On peut maintenant faire l’appel vers l’API pour initialiser la session en y intégrant le token (incluant donc c lient id et secret id) et le code d’autorisation unique de l’étape B :

curl -X POST « https://api.secure.mercedes-benz.com/oidc10/auth/oauth/v2/token » -H « authorization: Basic MzZmNTA0YWEtZjE0MS00MWFkLTkz……QxNjUtNDUwOS04MTdhLTE2YTRkZjViMTM3ZA== » -H « content-type: application/x-www-form-urlencoded » -d « grant_type=authorization_code&code=6dee706b-9ce9-4f3a-85e8-bd696aae8bef&redirect_uri=https%3A%2F%2Flocalhost »

Hourra ! Le serveur Mercedes nous a renvoyé les jetons de connexion (access token)

5.         Utilisation de la session : appel d’une API : la liste des voitures

Une fois qu’on a un jeton de session on peut s’en servir pour appeler les APIs avec Curl.

On va appeler ici trois API tel que documentées ici : https://developer.mercedes-benz.com/apis/connected_vehicle_experimental_api/ .

a)              Obtenir la liste des voitures

curl -X GET « https://api.mercedes-benz.com/experimental/connectedvehicle/v1/vehicles »  -H « accept: application/json »  -H « authorization: Bearer f9da01aa-0c07-412b-bb0c-f31a7b413b7b »

On peut lire l’Id de la voiture dans la réponse : E09C4378AD45F1A304

Documentation : https://developer.mercedes-benz.com/apis/connected_vehicle_experimental_api/docs#_get_the_vehicleid_from_the_connected_vehicle_api

b)              Obtenir les informations sur la voiture

curl -X GET « https://api.mercedes-benz.com/experimental/connectedvehicle/v1/vehicles/E09C4378AD45F1A304 » -H « accept: application/json » -H « authorization: Bearer f9da01aa-0c07-412b-bb0c-f31a7b413b7b »

Documentation : https://developer.mercedes-benz.com/apis/connected_vehicle_experimental_api/docs#_2_get_additional_information_about_your_car

c)              Verrouiller les portes

curl -X POST « https://api.mercedes-benz.com/experimental/connectedvehicle/v1/vehicles/E09C4378AD45F1A304/doors » -H « Content-Type: application/json » -H « authorization: Bearer f9da01aa-0c07-412b-bb0c-f31a7b413b7b »  -d « { \ »command\ »: \ »LOCK\ »} »

Documentation: https://developer.mercedes-benz.com/apis/connected_vehicle_experimental_api/docs#_4_set_the_lock_state_of_your_car_doors

 

 

Nous voici donc en mesure maintenant d’appeler les API du serveur Cloud de Mercedes pour notre application de jeu ! C’est ce que nous ferons dans les articles prochains !

Connectons-nous au serveur Cloud de Mercedes (1/2) – Le service Cloud

Pour ce projet, nous allons devoir nous connecter au serveur Cloud de Mercedes qui sait gérer les informations concernant la voiture.

A.       Mercedes Benz « Mercedes Me »

Les voitures d’aujourd’hui ne sont pas juste « des moteurs et une carrosserie » mais elles comportent un ordinateur qui analyse et contrôle le comportement de la voiture.

Avec cet ordinateur il est maintenant possible de transférer des données de ses voitures vers le serveur Mercedes. Pour cela il faut enregistrer sa voiture sur le compte « Mercedes Me ».

Il y a une application smartphone :

  • qui permet de visualiser les données de ses voitures sur son téléphone.
  • qui récupère les données de la voiture et les envoie par le réseau GSM au serveur Mercedes.

B.        Présentation de l’API Mercedes

En janvier 2018 Mercedes a sorti une API vers les serveurs Mercedes (mais ce n’est pas encore la version finale).

Avec cette API on peut :

  • lire des données sur la voiture à distance (position, quantité de carburant, l’état des portes,…)
  • commander la voiture à distance (ouvrir et fermer les portes, mais pas la faire rouler toute seule…).

C’est une Api vers les serveurs Mercedes, ce qui veut dire qu’on n’interroge pas directement la voiture. Seuls les serveurs Mercedes communiquent avec la voiture.

Les informations sur cette API sont disponibles sur le site web « Mercedes Developer : https://developer.mercedes-benz.com/apis

1.         Création du compte Mercedes

Pour pouvoir utiliser l’API il faut créer un compte sur Mercedes.

Pour cela il faut aller sur ce site : https://login.secure.mercedes-benz.com/profile/register?app-id=ONEAPI.PROD

 

2.         Création de l’application connectée

Pour pouvoir accéder aux données d’une voiture, on doit déclarer chez Mercedes l’« application » qui va accéder au serveur Mercedes. Pour déclarer cette application, il faut aller dans le menu Console

puis cliquer sur ADD NEW APP

a)              Ajout de l’API

On doit ensuite indiquer les API de Mercedes que l’application va utiliser.

Pour ajouter une API il faut aller dans le menu API. Ensuite on cherche l’API qu’on veut puis on l’ajoute en cliquant sur « Try It Now ».

Nous avons besoin de l’API « Connected API Experimental »

Ensuite on clique sur Subscribe pour pouvoir l’utiliser.

3.         Activation du Car Simulator

Avant de pouvoir utiliser les API pour une vraie voiture, Mercedes propose d’utiliser un simulateur pour faire des tests.

Pour commencer à utiliser le simulateur il faut ouvrir https://car-simulator.developer.mercedes-benz.com et se connecter avec ses informations de compte Mercedes-Benz /developers. Maintenant le simulateur est ouvert.

Dans l’article suivant, nous allons expliquer comment manipuler l’API du service Cloud mercedes en utilisant curl.

 

Cars and Clouds – Projet Personnel 2019

Pour mon travail personnel au Lycée Ermesinde pour l’année 2018-2019,    j’ai eu l’idée de regrouper des thèmes qui m’intéressent et que j’ai déjà examinés dans mes précédents travaux personnels :

A ces sujets que j’aime bien j’ajoute un nouveau thème : Les serveurs sur internet et le cloud computing. J’ai déjà exploré ce thème dans mon temps libre en créent un serveur Minecraft et en le personnalisant (j’en avais parlé dans ce blog).

Pour ce travail personnel je voulais faire un projet avec des voitures réelles qui échangent des données avec un serveur cloud sur lequel tourne un jeu que j’aurais programmé.

Pour la voiture j’ai décidé d’utiliser l’API de Mercedes (interface de programmation) qui permet d’échanger des informations comme la position, la vitesse, la consommation d’une voiture.

Pour le cloud, j’ai besoin d’un endroit pour stocker les informations, les afficher aux joueurs, communiquer et programmer les règles du jeu. Pour cela j’ai choisi les serveurs cloud de Salesforce.

Pour le jeu j’avais l’idée de créer une course virtuelle qui compare différents joueurs, ou plutôt des conducteurs sur le même trajet.

Ce projet a comporté différentes parties pratiques :

Cela a été un long travail mais c’est intéressant d’assembler des différents thèmes.

J’ai passé beaucoup de temps le Weekend et pendant les Vacances pour ce travail, une quinzaine de fois entre deux et quatre heures, car il y avait beaucoup plus de choses à découvrir que je ne le pensais quand j’ai commencé ! Mais cela a été très intéressant et je suis content de le partager avec vous dans ce blog.