Erkundung von Daten mit Serverless und Vue: Automatische Aktualisierung von GitHub-Dateien mit Serverless Functions

Avatar of Sarah Drasner
Sarah Drasner am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Ich arbeite in einem großen Team mit erstaunlichen Leuten wie Simona Cotin, John Papa, Jessie Frazelle, Burke Holland und Paige Bailey. Wir reden viel, da es zum Job eines Developer Advocates gehört, und wir werden auch häufig gefragt, wo wir sprechen werden. Zum größten Teil verwalten wir jeder unsere eigenen Websites, auf denen wir all diese Vorträge auflisten, aber das ist keine sehr gute Erfahrung für Leute, die erkunden wollen. Daher habe ich eine Demo erstellt, die es einfach macht, zu sehen, wer wann auf welchen Konferenzen spricht, mit Links zu all diesen Informationen. Nur zum Spaß habe ich three.js verwendet, damit Sie schnell visualisieren können, wie viele Orte wir alle besuchen.

Sie können sich die Live-Demo hier ansehen oder den Code auf GitHub erkunden.

In diesem Tutorial werde ich erläutern, wie wir die Weltkugel mithilfe einer Serverless-Funktion eingerichtet haben, die Geolocation-Daten von Google für all unsere Vortragsorte abruft. Ich werde auch darauf eingehen, wie wir Vuex (was im Grunde Vues Version von Redux ist) verwenden werden, um all diese Daten zu speichern und sie in der Tabelle und auf der Weltkugel auszugeben, und wie wir mit berechneten Eigenschaften in Vue die Tabelle super performant und schlank sortieren werden.

Artikelserie

  1. Automatische Aktualisierung von GitHub-Dateien mit Serverless Functions (Sie sind hier!)
  2. Filtern und Verwenden der Daten

Serverless Functions

Was zum Teufel?

Vor kurzem habe ich getwittert, dass „Serverless etwas wirklich Interessantes mit dem clickbaitysten Titel ist.“ Das stehe ich auch hier zu und sage, dass das Erste, was Ihnen jeder sagen wird, ist, dass Serverless ein falscher Name ist, weil Sie *tatsächlich immer noch Server benutzen*. Das stimmt. Warum also Serverless nennen? Das Versprechen von Serverless ist, weniger Zeit für die Einrichtung und Wartung eines Servers aufzuwenden. Sie lassen im Wesentlichen den Dienst die Wartung und Skalierung für Sie übernehmen, und Sie reduzieren das, was Sie brauchen, auf Funktionen, die besagen: Wenn diese Anfrage kommt, führe diesen Code aus. Aus diesem Grund bezeichnen manche Leute sie manchmal als Functions as a Service oder FaaS.

Ist das nützlich? Aber sicher! Ich liebe es, keinen Server babysitten zu müssen, wenn er unnötig ist, und die Bezahlung skaliert auch automatisch, was bedeutet, dass Sie nicht für etwas bezahlen, das Sie nicht benutzen.

Ist FaaS immer das Richtige? Äh, nicht gerade. Es ist wirklich nützlich, wenn Sie kleine Ausführungen verwalten möchten. Serverless Functions können Daten abrufen, E-Mail-Benachrichtigungen senden, sie können sogar Dinge wie das Zuschneiden von Bildern im Handumdrehen erledigen. Aber für alles, bei dem Sie Prozesse haben, die Ressourcen beanspruchen oder viel Rechenleistung erfordern, kann die Kommunikation mit einem Server, wie Sie es normalerweise tun, tatsächlich effizienter sein.

Unsere Demo hier ist ein gutes Beispiel dafür, wofür wir Serverless verwenden möchten. Wir pflegen und aktualisieren hauptsächlich eine einzige JSON-Datei. Wir werden all unsere anfänglichen Sprecherdaten haben und Geolocation-Daten von Google benötigen, um unsere Weltkugel zu erstellen. Wir können alles auch mit GitHub Commits auslösen. Lassen Sie uns eintauchen.

Erstellung der Serverless Function

Wir beginnen mit einer großen JSON-Datei, die ich aus einer Tabelle mit den Vortragsengagements meiner Kollegen extrahiert habe. Diese Datei enthält alles, was ich für die Tabelle benötige, aber für die Weltkugel werde ich diese WebGL-Weltkugel von Google data arts verwenden, die ich modifizieren werde. Im README können Sie sehen, dass ich meine Daten schließlich formatieren werde, um die Jahre zu extrahieren, aber ich benötige auch den Längen- und Breitengrad jedes Ortes, den wir besuchen.

var data = [
    [
    'seriesA', [ latitude, longitude, magnitude, latitude, longitude, magnitude, ... ]
    ],
    [
    'seriesB', [ latitude, longitude, magnitude, latitude, longitude, magnitude, ... ]
    ]
];

Schließlich werde ich auch die duplizierten Instanzen pro Jahr reduzieren müssen, um die Magnitude zu erstellen, aber diese Modifikation unserer Daten werden wir innerhalb von Vue im zweiten Teil dieser Serie behandeln.

