@@ -15,6 +15,9 @@ import {
1515import type { RelationInstance } from "~/types" ;
1616import { getAvailableGroupIds } from "./importNodes" ;
1717import { syncAllNodesAndRelations } from "./syncDgNodesToSupabase" ;
18+ import type { DiscourseNodeInVault } from "./getDiscourseNodes" ;
19+ import type { SupabaseContext } from "./supabaseContext" ;
20+ import type { TablesInsert } from "@repo/database/dbTypes" ;
1821
1922const 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+
7795export 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+
252380export const publishNodeToGroup = async ( {
253381 plugin,
254382 file,
0 commit comments