Hey, lass uns eine funktionale Kalender-App mit dem JAMstack erstellen

Avatar of Chris Nwamba
Chris Nwamba am

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

Hey, lasst uns eine funktionale Kalender-App mit dem JAMstack erstellen

Ich habe mich schon immer gefragt, wie dynamische Zeitplanung funktioniert, also habe ich beschlossen, umfangreiche Recherchen durchzuführen, neue Dinge zu lernen und über den technischen Teil der Reise zu schreiben. Es ist nur fair, dich zu warnen: Alles, was ich hier behandle, sind drei Wochen Recherche, komprimiert in einem einzigen Artikel. Obwohl er anfängerfreundlich ist, ist er reichlich zu lesen. Also, bitte, zieh dir einen Stuhl heran, setz dich hin und lass uns ein Abenteuer erleben.

Mein Plan war es, etwas zu bauen, das wie der Google Kalender aussieht, aber nur drei Kernfunktionen zu demonstrieren

  1. Auflistung aller bestehenden Termine in einem Kalender
  2. Erstellung neuer Termine
  3. Terminplanung und E-Mail-Benachrichtigung basierend auf dem bei der Erstellung gewählten Datum. Die Zeitplanung sollte Code ausführen, um den Benutzer zu benachrichtigen, wenn *die Zeit reif ist*.

Schön, oder? Lies bis zum Ende des Artikels, denn das werden wir erstellen.

A calendar month view with a pop-up form for creating a new event as an overlay.

Das einzige Wissen, das ich über das Ausführen von Code zu einem späteren oder verzögerten Zeitpunkt hatte, waren CRON-Jobs. Der einfachste Weg, einen CRON-Job zu verwenden, besteht darin, einen Job **statisch** in deinem Code zu definieren. Dies ist ad hoc – **statisch** bedeutet, dass ich nicht einfach einen Termin wie den Google Kalender planen und *einfach* meinen CRON-Code aktualisieren lassen kann. Wenn du Erfahrung mit dem Schreiben von CRON-Triggern hast, fühlst du meinen Schmerz. Wenn nicht, hast du Glück, dass du CRON vielleicht nie auf diese Weise verwenden musst.

Um meine Frustration weiter zu verdeutlichen, musste ich eine Zeitplanung basierend auf einer Nutzlast von HTTP-Anfragen auslösen. Die Daten und Informationen zu dieser Zeitplanung würden über die HTTP-Anfrage übergeben werden. Das bedeutet, dass wir Dinge wie das geplante Datum nicht im Voraus kennen.

Wir (meine Kollegen und ich) haben einen Weg gefunden, dies zu realisieren und – mit Hilfe von Sarah Drasners Artikel über Durable Functions – habe ich verstanden, was ich lernen (und was ich verlernen) musste. Du wirst alles lernen, woran ich in diesem Artikel gearbeitet habe, von der Terminverwaltung bis zur E-Mail-Planung und Kalenderübersichten. Hier ist ein Video der App in Aktion

https://www.youtube.com/watch?v=simaM4FxPoo&

Du bemerkst vielleicht die subtile Verzögerung. Das hat nichts mit der Ausführungszeit der Planung oder der Ausführung des Codes zu tun. Ich teste mit einem kostenlosen SendGrid-Konto, das meiner Meinung nach eine gewisse Latenz aufweist. Du kannst dies bestätigen, indem du die verantwortliche serverlose Funktion testest, ohne E-Mails zu senden. Du wirst feststellen, dass der Code genau zur geplanten Zeit ausgeführt wird.

Werkzeuge und Architektur

Hier sind die drei grundlegenden Einheiten dieses Projekts

  1. React Frontend: Kalender-Benutzeroberfläche (UI), einschließlich der Benutzeroberfläche zum Erstellen, Aktualisieren oder Löschen von Terminen.
  2. 8Base GraphQL: Eine Backend-Datenbankschicht für die App. Hier werden wir unsere Daten speichern, lesen und aktualisieren. Das Lustige daran ist, dass du für dieses Backend keinen Code schreiben wirst.
  3. Durable Functions: Durable Functions sind eine Art serverlose Funktionen, die die Fähigkeit besitzen, ihren Zustand von früheren Ausführungen zu merken. Dies ersetzt CRON-Jobs und löst das zuvor beschriebene Ad-hoc-Problem.