Um zu beginnen, erstellen Sie, falls noch nicht geschehen, ein kostenloses Azure-Testkonto. Gehen Sie dann zum Portal: preview.portal.azure.com

Im Inneren sehen Sie eine Seitenleiste mit vielen Optionen. Oben steht Neu. Klicken Sie darauf.

Showing the sidebar in Azure where you can create a new service

Als Nächstes wählen wir Function App aus der Liste und geben den neuen Namen unserer Funktion ein. Dies gibt uns einige Optionen. Sie können sehen, dass unsere Ressourcengruppe und unser Abonnement bereits übernommen werden und ein Speicherkonto erstellt wird. Es werden auch die Standortdaten aus der Ressourcengruppe verwendet, sodass es erfreulicherweise ziemlich einfach zu befüllen ist, wie im GIF unten gezeigt.

Create a new function

Die Standardeinstellungen sind wahrscheinlich ziemlich gut für Ihre Bedürfnisse. Wie Sie im obigen GIF sehen können, werden die meisten Felder bereits aus dem App-Namen ausgefüllt. Möglicherweise möchten Sie Ihren Standort ändern, je nachdem, woher der meiste Datenverkehr kommt, oder einen Mittelpunkt wählen (d. h., wenn Sie viel Datenverkehr sowohl in San Francisco als auch in New York haben), ist es am besten, einen Standort in der Mitte der Vereinigten Staaten zu wählen.

Der Hosting-Plan kann Consumption (Standard) oder App Service Plan sein. Ich wähle Consumption, da Ressourcen dynamisch hinzugefügt oder abgezogen werden, was die Magie dieses gesamten Serverless-Konzepts ausmacht. Wenn Sie ein höheres Maß an Kontrolle oder Detail wünschen, möchten Sie wahrscheinlich den App Service Plan, aber denken Sie daran, dass Sie dann Ressourcen manuell skalieren und hinzufügen müssen, was zusätzliche Arbeit für Sie bedeutet.

Modify the function once it's created

Sie werden zu einem Bildschirm weitergeleitet, der Ihnen viele Informationen über Ihre Funktion anzeigt. Überprüfen Sie, ob alles in Ordnung ist, und klicken Sie dann auf das Pluszeichen für Funktionen in der Seitenleiste.

Add in the actual function

Von dort können Sie eine Vorlage auswählen. Wir werden etwas nach unten scrollen und aus den angebotenen Optionen GitHub Webhook – JavaScript auswählen.

choose the github javascript template from the templates in the portal

Die Auswahl dieser Option bringt Sie zu einer Seite mit einer Datei `index.js`. Sie können Code eingeben, wenn Sie möchten, aber sie geben uns etwas Standardcode, um einen ersten Test durchzuführen und zu sehen, ob alles richtig funktioniert. Bevor wir unsere Funktion erstellen, testen wir sie zuerst aus, um zu sehen, ob alles in Ordnung aussieht.

New default template

Wir klicken auf die Schaltflächen Speichern und Ausführen oben, und hier ist, was wir zurückbekommen. Sie sehen, dass die Ausgabe einen Kommentar enthält, wir erhalten einen Status von 200 OK in Grün und einige Protokolle, die validieren, dass unser GitHub-Webhook erfolgreich ausgelöst wurde.

testing the default output

Ziemlich gut! Jetzt kommt der lustige Teil: Schreiben wir unsere eigene Funktion.

Schreiben unserer ersten Serverless Function

In unserem Fall haben wir die Standortdaten für alle Vorträge, die wir für unsere Tabelle benötigen, aber um die JSON für unsere Weltkugel zu erstellen, benötigen wir noch eine weitere Information: Wir brauchen den Längen- und Breitengrad für alle Vortragsveranstaltungen. Die JSON-Datei wird von unserem zentralen Vuex-Store gelesen, und wir können die Teile, die gelesen werden müssen, an jede Komponente weitergeben.

Die Datei, die ich für die Serverless-Funktion verwendet habe, ist in meinem GitHub-Repo gespeichert. Sie können die gesamte Datei hier erkunden, aber gehen wir sie auch etwas durch.

Das erste, was ich erwähnen werde, ist, dass ich diese Variablen mit Konfigurationsoptionen für die Zwecke dieses Tutorials gefüllt habe, weil ich Ihnen nicht alle meine privaten Informationen geben möchte. Ich meine, es ist toll, wir sind Freunde und so, aber ich habe Sie gerade erst kennengelernt.

// GitHub configuration is read from process.env
let GH_USER = process.env.GH_USER;
let GH_KEY = process.env.GH_KEY;
let GH_REPO = process.env.GH_REPO;
let GH_FILE = process.env.GH_FILE;

In einem realen Szenario könnte ich die Daten einfach eintragen.

// GitHub configuration is read from process.env
let GH_USER = sdras;

... und so weiter. Um diese Umgebungsvariablen zu verwenden (falls Sie sie ebenfalls speichern und privat halten möchten), können Sie sie wie oben gezeigt verwenden und zu Ihrer Funktion im Dashboard gehen. Dort sehen Sie einen Bereich namens Konfigurierte Features. Klicken Sie auf Anwendungseinstellungen und Sie gelangen zu einer Seite mit einer Tabelle, auf der Sie diese Informationen eingeben können.

