Käsitteet
Sivutus ja synkronointi
Luettelot vastaavat sivu kerrallaan kursorin kanssa; updatedSince hakee vain sen, mikä on muuttunut edellisen ajon jälkeen.
Sivut ja kursorit
Jokainen luettelo vastaa samalla tavalla: kohteet, seuraavan sivun kursori ja tieto siitä, onko seuraavaa sivua.
{
"data": [ { "sessionId": "b3ac0004-0000-4000-8000-000000000000", "title": "Registration and coffee" } ],
"nextCursor": "YzE6MDYzOTI3NjEwODQ4MjgyMDAwMHxiM2Fj...",
"hasMore": true
}Lähetä nextCursor takaisin cursor-parametrina samoilla suodattimilla, kunnes se on null. limit määrää sivun koon: oletus on 100 ja enimmäismäärä 500. Kursori on läpinäkymätön: säilytä se sellaisenaan äläkä muodosta sitä itse. Kursori, jota palvelin ei ole tehnyt, hylätään koodilla invalidCursor sen sijaan, että aloitettaisiin hiljaa alusta.
Luettelot ovat kiinteässä järjestyksessä, vanhin muutos ensin ja tunniste ratkaisee tasatilanteet, joten sivu ei koskaan ohita tai toista kohdetta, joka ei muuttunut lukemisesi aikana. Kohde, joka muuttuu sivuttaessa, siirtyy loppuun ja näet sen uudelleen, mikä on juuri sitä, mitä synkronointi haluaa.
Osallistujien, tilausten ja saapumiskirjausten luettelot ottavat myös vastaan parametrit page ja pageSize, joita ne käyttivät ennen kursoreita. Ne toimivat yhä; suosi kuitenkin kursoria, joka ei koskaan laske.
Synkronointi: vain muuttuneet
Muuttuvien asioiden luettelot kertovat kentässä updatedUtc, milloin kukin kohde viimeksi muuttui, ja ottavat parametrin updatedSince. Synkronointi säilyttää uusimman näkemänsä updatedUtc-arvon ja kysyy seuraavalla kerralla siitä eteenpäin:
- Lue jokainen sivu niin, että
updatedSinceon edellisellä kerralla säilyttämäsi arvo (ensimmäisellä kerralla ei mitään). - Tallenna tai päivitä jokainen kohde sen tunnisteen perusteella.
- Säilytä ajon uusin
updatedUtcseuraavaa kertaa varten.
updatedSince on mukaan lukeva: kohde, joka muuttui samalla millisekunnilla kuin merkintäsi, tulee uudelleen sen sijaan, että se jäisi väliin. Jo olemassa olevan kohteen päivittäminen ei haittaa; kohteen menettäminen haittaa.
const base = "https://attendbeacon.com/api/v1/events/b3ac0000-0000-4000-8000-000000000001/attendees";
const headers = { Authorization: `Bearer ${process.env.ATTENDBEACON_KEY}` };
let since = await loadMark(); // null on the first run
let newest = since;
let cursor = null;
do {
const url = new URL(base);
url.searchParams.set("limit", "500");
if (since) url.searchParams.set("updatedSince", since);
if (cursor) url.searchParams.set("cursor", cursor);
const page = await fetch(url, { headers }).then((r) => r.json());
for (const attendee of page.data) {
await upsert(attendee); // by attendee.attendeeId
if (!newest || attendee.updatedUtc > newest) newest = attendee.updatedUtc;
}
cursor = page.nextCursor;
} while (cursor);
await saveMark(newest);var since = await LoadMarkAsync(); // null on the first run
var newest = since;
string? cursor = null;
do
{
var query = $"events/b3ac0000-0000-4000-8000-000000000001/attendees?limit=500"
+ (since is null ? "" : $"&updatedSince={Uri.EscapeDataString(since)}")
+ (cursor is null ? "" : $"&cursor={cursor}");
var page = await client.GetFromJsonAsync<JsonElement>(query);
foreach (var attendee in page.GetProperty("data").EnumerateArray())
{
await UpsertAsync(attendee); // by attendeeId
var updated = attendee.GetProperty("updatedUtc").GetString()!;
if (newest is null || string.CompareOrdinal(updated, newest) > 0) newest = updated;
}
cursor = page.GetProperty("nextCursor").GetString();
}
while (cursor is not null);
await SaveMarkAsync(newest);base = "https://attendbeacon.com/api/v1/events/b3ac0000-0000-4000-8000-000000000001/attendees"
headers = {"Authorization": f"Bearer {os.environ['ATTENDBEACON_KEY']}"}
since = load_mark() # None on the first run
newest = since
cursor = None
while True:
params = {"limit": 500}
if since: params["updatedSince"] = since
if cursor: params["cursor"] = cursor
page = requests.get(base, params=params, headers=headers).json()
for attendee in page["data"]:
upsert(attendee) # by attendee["attendeeId"]
if newest is None or attendee["updatedUtc"] > newest:
newest = attendee["updatedUtc"]
cursor = page["nextCursor"]
if cursor is None:
break
save_mark(newest)$base = "https://attendbeacon.com/api/v1/events/b3ac0000-0000-4000-8000-000000000001/attendees"
$since = Get-Content mark.txt -ErrorAction SilentlyContinue # nothing on the first run
$newest = $since
$cursor = $null
do {
$query = "?limit=500"
if ($since) { $query += "&updatedSince=" + [uri]::EscapeDataString($since) }
if ($cursor) { $query += "&cursor=$cursor" }
$page = Invoke-RestMethod -Uri ($base + $query) -Headers $headers
foreach ($attendee in $page.data) {
Save-Attendee $attendee # by attendeeId
if (-not $newest -or $attendee.updatedUtc -gt $newest) { $newest = $attendee.updatedUtc }
}
$cursor = $page.nextCursor
} while ($cursor)
Set-Content mark.txt $newestAjat ovat ISO 8601 -muodossa UTC-aikana kolmella desimaalilla ja Z-päätteellä, joten ne vertautuvat oikein myös tekstinä.
Mitä ilman muutosaikaa
Tilat, ohjelmalinjat, puhujat, sponsorit ja sponsoritasot ovat pieniä luetteloita ilman omaa muutosaikaa: lue ne kokonaan, halutessasi kursorin avulla. Niiden yksittäiset resurssit ja itse tapahtuma vastaavat ETag-otsakkeella, joten uusi kysely If-None-Match-otsakkeella vastaa 304 Not Modified ilman runkoa, jos mikään ei ole muuttunut.
Saapumiskirjaukset eivät koskaan muutu tehdyn jälkeen: sivuta niitä cursor-parametrilla tai parametrilla after, joka on viimeisin hallussasi oleva checkInId ja joka vain kasvaa.
Poistot ja tietojen poistaminen
Peruttu lippu jää luetteloon tilalla "status": "cancelled", joten synkronointi näkee perumisen. Kun joku poistaa tietonsa tapahtumasovelluksesta, hänen osallistujatietueensa jää luetteloon arvolla "erased": true ilman henkilötietoja; attendee.erased-webhook kertoo siitä heti, joten voit poistaa oman kopiosi. Katso Tietosuoja ja tietojen poistaminen.
Webhookit vai kyselyt
updatedSince-kysely muutaman minuutin välein on yksinkertainen ja tavoittaa kaiken. Webhookit kertovat sekunneissa. Monet integraatiot tekevät molempia: webhookit nopeuden vuoksi, öinen synkronointi varmuuden vuoksi.