Siehe den Pen
durable-func1
von Chris Nwamba (@codebeast)
auf CodePen.

Der Rest dieses Beitrags wird drei Hauptabschnitte haben, basierend auf den drei oben genannten Einheiten. Wir werden sie nacheinander durchgehen, ausbauen, testen und sogar die Arbeit bereitstellen. Bevor wir damit weitermachen, lass uns mit einem von mir erstellten Starterprojekt beginnen, das uns den Einstieg erleichtert.

Projekt-Repository

Erste Schritte

Du kannst dieses Projekt auf verschiedene Arten einrichten – entweder als **Full-Stack-Projekt** mit den drei Einheiten in einem Projekt oder als **eigenständiges Projekt**, bei dem jede Einheit ihr eigenes Stammverzeichnis hat. Nun, ich habe mich für die erste Variante entschieden, da sie prägnanter, leichter zu lehren und überschaubar ist, da es sich um ein einziges Projekt handelt.

Die App wird ein Create-React-App-Projekt sein, und ich habe einen Starter für uns erstellt, um die Hürde für die Einrichtung zu senken. Er enthält zusätzlichen Code und Logik, die wir nicht erklären müssen, da sie außerhalb des Umfangs des Artikels liegen. Die folgenden Dinge sind für uns eingerichtet

  1. Kalenderkomponente
  2. Modal- und Popover-Komponenten zur Darstellung von Terminformularen
  3. Terminformular-Komponente
  4. Einige GraphQL-Logik zum Abfragen und Mutieren von Daten
  5. Ein Durable Serverless Function-Gerüst, in dem wir die Zeitpläne schreiben werden

Tipp: Jede vorhandene Datei, die uns wichtig ist, hat oben im Dokument einen Kommentarblock. Der Kommentarblock erklärt, was gerade in der Code-Datei passiert, und einen To-Do-Bereich, der beschreibt, was wir als Nächstes tun müssen.

Beginne damit, den Starter von Github zu klonen

git clone -b starter --single-branch https://github.com/christiannwamba/calendar-app.git

Installiere die npm-Abhängigkeiten, die in der `package.json`-Datei im Stammverzeichnis sowie in der `package.json`-Datei des Serverless beschrieben sind

npm install

Orchestrierte Durable Functions für die Terminplanung

Es gibt zwei Wörter, die wir vorab klären müssen, bevor wir diesen Begriff verstehen können – **Orchestrierung** und **dauerhaft**.

Orchestrierung wurde ursprünglich verwendet, um eine Ansammlung gut koordinierter Ereignisse, Aktionen usw. zu beschreiben. In der Informatik wird dies stark entlehnt, um eine reibungslose Koordination von Computersystemen zu beschreiben. Das Schlüsselwort ist *koordinieren*. Wir müssen zwei oder mehr Einheiten eines Systems auf koordinierte Weise zusammenführen.

Dauerhaft wird verwendet, um alles zu beschreiben, das die herausragende Eigenschaft hat, länger zu bestehen.

Wenn man Systemkoordination und Langlebigkeit zusammenfügt, erhält man Durable Functions. Dies ist das mächtigste Merkmal von Azure Serverless Functions. Durable Functions basieren auf dem, was wir jetzt wissen, und haben diese beiden Merkmale

  1. Sie können verwendet werden, um die Ausführung von zwei oder mehr Funktionen zusammenzufassen und sie so zu koordinieren, dass keine Wettlaufsituationen auftreten (Orchestrierung).
  2. Durable Functions erinnern sich an Dinge. Das macht sie so mächtig. Sie brechen die Regel Nummer eins von HTTP: Zustandslosigkeit. Durable Functions behalten ihren Zustand intakt, egal wie lange sie warten müssen. Erstelle eine Zeitplanung für eine Million Jahre in die Zukunft, und eine Durable Function wird nach einer Million Jahre ausgeführt, während sie die Parameter behält, die ihr am Tag des Auslösers übergeben wurden. **Das bedeutet, Durable Functions sind zustandsbehaftet**.

