Pular para o conteúdo principal

Ler o feed de eventos

O feed devolve os eventos da organização em ordem de sequence, com o mesmo corpo do webhook. A leitura guarda o next de cada página e para quando uma página vem vazia. As regras estão em Webhook de venda.

  • Guarde o cursor de forma durável (banco, não memória), e só depois de gravar os eventos da página.
  • Grave cada evento pela mesma rotina do receptor, que deduplica pelo id.
  • Um 401 renova o token uma vez; um segundo 401 seguido indica credencial desativada e interrompe a leitura.
  • O feed alcança os últimos 30 dias. Um cursor parado há mais tempo que isso perde eventos.
public async Task SincronizarAsync(CancellationToken ct)
{
var after = await cursores.LerAsync("etech-feed") ?? 0;
var renovouToken = false;

while (true)
{
using var pedido = new HttpRequestMessage(HttpMethod.Get, $"/integration/v1/events?after={after}&limit=500");
pedido.Headers.Add("x-api-key", apiKey);
pedido.Headers.Authorization = new("Bearer", await tokens.ObterAsync(ct));

using var resposta = await http.SendAsync(pedido, ct);
if (resposta.StatusCode == HttpStatusCode.Unauthorized && !renovouToken)
{
tokens.Descartar();
renovouToken = true;
continue;
}
resposta.EnsureSuccessStatusCode();
renovouToken = false;

var pagina = await resposta.Content.ReadFromJsonAsync<PaginaDoFeed>(ct);
if (pagina is null || pagina.Events.Count == 0)
break;

foreach (var evento in pagina.Events)
await eventos.GravarSeNovoAsync(evento.GetRawText());

after = pagina.Next;
await cursores.GravarAsync("etech-feed", after);
}
}

sealed record PaginaDoFeed(
[property: JsonPropertyName("events")] List<JsonElement> Events,
[property: JsonPropertyName("next")] long Next);