Collections & Datensätze
Die vierundzwanzig Datensatztypen neben den Lieferungen — der Snapshot-Umschlag, den sie teilen, was ein Snapshot ist und wie Sie einen durchlaufen.
Lieferungen sind eine kuratierte Ressource, von Hand geformt, weil der Wareneingang das Erste war, wonach überhaupt jemand gefragt hat. Collections sind der Rest der Aufzeichnungen: Temperaturen, Kühlzyklen, Reinigung, Fritteusenkontrollen, Etiketten, vierundzwanzig an der Zahl, ausgeliefert über eine einzige generische Struktur.
Wo der Liefer-Endpunkt Ihnen ein entworfenes Objekt gibt, gibt Ihnen eine Collection den Datensatz so, wie die App ihn hält, verpackt in einen Umschlag, der sagt, wann er erfasst wurde und ob es ihn noch gibt. Dieser Tausch ist Absicht: Er ist der Grund, warum ein neues Modul diese API in der Woche erreicht, in der es erscheint, und nicht erst im Quartal danach.
Jede Collection ist eine eigene Zuweisung. Ein Schlüssel liest
temperature-records, weil jemand temperature-records:read zugewiesen hat,
und nichts sonst kommt damit.
Was Sie lesen können
GET /v1/restaurants/{restaurantId}/collections
GET /v1/restaurants/{restaurantId}/collections/{collection}/records?limit=…&cursor=…
GET /v1/restaurants/{restaurantId}/collections/{collection}/records/{recordId}
GET /v1/restaurants/{restaurantId}/collections/{collection}/records/{recordId}/assets
Beginnen Sie mit dem ersten. Er listet nur die Collections, die Ihrem Schlüssel zugewiesen wurden, jede mit der Berechtigung, die sie geöffnet hat, Sie müssen also nie raten:
{
"data": [
{
"collection": "temperature-records",
"readScope": "temperature-records:read",
"description": "Received shadow snapshots for temperature-records; not complete primary-store history.",
"stateKind": "received-shadow-snapshot",
"payloadVersion": 1
}
]
}
Ein leeres data ist kein Fehler und keine Störung: Es ist ein Schlüssel ohne
Collection-Zuweisungen. Die meisten Schlüssel, die ausgestellt wurden, bevor es
diese Oberfläche gab, haben genau das, und einen zu erweitern ist
eine E-Mail.
Der Umschlag
Jeder Datensatz in jeder Collection trägt dieselben äußeren Felder. Nur data
ändert seine Struktur.
| Feld | Typ | Bedeutung |
|---|---|---|
id | string | Die Kennung des Datensatzes, stabil und eindeutig innerhalb der Collection. |
restaurantId | string | Das Restaurant, zu dem er gehört. Spiegelt den Pfad. |
collection | string | Aus welcher Collection er stammt. Spiegelt den Pfad. |
data | object | fehlt | Der Datensatz selbst. Eine Löschmarkierung ist der einzige Fall, in dem es fehlen kann; überall sonst dürfen Sie es als vorhanden behandeln. |
deleted | boolean | true bedeutet eine Löschmarkierung: Der Datensatz wurde in der App gelöscht. |
capturedAt | string | Wann das Gerät die Änderung erfasst hat, als Zeichenkette mit Millisekunden seit der Epoche. |
receivedAt | string | Wann diese API sie empfangen hat. Später als capturedAt, manchmal um Stunden. |
sequence | string | Eine monoton steigende Position im Strom der Änderungen. Vergleichen Sie zwei davon; rechnen Sie nicht mit einer. |
mutationId | string | Die Kennung der Änderung, die diesen Zustand erzeugt hat. Auf unserer Seite ein Idempotenzschlüssel, auf Ihrer ein Schlüssel zur Deduplizierung. |
sourceVersion | string | null | Der Versionsstempel des ursprünglichen Datensatzes, sofern er einen hat. |
payloadVersion | number | Derzeit immer 1. Er steigt, falls sich die Bedeutung von data je ändert. |
stateKind | string | Immer received-shadow-snapshot. Lesen Sie den nächsten Abschnitt. |
capturedAt und receivedAt zählen beide. Ein Tablet in einem Kühlraum ohne
Empfang erfasst um 09:00 Uhr und lädt um 14:00 Uhr hoch; Ihre eigene Pipeline
nach receivedAt zu ordnen hält sie korrekt, und nach capturedAt zu berichten
hält sie wahr.
Was ein Snapshot ist und was nicht
stateKind sagt received-shadow-snapshot, und die Formulierung ist mit
Bedacht gewählt.
- Es ist der aktuelle Zustand eines Datensatzes, kein Protokoll jeder Änderung. Lesen Sie einen Datensatz zweimal, und Sie bekommen beide Male, wie er jetzt aussieht.
- Es ist, was wir empfangen haben, nicht, was das Restaurant hält. Ein Gerät, das eine Änderung nie hochgeladen hat, heißt: ein Datensatz, den diese API nie gesehen hat.
- Es ist keine rückwirkende Nachlieferung. Eine Collection beginnt für ein Restaurant an dem Tag, an dem die Erfassung dafür eingeschaltet wird. Datensätze, die davor entstanden sind, liegen in der App und nicht hier.
Ein fehlender Datensatz ist also wirklich mehrdeutig: nie erfasst, oder erfasst und noch nicht angekommen. Bauen Sie darauf keine Zahl, die sich wie ein Audit liest, und lesen Sie die Anmerkung zur Vollständigkeit, bevor Sie irgendjemandem eine Anzahl nennen.
Löschmarkierungen sind der eine Fall, in dem Abwesenheit eindeutig ist.
deleted: true ohne data heißt, dass der Datensatz existiert hat und gelöscht
wurde, und es ist der einzige Weg, auf dem Sie erfahren, dass etwas verschwunden
ist.
Die vierundzwanzig Collections
Jede davon nimmt <collection>:read als Berechtigung — cooling braucht
cooling:read, und so weiter die Liste hinunter.
| Collection | Was ein Datensatz ist |
|---|---|
restaurants | Der Standort selbst: Name, Adresse, Schließtage, Abonnement und Einstellungen. |
users | Die Mitarbeiterkonten, die Kontrollen erfassen, und welche Module sie nutzen. |
areas | Die Zonen, in die ein Restaurant aufgeteilt ist — Küche, Kühlraum, Bar. |
equipment | Kühlschränke, Gefriergeräte und Kühlketten-Einheiten, mit ihren Min-/Max-Schwellen. |
sensors | Die Funkfühler, über ihre MAC-Adresse, und das Gerät, das jeder von ihnen überwacht. |
temperature-records | Eine Temperatur, die eine Person an einem Gerät gemessen hat, mit der Schicht und einer etwaigen Korrekturmaßnahme. |
temperature-readings | Eine Temperatur, die ein Sensor von sich aus gemeldet hat, unbeaufsichtigt. |
suppliers | Wer liefert, mit Kontaktwegen und Kundennummer. |
products | Die Produkte, die angenommen und verwendet werden. |
preparations | Eigene Zubereitungen, mit Haltbarkeit und Allergenen. |
cleaning-tasks | Der Reinigungsplan: jede Aufgabe, ihr Bereich, ihre Wiederholung und ob sie ein Foto verlangt. |
cleaning-task-records | Eine tatsächlich erledigte Reinigungsaufgabe — wann und von wem. |
cleaning-task-pictures | Das Foto, das es belegt. Trägt eine Datei; siehe den Abschnitt zu Assets weiter unten. |
cooling | Ein Kühlzyklus: Produkt, Anfangs- und Endtemperatur, Anfangs- und Endzeit. |
freezing | Ein Gefriervorgang, dieselbe Struktur wie beim Kühlen. |
reheating | Ein Wiedererhitzungsvorgang, dieselbe Struktur wie beim Kühlen. |
transport | Ein transportiertes Produkt, mit Abgangs- und Ankunftsort, Zeit und Temperatur. |
fryer-equipment | Die Fritteusen. |
fryer-checks | Eine Prüfung der Ölqualität und was entschieden wurde — gefiltert, gewechselt, belassen. |
cooking-equipment | Öfen und Gargeräte, mit ihren Schwellen. |
cooking-temperature-records | Eine Gartemperatur, von einer Person an einem Gargerät gemessen. |
surface-analyses | Ein Oberflächenabstrich: was geprüft wurde, ob er bestanden wurde, und der Maßnahmenplan, falls nicht. |
traceability-labels | Ein gedrucktes Rückverfolgbarkeitsetikett. Trägt eine Datei; siehe den Abschnitt zu Assets weiter unten. |
drive-files | Ein Dokument, das im Drive des Restaurants abgelegt ist. Trägt eine Datei; siehe den Abschnitt zu Assets weiter unten. |
Die Struktur jedes data-Objekts, Feld für Feld, steht im
OpenAPI-Dokument, das aus dem
laufenden Dienst erzeugt wird — es ist die eine Beschreibung, die nicht von dem
abweichen kann, was ausgerollt ist.
Eine Collection durchlaufen
GET /v1/restaurants/{restaurantId}/collections/{collection}/records?limit=100
| Parameter | Pflicht | Regeln |
|---|---|---|
limit | nein | 1 bis 100. Standard ist 50. |
cursor | nein | Der nextCursor der vorherigen Seite, wortgleich. |
Hier gibt es keinen Zeitraum, anders als bei Lieferungen: Sie durchlaufen eine Collection, nicht ein Fenster daraus.
Ein Durchlauf ist ein konsistenter Snapshot. Die erste Seite fixiert die
Position im Strom, und jede spätere Seite wird zu genau dieser Position
beantwortet. Datensätze, die geschrieben werden, während Sie blättern,
verschieben die Seiten unter Ihnen nicht und tauchen mitten im Durchlauf nicht
auf — Sie sehen sie beim nächsten Durchlauf. Sortiert wird aufsteigend nach
sequence.
Der Cursor ist an das Restaurant, die Collection und das Limit gebunden. Ändern Sie die Seitengröße mitten im Durchlauf, wird sie abgelehnt: Wählen Sie ein Limit und behalten Sie es für den ganzen Durchlauf.
async function* records({ apiKey, restaurantId, collection }) {
const base = `https://api.backresto.com/v1/restaurants/${restaurantId}/collections/${collection}/records`;
let cursor = null;
do {
const url = new URL(base);
url.searchParams.set('limit', '100');
if (cursor !== null) {
url.searchParams.set('cursor', cursor);
}
const response = await fetch(url, {
headers: { Authorization: `Bearer ${apiKey}` }
});
if (!response.ok) {
throw new Error(`BackResto ${response.status}`);
}
const page = await response.json();
yield* page.data;
cursor = page.nextCursor;
} while (cursor !== null);
}
Eine Kopie aktuell halten
Es gibt heute keinen since-Parameter. Eine Auffrischung ist ein weiterer
Durchlauf, und Sie gleichen ihn mit dem ab, was Sie schon halten:
- Schlüsseln Sie auf
idinnerhalb einer Collection und überschreiben Sie, wenn diesequence, die Sie bekommen, höher ist als die gespeicherte. - Beachten Sie Löschmarkierungen.
deleted: trueist die Anweisung zu löschen; sie anzuwenden ist der einzige Weg, auf dem Ihre Kopie aufhört auseinanderzulaufen. - Durchlaufen Sie zu einer vernünftigen Stunde. Eine vollständige Collection
mit
limit=100ist für ein einzelnes Restaurant eine Handvoll Anfragen, und das Budget sind 300 pro Minute — aber mehrere hundert Restaurants in derselben Cron-Minute sind eine Spitze, die Ihnen gehört.
Wenn ein inkrementeller Cursor ändern würde, was Sie bauen können, sagen Sie es. Es ist eine kleine Änderung an einem Strom, der ohnehin geordnet ist — der Grund, warum es ihn nicht gibt, ist, dass ihn bisher niemand gebraucht hat.
Assets
Drei Collections tragen eine Datei: cleaning-task-pictures,
traceability-labels und drive-files. Der Datensatz benennt sie in
data.asset; die Bytes kommen vom Assets-Endpunkt.
GET /v1/restaurants/{restaurantId}/collections/{collection}/records/{recordId}/assets
{
"data": [
{
"id": "a17c93be4f02",
"contentType": "application/pdf",
"byteLength": 184320,
"sha256": "9f2a…",
"uploadedAt": "1789000000000",
"url": "https://…?X-Amz-Signature=…",
"urlExpiresAt": "1789000900000"
}
]
}
Er antwortet weitgehend so wie die Lieferfotos: eine Liste signierter URLs, jede fünfzehn Minuten gültig, jede mit ihrer eigenen Autorisierung, sodass der Download keinen Header braucht. Zwei Unterschiede sollte man kennen.
Das sind Dateien, nicht nur Bilder. Ein Reinigungsbeleg ist ein Foto, aber
ein Rückverfolgbarkeitsetikett oder eine Drive-Datei kann ein PDF, eine CSV,
eine Tabelle oder ein Word-Dokument sein — lesen Sie contentType, statt ein
Bild anzunehmen, und benennen Sie beim Import nicht alles in .jpg um.
sha256 ist da, damit Sie sich Arbeit sparen können. Es ist der Digest der
Bytes: Passt er zu etwas, das Sie schon gespeichert haben, müssen Sie es nicht
erneut herunterladen.
Es erscheinen nur Assets, deren Upload abgeschlossen und geprüft ist — eines, das noch unterwegs ist, fehlt, statt kaputt zu sein, und dasselbe gilt, sobald der übergeordnete Datensatz gelöscht ist.
Der Rest des Rats ist identisch, und es lohnt sich, ihn zu wiederholen, weil der Fehlschlag leise ist: Holen Sie die Bytes jetzt, speichern Sie die Bytes statt des Links, und rufen Sie den Endpunkt erneut auf, wenn Sie eine frische URL brauchen.
Die Fehler, die Ihnen begegnen werden
403 — Ihr Schlüssel erreicht das Restaurant, aber nicht mit der
Berechtigung dieser Collection. Der Collections-Endpunkt ist der billige Weg
herauszufinden, welche er hat.
404 — überhaupt keine Zuweisung für dieses Restaurant, oder es gibt diesen
Datensatz nicht. Beides ist absichtlich nicht unterscheidbar.
400 — eine Collection außerhalb der vierundzwanzig oben, oder ein Cursor,
der nicht zu diesem Restaurant, dieser Collection und diesem Limit gehört.
Alle drei sind Problemdokumente mit einer requestId.