Diese Dauerhaftigkeitsmerkmale eröffnen neue Möglichkeiten für serverlose Funktionen, und deshalb erforschen wir heute eines dieser Merkmale. Ich empfehle Sarahs Artikel noch einmal, um eine visualisierte Version einiger möglicher Anwendungsfälle von Durable Functions zu sehen.

Ich habe auch eine visuelle Darstellung des Verhaltens der Durable Functions erstellt, die wir heute schreiben werden. Betrachte dies als ein animiertes Architekturdiagramm

Shows the touch-points of a serverless system.

Eine Datenmutation aus einem externen System (8Base) löst die Orchestrierung aus, indem sie den **HTTP-Trigger** aufruft. Der Trigger ruft dann die **Orchestrierungsfunktion** auf, die einen Termin plant. Wenn die Ausführungszeit fällig ist, wird die Orchestrierungsfunktion erneut aufgerufen, diesmal überspringt sie die Orchestrierung und ruft die **Aktivitätsfunktion** auf. Die Aktivitätsfunktion ist der Aktionsausführer. Dies ist die eigentliche Aktion, die stattfindet, z. B. "E-Mail-Benachrichtigung senden".

Erstellen von orchestrierten Durable Functions

Ich führe dich durch die Erstellung von Funktionen mit VS Code. Du benötigst zwei Dinge

  1. Ein Azure-Konto
  2. VS Code

Sobald du beides eingerichtet hast, musst du sie miteinander verbinden. Du kannst dies über eine VS Code-Erweiterung und ein Node-CLI-Tool tun. Beginne mit der Installation des CLI-Tools


npm install -g azure-functions-core-tools

# OR

brew tap azure/functions
brew install azure-functions-core-tools

Installiere als Nächstes die Azure Functions-Erweiterung, um VS Code mit Functions in Azure zu verbinden. Du kannst mehr über das Einrichten von Azure Functions in meinem vorherigen Artikel lesen.


Jetzt, wo du die gesamte Einrichtung abgeschlossen hast, lass uns mit der Erstellung dieser Funktionen beginnen. Die Funktionen, die wir erstellen werden, entsprechen den folgenden Ordnern.

Ordner Funktion
schedule Durable HTTP Trigger
scheduleOrchestrator Durable Orchestrierung
sendEmail Durable Activity

Beginne mit dem Trigger.

  1. Klicke auf das Azure-Erweiterungssymbol und folge dem Bild unten, um die `schedule`-Funktion zu erstellen
    Shows the interface steps going from Browse to JavaScript to Durable Functions HTTP start to naming the function schedule.
  2. Da dies die erste Funktion ist, wählen wir das Ordnersymbol, um ein Funktionsprojekt zu erstellen. Das Symbol danach erstellt eine einzelne Funktion (kein Projekt).
  3. Klicke auf "Durchsuchen" und erstelle einen `serverless`-Ordner im Projekt. Wähle den neuen `serverless`-Ordner aus.
  4. Wähle JavaScript als Sprache. Wenn TypeScript (oder eine andere Sprache) dein Ding ist, nur zu.
  5. Wähle `Durable Functions HTTP starter`. Das ist der Trigger.
  6. Benenne die erste Funktion `schedule`

Erstelle als Nächstes den Orchestrator. Anstatt ein Funktionsprojekt zu erstellen, erstelle stattdessen eine Funktion.

  1. Klicke auf das Funktionssymbol
  2. Wähle `Durable Functions orchestrator`.
  3. Gib ihr den Namen `scheduleOrchestrator` und drücke Enter.
  4. Du wirst aufgefordert, ein Speicherkonto auszuwählen. Orchestrator verwendet Speicher, um den Zustand einer *Funktion im Prozess* zu erhalten.
  5. Wähle ein Abonnement in deinem Azure-Konto aus. In meinem Fall habe ich das kostenlose Testabonnement gewählt.
  6. Folge den restlichen Schritten, um ein Speicherkonto zu erstellen.

Schließlich wiederhole den vorherigen Schritt, um eine Aktivität zu erstellen. Diesmal sollten folgende Punkte anders sein

  • Wähle `Durable Functions activity`.
  • Benenne sie `sendEmail`.
  • Es wird kein Speicherkonto benötigt.

Zeitplanung mit einem Durable HTTP-Trigger

