Skip to content

Commit 6bfab2f

Browse files
trangdoan982maparent
authored andcommitted
ENG-1343 upload obsidian node schema to supabase
1 parent 6e31930 commit 6bfab2f

File tree

2 files changed

+75
-58
lines changed

2 files changed

+75
-58
lines changed

apps/obsidian/src/utils/conceptConversion.ts

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
/* eslint-disable @typescript-eslint/naming-convention */
2-
import { TFile } from "obsidian";
3-
import { DiscourseNode } from "~/types";
4-
import { SupabaseContext } from "./supabaseContext";
5-
import { LocalConceptDataInput } from "@repo/database/inputTypes";
6-
import { ObsidianDiscourseNodeData } from "./syncDgNodesToSupabase";
7-
import { Json } from "@repo/database/dbTypes";
2+
import type { TFile } from "obsidian";
3+
import type { DiscourseNode } from "~/types";
4+
import type { SupabaseContext } from "./supabaseContext";
5+
import type { LocalConceptDataInput } from "@repo/database/inputTypes";
6+
import type { ObsidianDiscourseNodeData } from "./syncDgNodesToSupabase";
7+
import type { Json } from "@repo/database/dbTypes";
88

99
/**
1010
* Get extra data (author, timestamps) from file metadata
@@ -34,15 +34,21 @@ export const discourseNodeSchemaToLocalConcept = ({
3434
accountLocalId: string;
3535
}): LocalConceptDataInput => {
3636
const now = new Date().toISOString();
37+
const { description, template, id, name, ...otherData } = node;
3738
return {
3839
space_id: context.spaceId,
39-
name: node.name,
40-
source_local_id: node.id,
40+
name: name,
41+
source_local_id: id,
4142
is_schema: true,
4243
author_local_id: accountLocalId,
4344
created: now,
44-
// TODO: get the template or any other info to put into literal_content jsonb
4545
last_modified: now,
46+
description: description,
47+
literal_content: {
48+
label: name,
49+
template: template,
50+
source_data: otherData,
51+
},
4652
};
4753
};
4854

@@ -59,22 +65,19 @@ export const discourseNodeInstanceToLocalConcept = ({
5965
accountLocalId: string;
6066
}): LocalConceptDataInput => {
6167
const extraData = getNodeExtraData(nodeData.file, accountLocalId);
62-
console.log(nodeData.frontmatter);
63-
const concept = {
68+
const { nodeInstanceId, nodeTypeId, ...otherData } = nodeData.frontmatter;
69+
return {
6470
space_id: context.spaceId,
65-
name: nodeData.file.basename,
66-
source_local_id: nodeData.nodeInstanceId,
67-
schema_represented_by_local_id: nodeData.nodeTypeId,
71+
name: nodeData.file.path,
72+
source_local_id: nodeInstanceId as string,
73+
schema_represented_by_local_id: nodeTypeId as string,
6874
is_schema: false,
6975
literal_content: {
70-
...nodeData.frontmatter,
71-
} as unknown as Json,
76+
label: nodeData.file.basename,
77+
source_data: otherData as unknown as Json,
78+
},
7279
...extraData,
7380
};
74-
console.log(
75-
`[discourseNodeInstanceToLocalConcept] Converting concept: source_local_id=${nodeData.nodeInstanceId}, name="${nodeData.file.basename}"`,
76-
);
77-
return concept;
7881
};
7982

8083
export const relatedConcepts = (concept: LocalConceptDataInput): string[] => {
@@ -135,6 +138,7 @@ export const orderConceptsByDependency = (
135138
...missing,
136139
...orderConceptsRec(ordered, first, conceptById),
137140
]);
141+
if (missing.size > 0) console.error(`missing: ${[...missing]}`);
138142
}
139143
return { ordered, missing: Array.from(missing) };
140144
};

apps/obsidian/src/utils/syncDgNodesToSupabase.ts

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
/* eslint-disable @typescript-eslint/naming-convention */
22
import { Notice, TFile } from "obsidian";
3-
import { DGSupabaseClient } from "@repo/database/lib/client";
4-
import { Json } from "@repo/database/dbTypes";
3+
import type { DGSupabaseClient } from "@repo/database/lib/client";
4+
import type { Json } from "@repo/database/dbTypes";
55
import {
66
getSupabaseContext,
77
getLoggedInClient,
8-
SupabaseContext,
8+
type SupabaseContext,
99
} from "./supabaseContext";
1010
import { default as DiscourseGraphPlugin } from "~/index";
1111
import { upsertNodesToSupabaseAsContentWithEmbeddings } from "./upsertNodesAsContentWithEmbeddings";
1212
import {
1313
orderConceptsByDependency,
1414
discourseNodeInstanceToLocalConcept,
15+
discourseNodeSchemaToLocalConcept,
1516
} from "./conceptConversion";
16-
import { LocalConceptDataInput } from "@repo/database/inputTypes";
17+
import type { LocalConceptDataInput } from "@repo/database/inputTypes";
1718

