mirror of
https://github.com/block/buzz.git
synced 2026-11-01 06:51:17 +00:00
fix(projects): scope revision reads and advance heads
Signed-off-by: John Tennant <jtennant@squareup.com>
This commit is contained in:
@@ -82,13 +82,13 @@ test("buildProjectHomeFromFetcher scopes startup lookup to the active channel",
|
||||
extraFilter: { "#buzz-channel": [channelId] },
|
||||
});
|
||||
assert.deepEqual(calls[1], {
|
||||
kinds: [47001],
|
||||
extraFilter: undefined,
|
||||
});
|
||||
assert.deepEqual(calls[2], {
|
||||
kinds: [30617],
|
||||
extraFilter: { "#buzz-channel": [channelId] },
|
||||
});
|
||||
assert.deepEqual(calls[2], {
|
||||
kinds: [47001],
|
||||
extraFilter: { "#a": [`30621:${owner}:relay`] },
|
||||
});
|
||||
assert.deepEqual(calls[3], {
|
||||
kinds: [5],
|
||||
extraFilter: { "#a": [`30621:${owner}:relay`, repositoryAddress] },
|
||||
@@ -255,6 +255,31 @@ test("buildProjectsFromFetcher scopes the tombstone fetch to announcement coordi
|
||||
});
|
||||
});
|
||||
|
||||
test("buildProjectsFromFetcher scopes revisions to announced Project coordinates", async () => {
|
||||
const OWNER = "a".repeat(64);
|
||||
const projectEvent = {
|
||||
id: "p".repeat(64),
|
||||
kind: 30621,
|
||||
pubkey: OWNER,
|
||||
created_at: 200,
|
||||
content: "",
|
||||
tags: [["d", "proj"]],
|
||||
};
|
||||
const revisionCalls = [];
|
||||
const fetchExhaustively = async (kinds, extraFilter) => {
|
||||
if (kinds.includes(30621)) return [projectEvent];
|
||||
if (kinds.includes(47001)) {
|
||||
revisionCalls.push(extraFilter);
|
||||
return [];
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
await buildProjectsFromFetcher(fetchExhaustively);
|
||||
|
||||
assert.deepEqual(revisionCalls, [{ "#a": [`30621:${OWNER}:proj`] }]);
|
||||
});
|
||||
|
||||
test("buildProjectsFromFetcher skips the tombstone fetch when there are no announcements", async () => {
|
||||
let deletionQueried = false;
|
||||
const fetchExhaustively = async (kinds) => {
|
||||
|
||||
@@ -14,7 +14,7 @@ const PROJECT_ENUMERATION_PAGE_SIZE = 500;
|
||||
|
||||
// Relays commonly cap filter tag-value lists; chunk `#a` scoping well below
|
||||
// any such cap.
|
||||
const TOMBSTONE_COORDINATE_CHUNK_SIZE = 100;
|
||||
const PROJECT_COORDINATE_CHUNK_SIZE = 100;
|
||||
|
||||
/** Additional server-side scoping merged into every enumeration page. */
|
||||
export type ProjectEventExtraFilter = {
|
||||
@@ -149,10 +149,10 @@ async function fetchScopedDeletionEvents(
|
||||
for (
|
||||
let index = 0;
|
||||
index < coordinates.length;
|
||||
index += TOMBSTONE_COORDINATE_CHUNK_SIZE
|
||||
index += PROJECT_COORDINATE_CHUNK_SIZE
|
||||
) {
|
||||
chunks.push(
|
||||
coordinates.slice(index, index + TOMBSTONE_COORDINATE_CHUNK_SIZE),
|
||||
coordinates.slice(index, index + PROJECT_COORDINATE_CHUNK_SIZE),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -162,6 +162,36 @@ async function fetchScopedDeletionEvents(
|
||||
return pages.flat();
|
||||
}
|
||||
|
||||
async function fetchScopedProjectRevisionEvents(
|
||||
fetchExhaustively: FetchProjectEventsExhaustively,
|
||||
projectEvents: RelayEvent[],
|
||||
): Promise<RelayEvent[]> {
|
||||
const coordinates = [
|
||||
...new Set(
|
||||
projectEvents.flatMap((event) => {
|
||||
const coordinate = eventCoordinate(event);
|
||||
return coordinate ? [coordinate] : [];
|
||||
}),
|
||||
),
|
||||
];
|
||||
const chunks: string[][] = [];
|
||||
for (
|
||||
let index = 0;
|
||||
index < coordinates.length;
|
||||
index += PROJECT_COORDINATE_CHUNK_SIZE
|
||||
) {
|
||||
chunks.push(
|
||||
coordinates.slice(index, index + PROJECT_COORDINATE_CHUNK_SIZE),
|
||||
);
|
||||
}
|
||||
const pages = await Promise.all(
|
||||
chunks.map((chunk) =>
|
||||
fetchExhaustively([KIND_PROJECT_REVISION], { "#a": chunk }),
|
||||
),
|
||||
);
|
||||
return pages.flat();
|
||||
}
|
||||
|
||||
/**
|
||||
* Core fetch-and-build logic for `fetchProjects`, extracted for testability.
|
||||
*
|
||||
@@ -180,12 +210,14 @@ export async function buildProjectsFromFetcher(
|
||||
viewerPubkey?: string | null;
|
||||
} = {},
|
||||
): Promise<Project[]> {
|
||||
const [projectEvents, projectRevisionEvents, repositoryEvents] =
|
||||
await Promise.all([
|
||||
fetchExhaustively([KIND_PROJECT_ANNOUNCEMENT]),
|
||||
fetchExhaustively([KIND_PROJECT_REVISION]),
|
||||
fetchExhaustively([KIND_REPO_ANNOUNCEMENT]),
|
||||
]);
|
||||
const [projectEvents, repositoryEvents] = await Promise.all([
|
||||
fetchExhaustively([KIND_PROJECT_ANNOUNCEMENT]),
|
||||
fetchExhaustively([KIND_REPO_ANNOUNCEMENT]),
|
||||
]);
|
||||
const projectRevisionEvents = await fetchScopedProjectRevisionEvents(
|
||||
fetchExhaustively,
|
||||
projectEvents,
|
||||
);
|
||||
|
||||
// Tombstones are fetched second (not in parallel) because the `#a` scoping
|
||||
// needs the announcement coordinates; both announcement kinds are small,
|
||||
|
||||
@@ -311,11 +311,13 @@ test("addRepositoryToProject promotes a legacy repository route to a project coo
|
||||
legacyProject,
|
||||
attachedRepository,
|
||||
300,
|
||||
"f".repeat(64),
|
||||
);
|
||||
|
||||
assert.equal(updated.id, `30621:${PROJECT_OWNER}:sprout`);
|
||||
assert.equal(updated.legacy, false);
|
||||
assert.equal(updated.repositories.length, 2);
|
||||
assert.equal(updated.effectiveRevisionId, "f".repeat(64));
|
||||
});
|
||||
|
||||
test("selectProjectRepository honors a request and falls back to primary", () => {
|
||||
|
||||
@@ -629,6 +629,7 @@ export function addRepositoryToProject(
|
||||
project: Project,
|
||||
repository: Repository,
|
||||
createdAt: number,
|
||||
effectiveRevisionId = project.effectiveRevisionId,
|
||||
): Project {
|
||||
const projectAddress = `${KIND_PROJECT_ANNOUNCEMENT}:${project.owner}:${project.dtag}`;
|
||||
const repositoryAddresses = [
|
||||
@@ -645,6 +646,7 @@ export function addRepositoryToProject(
|
||||
...project,
|
||||
id: projectAddress,
|
||||
createdAt,
|
||||
effectiveRevisionId,
|
||||
legacy: false,
|
||||
projectAddress,
|
||||
primaryRepositoryAddress:
|
||||
|
||||
@@ -186,6 +186,7 @@ export async function addProjectRepository(
|
||||
project,
|
||||
repository,
|
||||
projectEvent.created_at,
|
||||
projectEvent.id.toLowerCase(),
|
||||
),
|
||||
repository,
|
||||
};
|
||||
@@ -306,6 +307,7 @@ export async function addProjectRepository(
|
||||
project,
|
||||
repository,
|
||||
projectEvent.created_at,
|
||||
projectEvent.id.toLowerCase(),
|
||||
),
|
||||
repository,
|
||||
};
|
||||
|
||||
@@ -80,6 +80,7 @@ async function attachProjectRepository({
|
||||
project,
|
||||
repository,
|
||||
projectEvent.created_at,
|
||||
projectEvent.id.toLowerCase(),
|
||||
),
|
||||
repository,
|
||||
};
|
||||
@@ -101,6 +102,7 @@ async function attachProjectRepository({
|
||||
project,
|
||||
repository,
|
||||
projectEvent.created_at,
|
||||
projectEvent.id.toLowerCase(),
|
||||
),
|
||||
repository,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user