Der Code in `serverless/schedule/index.js` muss nicht angefasst werden. So sieht er ursprünglich aus, wenn die Funktion mit VS Code oder dem CLI-Tool erstellt wird.

const df = require("durable-functions");
module.exports = async function (context, req) {
  const client = df.getClient(context);
  const instanceId = await client.startNew(req.params.functionName, undefined, req.body);
  context.log(`Started orchestration with ID = '${instanceId}'.`);
  return client.createCheckStatusResponse(context.bindingData.req, instanceId);
};

Was passiert hier?

  1. Wir erstellen auf der Clientseite eine Durable Function, die auf dem Kontext der Anfrage basiert.
  2. Wir rufen den Orchestrator mit der `startNew()`-Funktion des Clients auf. Der Name der Orchestrierungsfunktion wird als erstes Argument an `startNew()` über das `params`-Objekt übergeben. Ein `req.body` wird ebenfalls als drittes Argument an `startNew()` übergeben, das an den Orchestrator weitergeleitet wird.
  3. Schließlich geben wir eine Reihe von Daten zurück, die verwendet werden können, um den Status der Orchestrierungsfunktion zu überprüfen oder den Prozess abzubrechen, bevor er abgeschlossen ist.

Die URL zum Aufruf der obigen Funktion würde so aussehen

https://:7071/api/orchestrators/{functionName}

Wobei `functionName` der Name ist, der an `startNew` übergeben wurde. In unserem Fall sollte es sein

//:7071/api/orchestrators/scheduleOrchestrator

Es ist auch gut zu wissen, dass du ändern kannst, wie diese URL aussieht.

Orchestrierung mit einem Durable Orchestrator

Der HTTP-Trigger `startNew()` ruft eine Funktion basierend auf dem Namen auf, den wir ihm übergeben. Dieser Name entspricht dem Namen der Funktion und dem Ordner, der die Orchestrierungslogik enthält. Die Datei `serverless/scheduleOrchestrator/index.js` exportiert eine Durable Function. Ersetze den Inhalt durch Folgendes

const df = require("durable-functions");
module.exports = df.orchestrator(function* (context) {
  const input = context.df.getInput()
  // TODO -- 1
  
  // TODO -- 2
});

Die Orchestrierungsfunktion ruft den Request Body vom HTTP-Trigger über `context.df.getInput()` ab.

Ersetze `TODO -- 1` durch die folgende Codezeile, die vielleicht das Wichtigste in dieser gesamten Demo ist

yield context.df.createTimer(new Date(input.startAt))

Diese Zeile nutzt die Durable Function, um einen Timer basierend auf dem vom Request Body über den HTTP-Trigger übergebenen Datum zu erstellen.

Wenn diese Funktion ausgeführt wird und hier ankommt, wird sie den Timer auslösen und vorübergehend abbrechen. Wenn die Zeitplanung fällig ist, kehrt sie zurück, überspringt diese Zeile und ruft die folgende Zeile auf, die du anstelle von `TODO -- 2` verwenden solltest.

return yield context.df.callActivity('sendEmail', input);

Die Funktion ruft die Aktivitätsfunktion auf, um eine E-Mail zu senden. Wir übergeben auch eine Nutzlast als zweites Argument.

So würde die abgeschlossene Funktion aussehen

const df = require("durable-functions");

module.exports = df.orchestrator(function* (context) {
  const input = context.df.getInput()
    
  yield context.df.createTimer(new Date(input.startAt))
    
  return yield context.df.callActivity('sendEmail', input);
});

E-Mail-Versand mit einer Durable Activity

Wenn eine Zeitplanung fällig ist, kommt der Orchestrator zurück, um die Aktivität aufzurufen. Die Aktivitätsdatei befindet sich in `serverless/sendEmail/index.js`. Ersetze den Inhalt durch Folgendes

const sgMail = require('@sendgrid/mail');
sgMail.setApiKey(process.env['SENDGRID_API_KEY']);

module.exports = async function(context) {
  // TODO -- 1
  const msg = {}
  // TODO -- 2
  return msg;
};

Sie importiert derzeit den Mailer von SendGrid und setzt den API-Schlüssel. Du kannst einen API-Schlüssel erhalten, indem du diesen Anweisungen folgst.

