mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 22:02:16 +00:00
feat(api): date deprecations and log which builds still call them
/api/feature-flags answered Deprecation: true and nothing else: a client learned the route was going, never when, and the log could not tell which builds still called it. deprecated() (src/lib/http/deprecation.ts) applies one reviewed notice per route: Deprecation as an RFC 9745 date, Sunset as an RFC 8594 HTTP-date, the successor Link, and a wide-event annotation with the client build read from the User-Agent. feature-flags was deprecated on 2026-09-24 (v1.39.0); its sunset is 2027-03-31. The headers are declared on the operation, and a test holds every notice to a deprecated operation that answers through the helper.
This commit is contained in:
1 parent
39d23dbb7d
commit
9fda88586c
6 files changed
+237
-10
No files matched your search
@@ -150,7 +150,10 @@ describe("GET /api/feature-flags", () => {
|
||||
vi.mocked(getSession).mockResolvedValue(SESSION_OK as never);
|
||||
FIND.mockResolvedValue(null);
|
||||
const res = await GET(req());
|
||||
expect(res.headers.get("Deprecation")).toBe("true");
|
||||
// RFC 9745: the deprecation date as `@` and Unix seconds — 2026-09-24.
|
||||
expect(res.headers.get("Deprecation")).toBe("@1790208000");
|
||||
// RFC 8594: the first day it may stop answering.
|
||||
expect(res.headers.get("Sunset")).toBe("Wed, 31 Mar 2027 00:00:00 GMT");
|
||||
expect(res.headers.get("Link")).toBe(
|
||||
'</api/auth/me>; rel="successor-version"',
|
||||
);
|
||||
|
||||
@@ -12,8 +12,10 @@
|
||||
* "insightStatus", "documentAi" } } }
|
||||
*
|
||||
* The master is applied (every sub-switch reads false when it is off), and a
|
||||
* switch read that fails answers every switch off. The response carries
|
||||
* `Deprecation: true` and a `Link` to its successor.
|
||||
* switch read that fails answers every switch off. The response carries the
|
||||
* deprecation date, the sunset and a `Link` to its successor
|
||||
* (`src/lib/http/deprecation.ts`), and the wide event names the client build
|
||||
* that still asked.
|
||||
*
|
||||
* `Cache-Control: private, max-age=60`: switches flip rarely.
|
||||
*/
|
||||
@@ -22,9 +24,10 @@ import type { NextRequest } from "next/server";
|
||||
import { apiHandler, requireActorAuth } from "@/lib/api-handler";
|
||||
import { apiSuccess } from "@/lib/api-response";
|
||||
import { getAssistantFlags } from "@/lib/feature-flags";
|
||||
import { deprecated } from "@/lib/http/deprecation";
|
||||
import { annotate } from "@/lib/logging/context";
|
||||
|
||||
export const GET = apiHandler(async (_request: NextRequest) => {
|
||||
export const GET = apiHandler(async (request: NextRequest) => {
|
||||
// An actor surface, and the easiest call in the set: the answer comes off
|
||||
// the `AppSettings` singleton and reads no user row at all, so there is no
|
||||
// record for a switch to substitute. It stays reachable while a switch is on
|
||||
@@ -45,7 +48,5 @@ export const GET = apiHandler(async (_request: NextRequest) => {
|
||||
|
||||
const response = apiSuccess({ assistant });
|
||||
response.headers.set("Cache-Control", "private, max-age=60");
|
||||
response.headers.set("Deprecation", "true");
|
||||
response.headers.set("Link", '</api/auth/me>; rel="successor-version"');
|
||||
return response;
|
||||
return deprecated(response, "/api/feature-flags", request);
|
||||
});
|
||||
Reference in new issue
Block a user