Connecter une application Cordova aux API Microsoft Graph - partie 3
Dans le précédent article de cette série, nous nous sommes connectés à l'API Microsoft Graph. Nous allons maintenant utiliser l'API pour télécharger un fichier depuis OneDrive dans notre application Cordova.
Pour rappel, les articles de la série sont les suivants :
- Déclaration de l'application OneDrive.
- Connexion Oauth au service OneDrive depuis l'application Cordova.
- Cet article - Utilisation de l'API
Principe d'authentification auprès de l'API
Dans l'épisode précédent nous avons obtenu un jeton d'accès. Il va falloir le fournir à chaque appel à l'API dans le header 'Authorization' pour pouvoir y accéder. Le jeton ayant une durée de vie limitée, il est possible que celui-ci expire et vous devrez alors le rafraîchir en suivant la documentation décrite dans la documentation.
Encore une fois, pour ne pas être embêté par CORS, je vais utiliser le plugin HTTP Native de Cordova pour faire mes différents appels. La manière la plus simple de le faire est de le mettre une fois pour toute dans les headers envoyés :
this.http.setHeader('Authorization', infoToken.access_token);
Demander les informations sur un fichier
Dans le cas de mon application ConsoTracker, j'ai besoin de récupérer un fichier de sauvegarde en XML placé à la racine du répertoire de l'utilisateur. Je connais son nom (ConsoTracker.backup) et pour savoir à quelle adresse je peux le télécharger, je vais devoir demander ses informations à OneDrive. Je peux pour cela créer une URL pointant directement sur mon fichier en suivant la documentation :
private archiveInfoUrl = `https://graph.microsoft.com/v1.0/me/drive/root:/ConsoTracker.backup`;
On va donc faire un appel GET sans paramètre sur cette ressource et interpréter le résultat pour y lire la propriété '@microsoft.graph.downloadUrl' correspondant à l'URL de téléchargement. Dans le code ci-dessous, je donne une autre façon de passer le header d'authentification si nécessaire.
return this.http .get(archiveInfoUrl, {}, // pas de paramètre // header d'authentification { Authorization: infoToken.access_token } ) .then(infoResponse => { // Parsons le résultat const itemInfo = JSON.parse(infoResponse.data); // lecture de l'URL de téléchargement. const itemUrl = itemInfo['@microsoft.graph.downloadUrl']; });
Télécharger le fichier en mémoire
Les avantages de cette URL sont multiples :
- Elle est open-bar pendant quelques minutes et ne nécessite pas de s'authentifier.
- Elle n'est pas sujette au problème de CORS et on peut donc repasser sur des HttpClient Angular classiques et partager ainsi le plus de code possible avec notre implémentation pour le Browser pendant les développements.
Je vais donc faire une lecture GET sur cette URL en demandant un type blob en retour de cet appel. J'appelle la méthode extractArchive avec le résultat une fois le téléchargement terminé.
this.httpClient .get( itemUrl, { responseType: 'blob'} ) .subscribe(data => this.extractArchive(data)) .mergeMap(xml=>this.interpretXMl(xm));
Lire le fichier
La lecture du fichier consiste à interpréter notre donnée binaire (le blob) pour le transformer en XML. Pour cela, je vais utiliser un objet de type FileReader en lui demandant de lire le contenu comme du texte. Cet objet n'expose que des événements et je vais donc devoir créer un Subject qui sera retourné par ma méthode extractArchive pour qu'une autre partie de mon code puisse utiliser l'XML à toute fins utiles. Je m'abonne donc à la méthode onloadend du FileReader, et j'utilise le résultat de celui-ci dans un objet de type DomParser pour le transformer en Document XML et l'insuffler dans mon Subject.
private extractArchive(data: Blob) : Observable<ExtractResult[]> { // création d'un subject pour transformer // un event en Observable const subject = new Subject<Document>(); const reader = new FileReader(); // traitement quand lecture terminée reader.onloadend = () => { // le résultat est stocké ici if(reader.error) { subject.OnError(reader.error);} const text = reader.result; const parser = new DOMParser(); const xml = parser.parseFromString(text, 'text/xml'); subject.next(xml); }; // demande de lecture au format texte reader.readAsText(data); // retour du subject au format Observable return subject.asObservable(); }
On pourra alors si nécessaire dans une méthode de traitement lire les données XML pour aller lire une liste de compteurs par exemple :
private interpretXMl(xml: Document) { const trackersElts = xml.children[0] .getElementsByTagName('Trackers')[0] .getElementsByTagName('Tracker'); }
Happy coding :)
Commentaires