Ich setze den Schlüssel in einer Umgebungsvariablen, um meine Anmeldedaten sicher aufzubewahren. Du kannst deine auf die gleiche Weise sicher speichern, indem du einen `SENDGRID_API_KEY`-Schlüssel in `serverless/local.settings.json` mit deinem SendGrid-Schlüssel als Wert erstellst

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "<<AzureWebJobsStorage>",
    "FUNCTIONS_WORKER_RUNTIME": "node",
    "SENDGRID_API_KEY": "<<SENDGRID_API_KEY>"
  }
}

Ersetze `TODO -- 1` durch die folgende Zeile

const { email, title, startAt, description } = context.bindings.payload;

Dies extrahiert die Ereignisinformationen aus der Eingabe der Orchestrierungsfunktion. Die Eingabe ist an `context.bindings` angehängt. `payload` kann beliebig benannt werden, also gehe zu `serverless/sendEmail/function.json` und ändere den `name`-Wert zu `payload`

{
  "bindings": [
    {
      "name": "payload",
      "type": "activityTrigger",
      "direction": "in"
    }
  ]
}

Aktualisiere als Nächstes `TODO -- 2` mit dem folgenden Block, um eine E-Mail zu senden

const msg = {
  to: email,
  from: { email: '[email protected]', name: 'Codebeast Calendar' },
  subject: `Event: ${title}`,
  html: `<h4>${title} @ ${startAt}</h4> <p>${description}</p>`
};
sgMail.send(msg);

return msg;

Hier ist die vollständige Version

const sgMail = require('@sendgrid/mail');
sgMail.setApiKey(process.env['SENDGRID_API_KEY']);

module.exports = async function(context) {
  const { email, title, startAt, description } = context.bindings.payload;
  const msg = {
    to: email,
    from: { email: '[email protected]', name: 'Codebeast Calendar' },
    subject: `Event: ${title}`,
    html: `<h4>${title} @ ${startAt}</h4> <p>${description}</p>`
  };
  sgMail.send(msg);

  return msg;
};

Bereitstellung von Funktionen in Azure

Die Bereitstellung von Funktionen in Azure ist einfach. Es ist nur einen Klick vom VS Code-Editor entfernt. Klicke auf das eingekreiste Symbol, um bereitzustellen und eine Bereitstellungs-URL zu erhalten

Bist du noch bis hierher dabei? Du machst großartige Fortschritte! Es ist völlig in Ordnung, hier eine Pause zu machen, zu schlafen, dich zu strecken oder dich auszuruhen. Das habe ich beim Schreiben dieses Beitrags definitiv getan.

Daten- und GraphQL-Schicht mit 8Base

Meine einfachste Beschreibung und mein Verständnis von 8Base ist "Firebase für GraphQL". 8Base ist eine Datenbankschicht für jede Art von App, die du dir vorstellen kannst, und der interessanteste Aspekt daran ist, dass sie auf GraphQL basiert.

Der beste Weg, um zu beschreiben, wo 8Base in deinen Stack passt, ist, ein Szenario zu malen.

Stell dir vor, du bist ein freiberuflicher Entwickler mit einem Vertrag mittlerer Größe, um für einen Kunden einen E-Commerce-Shop zu erstellen. Deine Kernkompetenzen liegen im Web, daher bist du im Backend nicht sehr versiert, obwohl du etwas Node schreiben kannst.

Leider erfordert E-Commerce die Verwaltung von Inventaren, Auftragsverwaltung, Kaufverwaltung, Authentifizierung und Identitätsmanagement usw. "Verwalten" bedeutet auf fundamentaler Ebene einfach Daten-CRUD und Datenzugriff.

Anstatt des redundanten und langweiligen Prozesses des Erstellens, Lesens, Aktualisierens, Löschens und Verwalten von Zugriffen für Entitäten in unserem Backend-Code, was wäre, wenn wir diese Geschäftsanforderungen in einer UI beschreiben könnten? Was wäre, wenn wir Tabellen erstellen könnten, die es uns ermöglichen, CRUD-Operationen, Authentifizierung und Zugriff zu konfigurieren? Was wäre, wenn wir solche Hilfe hätten und uns nur darauf konzentrieren würden, Frontend-Code zu erstellen und Abfragen zu schreiben? Alles, was wir gerade beschrieben haben, wird von 8Base abgedeckt.

