diff --git a/apps/website/app/api/supabase/account/route.ts b/apps/website/app/api/supabase/account/route.ts deleted file mode 100644 index 30d352ff1..000000000 --- a/apps/website/app/api/supabase/account/route.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { NextResponse, NextRequest } from "next/server"; -import type { PostgrestSingleResponse } from "@supabase/supabase-js"; - -import { createClient } from "~/utils/supabase/server"; -import { getOrCreateEntity, ItemValidator } from "~/utils/supabase/dbUtils"; -import { - createApiResponse, - handleRouteError, - defaultOptionsHandler, - asPostgrestFailure, -} from "~/utils/supabase/apiUtils"; -import { Tables, TablesInsert } from "@repo/database/types.gen.ts"; - -type AccountDataInput = TablesInsert<"Account">; -type AccountRecord = Tables<"Account">; - -const validateAccount: ItemValidator = (account) => { - if (!account || typeof account !== "object") - return "Invalid request body: expected a JSON object."; - if (!account.agent_id) return "Missing required agent_id"; - if (!account.platform_id) return "Missing required platform_id"; - return null; -}; - -const getOrCreateAccount = async ( - supabasePromise: ReturnType, - accountData: AccountDataInput, -): Promise> => { - const { - agent_id, - platform_id, - active = true, - write_permission = true, - account_local_id, - } = accountData; - - const error = validateAccount(accountData); - if (error !== null) return asPostgrestFailure(error, "invalid"); - - const supabase = await supabasePromise; - - const result = await getOrCreateEntity<"Account">({ - supabase, - tableName: "Account", - insertData: { - agent_id, - platform_id, - active, - write_permission, - account_local_id, - }, - uniqueOn: ["agent_id", "platform_id"], - }); - return result; -}; - -export const POST = async (request: NextRequest): Promise => { - const supabasePromise = createClient(); - - try { - const body: AccountDataInput = await request.json(); - const result = await getOrCreateAccount(supabasePromise, body); - - return createApiResponse(request, result); - } catch (e: unknown) { - return handleRouteError(request, e, "/api/supabase/account"); - } -}; - -export const OPTIONS = defaultOptionsHandler; diff --git a/apps/website/app/api/supabase/account/[id].ts b/apps/website/app/api/supabase/agent-identifier/[id].ts similarity index 57% rename from apps/website/app/api/supabase/account/[id].ts rename to apps/website/app/api/supabase/agent-identifier/[id].ts index 86e8b0ca9..0c5266b5a 100644 --- a/apps/website/app/api/supabase/account/[id].ts +++ b/apps/website/app/api/supabase/agent-identifier/[id].ts @@ -4,8 +4,8 @@ import { makeDefaultDeleteHandler, } from "~/utils/supabase/apiUtils"; -export const GET = makeDefaultGetHandler("Account"); +export const GET = makeDefaultGetHandler("AgentIdentifier"); export const OPTIONS = defaultOptionsHandler; -export const DELETE = makeDefaultDeleteHandler("Account"); +export const DELETE = makeDefaultDeleteHandler("AgentIdentifier"); diff --git a/apps/website/app/api/supabase/agent-identifier/route.ts b/apps/website/app/api/supabase/agent-identifier/route.ts new file mode 100644 index 000000000..847cf5666 --- /dev/null +++ b/apps/website/app/api/supabase/agent-identifier/route.ts @@ -0,0 +1,66 @@ +import { NextResponse, NextRequest } from "next/server"; + +import { createClient } from "~/utils/supabase/server"; +import { getOrCreateEntity, ItemValidator } from "~/utils/supabase/dbUtils"; +import { + createApiResponse, + handleRouteError, + defaultOptionsHandler, + asPostgrestFailure, +} from "~/utils/supabase/apiUtils"; +import { TablesInsert, Constants } from "@repo/database/types.gen.ts"; + +type AgentIdentifierDataInput = TablesInsert<"AgentIdentifier">; +const { AgentIdentifierType } = Constants.public.Enums; + +const agentIdentifierValidator: ItemValidator = (agent_identifier: any) => { + if (!agent_identifier || typeof agent_identifier !== "object") + return "Invalid request body: expected a JSON object."; + const { + identifier_type, + account_id, + value, + trusted, + } = agent_identifier; + + if (!AgentIdentifierType.includes(identifier_type)) + return "Invalid identifier_type"; + if (!value || typeof value !== "string" || value.trim() === "") + return "Missing or invalid value"; + if (!account_id || Number.isNaN(Number.parseInt(account_id, 10))) + return "Missing or invalid account_id"; + if (trusted !== undefined && !["true", "false", true, false].includes(trusted)) + return "if included, trusted should be a boolean"; + + const keys = [ 'identifier_type', 'account_id', 'value', 'trusted' ]; + if (!Object.keys(agent_identifier).every((key)=>keys.includes(key))) + return "Invalid agent_identifier object: extra keys"; + return null; +}; + +export const POST = async (request: NextRequest): Promise => { + const supabasePromise = createClient(); + + try { + const body = await request.json(); + const error = agentIdentifierValidator(body); + if (error !== null) + return createApiResponse(request, asPostgrestFailure(error, "invalid")); + + body.account_id = Number.parseInt(body.account_id, 10); + body.trusted = body.trusted === true || body.trusted === "true" || false; + const supabase = await supabasePromise; + const result = await getOrCreateEntity<"AgentIdentifier">({ + supabase, + tableName: "AgentIdentifier", + insertData: body as AgentIdentifierDataInput, + uniqueOn: ["value", "identifier_type", "account_id"], + }); + + return createApiResponse(request, result); + } catch (e: unknown) { + return handleRouteError(request, e, "/api/supabase/agent-identifier"); + } +}; + +export const OPTIONS = defaultOptionsHandler; diff --git a/apps/website/app/api/supabase/person/[id].ts b/apps/website/app/api/supabase/person/[id].ts deleted file mode 100644 index c5338c75c..000000000 --- a/apps/website/app/api/supabase/person/[id].ts +++ /dev/null @@ -1,11 +0,0 @@ -import { - defaultOptionsHandler, - makeDefaultGetHandler, - makeDefaultDeleteHandler, -} from "~/utils/supabase/apiUtils"; - -export const GET = makeDefaultGetHandler("Person"); - -export const OPTIONS = defaultOptionsHandler; - -export const DELETE = makeDefaultDeleteHandler("Person"); diff --git a/apps/website/app/api/supabase/person/route.ts b/apps/website/app/api/supabase/person/route.ts deleted file mode 100644 index 3b87fdb44..000000000 --- a/apps/website/app/api/supabase/person/route.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { NextResponse, NextRequest } from "next/server"; -import type { PostgrestSingleResponse } from "@supabase/supabase-js"; - -import { createClient } from "~/utils/supabase/server"; -import { getOrCreateEntity, ItemValidator } from "~/utils/supabase/dbUtils"; -import { - createApiResponse, - handleRouteError, - defaultOptionsHandler, - asPostgrestFailure, -} from "~/utils/supabase/apiUtils"; -import { Tables, TablesInsert } from "@repo/database/types.gen.ts"; - -type PersonDataInput = TablesInsert<"Person">; -type PersonRecord = Tables<"Person">; - -const personValidator: ItemValidator = (person) => { - if (!person || typeof person !== "object") - return "Invalid request body: expected a JSON object."; - const { name, email } = person; - - if (!name || typeof name !== "string" || name.trim() === "") - return "Missing or invalid name for Person."; - if (!email || typeof email !== "string" || email.trim() === "") - return "Missing or invalid email for Person."; - return null; -}; - -const getOrCreatePersonInternal = async ( - supabasePromise: ReturnType, - email: string, - name: string, - orcid: string | null | undefined, -): Promise> => { - const supabase = await supabasePromise; - // TODO: Rewrite in a transaction with the ORM later. - const agentResponse = await getOrCreateEntity<"Agent">({ - supabase, - tableName: "Agent", - insertData: { type: "Person" }, - }); - if (agentResponse.error || agentResponse.data === null) - return agentResponse as any as PostgrestSingleResponse; - const result = await getOrCreateEntity<"Person">({ - supabase, - tableName: "Person", - insertData: { - id: agentResponse.data.id, - email: email.trim(), - name: name.trim(), - orcid: orcid || null, - }, - uniqueOn: ["email"], - }); - if (result.error) { - await supabase.from("Agent").delete().eq("id", agentResponse.data.id); - // not much to do if an error here - } - return result; -}; - -export const POST = async (request: NextRequest): Promise => { - const supabasePromise = createClient(); - - try { - const body: PersonDataInput = await request.json(); - const { name, email, orcid = null } = body; - const error = personValidator(body); - if (error !== null) - return createApiResponse(request, asPostgrestFailure(error, "invalid")); - - const personResult = await getOrCreatePersonInternal( - supabasePromise, - email, - name, - orcid, - ); - - return createApiResponse(request, personResult); - } catch (e: unknown) { - return handleRouteError(request, e, "/api/supabase/person"); - } -}; - -export const OPTIONS = defaultOptionsHandler; diff --git a/apps/website/app/api/supabase/platform/[id].ts b/apps/website/app/api/supabase/platform-account/[id].ts similarity index 57% rename from apps/website/app/api/supabase/platform/[id].ts rename to apps/website/app/api/supabase/platform-account/[id].ts index a376cb411..03f57b807 100644 --- a/apps/website/app/api/supabase/platform/[id].ts +++ b/apps/website/app/api/supabase/platform-account/[id].ts @@ -4,8 +4,8 @@ import { makeDefaultDeleteHandler, } from "~/utils/supabase/apiUtils"; -export const GET = makeDefaultGetHandler("Platform"); +export const GET = makeDefaultGetHandler("PlatformAccount"); export const OPTIONS = defaultOptionsHandler; -export const DELETE = makeDefaultDeleteHandler("Platform"); +export const DELETE = makeDefaultDeleteHandler("PlatformAccount"); diff --git a/apps/website/app/api/supabase/platform-account/route.ts b/apps/website/app/api/supabase/platform-account/route.ts new file mode 100644 index 000000000..45de67fe5 --- /dev/null +++ b/apps/website/app/api/supabase/platform-account/route.ts @@ -0,0 +1,80 @@ +import { NextResponse, NextRequest } from "next/server"; + +import { createClient } from "~/utils/supabase/server"; +import { getOrCreateEntity, ItemValidator } from "~/utils/supabase/dbUtils"; +import { + createApiResponse, + handleRouteError, + defaultOptionsHandler, + asPostgrestFailure, +} from "~/utils/supabase/apiUtils"; +import { TablesInsert, Constants } from "@repo/database/types.gen.ts"; + +const { AgentType, Platform } = Constants.public.Enums; + +type PlatformAccountDataInput = TablesInsert<"PlatformAccount">; + +const accountValidator: ItemValidator = (account: any) => { + if (!account || typeof account !== "object") + return "Invalid request body: expected a JSON object."; + const { name, platform, account_local_id, write_permission, active, agent_type, metadata, dg_account } = account; + + if (!name || typeof name !== "string" || name.trim() === "") + return "Missing or invalid name"; + // This is not dry, to be rewritten with Drizzle/Zed. + if (!Platform.includes(platform)) + return "Missing or invalid platform"; + if (agent_type !== undefined && !AgentType.includes(agent_type)) + return "Invalid agent_type"; + if (write_permission !== undefined && typeof write_permission != 'boolean') + return "write_permission must be boolean"; + if (active !== undefined && typeof active != 'boolean') + return "active must be boolean"; + if (metadata !== undefined) { + if (typeof metadata != 'string') + return "metadata should be a JSON string"; + else try { + JSON.parse(metadata) + } catch (error) { + return "metadata should be a JSON string"; + } + } + if (!account_local_id || typeof account_local_id != "string" || account_local_id.trim() === "") + return "Missing or invalid account_local_id"; + if (dg_account != undefined) { + if (typeof dg_account != "string") + return "dg_account should be a UUID string"; + const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + if (!uuidRegex.test(dg_account)) + return "dg_account must be a valid UUID"; + } + const keys = [ 'name', 'platform', 'account_local_id', 'write_permission', 'active', 'agent_type', 'metadata', 'dg_account' ]; + if (!Object.keys(account).every((key)=>keys.includes(key))) + return "Invalid account object: extra keys"; + return null; +}; + +export const POST = async (request: NextRequest): Promise => { + const supabasePromise = createClient(); + + try { + const body = await request.json(); + const error = accountValidator(body); + if (error !== null) + return createApiResponse(request, asPostgrestFailure(error, "invalid")); + + const supabase = await supabasePromise; + const result = await getOrCreateEntity<"PlatformAccount">({ + supabase, + tableName: "PlatformAccount", + insertData: body as PlatformAccountDataInput, + uniqueOn: ["account_local_id", "platform"], + }); + + return createApiResponse(request, result); + } catch (e: unknown) { + return handleRouteError(request, e, "/api/supabase/platfor-account"); + } +}; + +export const OPTIONS = defaultOptionsHandler; diff --git a/apps/website/app/api/supabase/platform/route.ts b/apps/website/app/api/supabase/platform/route.ts deleted file mode 100644 index de0b7de99..000000000 --- a/apps/website/app/api/supabase/platform/route.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { NextResponse, NextRequest } from "next/server"; -import type { PostgrestSingleResponse } from "@supabase/supabase-js"; - -import { createClient } from "~/utils/supabase/server"; -import { getOrCreateEntity, ItemValidator } from "~/utils/supabase/dbUtils"; -import { - createApiResponse, - handleRouteError, - defaultOptionsHandler, - asPostgrestFailure, -} from "~/utils/supabase/apiUtils"; -import { Tables, TablesInsert } from "@repo/database/types.gen.ts"; - -type PlatformDataInput = TablesInsert<"Platform">; -type PlatformRecord = Tables<"Platform">; - -const platformValidator: ItemValidator = (platform) => { - if (!platform || typeof platform !== "object") - return "Invalid request body: expected a JSON object."; - - if (!platform.url || typeof platform.url !== "string") { - return "Missing or invalid url field."; - } - - const lowerCaseURL = platform.url.toLowerCase(); - - if (!lowerCaseURL.includes("roamresearch.com")) - return "Could not determine platform from URL:"; - return null; -}; - -const getOrCreatePlatformFromURL = async ( - supabasePromise: ReturnType, - platform: PlatformDataInput, -): Promise> => { - const error = platformValidator(platform); - if (error !== null) return asPostgrestFailure(error, "invalid"); - const lowerCaseURL = platform.url.toLowerCase(); - - if (lowerCaseURL.includes("roamresearch.com")) { - platform.name = "roamresearch"; - platform.url = "https://roamresearch.com"; - } else { - throw Error("No path should reach here."); - } - - const supabase = await supabasePromise; - return getOrCreateEntity<"Platform">({ - supabase, - tableName: "Platform", - insertData: platform, - uniqueOn: ["url"], - }); -}; - -export const POST = async (request: NextRequest): Promise => { - const supabase = createClient(); - - try { - const body: PlatformDataInput = await request.json(); - const { url } = body; - - if (!url || typeof url !== "string") { - return createApiResponse( - request, - asPostgrestFailure( - "Missing or invalid url in request body.", - "invalid", - ), - ); - } - - const result = await getOrCreatePlatformFromURL(supabase, body); - return createApiResponse(request, result); - } catch (e: unknown) { - return handleRouteError(request, e, "/api/supabase/platform"); - } -}; - -export const OPTIONS = defaultOptionsHandler; diff --git a/apps/website/app/api/supabase/space/route.ts b/apps/website/app/api/supabase/space/route.ts index c4fbb0020..7bd36c891 100644 --- a/apps/website/app/api/supabase/space/route.ts +++ b/apps/website/app/api/supabase/space/route.ts @@ -17,18 +17,17 @@ type SpaceRecord = Tables<"Space">; const spaceValidator: ItemValidator = (space) => { if (!space || typeof space !== "object") return "Invalid request body: expected a JSON object."; - const { name, url, platform_id } = space; + const { name, url, platform } = space; if (!name || typeof name !== "string" || name.trim() === "") return "Missing or invalid name."; if (!url || typeof url !== "string" || url.trim() === "") return "Missing or invalid URL."; if ( - platform_id === undefined || - platform_id === null || - typeof platform_id !== "number" + platform === undefined || + !["Roam", "Obsidian"].includes(platform) ) - return "Missing or invalid platform_id."; + return "Missing or invalid platform."; return null; }; @@ -36,7 +35,7 @@ const processAndGetOrCreateSpace = async ( supabasePromise: ReturnType, data: SpaceDataInput, ): Promise> => { - const { name, url, platform_id } = data; + const { name, url, platform } = data; const error = spaceValidator(data); if (error !== null) return asPostgrestFailure(error, "invalid"); @@ -50,7 +49,7 @@ const processAndGetOrCreateSpace = async ( insertData: { name: trimmedName, url: normalizedUrl, - platform_id: platform_id, + platform: platform, }, uniqueOn: ["url"], }); diff --git a/packages/database/schema.puml b/packages/database/schema.puml index c728ab7a1..1943c13c0 100644 --- a/packages/database/schema.puml +++ b/packages/database/schema.puml @@ -2,27 +2,31 @@ skinparam nodesep 10 hide circle hide empty members -class "SpaceAccess" [[{An access control entry for a space}]] { - {field} editor : boolean -} -class "Account" [[{A user account on a platform}]] { +abstract "PlatformAccount" [[{An account for an agent on a platform}]] { {field} id : integer + {field} name : string + {field} platform : Platform + {field} account_local_id : string {field} write_permission : boolean {field} active : boolean - {field} account_local_id : string + {field} agent_type : AgentType + {field} metadata : JSON +} +class "Users" [[{A database user account. Managed by the auth infrastructure.}]] { + {field} id : integer +} +"PlatformAccount" --> "0..1" "Users" : "dg_account" +class "SpaceAccess" [[{An access control entry for a space}]] { + {field} editor : boolean } class "Space" [[{A space on a platform representing a community engaged in a conversation}]] { {field} id : integer {field} url : string {field} name : string + {field} platform : Platform } -"SpaceAccess" --> "1" "Account" : "account" +"SpaceAccess" --> "1" "PlatformAccount" : "account" "SpaceAccess" --> "0..1" "Space" : "space" -class "Platform" [[{A data platform where discourse happens}]] { - {field} id : integer - {field} name : string - {field} url : string -} class "Content" [[{A unit of content}]] { {field} id : integer {field} source_local_id : string @@ -32,13 +36,13 @@ class "Content" [[{A unit of content}]] { {field} scale : Scale {field} last_modified : datetime } -class "Document" [[{None}]] { +class "Document" [[{An external document, or a high-level unit on a platform (eg Roam page)}]] { {field} id : integer {field} source_local_id : string - {field} url : string {field} created : datetime {field} metadata : JSON {field} last_modified : datetime + {field} url : string {field} contents : blob } class "Concept" [[{An abstract concept, claim or relation}]] { @@ -52,30 +56,32 @@ class "Concept" [[{An abstract concept, claim or relation}]] { {field} content : JSON {field} is_schema : boolean } -"Space" --> "1" "Platform" : "platform" "Content" --> "0..1" "Space" : "space" "Document" --> "0..1" "Space" : "space" "Concept" --> "0..1" "Space" : "space" -"Account" --> "1" "Platform" : "platform" -abstract "Agent" [[{An agent that acts in the system}]] { - {field} id : integer - {field} type : EntityType +class "AgentIdentifier" [[{An identifying attribute associated with an agent, can be a basis for unification}]] { + {field} identifier_type : AgentIdentifierType + {field} value : string + {field} trusted : boolean } -"Document" --> "0..*" "Agent" : "contributors" -"Document" --> "1" "Agent" : "author" +"AgentIdentifier" --> "1" "PlatformAccount" : "account" +"Content" --> "1" "PlatformAccount" : "author" +"Document" --> "1" "PlatformAccount" : "author" +"Concept" --> "1" "PlatformAccount" : "author" +"Content" --> "0..*" "PlatformAccount" : "contributors" +"Document" --> "0..*" "PlatformAccount" : "contributors" +"Concept" --> "0..*" "PlatformAccount" : "contributors" +"Content" --> "1" "PlatformAccount" : "creator" "Content" --> "1" "Document" : "document" -class "ContentEmbedding" [[{None}]] { +class "ContentEmbedding" [[{An embedding for a piece of content.}]] { {field} model : EmbeddingName {field} vector : vector {field} obsolete : boolean } "ContentEmbedding" --> "1" "Content" : "target" "Content" --> "0..1" "Content" : "part_of" -"Content" --> "0..*" "Agent" : "contributors" -"Content" --> "1" "Agent" : "creator" -"Content" --> "1" "Agent" : "author" "Concept" --> "0..1" "Content" : "represented_by" -class "ConceptSchema" [[{None}]] { +class "ConceptSchema" [[{A Concept that describes a schema (type) for other concepts}]] { {field} id(i) : integer {field} epistemic_status(i) : EpistemicStatus {field} name(i) : string @@ -87,25 +93,5 @@ class "ConceptSchema" [[{None}]] { {field} is_schema(i) : boolean } "Concept" --> "1" "ConceptSchema" : "schema" -"Concept" --> "0..*" "Agent" : "contributors" -"Concept" --> "1" "Agent" : "author" "Concept" ^-- "ConceptSchema" -class "Person" [[{A person using the system}]] { - {field} name : string - {field} orcid : string - {field} email : string - {field} id(i) : integer - {field} type(i) : EntityType -} -class "AutomatedAgent" [[{An automated agent}]] { - {field} metadata : JSON - {field} name : string - {field} deterministic : boolean - {field} version : string - {field} id(i) : integer - {field} type(i) : EntityType -} -"Account" --> "1" "Agent" : "agent" -"Agent" ^-- "Person" -"Agent" ^-- "AutomatedAgent" @enduml diff --git a/packages/database/schema.svg b/packages/database/schema.svg index 6d1cef850..ee427c960 100644 --- a/packages/database/schema.svg +++ b/packages/database/schema.svg @@ -1 +1 @@ -SpaceAccesseditor : booleanAccountid : integerwrite_permission : booleanactive : booleanaccount_local_id : stringSpaceid : integerurl : stringname : stringPlatformid : integername : stringurl : stringContentid : integersource_local_id : stringcreated : datetimetext : stringmetadata : JSONscale : Scalelast_modified : datetimeDocumentid : integersource_local_id : stringurl : stringcreated : datetimemetadata : JSONlast_modified : datetimecontents : blobConceptid : integerepistemic_status : EpistemicStatusname : stringdescription : stringcreated : datetimelast_modified : datetimearity : integercontent : JSONis_schema : booleanAgentid : integertype : EntityTypeContentEmbeddingmodel : EmbeddingNamevector : vectorobsolete : booleanConceptSchemaid(i) : integerepistemic_status(i) : EpistemicStatusname(i) : stringdescription(i) : stringcreated(i) : datetimelast_modified(i) : datetimearity(i) : integercontent(i) : JSONis_schema(i) : booleanPersonname : stringorcid : stringemail : stringid(i) : integertype(i) : EntityTypeAutomatedAgentmetadata : JSONname : stringdeterministic : booleanversion : stringid(i) : integertype(i) : EntityTypeaccount1space0..1platform1space0..1space0..1space0..1platform1contributors0..*author1document1target1part_of0..1contributors0..*creator1author1represented_by0..1schema1contributors0..*author1agent1 \ No newline at end of file +PlatformAccountid : integername : stringplatform : Platformaccount_local_id : stringwrite_permission : booleanactive : booleanagent_type : AgentTypemetadata : JSONUsersid : integerSpaceAccesseditor : booleanSpaceid : integerurl : stringname : stringplatform : PlatformContentid : integersource_local_id : stringcreated : datetimetext : stringmetadata : JSONscale : Scalelast_modified : datetimeDocumentid : integersource_local_id : stringcreated : datetimemetadata : JSONlast_modified : datetimeurl : stringcontents : blobConceptid : integerepistemic_status : EpistemicStatusname : stringdescription : stringcreated : datetimelast_modified : datetimearity : integercontent : JSONis_schema : booleanAgentIdentifieridentifier_type : AgentIdentifierTypevalue : stringtrusted : booleanContentEmbeddingmodel : EmbeddingNamevector : vectorobsolete : booleanConceptSchemaid(i) : integerepistemic_status(i) : EpistemicStatusname(i) : stringdescription(i) : stringcreated(i) : datetimelast_modified(i) : datetimearity(i) : integercontent(i) : JSONis_schema(i) : booleandg_account0..1account1space0..1space0..1space0..1space0..1account1author1contributors0..*creator1author1contributors0..*author1contributors0..*document1target1part_of0..1represented_by0..1schema1 \ No newline at end of file diff --git a/packages/database/schema.yaml b/packages/database/schema.yaml index 063740d47..cc1648b02 100644 --- a/packages/database/schema.yaml +++ b/packages/database/schema.yaml @@ -45,14 +45,29 @@ enums: could_be_true: strong_evidence_for: certain: + Platform: + description: A known platform on which DiscourseGraph operates + permissible_values: + Roam: + Obsidian: + AgentType: + description: The type of agent + permissible_values: + person: + organization: + automated_agent: + AgentIdentifierType: + description: A namespace for identifiers that can help identify an agent + permissible_values: + email: + orcid: EntityType: description: The type of an entity permissible_values: Platform: Space: - Account: - Person: - AutomatedAgent: + PlatformAccount: + AgentIdentifier: Document: Content: Concept: @@ -104,90 +119,71 @@ types: sql_type: sqlalchemy.dialects.postgresql.BLOB description: A binary large object classes: - Agent: - description: An agent that acts in the system - abstract: true + Users: + description: A database user account. Managed by the auth infrastructure. slots: - id - - type - Person: - description: A person using the system - is_a: Agent - slots: - - name - - orcid - attributes: - email: - required: true - # TODO: known skills, i.e. what processes can they confirm. - unique_keys: - person_email: - unique_key_slots: - - email - person_orcid: - unique_key_slots: - - orcid - AutomatedAgent: - description: An automated agent - is_a: Agent - slots: - - metadata - - name - attributes: - deterministic: - range: boolean - ifabsent: false - version: - range: string - unique_keys: - automated_agent_name_version: - unique_key_slots: - - name - - version - Platform: - description: A data platform where discourse happens + PlatformAccount: + description: An account for an agent on a platform slots: - id - name - - url - unique_keys: - platform_url: - unique_key_slots: - - url - Account: - description: A user account on a platform - slots: - - id - platform attributes: - agent: - range: Agent + account_local_id: required: true + description: The identity of the person in this space write_permission: range: boolean required: true active: range: boolean required: true - ifabsent: true - account_local_id: + ifabsent: 'true' + agent_type: required: true - description: The identity of the person in this space + range: AgentType + ifabsent: AgentType(person) + metadata: + range: JSON + description: Additional platform-specific information about the account + ifabsent: '{}' + dg_account: + range: Users unique_keys: - account_platform_and_local_id: + account_platform_and_id: unique_key_slots: - - platform - account_local_id + - platform + AgentIdentifier: + description: An identifying attribute associated with an agent, can be a basis for unification + attributes: + identifier_type: + range: AgentIdentifierType + required: true + account: + range: PlatformAccount + required: true + value: + required: true + trusted: + description: If we trust this identifier was verified by the platform and can be used automatically + range: boolean + required: true + ifabsent: "false" + unique_keys: + account_attribute_value: + unique_key_slots: + - value + - identifier_type + - account Space: description: A space on a platform representing a community engaged in a conversation slots: - id - url - name - attributes: - platform: - range: Platform - required: true + - platform unique_keys: space_url: unique_key_slots: @@ -198,7 +194,7 @@ classes: - space attributes: account: - range: Account + range: PlatformAccount required: true editor: range: boolean @@ -253,6 +249,7 @@ classes: # range: DerivedTextVariant # required: true Document: + description: An external document, or a high-level unit on a platform (eg Roam page) slots: - id - space @@ -282,6 +279,7 @@ classes: # - issn # - abstract ContentEmbedding: + description: An embedding for a piece of content. # abstract: true attributes: target: @@ -300,7 +298,7 @@ classes: obsolete: description: Whether this embedding is obsolete (becauses the Content was modified) range: boolean - ifabsent: false + ifabsent: 'false' Concept: description: An abstract concept, claim or relation slots: @@ -317,7 +315,7 @@ classes: arity: range: integer required: true - ifabsent: 0 + ifabsent: '0' description: The number of roles in this relation; nodes have zero, binary relations have 2, etc. schema: range: ConceptSchema @@ -328,7 +326,7 @@ classes: is_schema: range: boolean required: true - ifabsent: false + ifabsent: 'false' represented_by: description: This concept is explicitly represented by a given content unit range: Content @@ -350,6 +348,7 @@ classes: - name ConceptSchema: + description: A Concept that describes a schema (type) for other concepts is_a: Concept # Reference: @@ -393,16 +392,16 @@ slots: name: required: true author: - range: Agent + range: PlatformAccount description: The author of content required: true creator: - range: Agent + range: PlatformAccount description: The creator of a logical structure, such as a content subdivision required: true contributors: multivalued: true - range: Agent + range: PlatformAccount text: required: true description: @@ -436,13 +435,13 @@ slots: position: description: The ordinal position of the content within its parent, wrt other content units of the same scale range: integer - ifabsent: 0 + ifabsent: '0' required: true char_position: description: The character position of the content within its parent. # Does not apply to outline sub-elements range: integer - ifabsent: 0 + ifabsent: '0' validation: range: Validation required: true diff --git a/packages/database/supabase/config.toml b/packages/database/supabase/config.toml index ca2dc47aa..f74600c72 100644 --- a/packages/database/supabase/config.toml +++ b/packages/database/supabase/config.toml @@ -51,7 +51,6 @@ max_client_conn = 100 schema_paths = [ './schemas/base.sql', './schemas/extensions.sql', - './schemas/agent.sql', './schemas/space.sql', './schemas/account.sql', './schemas/content.sql', diff --git a/packages/database/supabase/migrations/20250603144146_account_centric.sql b/packages/database/supabase/migrations/20250603144146_account_centric.sql new file mode 100644 index 000000000..26f98ec3d --- /dev/null +++ b/packages/database/supabase/migrations/20250603144146_account_centric.sql @@ -0,0 +1,180 @@ +COMMENT ON TYPE public."EntityType" IS 'The type of an entity'; + +CREATE TYPE public."PlatformE" AS ENUM ( + 'Roam', + 'Obsidian' +); + +ALTER TYPE public."PlatformE" OWNER TO postgres; + +CREATE TYPE public."AgentType" AS ENUM ( + 'person', + 'organization', + 'automated_agent' +); + +ALTER TYPE public."AgentType" OWNER TO postgres; + +COMMENT ON TYPE public."AgentType" IS 'The type of agent'; + +ALTER TYPE public."EntityType" RENAME VALUE 'Account' TO 'PlatformAccount'; + +ALTER TABLE public."Person" RENAME TO "PlatformAccount"; + +ALTER INDEX "Person_pkey" RENAME TO "PlatformAccount_pkey"; + +ALTER TABLE public."PlatformAccount" DROP CONSTRAINT "person_id_fkey"; + +ALTER TABLE "public"."PlatformAccount" ALTER COLUMN "id" SET DEFAULT nextval('entity_id_seq'::regclass); + +DROP TABLE public."AutomatedAgent"; + +ALTER TABLE public."PlatformAccount" ADD COLUMN platform public."PlatformE"; + +ALTER TABLE public."PlatformAccount" ADD COLUMN write_permission BOOLEAN DEFAULT TRUE; + +ALTER TABLE public."PlatformAccount" ADD COLUMN active BOOLEAN DEFAULT TRUE; + +ALTER TABLE public."PlatformAccount" ADD COLUMN agent_type public."AgentType" NOT NULL DEFAULT 'person'; + +ALTER TABLE public."PlatformAccount" ADD COLUMN metadata JSONB NOT NULL DEFAULT '{}'; + +ALTER TABLE public."PlatformAccount" ADD COLUMN dg_account UUID; + +ALTER TABLE public."PlatformAccount" ADD COLUMN account_local_id VARCHAR; + +COMMENT ON TABLE public."PlatformAccount" IS 'An account for an agent on a platform'; + +CREATE INDEX platform_account_dg_account_idx ON public."PlatformAccount" (dg_account); + +WITH s AS (SELECT agent_id, write_permission, active, account_local_id + FROM public."Account" + JOIN public."Platform" AS p ON (platform_id=p.id) + WHERE url='https://roamresearch.com') +UPDATE public."PlatformAccount" AS pa + SET platform = 'Roam', + active = s.active, + write_permission = s.write_permission, + account_local_id = s.account_local_id + FROM s + WHERE pa.id = s.agent_id; + +DELETE FROM public."PlatformAccount" WHERE account_local_id IS NULL; + +CREATE UNIQUE INDEX account_platform_and_id_idx ON public."PlatformAccount" (account_local_id, platform); + + +ALTER TABLE public."PlatformAccount" ADD FOREIGN KEY(dg_account) REFERENCES auth.users (id) ON DELETE SET NULL ON UPDATE CASCADE; + + +ALTER TABLE ONLY public."SpaceAccess" DROP CONSTRAINT "SpaceAccess_account_id_fkey"; + +WITH s AS (SELECT id, agent_id FROM public."Account") +UPDATE public."SpaceAccess" as sa SET account_id=s.agent_id FROM s WHERE sa.account_id = s.id; + +ALTER TABLE ONLY public."SpaceAccess" +ADD CONSTRAINT "SpaceAccess_account_id_fkey" FOREIGN KEY ( + account_id +) REFERENCES public."PlatformAccount" (id) ON UPDATE CASCADE ON DELETE CASCADE; + +DROP TABLE public."Account"; + + +CREATE TYPE public."AgentIdentifierType" AS ENUM ( + 'email', + 'orcid' +); + +ALTER TYPE public."AgentIdentifierType" OWNER TO postgres; + +COMMENT ON TYPE public."AgentIdentifierType" IS 'A namespace for identifiers that can help identify an agent'; + +CREATE TABLE public."AgentIdentifier" ( + identifier_type public."AgentIdentifierType" NOT NULL, + account_id BIGINT NOT NULL, + value VARCHAR NOT NULL, + trusted BOOLEAN NOT NULL DEFAULT false, + PRIMARY KEY (value, identifier_type, account_id), + FOREIGN KEY(account_id) REFERENCES public."PlatformAccount" (id) +); + + +ALTER TABLE public."AgentIdentifier" OWNER TO "postgres"; + +COMMENT ON TABLE public."AgentIdentifier" IS 'An identifying attribute associated with an account, can be a basis for unification'; + +GRANT ALL ON TABLE public."AgentIdentifier" TO anon; +GRANT ALL ON TABLE public."AgentIdentifier" TO authenticated; +GRANT ALL ON TABLE public."AgentIdentifier" TO service_role; + + +INSERT INTO public."AgentIdentifier" SELECT 'email', id, email from public."PlatformAccount"; + +ALTER TABLE public."PlatformAccount" DROP COLUMN email; + +INSERT INTO public."AgentIdentifier" SELECT 'orcid', id, orcid from public."PlatformAccount" WHERE orcid IS NOT NULL; +ALTER TABLE public."PlatformAccount" DROP COLUMN orcid; + + +ALTER TABLE public."PlatformAccount" ALTER COLUMN platform SET NOT NULL; +ALTER TABLE public."PlatformAccount" ALTER COLUMN account_local_id SET NOT NULL; +ALTER TABLE public."PlatformAccount" ALTER COLUMN write_permission SET NOT NULL; +ALTER TABLE public."PlatformAccount" ALTER COLUMN active SET NOT NULL; + +ALTER TABLE public."Space" ADD COLUMN platform public."PlatformE"; + +WITH s AS (SELECT id + FROM public."Platform" + WHERE url='https://roamresearch.com') +UPDATE public."Space" AS space + SET platform = 'Roam' + FROM s + WHERE platform_id = s.id; + +ALTER TABLE public."Space" DROP COLUMN platform_id; + +ALTER TABLE public."Space" ALTER COLUMN "platform" SET NOT NULL; + +DROP TABLE public."Platform"; +ALTER TYPE public."PlatformE" RENAME TO "Platform"; + +ALTER TABLE ONLY public."Document" DROP CONSTRAINT "Document_author_id_fkey"; + +ALTER TABLE ONLY public."Document" +ADD CONSTRAINT "Document_author_id_fkey" FOREIGN KEY ( + author_id +) REFERENCES public."PlatformAccount" (id) ON UPDATE CASCADE ON DELETE CASCADE; + +ALTER TABLE ONLY public."Content" DROP CONSTRAINT "Content_author_id_fkey"; +ALTER TABLE ONLY public."Content" +ADD CONSTRAINT "Content_author_id_fkey" FOREIGN KEY ( + author_id +) REFERENCES public."PlatformAccount" (id) ON UPDATE CASCADE ON DELETE SET NULL; + +ALTER TABLE ONLY public."Content" DROP CONSTRAINT "Content_creator_id_fkey"; +ALTER TABLE ONLY public."Content" +ADD CONSTRAINT "Content_creator_id_fkey" FOREIGN KEY ( + creator_id +) REFERENCES public."PlatformAccount" (id) ON UPDATE CASCADE ON DELETE SET NULL; + +ALTER TABLE ONLY public."Concept" DROP CONSTRAINT "Concept_author_id_fkey"; +ALTER TABLE ONLY public."Concept" +ADD CONSTRAINT "Concept_author_id_fkey" FOREIGN KEY ( + author_id +) REFERENCES public."PlatformAccount" (id) ON UPDATE CASCADE ON DELETE SET NULL; + + +ALTER TABLE ONLY public.content_contributors DROP CONSTRAINT content_contributors_contributor_id_fkey; +ALTER TABLE ONLY public.content_contributors +ADD CONSTRAINT content_contributors_contributor_id_fkey FOREIGN KEY ( + contributor_id +) REFERENCES public."PlatformAccount" (id) ON UPDATE CASCADE ON DELETE CASCADE; + +ALTER TABLE ONLY public.concept_contributors DROP CONSTRAINT concept_contributors_contributor_id_fkey; +ALTER TABLE ONLY public.concept_contributors +ADD CONSTRAINT concept_contributors_contributor_id_fkey FOREIGN KEY ( + contributor_id +) REFERENCES public."PlatformAccount" (id) ON UPDATE CASCADE ON DELETE CASCADE; + + +DROP TABLE public."Agent"; diff --git a/packages/database/supabase/schemas/account.sql b/packages/database/supabase/schemas/account.sql index f7f95f707..8abb214fe 100644 --- a/packages/database/supabase/schemas/account.sql +++ b/packages/database/supabase/schemas/account.sql @@ -1,35 +1,68 @@ -CREATE TABLE IF NOT EXISTS public."Account" ( +CREATE TYPE public."AgentType" AS ENUM ( + 'person', + 'organization', + 'automated_agent' +); + +ALTER TYPE public."AgentType" OWNER TO postgres; + +COMMENT ON TYPE public."AgentType" IS 'The type of agent'; + +CREATE TYPE public."AgentIdentifierType" AS ENUM ( + 'email', + 'orcid' +); + +ALTER TYPE public."AgentIdentifierType" OWNER TO postgres; + +COMMENT ON TYPE public."AgentIdentifierType" IS 'A namespace for identifiers that can help identify an agent'; + + +CREATE TABLE IF NOT EXISTS public."PlatformAccount" ( id bigint DEFAULT nextval( 'public.entity_id_seq'::regclass - ) NOT NULL, - platform_id bigint NOT NULL, - agent_id bigint NOT NULL, - account_local_id varchar NOT NULL, - write_permission boolean NOT NULL, - active boolean DEFAULT true NOT NULL + ) NOT NULL PRIMARY KEY, + name VARCHAR NOT NULL, + platform public."Platform" NOT NULL, + account_local_id VARCHAR NOT NULL, + write_permission BOOLEAN NOT NULL DEFAULT true, + active BOOLEAN NOT NULL DEFAULT true, + agent_type public."AgentType" NOT NULL DEFAULT 'person', + metadata JSONB NOT NULL DEFAULT '{}', + dg_account UUID, + FOREIGN KEY(dg_account) REFERENCES auth.users (id) ON DELETE SET NULL ON UPDATE CASCADE ); -ALTER TABLE public."Account" OWNER TO "postgres"; +ALTER TABLE public."PlatformAccount" OWNER TO "postgres"; -COMMENT ON TABLE public."Account" IS 'A user account on a platform'; +COMMENT ON TABLE public."PlatformAccount" IS 'An account for an agent on a platform'; +CREATE UNIQUE INDEX account_platform_and_id_idx ON public."PlatformAccount" (account_local_id, platform); -ALTER TABLE ONLY public."Account" -ADD CONSTRAINT "Account_agent_id_fkey" FOREIGN KEY ( - agent_id -) REFERENCES public."Agent" (id) ON UPDATE CASCADE ON DELETE CASCADE; +GRANT ALL ON TABLE public."PlatformAccount" TO anon; +GRANT ALL ON TABLE public."PlatformAccount" TO authenticated; +GRANT ALL ON TABLE public."PlatformAccount" TO service_role; -ALTER TABLE ONLY public."Account" -ADD CONSTRAINT "Account_platform_id_fkey" FOREIGN KEY ( - platform_id -) REFERENCES public."Platform" ( - id -) ON UPDATE CASCADE ON DELETE CASCADE; -ALTER TABLE ONLY public."Account" -ADD CONSTRAINT "Account_pkey" PRIMARY KEY (id); +CREATE TABLE public."AgentIdentifier" ( + identifier_type public."AgentIdentifierType" NOT NULL, + account_id BIGINT NOT NULL, + value VARCHAR NOT NULL, + trusted BOOLEAN NOT NULL DEFAULT false, + PRIMARY KEY (value, identifier_type, account_id), + FOREIGN KEY(account_id) REFERENCES public."PlatformAccount" (id) +); + +ALTER TABLE public."AgentIdentifier" OWNER TO "postgres"; + +COMMENT ON TABLE public."AgentIdentifier" IS 'An identifying attribute associated with an account, can be a basis for unification'; + +GRANT ALL ON TABLE public."AgentIdentifier" TO anon; +GRANT ALL ON TABLE public."AgentIdentifier" TO authenticated; +GRANT ALL ON TABLE public."AgentIdentifier" TO service_role; + +CREATE INDEX platform_account_dg_account_idx ON public."PlatformAccount" (dg_account); -CREATE UNIQUE INDEX account_platform_and_local_id_idx ON public."Account" USING btree (platform_id, account_local_id); CREATE TABLE IF NOT EXISTS public."SpaceAccess" ( space_id bigint, @@ -52,7 +85,7 @@ COMMENT ON COLUMN public."SpaceAccess".account_id IS 'The identity of the accoun ALTER TABLE ONLY public."SpaceAccess" ADD CONSTRAINT "SpaceAccess_account_id_fkey" FOREIGN KEY ( account_id -) REFERENCES public."Account" (id) ON UPDATE CASCADE ON DELETE CASCADE; +) REFERENCES public."PlatformAccount" (id) ON UPDATE CASCADE ON DELETE CASCADE; ALTER TABLE ONLY public."SpaceAccess" ADD CONSTRAINT "SpaceAccess_space_id_fkey" FOREIGN KEY ( @@ -65,7 +98,3 @@ GRANT ALL ON TABLE public."SpaceAccess" TO anon; GRANT ALL ON TABLE public."SpaceAccess" TO authenticated; GRANT ALL ON TABLE public."SpaceAccess" TO service_role; - -GRANT ALL ON TABLE public."Account" TO anon; -GRANT ALL ON TABLE public."Account" TO authenticated; -GRANT ALL ON TABLE public."Account" TO service_role; diff --git a/packages/database/supabase/schemas/agent.sql b/packages/database/supabase/schemas/agent.sql deleted file mode 100644 index 0384b127b..000000000 --- a/packages/database/supabase/schemas/agent.sql +++ /dev/null @@ -1,71 +0,0 @@ -CREATE TABLE IF NOT EXISTS public."Agent" ( - id bigint DEFAULT nextval( - 'public.entity_id_seq'::regclass - ) NOT NULL, - type public."EntityType" NOT NULL -); - - -ALTER TABLE ONLY public."Agent" -ADD CONSTRAINT "Agent_pkey" PRIMARY KEY (id); - -ALTER TABLE public."Agent" OWNER TO "postgres"; - -COMMENT ON TABLE public."Agent" IS 'An agent that acts in the system'; - -CREATE TABLE IF NOT EXISTS public."AutomatedAgent" ( - id bigint NOT NULL, - name character varying NOT NULL, - metadata jsonb DEFAULT '{}'::jsonb NOT NULL, - deterministic boolean DEFAULT false, - version character varying NOT NULL -); - -ALTER TABLE ONLY public."AutomatedAgent" -ADD CONSTRAINT "AutomatedAgent_pkey" PRIMARY KEY (id); - -ALTER TABLE ONLY public."AutomatedAgent" -ADD CONSTRAINT automated_agent_id_fkey FOREIGN KEY ( - id -) REFERENCES public."Agent" (id) ON UPDATE CASCADE ON DELETE CASCADE; - -CREATE UNIQUE INDEX automated_agent_name_version_idx ON public."AutomatedAgent" USING btree (name, version); - -ALTER TABLE public."AutomatedAgent" OWNER TO "postgres"; - -COMMENT ON TABLE public."AutomatedAgent" IS 'An automated agent'; - -CREATE TABLE IF NOT EXISTS public."Person" ( - id bigint NOT NULL, - name character varying NOT NULL, - orcid character varying(20), - email character varying NOT NULL -); - -ALTER TABLE ONLY public."Person" -ADD CONSTRAINT "Person_pkey" PRIMARY KEY (id); - -ALTER TABLE ONLY public."Person" -ADD CONSTRAINT person_id_fkey FOREIGN KEY ( - id -) REFERENCES public."Agent" (id) ON UPDATE CASCADE ON DELETE CASCADE; - -CREATE UNIQUE INDEX person_email_idx ON public."Person" USING btree (email); -CREATE UNIQUE INDEX person_orcid_idx ON public."Person" USING btree (orcid); - -ALTER TABLE public."Person" OWNER TO "postgres"; - -COMMENT ON TABLE public."Person" IS 'A person using the system'; - - -GRANT ALL ON TABLE public."Agent" TO anon; -GRANT ALL ON TABLE public."Agent" TO authenticated; -GRANT ALL ON TABLE public."Agent" TO service_role; - -GRANT ALL ON TABLE public."AutomatedAgent" TO anon; -GRANT ALL ON TABLE public."AutomatedAgent" TO authenticated; -GRANT ALL ON TABLE public."AutomatedAgent" TO service_role; - -GRANT ALL ON TABLE public."Person" TO anon; -GRANT ALL ON TABLE public."Person" TO authenticated; -GRANT ALL ON TABLE public."Person" TO service_role; diff --git a/packages/database/supabase/schemas/base.sql b/packages/database/supabase/schemas/base.sql index 64947f9e2..b01c5957e 100644 --- a/packages/database/supabase/schemas/base.sql +++ b/packages/database/supabase/schemas/base.sql @@ -24,7 +24,7 @@ GRANT USAGE ON SCHEMA public TO service_role; CREATE TYPE public."EntityType" AS ENUM ( 'Platform', 'Space', - 'Account', + 'PlatformAccount', 'Person', 'AutomatedAgent', 'Document', @@ -37,6 +37,8 @@ CREATE TYPE public."EntityType" AS ENUM ( ALTER TYPE public."EntityType" OWNER TO postgres; +COMMENT ON TYPE public."EntityType" IS 'The type of an entity'; + CREATE SEQUENCE IF NOT EXISTS public.entity_id_seq START WITH 1 INCREMENT BY 1 diff --git a/packages/database/supabase/schemas/concept.sql b/packages/database/supabase/schemas/concept.sql index 8a4b06ada..735b3e903 100644 --- a/packages/database/supabase/schemas/concept.sql +++ b/packages/database/supabase/schemas/concept.sql @@ -70,7 +70,7 @@ CREATE UNIQUE INDEX concept_space_and_name_idx ON public."Concept" (space_id, na ALTER TABLE ONLY public."Concept" ADD CONSTRAINT "Concept_author_id_fkey" FOREIGN KEY ( author_id -) REFERENCES public."Agent" (id) ON UPDATE CASCADE ON DELETE SET NULL; +) REFERENCES public."PlatformAccount" (id) ON UPDATE CASCADE ON DELETE SET NULL; ALTER TABLE ONLY public."Concept" ADD CONSTRAINT "Concept_schema_id_fkey" FOREIGN KEY ( diff --git a/packages/database/supabase/schemas/content.sql b/packages/database/supabase/schemas/content.sql index 3778cc753..d658bef59 100644 --- a/packages/database/supabase/schemas/content.sql +++ b/packages/database/supabase/schemas/content.sql @@ -33,7 +33,7 @@ ADD CONSTRAINT "Document_pkey" PRIMARY KEY (id); ALTER TABLE ONLY public."Document" ADD CONSTRAINT "Document_author_id_fkey" FOREIGN KEY ( author_id -) REFERENCES public."Agent" (id) ON UPDATE CASCADE ON DELETE CASCADE; +) REFERENCES public."PlatformAccount" (id) ON UPDATE CASCADE ON DELETE CASCADE; ALTER TABLE ONLY public."Document" ADD CONSTRAINT "Document_space_id_fkey" FOREIGN KEY ( @@ -85,12 +85,12 @@ ADD CONSTRAINT "Content_pkey" PRIMARY KEY (id); ALTER TABLE ONLY public."Content" ADD CONSTRAINT "Content_author_id_fkey" FOREIGN KEY ( author_id -) REFERENCES public."Agent" (id) ON UPDATE CASCADE ON DELETE SET NULL; +) REFERENCES public."PlatformAccount" (id) ON UPDATE CASCADE ON DELETE SET NULL; ALTER TABLE ONLY public."Content" ADD CONSTRAINT "Content_creator_id_fkey" FOREIGN KEY ( creator_id -) REFERENCES public."Agent" (id) ON UPDATE CASCADE ON DELETE SET NULL; +) REFERENCES public."PlatformAccount" (id) ON UPDATE CASCADE ON DELETE SET NULL; ALTER TABLE ONLY public."Content" ADD CONSTRAINT "Content_document_id_fkey" FOREIGN KEY ( diff --git a/packages/database/supabase/schemas/contributor.sql b/packages/database/supabase/schemas/contributor.sql index f83b9a40a..6c46a9015 100644 --- a/packages/database/supabase/schemas/contributor.sql +++ b/packages/database/supabase/schemas/contributor.sql @@ -16,7 +16,7 @@ ADD CONSTRAINT content_contributors_content_id_fkey FOREIGN KEY ( ALTER TABLE ONLY public.content_contributors ADD CONSTRAINT content_contributors_contributor_id_fkey FOREIGN KEY ( contributor_id -) REFERENCES public."Agent" (id) ON UPDATE CASCADE ON DELETE CASCADE; +) REFERENCES public."PlatformAccount" (id) ON UPDATE CASCADE ON DELETE CASCADE; ALTER TABLE public.content_contributors OWNER TO "postgres"; @@ -36,7 +36,7 @@ ADD CONSTRAINT concept_contributors_concept_id_fkey FOREIGN KEY ( ALTER TABLE ONLY public.concept_contributors ADD CONSTRAINT concept_contributors_contributor_id_fkey FOREIGN KEY ( contributor_id -) REFERENCES public."Agent" (id) ON UPDATE CASCADE ON DELETE CASCADE; +) REFERENCES public."PlatformAccount" (id) ON UPDATE CASCADE ON DELETE CASCADE; ALTER TABLE ONLY public.concept_contributors ADD CONSTRAINT concept_contributors_pkey PRIMARY KEY ( diff --git a/packages/database/supabase/schemas/space.sql b/packages/database/supabase/schemas/space.sql index 1ae1536d8..2f37fa471 100644 --- a/packages/database/supabase/schemas/space.sql +++ b/packages/database/supabase/schemas/space.sql @@ -1,18 +1,9 @@ -CREATE TABLE IF NOT EXISTS public."Platform" ( - id bigint DEFAULT nextval( - 'public."entity_id_seq"'::regclass - ) NOT NULL, - name character varying NOT NULL, - url character varying NOT NULL +CREATE TYPE public."Platform" AS ENUM ( + 'Roam', + 'Obsidian' ); -ALTER TABLE ONLY public."Platform" -ADD CONSTRAINT "Platform_pkey" PRIMARY KEY (id); - -CREATE UNIQUE INDEX platform_url_idx ON public."Platform" USING btree (url); - -COMMENT ON TABLE public."Platform" IS -'A data platform where discourse happens'; +ALTER TYPE public."Platform" OWNER TO postgres; CREATE TABLE IF NOT EXISTS public."Space" ( id bigint DEFAULT nextval( @@ -20,7 +11,7 @@ CREATE TABLE IF NOT EXISTS public."Space" ( ) NOT NULL, url character varying NOT NULL, name character varying NOT NULL, - platform_id bigint NOT NULL + platform public."Platform" NOT NULL ); ALTER TABLE ONLY public."Space" @@ -28,23 +19,11 @@ ADD CONSTRAINT "Space_pkey" PRIMARY KEY (id); CREATE UNIQUE INDEX space_url_idx ON public."Space" USING btree (url); -ALTER TABLE ONLY public."Space" -ADD CONSTRAINT "Space_platform_id_fkey" FOREIGN KEY ( - platform_id -) REFERENCES public."Platform" ( - id -) ON UPDATE CASCADE ON DELETE CASCADE; - COMMENT ON TABLE public."Space" IS 'A space on a platform representing a community engaged in a conversation'; -ALTER TABLE public."Platform" OWNER TO "postgres"; ALTER TABLE public."Space" OWNER TO "postgres"; -GRANT ALL ON TABLE public."Platform" TO anon; -GRANT ALL ON TABLE public."Platform" TO authenticated; -GRANT ALL ON TABLE public."Platform" TO service_role; - GRANT ALL ON TABLE public."Space" TO anon; GRANT ALL ON TABLE public."Space" TO authenticated; GRANT ALL ON TABLE public."Space" TO service_role; diff --git a/packages/database/types.gen.ts b/packages/database/types.gen.ts index 990aa4b6a..c433dad00 100644 --- a/packages/database/types.gen.ts +++ b/packages/database/types.gen.ts @@ -9,91 +9,31 @@ export type Json = export type Database = { public: { Tables: { - Account: { + AgentIdentifier: { Row: { - account_local_id: string - active: boolean - agent_id: number - id: number - platform_id: number - write_permission: boolean + account_id: number + identifier_type: Database["public"]["Enums"]["AgentIdentifierType"] + trusted: boolean + value: string } Insert: { - account_local_id: string - active?: boolean - agent_id: number - id?: number - platform_id: number - write_permission: boolean + account_id: number + identifier_type: Database["public"]["Enums"]["AgentIdentifierType"] + trusted?: boolean + value: string } Update: { - account_local_id?: string - active?: boolean - agent_id?: number - id?: number - platform_id?: number - write_permission?: boolean + account_id?: number + identifier_type?: Database["public"]["Enums"]["AgentIdentifierType"] + trusted?: boolean + value?: string } Relationships: [ { - foreignKeyName: "Account_agent_id_fkey" - columns: ["agent_id"] - isOneToOne: false - referencedRelation: "Agent" - referencedColumns: ["id"] - }, - { - foreignKeyName: "Account_platform_id_fkey" - columns: ["platform_id"] + foreignKeyName: "AgentIdentifier_account_id_fkey" + columns: ["account_id"] isOneToOne: false - referencedRelation: "Platform" - referencedColumns: ["id"] - }, - ] - } - Agent: { - Row: { - id: number - type: Database["public"]["Enums"]["EntityType"] - } - Insert: { - id?: number - type: Database["public"]["Enums"]["EntityType"] - } - Update: { - id?: number - type?: Database["public"]["Enums"]["EntityType"] - } - Relationships: [] - } - AutomatedAgent: { - Row: { - deterministic: boolean | null - id: number - metadata: Json - name: string - version: string - } - Insert: { - deterministic?: boolean | null - id: number - metadata?: Json - name: string - version: string - } - Update: { - deterministic?: boolean | null - id?: number - metadata?: Json - name?: string - version?: string - } - Relationships: [ - { - foreignKeyName: "automated_agent_id_fkey" - columns: ["id"] - isOneToOne: true - referencedRelation: "Agent" + referencedRelation: "PlatformAccount" referencedColumns: ["id"] }, ] @@ -149,7 +89,7 @@ export type Database = { foreignKeyName: "Concept_author_id_fkey" columns: ["author_id"] isOneToOne: false - referencedRelation: "Agent" + referencedRelation: "PlatformAccount" referencedColumns: ["id"] }, { @@ -200,7 +140,7 @@ export type Database = { foreignKeyName: "concept_contributors_contributor_id_fkey" columns: ["contributor_id"] isOneToOne: false - referencedRelation: "Agent" + referencedRelation: "PlatformAccount" referencedColumns: ["id"] }, ] @@ -253,14 +193,14 @@ export type Database = { foreignKeyName: "Content_author_id_fkey" columns: ["author_id"] isOneToOne: false - referencedRelation: "Agent" + referencedRelation: "PlatformAccount" referencedColumns: ["id"] }, { foreignKeyName: "Content_creator_id_fkey" columns: ["creator_id"] isOneToOne: false - referencedRelation: "Agent" + referencedRelation: "PlatformAccount" referencedColumns: ["id"] }, { @@ -311,7 +251,7 @@ export type Database = { foreignKeyName: "content_contributors_contributor_id_fkey" columns: ["contributor_id"] isOneToOne: false - referencedRelation: "Agent" + referencedRelation: "PlatformAccount" referencedColumns: ["id"] }, ] @@ -384,7 +324,7 @@ export type Database = { foreignKeyName: "Document_author_id_fkey" columns: ["author_id"] isOneToOne: false - referencedRelation: "Agent" + referencedRelation: "PlatformAccount" referencedColumns: ["id"] }, { @@ -396,50 +336,39 @@ export type Database = { }, ] } - Person: { - Row: { - email: string - id: number - name: string - orcid: string | null - } - Insert: { - email: string - id: number - name: string - orcid?: string | null - } - Update: { - email?: string - id?: number - name?: string - orcid?: string | null - } - Relationships: [ - { - foreignKeyName: "person_id_fkey" - columns: ["id"] - isOneToOne: true - referencedRelation: "Agent" - referencedColumns: ["id"] - }, - ] - } - Platform: { + PlatformAccount: { Row: { + account_local_id: string + active: boolean + agent_type: Database["public"]["Enums"]["AgentType"] + dg_account: string | null id: number + metadata: Json name: string - url: string + platform: Database["public"]["Enums"]["Platform"] + write_permission: boolean } Insert: { + account_local_id: string + active?: boolean + agent_type?: Database["public"]["Enums"]["AgentType"] + dg_account?: string | null id?: number + metadata?: Json name: string - url: string + platform: Database["public"]["Enums"]["Platform"] + write_permission?: boolean } Update: { + account_local_id?: string + active?: boolean + agent_type?: Database["public"]["Enums"]["AgentType"] + dg_account?: string | null id?: number + metadata?: Json name?: string - url?: string + platform?: Database["public"]["Enums"]["Platform"] + write_permission?: boolean } Relationships: [] } @@ -447,30 +376,22 @@ export type Database = { Row: { id: number name: string - platform_id: number + platform: Database["public"]["Enums"]["Platform"] url: string } Insert: { id?: number name: string - platform_id: number + platform: Database["public"]["Enums"]["Platform"] url: string } Update: { id?: number name?: string - platform_id?: number + platform?: Database["public"]["Enums"]["Platform"] url?: string } - Relationships: [ - { - foreignKeyName: "Space_platform_id_fkey" - columns: ["platform_id"] - isOneToOne: false - referencedRelation: "Platform" - referencedColumns: ["id"] - }, - ] + Relationships: [] } SpaceAccess: { Row: { @@ -493,7 +414,7 @@ export type Database = { foreignKeyName: "SpaceAccess_account_id_fkey" columns: ["account_id"] isOneToOne: false - referencedRelation: "Account" + referencedRelation: "PlatformAccount" referencedColumns: ["id"] }, { @@ -596,6 +517,8 @@ export type Database = { } } Enums: { + AgentIdentifierType: "email" | "orcid" + AgentType: "person" | "organization" | "automated_agent" EmbeddingName: | "openai_text_embedding_ada2_1536" | "openai_text_embedding_3_small_512" @@ -606,7 +529,7 @@ export type Database = { EntityType: | "Platform" | "Space" - | "Account" + | "PlatformAccount" | "Person" | "AutomatedAgent" | "Document" @@ -625,6 +548,7 @@ export type Database = { | "could_be_true" | "strong_evidence_for" | "certain" + Platform: "Roam" | "Obsidian" Scale: | "document" | "post" @@ -752,6 +676,8 @@ export type CompositeTypes< export const Constants = { public: { Enums: { + AgentIdentifierType: ["email", "orcid"], + AgentType: ["person", "organization", "automated_agent"], EmbeddingName: [ "openai_text_embedding_ada2_1536", "openai_text_embedding_3_small_512", @@ -763,7 +689,7 @@ export const Constants = { EntityType: [ "Platform", "Space", - "Account", + "PlatformAccount", "Person", "AutomatedAgent", "Document", @@ -784,6 +710,7 @@ export const Constants = { "strong_evidence_for", "certain", ], + Platform: ["Roam", "Obsidian"], Scale: [ "document", "post",