mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 14:16:12 +00:00
Merge branch 'fix/v1437-data' into release/v1.43.7
This commit is contained in:
commit
dee6a27ae4
100 files changed
+2314
-506
No files matched your search
@@ -0,0 +1,30 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { apiListSuccess, capRows, overfetch } from "../list-cap";
|
||||
|
||||
describe("list cap", () => {
|
||||
it("asks for one row past the limit", () => {
|
||||
expect(overfetch(100)).toBe(101);
|
||||
});
|
||||
|
||||
it("cuts an over-fetched result back and says it was cut", () => {
|
||||
expect(capRows([1, 2, 3], 2)).toEqual({
|
||||
rows: [1, 2],
|
||||
cap: { truncated: true, limit: 2 },
|
||||
});
|
||||
});
|
||||
|
||||
it("does not call a list that is exactly full cut", () => {
|
||||
expect(capRows([1, 2], 2).cap.truncated).toBe(false);
|
||||
expect(capRows([], 2).cap.truncated).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps data as it is and puts the cap in meta", async () => {
|
||||
const res = apiListSuccess({ events: [1] }, { truncated: true, limit: 1 });
|
||||
expect(await res.json()).toEqual({
|
||||
data: { events: [1] },
|
||||
error: null,
|
||||
meta: { truncated: true, limit: 1 },
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* One signal for a list the server cut at a ceiling.
|
||||
*
|
||||
* A list endpoint that stops at its own `take` and says nothing is the
|
||||
* quieter of the two completeness lies: the client renders the last row it
|
||||
* got as the oldest one there is. Several routes had grown their own answer
|
||||
* (a `truncated` field here, `rows.length === MAX` there, which reports a
|
||||
* cut on a list that is exactly full) and most had none.
|
||||
*
|
||||
* The pattern, in three steps:
|
||||
*
|
||||
* 1. query one row past the limit: `take: overfetch(limit)`;
|
||||
* 2. cut it back: `const { rows, cap } = capRows(found, limit)`;
|
||||
* 3. answer with `apiListSuccess(data, cap)`.
|
||||
*
|
||||
* The signal rides the envelope's `meta` (`meta.truncated`, `meta.limit`), so
|
||||
* `data` keeps exactly the shape the route always had and a client that never
|
||||
* reads `meta` decodes it unchanged. `truncated` is exact: true only when a
|
||||
* row past the limit exists. The OpenAPI side is `listEnvelope` in
|
||||
* `src/lib/openapi/routes/shared.ts`.
|
||||
*/
|
||||
import { apiSuccessWithMeta } from "@/lib/api-response";
|
||||
|
||||
export interface ListCap {
|
||||
/** A row past `limit` exists and was not returned. */
|
||||
truncated: boolean;
|
||||
/** The most rows this response could carry. */
|
||||
limit: number;
|
||||
}
|
||||
|
||||
/** The `take` that lets {@link capRows} tell a full list from a cut one. */
|
||||
export function overfetch(limit: number): number {
|
||||
return limit + 1;
|
||||
}
|
||||
|
||||
/** Cut an over-fetched result back to `limit` and say whether it was cut. */
|
||||
export function capRows<T>(
|
||||
found: readonly T[],
|
||||
limit: number,
|
||||
): { rows: T[]; cap: ListCap } {
|
||||
const truncated = found.length > limit;
|
||||
return {
|
||||
rows: truncated ? found.slice(0, limit) : [...found],
|
||||
cap: { truncated, limit },
|
||||
};
|
||||
}
|
||||
|
||||
/** The success envelope for a capped list: `data` as-is, the cap in `meta`. */
|
||||
export function apiListSuccess<T>(data: T, cap: ListCap, status = 200) {
|
||||
return apiSuccessWithMeta(
|
||||
data,
|
||||
{ truncated: cap.truncated, limit: cap.limit },
|
||||
status,
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user