Hier ist eine Architektur einer App ohne Backend, die auf 8Base als Datenschicht angewiesen ist

Erstellen einer 8Base-Tabelle für die Speicherung und Abfrage von Ereignissen

Das erste, was wir tun müssen, bevor wir eine Tabelle erstellen, ist, ein Konto zu erstellen. Sobald du ein Konto hast, erstelle einen Workspace, der alle Tabellen und Logik für ein bestimmtes Projekt enthält.

Erstelle als Nächstes eine Tabelle, nenne die Tabelle `Events` und fülle die Tabellenfelder aus.

Wir müssen Zugriffsebenen konfigurieren. Derzeit gibt es nichts, was wir vor jedem Benutzer verbergen müssen, also können wir einfach den gesamten Zugriff auf die erstellte Events-Tabelle aktivieren

Die Einrichtung der Authentifizierung ist mit 8base super einfach, da sie mit Auth0 integriert ist. Wenn du Entitäten hast, die geschützt werden müssen, oder unser Beispiel um Authentifizierung erweitern möchtest, gehe wild vor.

Schließlich schnapp dir deine Endpunkt-URL für die Verwendung in der React-App

Testen von GraphQL-Abfragen und -Mutationen im Playground

Um sicherzugehen, dass wir bereit sind, die URL in die weite Welt hinauszutragen und mit dem Aufbau des Clients zu beginnen, testen wir zuerst die API mit einem GraphQL-Playground und sehen, ob das Setup in Ordnung ist. Klicke auf den Explorer.

Füge die folgende Abfrage in den Editor ein.

query {
  eventsList {
    count
    items {
      id
      title
      startAt
      endAt
      description
      allDay
      email
    }
  }
}

Ich habe einige Testdaten über die 8Base-Benutzeroberfläche erstellt und erhalte das Ergebnis zurück, wenn ich die Abfrage ausführe

Du kannst die gesamte Datenbank über das Schema-Dokument am rechten Ende der Explorer-Seite erkunden.

Kalender- und Terminformular-Benutzeroberfläche

Die dritte (und letzte) Einheit unseres Projekts ist die React App, die die Benutzeroberflächen erstellt. Es gibt vier Hauptkomponenten, die die UI bilden, und sie umfassen

  1. Kalender: Eine Kalender-Benutzeroberfläche, die alle bestehenden Termine auflistet
  2. Termin-Modal: Ein React-Modal, das die `EventForm`-Komponente rendert, um eine Komponente zu erstellen
  3. Termin-Popover: Popover-UI zum Lesen eines einzelnen Termins, Aktualisieren eines Termins mit `EventForm` oder Löschen eines Termins
  4. Terminformular: HTML-Formular zum Erstellen eines neuen Termins

Bevor wir uns direkt in die Kalenderkomponente stürzen, müssen wir den React Apollo Client einrichten. Der React Apollo Provider stattet dich mit Werkzeugen aus, um eine GraphQL-Datenquelle mit React-Mustern abzufragen. Der ursprüngliche Provider erlaubt dir, Higher-Order-Komponenten oder Render-Props zu verwenden, um Daten abzufragen und zu mutieren. Wir werden einen Wrapper für den ursprünglichen Provider verwenden, der es uns erlaubt, mit React Hooks abzufragen und zu mutieren.

Importiere in `src/index.js` die React Apollo Hooks und den 8Base Client in `TODO -- 1`

import { ApolloProvider } from 'react-apollo-hooks';
import { EightBaseApolloClient } from '@8base/apollo-client';

Konfiguriere an `TODO -- 2` den Client mit der Endpunkt-URL, die wir in der 8Base-Einrichtungsphase erhalten haben

const URI = 'https://api.8base.com/cjvuk51i0000701s0hvvcbnxg';

const apolloClient = new EightBaseApolloClient({
  uri: URI,
  withAuth: false
});

Verwende diesen Client, um den gesamten `App`-Baum mit dem Provider an `TODO -- 3` zu umwickeln