Arbeiten mit unserem Datensatz

Zuerst rufen wir die ursprüngliche JSON-Datei von GitHub ab und dekodieren/parsen sie. Wir verwenden eine Methode, die die Datei aus einer GitHub-Antwort abruft und sie base64-kodiert (mehr Informationen dazu hier).

module.exports = function(context, data) {
 // Make the context available globally
 gContext = context;

 getGithubJson(githubFilename(), (data, err) => {
   if (!err) {
     // No error; base64 decode and JSON parse the data from the Github response
     let content = JSON.parse(
       new Buffer(data.content, 'base64').toString('ascii')
     );

Dann rufen wir die Geo-Informationen für jeden Eintrag in den Originaldaten ab. Wenn es gut gelaufen ist, laden wir sie wieder auf GitHub hoch, sonst gibt es einen Fehler. Wir werden zwei Fehler haben: einen für einen allgemeinen Fehler und einen für den Fall, dass wir eine korrekte Antwort erhalten, aber ein Geo-Fehler vorliegt, damit wir sie unterscheiden können. Sie werden feststellen, dass wir gContext.log verwenden, um in unsere Portal-Konsole auszugeben.

getGeo(makeIterator(content), (updatedContent, err) => {
       if (!err) {
         // we need to base64 encode the JSON to embed it into the PUT (dear god, why)
         let updatedContentB64 = new Buffer(
           JSON.stringify(updatedContent, null, 2)
         ).toString('base64');
         let pushData = {
           path: GH_FILE,
           message: 'Looked up locations, beep boop.',
           content: updatedContentB64,
           sha: data.sha
         };
         putGithubJson(githubFilename(), pushData, err => {
           context.log('All done!');
           context.done();
         });
       } else {
         gContext.log('All done with get Geo error: ' + err);
         context.done();
       }
     });
   } else {
     gContext.log('All done with error: ' + err);
     context.done();
   }
 });
};

Großartig! Nun, gegeben ein Array von Einträgen (eingewickelt in einen Iterator), durchlaufen wir jeden einzelnen und füllen den Längen- und Breitengrad mithilfe der Google Maps API. Beachten Sie, dass wir auch Orte zwischenspeichern, um einige API-Aufrufe zu sparen.

function getGeo(itr, cb) {
 let curr = itr.next();
 if (curr.done) {
   // All done processing- pass the (now-populated) entries to the next callback
   cb(curr.data);
   return;
 }

 let location = curr.value.Location;

Jetzt überprüfen wir den Cache, um zu sehen, ob wir diesen Ort bereits nachgeschlagen haben.

 if (location in GEO_CACHE) {
   gContext.log(
     'Cached ' +
       location +
       ' -> ' +
       GEO_CACHE[location].lat +
       ' ' +
       GEO_CACHE[location].long
   );
   curr.value.Latitude = GEO_CACHE[location].lat;
   curr.value.Longitude = GEO_CACHE[location].long;
   getGeo(itr, cb);
   return;
 }

Dann, wenn nichts im Cache gefunden wurde, führen wir eine Suche durch und speichern das Ergebnis im Cache oder teilen uns mit, dass wir nichts gefunden haben.

 getGoogleJson(location, (data, err) => {
   if (err) {
     gContext.log('Error on ' + location + ' :' + err);
   } else {
     if (data.results.length > 0) {
       let info = {
         lat: data.results[0].geometry.location.lat,
         long: data.results[0].geometry.location.lng
       };
       GEO_CACHE[location] = info;
       curr.value.Latitude = info.lat;
       curr.value.Longitude = info.long;
       gContext.log(location + ' -> ' + info.lat + ' ' + info.long);
     } else {
       gContext.log(
         "Didn't find anything for " + location + ' ::' + JSON.stringify(data)
       );
     }
   }
   setTimeout(() => getGeo(itr, cb), 1000);
 });
}

Wir haben unterwegs einige Hilfsfunktionen genutzt, die dabei helfen, Google JSON zu erhalten und GitHub JSON zu holen und zu setzen.

Wenn wir diese Funktion nun im Portal ausführen, sehen wir unsere Ausgabe.

Running the function and seeing the console: status 200 ok

Es funktioniert! Unsere Serverless-Funktion aktualisiert unsere JSON-Datei mit all den neuen Daten. Ich mag es wirklich, dass ich mit Backend-Diensten arbeiten kann, ohne JavaScript zu verlassen, was mir vertraut ist. Wir müssen nur `git pull` machen und können diese Datei als Zustand in unserem zentralen Vuex-Store verwenden. Dies ermöglicht es uns, die Tabelle zu füllen, was wir im nächsten Teil unserer Serie behandeln werden, und wir werden dies auch verwenden, um unsere Weltkugel zu aktualisieren. Wenn Sie mit einer Serverless-Funktion experimentieren und sie in Aktion sehen möchten, können Sie eine mit einem kostenlosen Testkonto erstellen.

Artikelserie

  1. Automatische Aktualisierung von GitHub-Dateien mit Serverless Functions (Sie sind hier!)
  2. Filtern und Verwenden der Daten