AngularJS, q$, Learning Partner, REST Api Design und noch vieles mehr

Nachdem Microsoft mit Hinweis auf Datenschutz keine Liste der Microsoft Learning Partner herausgibt, musste ich mir einen anderen Weg suchen. Das schöne daran ist, das viel zu lernen gab, um an die Lösung zu kommen. Wieder einmal.

microsoftgoldlearningpartner

Nur Microsoft Learning Partner dürfen offizielle Trainings zu Microsoft Produkten durchführen. ppedv ist neben aktuell rund 50 anderen, eines dieser zertifizierten Unternehmen in Deutschland. In der höchsten Stufe Gold sind es mit uns weniger als ein Dutzend. Es gibt eine Reihe von Bedingungen und Voraussetzungen, wie Qualität, zertifizierte Mitarbeiter und auch Geld. Als Gegenleistung gibt es die notwendigen Lizenzen für die Nutzung der Software. Auch wenn andere Anbieter Formulierungen verwenden wie “wir verzichten bewusst auf den Einsatz von Herstellerunterlagen” begehen diese eine Lizenzverletzung. Kurz gesagt ist es illegal mit Microsoft Produkten (auch SAP und co) Kundenschulungen durchzuführen.

Wenn Sie sich für Anbieter interessieren die Training durchführen, ohne das Verbrechen zu fördern, hilft die Website Microsoft Pinpoint. Man sucht nach Unternehmen, die in der Kompetenz Learning zertifiziert sind und landet dann auf folgender URL https://pinpoint.microsoft.com/de-DE/browse/companies?competency=100008&page=0

Aus natürlicher Neugierde lasse ich gerne auch mal Fiddler laufen um die Programmierung einer Website kennen zu lernen. Diese Seite nutzt das SPA JavaScript Framework Knockout und umfangreiche REST APi Calls. In Summe weit über 200 Requests. Entsprechend lange dauert der Seitenaufbau.

Das Design der  REST API folgt einem Ansatz, der pro Ressource oder Entität einen Call ausführt. Unter anderem für alle Learning Partner um eine Liste der ID’s zu bekommen oder die Kategorien.

image

Die erste HTTP Endpunkt ist folglich

https://api.pinpoint.microsoft.com/api/v2/de-DE/search/companies/?top=80&competency=100008&orderType=Relevance&sortDirection=1

Da hier offensichtlich ein Odata  Dialekt als Query Syntax verwendet wird, ist es ein leichtes, die Anzahl der Records von 20 auf 80 zu erhöhen.

Für die Firmendaten werden dann je Eintrag in der Liste mindestens zwei weitere REST Calls abgesetzt. Die Partner ID wird in der URL in () übergeben.

api/de-DE/companies(4295952006)/overviews

Durch ergänzen der Rest Methode –zb  Overviews – an die URL wird dann zur passenden Controller -Methode umgeleitet.

image

Rein aus API Gesichtspunkten sehr sauber definierte Schnittstellen. Wer Wert auf reduzierten Traffic legt und einfache Client Implementierung wird die Hände über den Kopf zusammen schlagen. Das fühlt sich an wie select * auf alle Tabellen und Client seitige Joins.

Dabei hat die so auftretende Problemstellung seinen Reiz. Zuerst kommt einen Liste von Partner Id’s, die dann asynchron mit merkbarer Verzögerung um Detail Daten ergänzt wird. Ein Parade Beispiel für den MVVM Ansatz den Angularjs erlaubt, nein vielmehr voraus setzt.

Mein View in HTML5 iteriert über eine Liste von Partner Objekten in dem ein Firma Objekt enthalten ist.

   1:  <div ng-controller="myController">
   2:          <div ng-show="partner.length==0">
   3:              Daten werden geladen...
   4:          </div>
   5:          <div ng-repeat="firma in partner.results | orderBy: 'firma.legal_name'">
   6:              {{firma.company_id}} {{firma.firma.legal_name}}<hr />
   7:          </div>
   8:          total {{partner.total_results}}
   9:      </div>

 

Damit hält sich der Code völlig an die JSON Struktur aus den beiden REST API aufrufen. Wehe die ändert wer!

image

 

image

Erst erfolgt der AJAX Aufruf der Companies und dann je einer pro Company. Um das besser erklären zu können in umgekehrter Reihenfolge erklärt. Der Aufruf der Firmendetails wird in eine Angular.jS Factory abstrahiert.  Mit getCompany(id) lässt sich dann der JSON Stream der Details auslesen.

   1:  .factory('myService', function ($http) {
   2:                  // /api/de-DE/companies(4295952006)/overviews
   3:        return {
   4:             getCompany: function (id) {
   5:            return $http.get('https://api.pinpoint.microsoft.com//api/de-DE/companies(' + id + ')');
   6:            }
   7:           }
   8:   });

 

AngularJS typisch dient ein Controller und ein Viewmodel. Dieser enthält den Scope, also die Daten, und de Funktionen um den Scope zu füllen. Um einen http AJAX Cal auszuführen wird neben dem $scope auch der $http Service per Dependency Injection als Singelton instanziert. Aber auch der vorhin erstellte Service und $q. Mit $q können asynchrone Funktionsaufrufe ohne komplexe Code Schachtelung realisiert werden.

   1:   angular.module('App', [])
   2:     .controller('myController', function ($scope, $http, $q, myService) {
   3:       $scope.partner = [];
   4:       $http.get('https://api.pinpoint.microsoft.com/api/v2/de-DE/search/companies/
?top=80&competency=100008&orderType=Relevance&sortDirection=1'
)
   5:        .then(function (Response) {

 

Auch $http verwendet intern $q. Damit kann dann wenn fertig die .then JavaScript Logik durchlaufen werden. Die Json Daten werden automatisch in ein JavaScript Objekt  geparst (hier Response genannt)).

image

Die Daten werden dann in Zeile 2 einem Scope Objekt zugewiesen und stehen damit für Bindung in der Form partner.total_results (siehe HTML view) bereit.

Als nächstes bekommt Q den Auftrag für alle Einträge in results (aus dem JSON modell) einen Aufuf on der Factory Methode getCompany in die Warteschange zu stellen. Map macht für jeden Eintrag des Arrays den Funktionsaufruf.

Wenn das fertig ist (.then Zeile 6) wird der passende Eintrag in der ursprünglichen Liste des Microsoft Partner Arrays (Scope.partner) gesucht und die erhaltenen Firmen Entität einfach angefügt.

   1:   .then(function (Response) {
   2:       $scope.partner = Response.data;
   3:      
   4:       $q.all(Response.data.results.map(function (result) {
   5:            myService.getCompany(result.company_id)
   6:            .then(function (firma) {
   7:   
   8:                  angular.forEach($scope.partner.results, function (value,key) {
   9:                      if (value.company_id == firma.data.id) {
  10:                          value.firma = firma.data;
  11:                       }
  12:             });
  13:      });
  14:   })
  15:  );

 

Man kann beim Aufruf der Web Seite sehr gut den asynchronen Aufbau des Inhaltes verfolgen, vor allem weil alphabetische Sortierung gewählt worden ist. Damit werden die Einträge während nachträglich pro neuem Eintrag in der Liste umsortiert.

image

Im Ergebnis wurde so ein verschachtelter (nested) Rest API Call asynchron durchgeführt und über eine Bindung Logik im View dargestellt. Ich halte die Lösung für wenig gelungen, weil die API ohne Berücksichtigung der geplanten Darstellung entworfen wurde. Der Universelle Ansatz lohnt bestenfalls nur, wenn auch mehrere vorher nicht feststehende UIs gebaut werden.

Kommentare sind geschlossen