ReactDOM.render(
  <ApolloProvider client={apolloClient}>
    <App />
  </ApolloProvider>,
  document.getElementById('root')
);

Anzeigen von Terminen im Kalender

Die `Calendar`-Komponente wird innerhalb der `App`-Komponente gerendert und importiert die `BigCalendar`-Komponente von npm. Dann

  1. Wir rendern `Calendar` mit einer Liste von Terminen.
  2. Wir geben `Calendar` ein benutzerdefiniertes Popover (`EventPopover`), das zum Bearbeiten von Terminen verwendet wird.
  3. Wir rendern ein Modal (`EventModal`), das zum Erstellen neuer Termine verwendet wird.

Das Einzige, was wir aktualisieren müssen, ist die Liste der Termine. Anstatt das statische Array von Terminen zu verwenden, möchten wir 8Base nach allen gespeicherten Terminen abfragen.

Ersetze `TODO -- 1` durch die folgende Zeile

const { data, error, loading } = useQuery(EVENTS_QUERY);

Importiere die `useQuery`-Bibliothek von npm und `EVENTS_QUERY` am Anfang der Datei

import { useQuery } from 'react-apollo-hooks';
import { EVENTS_QUERY } from '../../queries';

`EVENTS_QUERY` ist exakt die gleiche Abfrage, die wir im 8Base-Explorer getestet haben. Sie befindet sich in `src/queries` und sieht so aus

export const EVENTS_QUERY = gql`
  query {
    eventsList {
      count
      items {
        id
        ...
      }
    }
  }
`;

Fügen wir eine einfache Fehler- und Ladebehandlung bei `TODO -- 2` hinzu

if (error) return console.log(error);
  if (loading)
    return (
      <div className="calendar">
        <p>Loading...</p>
      </div>
    );

Beachte, dass die `Calendar`-Komponente die `EventPopover`-Komponente verwendet, um einen benutzerdefinierten Termin zu rendern. Du kannst auch beobachten, dass die `Calendar`-Komponentendatei ebenfalls `EventModal` rendert. Beide Komponenten sind für dich eingerichtet, und ihre einzige Aufgabe ist es, `EventForm` zu rendern.

Termine mit der EventForm-Komponente erstellen, aktualisieren und löschen

Die Komponente in `src/components/Event/EventForm.js` rendert ein Formular. Das Formular dient zum Erstellen, Bearbeiten oder Löschen eines Termins. Importiere bei `TODO -- 1` `useCreateUpdateMutation` und `useDeleteMutation`

import {useCreateUpdateMutation, useDeleteMutation} from './eventMutationHooks'
  • `useCreateUpdateMutation`: Diese Mutation erstellt oder aktualisiert einen Termin, je nachdem, ob der Termin bereits existierte.
  • `useDeleteMutation`: Diese Mutation löscht einen bestehenden Termin.

Ein Aufruf einer dieser Funktionen gibt eine weitere Funktion zurück. Diese Funktion kann dann als Ereignishandler dienen.

Ersetze nun `TODO -- 2` mit einem Aufruf beider Funktionen

const createUpdateEvent = useCreateUpdateMutation(
  payload,
  event,
  eventExists,
  () => closeModal()
);
const deleteEvent = useDeleteMutation(event, () => closeModal());

Dies sind benutzerdefinierte Hooks, die ich geschrieben habe, um `useMutation` aus React Apollo Hooks zu wrappen. Jeder Hook erstellt eine Mutation und übergibt die Mutationsvariablen an die `useMutation`-Abfrage. Die Blöcke, die in `src/components/Event/eventMutationHooks.js` wie folgt aussehen, sind die wichtigsten Teile

useMutation(mutationType, {
  variables: {
    data
  },
  update: (cache, { data }) => {
    const { eventsList } = cache.readQuery({
      query: EVENTS_QUERY
    });
    cache.writeQuery({
      query: EVENTS_QUERY,
      data: {
        eventsList: transformCacheUpdateData(eventsList, data)
      }
    });
    //..
  }
});

Aufruf des Durable Function HTTP-Triggers von 8Base

