From c5ee04295dd3b59bc7c1896ec8237f0195178ec5 Mon Sep 17 00:00:00 2001 From: m0ar Date: Thu, 5 Oct 2023 18:37:49 +0200 Subject: [PATCH 1/3] Add annotations model and relations --- composites/03-profileAttestation.graphql | 16 -------- .../04-researchObjectAttestation.graphql | 21 ---------- composites/11-annotation.graphql | 24 ++++++++++++ composites/additional-relations.graphql | 17 ++++++--- scripts/composites.mjs | 38 +++++++------------ 5 files changed, 50 insertions(+), 66 deletions(-) delete mode 100644 composites/03-profileAttestation.graphql delete mode 100644 composites/04-researchObjectAttestation.graphql create mode 100644 composites/11-annotation.graphql diff --git a/composites/03-profileAttestation.graphql b/composites/03-profileAttestation.graphql deleted file mode 100644 index 55e8f70..0000000 --- a/composites/03-profileAttestation.graphql +++ /dev/null @@ -1,16 +0,0 @@ -type Claim @loadModel(id: "$CLAIM_ID") { - id: ID! -} - -type UserAttestation - @createModel(accountRelation: LIST, description: "User attestation") - @createIndex(fields: [{ path: "revoked" }]) - @createIndex(fields: [{ path: "claimID" }]) - @createIndex(fields: [{ path: "target" }]) -{ - source: DID! @documentAccount - target: DID! @accountReference - revoked: Boolean - claimID: StreamID! @documentReference(model: "Claim") - claim: Claim! @relationDocument(property: "claimID") -} diff --git a/composites/04-researchObjectAttestation.graphql b/composites/04-researchObjectAttestation.graphql deleted file mode 100644 index 0934e10..0000000 --- a/composites/04-researchObjectAttestation.graphql +++ /dev/null @@ -1,21 +0,0 @@ -type ResearchObject @loadModel(id: "$RESEARCH_OBJECT_ID") { - id: ID! -} - -type Claim @loadModel(id: "$CLAIM_ID") { - id: ID! -} - -type ResearchObjectAttestation - @createModel(accountRelation: LIST, description: "Research object attestation") - @createIndex(fields: [{ path: "revoked" }]) - @createIndex(fields: [{ path: "targetID" }]) - @createIndex(fields: [{ path: "claimID" }]) -{ - source: DID! @documentAccount - targetID: StreamID! @documentReference(model: "ResearchObject") - target: ResearchObject! @relationDocument(property: "targetID") - claimID: StreamID! @documentReference(model: "Claim") - Claim: Claim! @relationDocument(property: "claimID") - revoked: Boolean -} diff --git a/composites/11-annotation.graphql b/composites/11-annotation.graphql new file mode 100644 index 0000000..d8d3b12 --- /dev/null +++ b/composites/11-annotation.graphql @@ -0,0 +1,24 @@ +type ResearchComponent @loadModel(id: "$RESEARCH_COMPONENT_ID") { + id: ID! +} + +type Claim @loadModel(id: "$CLAIM_ID") { + id: ID! +} + +type Annotation + @createModel(accountRelation: LIST, description: "Research component commentary") +{ + owner: DID! @documentAccount + comment: String! @string(maxLength: 1024) + + componentID: StreamID! @documentReference(model: "ResearchComponent") + component: ResearchComponent! @relationDocument(property: "componentID") + + # A way to identify the location of the annotation in the target component + # payload, for example a JSON path, line number, or coordinates in a pdf + path: String @string(maxLength: 512) + + claimID: StreamID @documentReference(model: "Claim") + claim: Claim @relationDocument(property: "claimID") +} diff --git a/composites/additional-relations.graphql b/composites/additional-relations.graphql index 6f7cac1..c662164 100644 --- a/composites/additional-relations.graphql +++ b/composites/additional-relations.graphql @@ -9,10 +9,6 @@ type Attestation @loadModel(id: "$ATTESTATION_ID") { id: ID! } -type ResearchComponent @loadModel(id: "$RESEARCH_COMPONENT_ID") { - id: ID! -} - type ReferenceRelation @loadModel(id: "$REFERENCE_RELATION_ID") { id: ID! } @@ -25,9 +21,21 @@ type ResearchFieldRelation @loadModel(id: "$RESEARCH_FIELD_RELATION_ID") { id: ID! } +type Annotation @loadModel(id: "$ANNOTATION_ID") { + id: ID! +} + +type ResearchComponent @loadModel(id: "$RESEARCH_COMPONENT_ID") { + annotations: [Annotation] @relationFrom(model: "Annotation", property: "componentID") + annotationCount: Int! @relationCountFrom(model: "Annotation", property: "componentID") +} + type Claim @loadModel(id: "$CLAIM_ID") { attestations: [Attestation] @relationFrom(model: "Attestation", property: "claimID") attestationCount: Int! @relationCountFrom(model: "Attestation", property: "claimID") + + annotations: [Annotation] @relationFrom(model: "Annotation", property: "claimID") + annotationCount: Int! @relationCountFrom(model: "Annotation", property: "claimID") } type ResearchObject @loadModel(id: "$RESEARCH_OBJECT_ID") { @@ -66,4 +74,3 @@ type ResearchField @loadModel(id: "$RESEARCH_FIELD_ID") { researchObjects: [ResearchFieldRelation] @relationFrom(model: "ResearchFieldRelation", property: "fieldID") researchObjectCount: Int! @relationCountFrom(model: "ResearchFieldRelation", property: "fieldID") } - diff --git a/scripts/composites.mjs b/scripts/composites.mjs index f39b94a..987dff5 100644 --- a/scripts/composites.mjs +++ b/scripts/composites.mjs @@ -99,26 +99,16 @@ export const writeComposite = async (spinner) => { schema: researchFieldRelationSchema }); - // const profAttestationSchema = readFileSync( - // "./composites/03-profileAttestation.graphql", - // { encoding: "utf-8" } - // ).replace("$CLAIM_ID", claimComposite.modelIDs[0]); - - // const profAttestationComposite = await Composite.create({ - // ceramic, - // schema: profAttestationSchema, - // }); - - // const researchAttestationSchema = readFileSync( - // "./composites/04-researchObjectAttestation.graphql", - // { encoding: "utf-8" } - // ).replace("$RESEARCH_OBJECT_ID", researchObj.modelIDs[0]) - // .replace("$CLAIM_ID", claimComposite.modelIDs[0]); - - // const researchAttestationComposite = await Composite.create({ - // ceramic, - // schema: researchAttestationSchema, - // }); + const annotationSchema = readFileSync( + "./composites/11-annotation.graphql", + { encoding: "utf-8"} + ).replace("$RESEARCH_COMPONENT_ID", componentComposite.modelIDs[1]) + .replace("$CLAIM_ID", researchFieldComposite.modelIDs[0]); + + const annotationComposite = await Composite.create({ + ceramic, + schema: annotationSchema + }); const additionalRelationsSchema = readFileSync( "./composites/additional-relations.graphql", @@ -134,7 +124,8 @@ export const writeComposite = async (spinner) => { .replace("$CONTRIBUTOR_RELATION_ID", contributorRelationComposite.modelIDs[2]) .replace("$REFERENCE_RELATION_ID", referenceRelationComposite.modelIDs[1]) .replace("$RESEARCH_FIELD_ID", researchFieldComposite.modelIDs[0]) - .replace("$RESEARCH_FIELD_RELATION_ID", researchFieldRelationComposite.modelIDs[2]); + .replace("$RESEARCH_FIELD_RELATION_ID", researchFieldRelationComposite.modelIDs[2]) + .replace("$ANNOTATION_ID", annotationComposite.modelIDs[2]); const additionalRelationsComposite = await Composite.create({ ceramic, @@ -152,9 +143,8 @@ export const writeComposite = async (spinner) => { contributorRelationComposite, referenceRelationComposite, researchFieldComposite, - researchFieldRelationComposite - // profAttestationComposite, - // researchAttestationComposite, + researchFieldRelationComposite, + annotationComposite ]); await writeEncodedComposite(composite, "./src/__generated__/definition.json"); From 56c472d0927ff6e8948e355121e17bdb87804453 Mon Sep 17 00:00:00 2001 From: m0ar Date: Fri, 6 Oct 2023 15:44:54 +0200 Subject: [PATCH 2/3] Setup annotation templating and fix composite aliasing --- composites/00-profile.graphql | 2 ++ scripts/composites.mjs | 20 ++++++++++++++++---- template_data.json | 27 ++++++++++++++++++++++++--- test/root.spec.ts | 5 ++--- types/index.ts | 11 ++++++++++- utils/templateData.d.ts | 7 +++++++ 6 files changed, 61 insertions(+), 11 deletions(-) diff --git a/composites/00-profile.graphql b/composites/00-profile.graphql index 3985017..e7388f1 100644 --- a/composites/00-profile.graphql +++ b/composites/00-profile.graphql @@ -7,6 +7,8 @@ type Profile owner: DID! @documentAccount version: CommitID! @documentVersion displayName: String! @string(maxLength: 256) + # Should probably be generic key-value pairs, but that won't work with indexing + # for filter and sort orcid: String @string(maxLength: 256) googleScholar: String @string(maxLength: 256) publicKey: String @string(maxLength: 512) diff --git a/scripts/composites.mjs b/scripts/composites.mjs index 987dff5..755f119 100644 --- a/scripts/composites.mjs +++ b/scripts/composites.mjs @@ -1,4 +1,4 @@ -import { readFileSync } from "fs"; +import { copyFileSync, readFileSync } from "fs"; import { CeramicClient } from "@ceramicnetwork/http-client"; import { createComposite, @@ -160,12 +160,24 @@ export const writeComposite = async (spinner) => { "./src/__generated__/definition.json" ); - await deployComposite.startIndexingOn(ceramic); + // This is rediculous but there is a combination of things forcing + // requirements on the filenames + copyFileSync( + './src/__generated__/definition.js', + './src/__generated__/definition.mjs' + ); + const { definition } = await import('../src/__generated__/definition.mjs'); + const aliases = Object.entries(definition.models) + .map(([name, model]) => [name, model.id]); + // console.log('ALIASES:', aliases) + + const aliasedDeployComposite = deployComposite.setAliases( + Object.fromEntries(aliases) + ); + await aliasedDeployComposite.startIndexingOn(ceramic); spinner.succeed("composite deployed & ready for use"); }; - - /** * Authenticating DID for publishing composite * @return {Promise} - return void when DID is authenticated. diff --git a/template_data.json b/template_data.json index 679e9b9..c187194 100644 --- a/template_data.json +++ b/template_data.json @@ -21,7 +21,8 @@ "contributorRelations": [], "referenceRelations": [], "researchFieldRelations": [], - "attestations": [] + "attestations": [], + "annotations": [] }, "dc0b1125d15f276c5e6fdaf2465ae9f25d5e9984a7cb062640345c941d3ed1e0": { "profile": { @@ -93,7 +94,8 @@ } ], "claims": [], - "attestations": [] + "attestations": [], + "annotations": [] }, "17cd4168d7f322ef17a01dde0ae0900cf9beebdf2c2edd39029041819cdb1015": { "profile": { @@ -160,7 +162,8 @@ 1 ] } - ] + ], + "annotations": [] }, "fdda86398d9559fdc448be872f8e69dc11d9a19ae4707c7903fad530aa165b2c": { "profile": { @@ -193,6 +196,24 @@ 0 ] } + ], + "annotations": [ + { + "comment": "I viewed this dataset and it seems to check out!", + "path": ".", + "componentPath": [ + "17cd4168d7f322ef17a01dde0ae0900cf9beebdf2c2edd39029041819cdb1015", + "researchObjects", + 0, + "components", + 0 + ], + "claimPath": [ + "1a40e10dc4830864781716eab4f88c60d74d8718cc5ed9af268d338a2096833f", + "claims", + 1 + ] + } ] } } diff --git a/test/root.spec.ts b/test/root.spec.ts index 1956877..f70f76d 100644 --- a/test/root.spec.ts +++ b/test/root.spec.ts @@ -1,16 +1,15 @@ import { ComposeClient } from '@composedb/client' import { definition } from '@/src/__generated__/definition' import { RuntimeCompositeDefinition } from '@composedb/types' -import { test, describe, beforeAll, expect } from 'vitest' +import { test, describe, beforeAll } from 'vitest' import { mutationCreateAttestation, mutationCreateClaim, mutationCreateProfile, - mutationCreateResearchObject, mutationUpdateAttestation, mutationUpdateResearchObject, queryResearchObjects + mutationCreateResearchObject, mutationUpdateAttestation, mutationUpdateResearchObject } from '../utils/queries' import { randomDID } from './util' import { CeramicClient } from '@ceramicnetwork/http-client' import { writeComposite } from 'scripts/composites.mjs' import { setTimeout } from "timers/promises"; -import { ROProps } from '@/types' const CERAMIC_API = 'http:/localhost:7007' const TIMEOUT = 7000 diff --git a/types/index.ts b/types/index.ts index 17f16fa..90f69ac 100644 --- a/types/index.ts +++ b/types/index.ts @@ -44,6 +44,16 @@ export type Attestation = { revoked: boolean }; +export type Annotation = { + id?: string, + comment: string, + path: string, + componentID: string, + component?: ResearchComponent, + claimID: string, + claim?: Claim +}; + export type ContributorRelation = { id?: string, role: string, @@ -72,4 +82,3 @@ export type SidebarProps = { export type RequiredKeys = { [K in keyof T as (undefined extends T[K] ? never : K)]: T[K] }; - diff --git a/utils/templateData.d.ts b/utils/templateData.d.ts index 70b0c7d..7b01d69 100644 --- a/utils/templateData.d.ts +++ b/utils/templateData.d.ts @@ -50,6 +50,13 @@ export type AttestationTemplate = { claimPath: ObjectPath }; +export type AnnotationTemplate = { + comment: string, + path: string, + componentPath: ObjectPath, + claimPath: ObjectPath +}; + export type ActorTemplate = { profile: ProfileTemplate, researchObjects: ResearchObjectTemplate[], From add7c8cf41e721736796dd0553e7ce8ec2528518 Mon Sep 17 00:00:00 2001 From: m0ar Date: Mon, 9 Oct 2023 15:24:24 +0200 Subject: [PATCH 3/3] Change component type enum to mimeType string --- composites/07-researchComponent.graphql | 14 ++------ template_data.json | 4 +-- types/index.ts | 48 ++++++++++++------------- utils/queries.ts | 46 ++++++++++++------------ utils/templateData.d.ts | 2 +- 5 files changed, 51 insertions(+), 63 deletions(-) diff --git a/composites/07-researchComponent.graphql b/composites/07-researchComponent.graphql index 4b65477..456b29f 100644 --- a/composites/07-researchComponent.graphql +++ b/composites/07-researchComponent.graphql @@ -12,20 +12,10 @@ type ResearchComponent { owner: DID! @documentAccount name: String! @string(maxLength: 512) - type: ComponentType! + mimeType: String! @string(maxLength: 128) dagNode: CID! + # The associated research object in which this component lives researchObjectID: StreamID! @documentReference(model: "ResearchObject") researchObject: ResearchObject! @relationDocument(property: "researchObjectID") } - -# Problematic to have hardcoded since extension will change the Component model -enum ComponentType { - DATA_BUCKET - UNKNOWN - PDF - CODE - VIDEO - DATA - LINK -} diff --git a/template_data.json b/template_data.json index c187194..e853e32 100644 --- a/template_data.json +++ b/template_data.json @@ -38,7 +38,7 @@ { "name": "A fancy dataset", "dagNode": "bafkreibtsll3aq2bynvlxnqh6nxafzdm4cpiovr3bcncbkzjcy32xaaaaa", - "type": "DATA" + "mimeType": "text/csv" } ] }, @@ -111,7 +111,7 @@ { "name": "Some cool data I found", "dagNode": "bafkreibtsll3aq2bynvlxnqh6nxafzdm4cpiovr3bcncbkzjcy32xaaaaa", - "type": "DATA" + "mimeType": "text/csv" } ] } diff --git a/types/index.ts b/types/index.ts index 90f69ac..fb7f799 100644 --- a/types/index.ts +++ b/types/index.ts @@ -19,58 +19,54 @@ export type ROProps = { export type ResearchComponent = { owner?: DID name: string - type: ComponentType + mimeType: string dagNode: string researchObjectID: string }; -export type ComponentType = - "DATA_BUCKET" | - "UNKNOWN" - export type Claim = { - id?: string, - title: string, - description: string, + id?: string + title: string + description: string badge?: string }; export type Attestation = { - id?: string, - source?: DID, - targetID: string, - claimID: string, - claim?: Claim , + id?: string + source?: DID + targetID: string + claimID: string + claim?: Claim revoked: boolean }; export type Annotation = { - id?: string, - comment: string, - path: string, - componentID: string, - component?: ResearchComponent, - claimID: string, + id?: string + comment: string + path: string + componentID: string + component?: ResearchComponent + claimID: string claim?: Claim }; export type ContributorRelation = { - id?: string, - role: string, + id?: string + role: string // info - contributorID: string, + contributorID: string researchObjectID: string }; export type ReferenceRelation = { - id?: string, - toID: string, + id?: string + toID: string fromID: string }; export type ResearchFieldRelation = { - id?: string, - fieldID: string, + id?: string + fieldID: string researchObjectID: string }; diff --git a/utils/queries.ts b/utils/queries.ts index 7c7a71c..3b24d7f 100644 --- a/utils/queries.ts +++ b/utils/queries.ts @@ -176,17 +176,19 @@ export const mutationCreateResearchComponent = async ( composeClient: ComposeClient, inputs: ResearchComponent ): Promise => { + const gqlTypes: Partial> = { + name: "String!", + mimeType: "String!", + dagNode: "InterPlanetaryCID!", + researchObjectID: "CeramicStreamID!" + }; + const [params, content] = getQueryFields(gqlTypes, inputs); const response = await composeClient.executeQuery< { createResearchComponent: { document: { id: string } } } >(` - mutation ($name: String!, $type: ResearchComponentComponentType!, $dagNode: InterPlanetaryCID!, $researchObjectID: CeramicStreamID!){ + mutation ( ${params} ){ createResearchComponent(input: { - content: { - name: $name - type: $type - dagNode: $dagNode - researchObjectID: $researchObjectID - } + content: { ${content} } }) { document { @@ -196,15 +198,15 @@ export const mutationCreateResearchComponent = async ( }`, inputs ) - assertMutationErrors(response, 'create research object') - return response.data!.createResearchComponent.document.id + assertMutationErrors(response, 'create research object'); + return response.data!.createResearchComponent.document.id; } export const mutationUpdateResearchObject = async ( composeClient: ComposeClient, inputs: Partial & { id: string } ): Promise => { - const gqlParamTypes: Record = { + const gqlParamTypes: Partial> = { manifest: "InterPlanetaryCID", title: "String" }; @@ -223,8 +225,8 @@ export const mutationUpdateResearchObject = async ( } }`, inputs - ) - assertMutationErrors(response, 'update research object') + ); + assertMutationErrors(response, 'update research object'); } export const mutationCreateProfile = async ( @@ -249,8 +251,8 @@ export const mutationCreateProfile = async ( }`, inputs ) - assertMutationErrors(response, 'create profile') - return response.data!.createProfile.document.id + assertMutationErrors(response, 'create profile'); + return response.data!.createProfile.document.id; } export const mutationCreateClaim = async ( @@ -337,8 +339,8 @@ export const mutationUpdateAttestation = async ( `, inputs ); - assertMutationErrors(response, 'update attestation') - return response.data!.updateAttestation.document.id + assertMutationErrors(response, 'update attestation'); + return response.data!.updateAttestation.document.id; } export const mutationCreateContributorRelation = async ( @@ -453,19 +455,19 @@ const assertMutationErrors = ( queryDescription: string ) => { if (result.errors) { - console.error('Error:', result.errors.toString()) + console.error('Error:', result.errors.toString()); throw new Error(`Mutation failed: ${queryDescription}`) - } + }; } const assertQueryErrors = ( result: SimpleQueryResult, queryDescription: string ) => { - if (result.errors || !result.data) { - console.error("Error:", result.errors?.toString()); - throw new Error(`Query failed: ${queryDescription}!`); - } + if (result.errors || !result.data) { + console.error("Error:", result.errors?.toString()); + throw new Error(`Query failed: ${queryDescription}!`); + }; } /** Get query parameters and doc content string depending on which diff --git a/utils/templateData.d.ts b/utils/templateData.d.ts index 7b01d69..a55439f 100644 --- a/utils/templateData.d.ts +++ b/utils/templateData.d.ts @@ -11,7 +11,7 @@ export type ProfileTemplate = { export type ComponentTemplate = { name: string, dagNode: string, - type: string + mimeType: string }; export type ResearchObjectTemplate = {