diff --git a/package.json b/package.json index 89b537f..5e165f4 100644 --- a/package.json +++ b/package.json @@ -59,5 +59,8 @@ "vite": "^5.2.11", "vite-plugin-dts": "^3.9.1", "vitest": "^1.6.0" + }, + "dependencies": { + "@topsort/sdk": "^0.1.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 22242f2..7a5d785 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,10 @@ settings: importers: .: + dependencies: + '@topsort/sdk': + specifier: ^0.1.0 + version: 0.1.0 devDependencies: '@biomejs/biome': specifier: ^1.8.2 @@ -532,6 +536,9 @@ packages: '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + '@topsort/sdk@0.1.0': + resolution: {integrity: sha512-s/yJTDMEFV+wTadfbLH0YP6rt4BSBNlR9dkmsubZ/ijVuPSp14yB9JVd6HdicGooout40xPkJjfQ1WvTibP8ug==} + '@types/argparse@1.0.38': resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} @@ -2448,6 +2455,8 @@ snapshots: '@sinclair/typebox@0.27.8': {} + '@topsort/sdk@0.1.0': {} + '@types/argparse@1.0.38': {} '@types/body-parser@1.19.2': diff --git a/src/detector.ts b/src/detector.ts index 663abb1..2c18750 100644 --- a/src/detector.ts +++ b/src/detector.ts @@ -1,6 +1,5 @@ -import { Entity, TopsortEvent } from "./events"; +import { type Config, Entity, TopsortEvent, reportEvent } from "@topsort/sdk"; import { ProcessorResult, Queue } from "./queue"; -import { reportEvent } from "./reporter"; import { BidStore } from "./store"; const MAX_EVENTS_SIZE = 2500; @@ -132,12 +131,19 @@ async function processor(data: ProductEvent[]): Promise { retry: new Set(), }; const promises = []; + const config: Config = { + apiKey: window.TS.token, + host: window.TS.url, + }; for (const entry of data) { promises.push( - reportEvent(getApiPayload(entry), window.TS) + reportEvent(config, getApiPayload(entry)) .then((result) => { - const q = result.retry ? r.retry : r.done; - q.add(entry.id); + if (result.ok) { + r.done.add(entry.id); + } else { + r.retry.add(entry.id); + } }) .catch(() => { r.done.add(entry.id); diff --git a/src/events.ts b/src/events.ts deleted file mode 100644 index 73952b2..0000000 --- a/src/events.ts +++ /dev/null @@ -1,47 +0,0 @@ -interface Placement { - path: string; -} - -export interface Entity { - type: "product"; - id: string; -} - -interface Impression { - resolvedBidId?: string; - entity?: Entity; - additionalAttribution?: Entity; - placement: Placement; - occurredAt: string; - opaqueUserId: string; - id: string; -} - -interface Click { - resolvedBidId?: string; - entity?: Entity; - additionalAttribution?: Entity; - placement: Placement; - occurredAt: string; - opaqueUserId: string; - id: string; -} - -interface Item { - productId: string; - quantity: number; - unitPrice: number; -} - -interface Purchase { - occurredAt: string; - opaqueUserId: string; - id: string; - items: Item[]; -} - -export interface TopsortEvent { - impressions?: Impression[]; - clicks?: Click[]; - purchases?: Purchase[]; -} diff --git a/src/reporter.test.ts b/src/reporter.test.ts deleted file mode 100644 index 0d2f5dd..0000000 --- a/src/reporter.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { http, HttpResponse } from "msw"; -import { setupServer } from "msw/node"; -import { afterAll, afterEach, beforeAll, expect, test } from "vitest"; -import type { TopsortEvent } from "./events"; -import { reportEvent } from "./reporter"; - -const server = setupServer( - http.post("https://api.topsort.com/v2/events", () => { - return HttpResponse.json({}, { status: 200 }); - }), - - http.post("https://error.api.topsort.com/v2/events", () => { - return HttpResponse.error(); - }), -); - -beforeAll(() => server.listen()); -afterAll(() => server.close()); -afterEach(() => server.resetHandlers()); - -function returnStatus(status: number, url = "https://api.topsort.com/v2/events"): void { - return server.use( - http.post(url, () => { - return HttpResponse.json({}, { status: status }); - }), - ); -} - -test("success", async () => { - await expect(reportEvent({} as TopsortEvent, { token: "token" })).resolves.toEqual({ - ok: true, - retry: false, - }); -}); - -test("network error", async () => { - await expect( - reportEvent({} as TopsortEvent, { - token: "token", - url: "https://error.api.topsort.com", - }), - ).resolves.toEqual({ - ok: false, - retry: true, - }); -}); - -test("permanent error", async () => { - returnStatus(400); - await expect(reportEvent({} as TopsortEvent, { token: "token" })).resolves.toEqual({ - ok: false, - retry: false, - }); -}); - -test("authentication error", async () => { - returnStatus(401); - await expect(reportEvent({} as TopsortEvent, { token: "token" })).resolves.toEqual({ - ok: false, - retry: false, - }); -}); - -test("retryable error", async () => { - returnStatus(429); - await expect(reportEvent({} as TopsortEvent, { token: "token" })).resolves.toEqual({ - ok: false, - retry: true, - }); -}); - -test("server error", async () => { - returnStatus(500); - await expect(reportEvent({} as TopsortEvent, { token: "token" })).resolves.toEqual({ - ok: false, - retry: true, - }); -}); - -test("custom url", async () => { - returnStatus(200, "https://demo.api.topsort.com/v2/events"); - await expect( - reportEvent({} as TopsortEvent, { - token: "token", - url: "https://demo.api.topsort.com", - }), - ).resolves.toEqual({ ok: true, retry: false }); -}); diff --git a/src/reporter.ts b/src/reporter.ts deleted file mode 100644 index 7aabe0f..0000000 --- a/src/reporter.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { version } from "../package.json"; -import type { TopsortEvent } from "./events"; - -interface Config { - token: string; - url?: string; -} - -export async function reportEvent(e: TopsortEvent, config: Config) { - try { - const url = (config.url || "https://api.topsort.com") + "/v2/events"; - const r = await fetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - // Can't use User-Agent header because of - // https://bugs.chromium.org/p/chromium/issues/detail?id=571722 - "X-UA": `ts.js/${version}`, - Authorization: "Bearer " + config.token, - }, - body: JSON.stringify(e), - // This parameter ensures in most browsers that the request is performed even in case the browser navigates to another page. - keepalive: true, - }); - return { ok: r.ok, retry: r.status === 429 || r.status >= 500 }; - } catch (error) { - return { ok: false, retry: true }; - } -}