Wir haben viel Zeit mit dem Aufbau der serverlosen Struktur, der Datenspeicherung und der UI-Schichten unserer Kalender-App verbracht. Zusammenfassend lässt sich sagen, dass die UI Daten zur Speicherung an 8Base sendet, **8Base Daten speichert und die Durable Function HTTP-Trigger auslöst**, der HTTP-Trigger die Orchestrierung startet und der Rest ist Geschichte. Derzeit speichern wir Daten mit einer Mutation, aber wir rufen die serverlose Funktion nirgendwo in 8Base auf.

8Base ermöglicht es dir, benutzerdefinierte Logik zu schreiben, was es sehr leistungsfähig und erweiterbar macht. Benutzerdefinierte Logik sind einfache Funktionen, die basierend auf Aktionen, die auf der 8Base-Datenbank ausgeführt werden, aufgerufen werden. Zum Beispiel können wir eine Logik einrichten, die jedes Mal aufgerufen wird, wenn eine Mutation auf einer Tabelle auftritt. Lassen wir eine erstellen, die aufgerufen wird, wenn ein Termin erstellt wird.

Beginne mit der Installation des 8Base CLI

npm install -g 8base

Führe im Kalender-App-Projekt den folgenden Befehl aus, um eine Starter-Logik zu erstellen

8base init 8base

Der Befehl `8base init` erstellt ein neues 8Base-Logik-Projekt. Du kannst ihm einen Verzeichnisnamen übergeben, in diesem Fall nennen wir den 8Base-Logik-Ordner `8base` – versteh das nicht falsch.

Auslösen der Zeitplanungslogik

Lösche alles in `8base/src` und erstelle eine Datei `triggerSchedule.js` im `src`-Ordner. Sobald du das getan hast, füge Folgendes in die Datei ein

const fetch = require('node-fetch');

module.exports = async event => {
  const res = await fetch('<HTTP Trigger URL>', {
    method: 'POST',
    body: JSON.stringify(event.data),
    headers: { 'Content-Type': 'application/json' }
  })
  const json = await res.json();
  console.log(event, json)
  return json;
};

Die Informationen über die GraphQL-Mutation sind im `event`-Objekt als Daten verfügbar.

Ersetze `` durch die URL, die du nach der Bereitstellung deiner Funktion erhalten hast. Du kannst die URL erhalten, indem du zur Funktion in deinem Azure-Portal gehst und auf "URL kopieren" klickst.

Du musst auch das `node-fetch`-Modul installieren, das die Daten von der API abruft

npm install --save node-fetch

8base Logikkonfiguration

Als Nächstes müssen Sie 8base mitteilen, welche exakte Mutation oder Abfrage diese Logik auslösen soll. In unserem Fall eine Erstellungs-Mutation auf der Tabelle Events. Sie können diese Informationen in der Datei 8base.yml beschreiben.

functions:
  triggerSchedule:
    handler:
      code: src/triggerSchedule.js
    type: trigger.after
    operation: Events.create

In gewisser Weise besagt dies: „Wenn eine Erstellungs-Mutation auf der Tabelle Events auftritt, rufen Sie bitte src/triggerSchedule.js auf, nachdem die Mutation stattgefunden hat.“

Wir wollen alles bereitstellen

Bevor etwas bereitgestellt werden kann, müssen wir uns beim 8Base-Konto anmelden, was wir über die Befehlszeile tun können.

8base login

Lassen Sie uns dann den Befehl deploy ausführen, um die App-Logik in Ihrer Workspace-Instanz zu senden und einzurichten.

8base deploy

Testen des gesamten Ablaufs

Um die App in ihrer vollen Pracht zu sehen, klicken Sie auf einen der Tage im Kalender. Sie sollten das Ereignisfenster mit dem Formular erhalten. Füllen Sie dieses aus und geben Sie ein zukünftiges Startdatum ein, damit wir eine Benachrichtigung auslösen. Versuchen Sie ein Datum, das mehr als 2-5 Minuten von der aktuellen Zeit entfernt ist, da ich keine Benachrichtigung schneller auslösen konnte.

https://www.youtube.com/watch?v=simaM4FxPoo&

Juhu, überprüfen Sie Ihre E-Mail! Die E-Mail sollte dank SendGrid angekommen sein. Jetzt haben wir eine App, mit der wir Ereignisse erstellen und Benachrichtigungen mit den Details zur Ereigniseinreichung erhalten können.