Skip to content

Commit 07a5ec2

Browse files
committed
eng-1485 rescan unpublished relations
1 parent eef3558 commit 07a5ec2

2 files changed

Lines changed: 140 additions & 1 deletion

File tree

apps/obsidian/src/utils/publishNode.ts

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import {
1515
import type { RelationInstance } from "~/types";
1616
import { getAvailableGroupIds } from "./importNodes";
1717
import { syncAllNodesAndRelations } from "./syncDgNodesToSupabase";
18+
import type { DiscourseNodeInVault } from "./getDiscourseNodes";
19+
import type { SupabaseContext } from "./supabaseContext";
20+
import type { TablesInsert } from "@repo/database/dbTypes";
1821

1922
const publishSchema = async ({
2023
client,
@@ -74,6 +77,21 @@ const intersection = <T>(set1: Set<T>, set2: Set<T>): Set<T> => {
7477
return r;
7578
};
7679

80+
const difference = <T>(set1: Set<T>, set2: Set<T>): Set<T> => {
81+
// @ts-expect-error - Set.difference is ES2025 feature
82+
if (set1.difference) return set1.difference(set2); // eslint-disable-line
83+
const result = new Set(set1);
84+
if (set1.size <= set2.size)
85+
for (const e of set1) {
86+
if (set2.has(e)) result.delete(e);
87+
}
88+
else
89+
for (const e of set2) {
90+
if (result.has(e)) result.delete(e);
91+
}
92+
return result;
93+
};
94+
7795
export const publishNewRelation = async (
7896
plugin: DiscourseGraphPlugin,
7997
relation: RelationInstance,
@@ -249,6 +267,116 @@ export const publishNode = async ({
249267
return await publishNodeToGroup({ plugin, file, frontmatter, myGroup });
250268
};
251269

270+
export const ensurePublishedRelationsAccuracy = async ({
271+
client,
272+
context,
273+
allNodesById,
274+
relationInstances,
275+
}: {
276+
client: DGSupabaseClient;
277+
context: SupabaseContext;
278+
allNodesById: Record<string, DiscourseNodeInVault>;
279+
relationInstances: RelationInstance[];
280+
}): Promise<void> => {
281+
const myGroups = await getAvailableGroupIds(client);
282+
const syncedRelationIdsResult = await client
283+
.from("Concept")
284+
.select("source_local_id")
285+
.eq("space_id", context.spaceId)
286+
.eq("is_schema", false)
287+
.gt("arity", 0);
288+
if (syncedRelationIdsResult.error) {
289+
console.error(
290+
"Could not get synced relation ids",
291+
syncedRelationIdsResult.error,
292+
);
293+
return;
294+
}
295+
const syncedRelationIds = new Set(
296+
(syncedRelationIdsResult.data || []).map((x) => x.source_local_id!),
297+
);
298+
// Also a good time to look at orphan relations
299+
const existingRelationIds = new Set(relationInstances.map((r) => r.id));
300+
const orphanRelationIds = difference(syncedRelationIds, existingRelationIds);
301+
if (orphanRelationIds.size) {
302+
const r = await client
303+
.from("Concept")
304+
.delete()
305+
.eq("space_id", context.spaceId)
306+
.in("source_local_id", [...orphanRelationIds]);
307+
if (!r.error) {
308+
for (const id of orphanRelationIds) {
309+
syncedRelationIds.delete(id);
310+
}
311+
}
312+
}
313+
const missingPublishRecords: TablesInsert<"ResourceAccess">[] = [];
314+
for (const group of myGroups) {
315+
const publishableRelations = relationInstances.filter(
316+
(r) =>
317+
!r.importedFromRid &&
318+
(
319+
(allNodesById[r.source]?.frontmatter?.publishedToGroups as
320+
| string[]
321+
| undefined) || []
322+
).indexOf(group) >= 0 &&
323+
(
324+
(allNodesById[r.destination]?.frontmatter?.publishedToGroups as
325+
| string[]
326+
| undefined) || []
327+
).indexOf(group) >= 0,
328+
);
329+
const publishableRelationIds = new Set(
330+
publishableRelations.map((x) => x.id),
331+
);
332+
const publishedIds = await client
333+
.from("ResourceAccess")
334+
.select("source_local_id")
335+
.eq("account_uid", group)
336+
.eq("space_id", context.spaceId);
337+
if (publishedIds.error) {
338+
console.error("Could not get synced relation ids", publishedIds.error);
339+
continue;
340+
}
341+
const publishedRelationIds = intersection(
342+
syncedRelationIds,
343+
new Set((publishedIds.data || []).map((x) => x.source_local_id)),
344+
);
345+
const missingPublishableIds = difference(
346+
publishableRelationIds,
347+
publishedRelationIds,
348+
);
349+
if (missingPublishableIds.size > 0) {
350+
missingPublishRecords.push(
351+
/* eslint-disable @typescript-eslint/naming-convention */
352+
...[...missingPublishableIds].map((source_local_id) => ({
353+
source_local_id,
354+
space_id: context.spaceId,
355+
account_uid: group,
356+
})),
357+
/* eslint-enable @typescript-eslint/naming-convention */
358+
);
359+
}
360+
const extraPublishableIds = difference(
361+
publishedRelationIds,
362+
publishableRelationIds,
363+
);
364+
if (extraPublishableIds.size > 0) {
365+
const r = await client
366+
.from("ResourceAccess")
367+
.delete()
368+
.eq("account_uid", group)
369+
.eq("space_id", context.spaceId)
370+
.in("source_local_id", [...extraPublishableIds]);
371+
if (r.error) console.error(r.error);
372+
}
373+
}
374+
if (missingPublishRecords.length > 0) {
375+
const r = await client.from("ResourceAccess").upsert(missingPublishRecords);
376+
if (r.error) console.error(r.error);
377+
}
378+
};
379+
252380
export const publishNodeToGroup = async ({
253381
plugin,
254382
file,

apps/obsidian/src/utils/syncDgNodesToSupabase.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
type SupabaseContext,
1010
} from "./supabaseContext";
1111
import { default as DiscourseGraphPlugin } from "~/index";
12-
import { publishNode } from "./publishNode";
12+
import { publishNode, ensurePublishedRelationsAccuracy } from "./publishNode";
1313
import { upsertNodesToSupabaseAsContentWithEmbeddings } from "./upsertNodesAsContentWithEmbeddings";
1414
import {
1515
orderConceptsByDependency,
@@ -416,6 +416,7 @@ export const syncAllNodesAndRelations = async (
416416
accountLocalId,
417417
plugin,
418418
allNodes,
419+
startupRun: true,
419420
});
420421

421422
// When synced nodes are already published, ensure non-text assets are in storage.
@@ -433,13 +434,15 @@ const convertDgToSupabaseConcepts = async ({
433434
accountLocalId,
434435
plugin,
435436
allNodes,
437+
startupRun,
436438
}: {
437439
nodesSince: ObsidianDiscourseNodeData[];
438440
supabaseClient: DGSupabaseClient;
439441
context: SupabaseContext;
440442
accountLocalId: string;
441443
plugin: DiscourseGraphPlugin;
442444
allNodes?: DiscourseNodeInVault[];
445+
startupRun?: boolean;
443446
}): Promise<void> => {
444447
const lastNodeSchemaSync = (
445448
await getLastNodeSchemaSyncTime(supabaseClient, context.spaceId)
@@ -562,6 +565,14 @@ const convertDgToSupabaseConcepts = async ({
562565
? error
563566
: JSON.stringify(error, null, 2);
564567
throw new Error(`upsert_concepts failed: ${errorMessage}`);
568+
} else if (startupRun === true) {
569+
// occasional extra work: Make sure relations that should be published are.
570+
await ensurePublishedRelationsAccuracy({
571+
client: supabaseClient,
572+
context,
573+
allNodesById,
574+
relationInstances: Object.values(relationInstancesData.relations),
575+
});
565576
}
566577
}
567578
};

0 commit comments

Comments
 (0)