diff --git a/src/interfaces/alertSearch.ts b/src/interfaces/alertSearch.ts new file mode 100644 index 00000000..1ccf85a1 --- /dev/null +++ b/src/interfaces/alertSearch.ts @@ -0,0 +1,125 @@ +/** + * alertSearch response item and envelope types. + * + * These buckets are new to this app. Item shapes are taken from the endorser-ch + * SELECT lists for alertSearch, not from similarly named existing report types. + * + * Existing types that were considered and not reused: + * - GenericCredWrapper — claims lack a `claim` body; extra jwt columns differ. + * - GiveSummaryRecord / OfferSummaryRecord — those use `jwtId` and give/offer + * summary fields; alertSearch jwt rows use `id` and jwt table columns. + * - PlanSummaryAndPreviousClaim — `/plansLastUpdatedBetween` wraps `{ plan, + * wrappedClaimBefore }`; alertSearch `trackedPlanUpdates` are plan_claim rows. + * - PlanSummaryRecord — overlapping plan fields, but the app type is a subset + * (missing fulfillsLinkConfirmed, result*, etc.) and required fields differ. + * - UserProfile — partner nearby rows include `updatedAt` / `rowId` and omit + * embedding flags that UserProfile models. + */ + +/** + * Server-issued ULID on a stored JWT/plan record, used as alertSearch afterId / + * beforeId. Not an authentication JWT and not a delegated notification JWT. + */ +export type AlertSearchCursorUlid = string; + +/** + * JWT row from endorser `jwtsWithDidAfterId` (no claim body). + * Cursor field: `id`. + */ +export interface AlertSearchClaimRecord { + id: AlertSearchCursorUlid; + issuedAt: string; + issuer: string; + subject?: string; + claimType?: string; + handleId?: string; + fromEntity?: string; + toEntity?: string; +} + +/** + * JWT row from `jwtsForUserPlanContributions` and + * `jwtsGiveActionOfferForPlanHandleIds`. `claim` is the jwt table TEXT + * (canonical JSON string); alertSearch does not JSON.parse it. + * Cursor field: `id`. + */ +export interface AlertSearchJwtWithClaimRecord extends AlertSearchClaimRecord { + claim?: string; +} + +/** + * plan_claim row from `plansLastUpdatedBetween` and `plansByLocationAfterId`. + * Cursor field: `jwtId` (not `id`). + */ +export interface AlertSearchPlanRecord { + handleId: string; + jwtId: AlertSearchCursorUlid; + issuerDid?: string; + agentDid?: string; + fulfillsLinkConfirmed?: boolean | number; + fulfillsPlanClaimId?: string; + fulfillsPlanHandleId?: string; + name?: string; + description?: string; + image?: string; + endTime?: string; + startTime?: string; + locLat?: number; + locLon?: number; + resultDescription?: string; + resultIdentifier?: string; + url?: string; +} + +/** + * user_profile row from partner `profilesByLocationAfterDate`. + * Profiles have no JWT `id`; partner paging uses dates decoded from cursor ULIDs. + */ +export interface AlertSearchProfileRecord { + rowId?: number; + issuerDid: string; + updatedAt?: string; + description: string; + locLat?: number; + locLon?: number; + locLat2?: number; + locLon2?: number; +} + +export interface EndorserAlertSearchData { + claims: AlertSearchClaimRecord[]; + personalPlanContributions: AlertSearchJwtWithClaimRecord[]; + trackedPlanUpdates: AlertSearchPlanRecord[]; + trackedPlanClaims: AlertSearchJwtWithClaimRecord[]; + plansNearby: AlertSearchPlanRecord[]; +} + +export interface PartnerAlertSearchData { + profilesNearby: AlertSearchProfileRecord[]; +} + +/** + * Endorser GET/POST /api/v2/report/alertSearch body. + * Per-bucket SQL hitLimit is not currently copied onto this envelope. + * Timeouts may set `userMessage` instead. + */ +export interface EndorserAlertSearchResponse { + data: EndorserAlertSearchData; + userMessage?: string; +} + +/** + * Partner GET/POST /api/partner/alertSearch body. + */ +export interface PartnerAlertSearchResponse { + data: PartnerAlertSearchData; + userMessage?: string; +} + +/** + * Union of the six alertSearch buckets for a future combined daily run. + * Not returned by a single server endpoint today. + */ +export interface CombinedAlertSearchData + extends EndorserAlertSearchData, + PartnerAlertSearchData {} diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index 8197df86..9a02e735 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -1,3 +1,4 @@ +export * from "./alertSearch"; export * from "./claims"; export * from "./claims-result"; export * from "./common"; diff --git a/src/libs/alertSearch.ts b/src/libs/alertSearch.ts new file mode 100644 index 00000000..b1ecf5a5 --- /dev/null +++ b/src/libs/alertSearch.ts @@ -0,0 +1,67 @@ +/** + * Typed alertSearch API contract only. No HTTP client yet. + * + * Hosts: use DEFAULT_ENDORSER_API_SERVER and DEFAULT_PARTNER_API_SERVER from + * `@/constants/app`. Do not duplicate those constants here. + * + * JWT kinds (do not mix these): + * - Authentication JWT: short-lived access token (`iss`/`iat`/`exp`) sent as + * `Authorization: Bearer` for interactive API calls (`accessToken` / + * `getHeaders`). Identifies the requester DID. + * - Delegated notification JWT: tokens in the native background pool (~100 + * strings, `jti` + long `exp`) passed to daily-notification-plugin / + * notify-api so background workers can call Endorser. That pool is unchanged + * in this phase and is not an alertSearch cursor. + * - alertSearch cursor ULID: server-issued record/JWT primary id (26-char + * ULID). `afterId` means ids strictly greater than that ULID; `beforeId` + * means strictly less. First daily run omits afterId. `beforeId` is for + * pagination within a run. These are not auth JWTs and not pool tokens. + * + * Truncation: each endorser bucket query uses a server hit-limit (typically + * 50). That flag is not currently returned on the alertSearch JSON envelope. + * Timeouts may add `userMessage`. A later caller must still paginate with + * beforeId when a bucket may be incomplete. + */ + +import type { + AlertSearchCursorUlid, + CombinedAlertSearchData, + EndorserAlertSearchResponse, + PartnerAlertSearchResponse, +} from "@/interfaces/alertSearch"; + +export const ENDORSER_ALERT_SEARCH_PATH = "/api/v2/report/alertSearch"; +export const PARTNER_ALERT_SEARCH_PATH = "/api/partner/alertSearch"; + +export interface AlertSearchLocationBBox { + minLocLat: number; + maxLocLat: number; + minLocLon: number; + maxLocLon: number; +} + +/** + * Query/body params accepted by endorser and partner alertSearch (GET or POST). + * GET is the planned daily method; the server also accepts POST. + */ +export interface AlertSearchRequestParams { + afterId?: AlertSearchCursorUlid; + beforeId?: AlertSearchCursorUlid; + afterDate?: string; + beforeDate?: string; + location?: AlertSearchLocationBBox; + minLocLat?: number; + maxLocLat?: number; + minLocLon?: number; + maxLocLon?: number; + planHandleIds?: string[]; + planIds?: string[]; + handleIds?: string[]; +} + +export type { + AlertSearchCursorUlid, + CombinedAlertSearchData, + EndorserAlertSearchResponse, + PartnerAlertSearchResponse, +};