diff --git a/src/release-sync.ts b/src/release-sync.ts index b176040..9afbee4 100644 --- a/src/release-sync.ts +++ b/src/release-sync.ts @@ -220,7 +220,7 @@ async function listSyncedVersions(prisma: PrismaClient, type: ReleaseType): Prom return new Set(releases.map(release => release.version)); } -async function releaseExists( +export async function releaseExists( prisma: PrismaClient, type: ReleaseType, version: string, diff --git a/src/releases.ts b/src/releases.ts index 30310fd..da98e0c 100644 --- a/src/releases.ts +++ b/src/releases.ts @@ -1,7 +1,7 @@ import { Request, Response } from "express"; import { prisma } from "./db"; import { BadRequestError, ConflictError, InternalServerError, NotFoundError } from "./errors"; -import type { ReleaseSyncRunner } from "./release-sync"; +import { releaseExists, type ReleaseSyncRunner } from "./release-sync"; import semver from "semver"; import { GetObjectCommand, ListObjectsV2Command } from "@aws-sdk/client-s3"; @@ -734,8 +734,10 @@ const syncBodySchema = z /** * POST /releases/sync: register every stable R2 version missing from the DB * and answer with the per-outcome counts. With `{ type, version }` in the - * body, that one version skips the settle window: the caller vouches its - * last object is written. Every other version keeps it. + * body, that one version skips the settle window (the caller vouches its + * last object is written) and the answer adds `registered`: whether a row + * for it exists once the run is over, whichever run created it. The counts + * alone cannot say that, since they cover every version scanned. */ export function Sync(runner: ReleaseSyncRunner) { return async (req: Request, res: Response) => { @@ -744,6 +746,10 @@ export function Sync(runner: ReleaseSyncRunner) { if (stats === "busy") { throw new ConflictError("A release sync is already in progress"); } - return res.json(stats); + if (!settled) { + return res.json(stats); + } + const registered = await releaseExists(prisma, settled.type, settled.version); + return res.json({ ...stats, registered }); }; } diff --git a/test/sync-releases.test.ts b/test/sync-releases.test.ts index 3d340d4..32c9710 100644 --- a/test/sync-releases.test.ts +++ b/test/sync-releases.test.ts @@ -546,7 +546,9 @@ describe("Sync handler", () => { await Sync(newRunner())(request({ type: "app", version: FRESH_VERSION }), res); - expect(res.json).toHaveBeenCalledWith(expect.objectContaining({ created: 1, uploading: 1 })); + expect(res.json).toHaveBeenCalledWith( + expect.objectContaining({ created: 1, uploading: 1, registered: true }), + ); expect( await testPrisma.release.findUnique({ where: { version_type: { version: FRESH_VERSION, type: "app" } }, @@ -566,6 +568,16 @@ describe("Sync handler", () => { await Sync(newRunner())(request(), res); expect(res.json).toHaveBeenCalledWith(expect.objectContaining({ created: 0, uploading: 2 })); + expect(res.json).not.toHaveBeenCalledWith(expect.objectContaining({ registered: expect.anything() })); + }); + + it("answers registered: false when the named version is not on R2", async () => { + mockS3ListVersions("app", []); + const res = response(); + + await Sync(newRunner())(request({ type: "app", version: "9.9.99" }), res); + + expect(res.json).toHaveBeenCalledWith(expect.objectContaining({ created: 0, registered: false })); }); it.each([