1819
const DEFAULT_TIME = new Date("1970-01-01");
1920
export type ChangeType = "title" | "content";
@@ -101,7 +102,10 @@ const deleteNodesFromSupabase = async (
101102
if (conceptDeleteError) {
102103
result.success = false;
103104
result.errors.concept = conceptDeleteError;
104-
console.error("Failed to delete concepts from Supabase:", conceptDeleteError);
105+
console.error(
106+
"Failed to delete concepts from Supabase:",
107+
conceptDeleteError,
108+
);
105109
}
106110

107111
const { error: contentDeleteError } = await supabaseClient
@@ -195,7 +199,6 @@ const mergeChangeTypes = (
195199
return Array.from(merged);
196200
};
197201

198-
199202
/**
200203
* Step 1: Collect all discourse nodes from the vault
201204
* Filters markdown files that have nodeTypeId in frontmatter
@@ -466,12 +469,20 @@ export const createOrUpdateDiscourseEmbedding = async (
466469
throw new Error("accountLocalId not found in plugin settings");
467470
}
468471

469-
await syncChangedNodesToSupabase({
470-
changedNodes: allNodeInstances,
472+
await upsertNodesToSupabaseAsContentWithEmbeddings({
473+
obsidianNodes: allNodeInstances,
474+
supabaseClient,
475+
context,
476+
accountLocalId,
471477
plugin,
478+
});
479+
480+
await convertDgToSupabaseConcepts({
481+
nodesSince: allNodeInstances,
472482
supabaseClient,
473483
context,
474484
accountLocalId,
485+
plugin,
475486
});
476487

477488
console.debug("Sync completed successfully");
@@ -486,25 +497,34 @@ const convertDgToSupabaseConcepts = async ({
486497
supabaseClient,
487498
context,
488499
accountLocalId,
500+
plugin,
489501
}: {
490502
nodesSince: ObsidianDiscourseNodeData[];
491503
supabaseClient: DGSupabaseClient;
492504
context: SupabaseContext;
493505
accountLocalId: string;
506+
plugin: DiscourseGraphPlugin;
494507
}): Promise<void> => {
495-
// TODO: handling schema (node types and relations) will be handled in the future by ENG-1181
496-
// Schema upsert will need allNodeTypes parameter when enabled
508+
const nodeTypes = plugin.settings.nodeTypes ?? [];
509+
510+
const nodesTypesToLocalConcepts = nodeTypes.map((nodeType) =>
511+
discourseNodeSchemaToLocalConcept({
512+
context,
513+
node: nodeType,
514+
accountLocalId,
515+
}),
516+
);
497517

498-
const nodeInstanceToLocalConcepts = nodesSince.map((node) => {
499-
return discourseNodeInstanceToLocalConcept({
518+
const nodeInstanceToLocalConcepts = nodesSince.map((node) =>
519+
discourseNodeInstanceToLocalConcept({
500520
context,
501521
nodeData: node,
502522
accountLocalId,
503-
});
504-
});
523+
}),
524+
);
505525

506526
const conceptsToUpsert: LocalConceptDataInput[] = [
507-
// ...nodesTypesToLocalConcepts,
527+
...nodesTypesToLocalConcepts,
508528
...nodeInstanceToLocalConcepts,
509529
];
510530

@@ -543,33 +563,29 @@ const syncChangedNodesToSupabase = async ({
543563
context: SupabaseContext;
544564
accountLocalId: string;
545565
}): Promise<void> => {
546-
if (changedNodes.length === 0) {
547-
console.debug("No nodes to sync");
548-
return;
566+
if (changedNodes.length > 0) {
567+
await upsertNodesToSupabaseAsContentWithEmbeddings({
568+
obsidianNodes: changedNodes,
569+
supabaseClient,
570+
context,
571+
accountLocalId,
572+
plugin,
573+
});
549574
}
550575

551-
await upsertNodesToSupabaseAsContentWithEmbeddings({
552-
obsidianNodes: changedNodes,
553-
supabaseClient,
554-
context,
555-
accountLocalId,
556-
plugin,
557-
});
558-
559576
// Only upsert concepts for nodes with title changes or new files
560577
// (concepts store the title, so content-only changes don't affect them)
561578
const nodesNeedingConceptUpsert = changedNodes.filter((node) =>
562579
node.changeTypes.includes("title"),
563580
);
564581

565-
if (nodesNeedingConceptUpsert.length > 0) {
566-
await convertDgToSupabaseConcepts({
567-
nodesSince: nodesNeedingConceptUpsert,
568-
supabaseClient,
569-
context,
570-
accountLocalId,
571-
});
572-
}
582+
await convertDgToSupabaseConcepts({
583+
nodesSince: nodesNeedingConceptUpsert,
584+
supabaseClient,
585+
context,
586+
accountLocalId,
587+
plugin,
588+
});
573589
};
574590

575591
/**
@@ -635,10 +651,7 @@ export const syncSpecificFiles = async (
635651
const changeTypesByPath = new Map<string, ChangeType[]>();
636652
for (const filePath of filePaths) {
637653
const existing = changeTypesByPath.get(filePath) ?? [];
638-
changeTypesByPath.set(
639-
filePath,
640-
mergeChangeTypes(existing, ["content"]),
641-
);
654+
changeTypesByPath.set(filePath, mergeChangeTypes(existing, ["content"]));
642655
}
643656

644657
await syncDiscourseNodeChanges(plugin, changeTypesByPath);

0 commit comments

Comments
 (0)