Funcionamiento
Paginación
Las listas se recorren con limit y un cursor opaco: la respuesta trae next_cursor y se pasa como cursor.
Las listas de la API (/v1/alerts, /v1/musters, /v1/workers, /v1/devices, /v1/events) se recorren con un cursor.
Cómo funciona
| Parámetro | Qué es |
|---|---|
limit | Elementos por página, de 1 a 500. Por defecto, 100. |
cursor | El next_cursor de la página anterior. En la primera página, no se manda. |
Cada respuesta trae la página en data y un next_cursor:
{
"data": [ … ],
"next_cursor": "eyJ0IjoiMjAyNi0wOS0yNFQxMTowNDoxMloifQ"
}Para la página siguiente, repite la misma petición con los mismos filtros y cursor=<next_cursor>. Cuando next_cursor es null, no hay más.
Recorrer una lista entera
async function* todas(ruta, filtros = {}) {
let cursor = null;
do {
const url = new URL(`https://sandbox.api.safeonuba.com${ruta}`);
for (const [k, v] of Object.entries(filtros)) url.searchParams.set(k, v);
url.searchParams.set('limit', '100');
if (cursor) url.searchParams.set('cursor', cursor);
const res = await fetch(url, { headers: { Authorization: `Bearer ${await tokenSafeOnuba()}` } });
if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
const pagina = await res.json();
yield* pagina.data;
cursor = pagina.next_cursor;
} while (cursor);
}
for await (const alerta of todas('/v1/alerts', { status: 'unacknowledged' })) {
console.log(alerta.id, alerta.title);
}def todas(ruta: str, **filtros):
cursor = None
while True:
params = {**filtros, "limit": 100}
if cursor:
params["cursor"] = cursor
res = requests.get(
f"https://sandbox.api.safeonuba.com{ruta}",
headers={"Authorization": f"Bearer {token_safeonuba()}"},
params=params,
timeout=30,
)
res.raise_for_status()
pagina = res.json()
yield from pagina["data"]
cursor = pagina["next_cursor"]
if not cursor:
return
for alerta in todas("/v1/alerts", status="unacknowledged"):
print(alerta["id"], alerta["title"])async IAsyncEnumerable<JsonElement> Todas(string ruta, string filtros = "")
{
string? cursor = null;
do
{
var url = $"https://sandbox.api.safeonuba.com{ruta}?limit=100{filtros}"
+ (cursor is null ? "" : $"&cursor={Uri.EscapeDataString(cursor)}");
var res = await http.GetAsync(url);
res.EnsureSuccessStatusCode();
var pagina = await res.Content.ReadFromJsonAsync<JsonElement>();
foreach (var elemento in pagina.GetProperty("data").EnumerateArray()) yield return elemento;
var siguiente = pagina.GetProperty("next_cursor");
cursor = siguiente.ValueKind == JsonValueKind.Null ? null : siguiente.GetString();
} while (cursor is not null);
}
await foreach (var alerta in Todas("/v1/alerts", "&status=unacknowledged"))
Console.WriteLine(alerta.GetProperty("title").GetString());Orden de cada lista
/v1/alerts: de la más reciente a la más antigua./v1/events: del más antiguo al más reciente, en orden de publicación. Así se reproduce en el mismo orden en que se envió.
Lo que cuesta una página
Una página de 500 elementos cuenta más para tu límite de tiempo de consulta que una de 50 (ver Límites de uso). Pide lo que vas a usar.
Las listas cortas que no se paginan (/v1/sites, /v1/sites/{id}/zones, /v1/musters/{id}/missing, /v1/positions/latest, /v1/webhooks) devuelven todo en data, sin next_cursor.