ceramicstudio / Roadmap

0 stars 0 forks source link

Interfaces #8

Open jpham2023 opened 1 year ago

jpham2023 commented 1 year ago

Interfaces are a way to group different data models that share some core structure. For example, you may have a “Verifiable Credential” interface with “Work Credential” and “Education Credential” models that implement that interface. Querying against the interface will let you get results from all indexed Models that implement that interface in a single query.

Details

Details:

SET account relation complements the SINGLE and LIST account relations by creating a new type of relation that must be unique per combination of account + field of document.

Use Cases

This allows to model relations such as friend requests, following, likes, etc. that are not unique per account + model combination as for the SINGLE relation, but unique per account + model + document field.

For example a friend request or following model would have a target did: DID! representing the unique value, or postID: StreamID! to represent a like.

This new type of account relation will allow to model social relations such as following and friend requests to target DIDs, as well as individual interactions with documents such as toggling favorites, votes and similar patterns.

SET account relation is needed for associating data to a user who is not the document owner.

GraphQL schema definition

Account to account

type Follow @createModel(accountRelation: SET, accountRelationField: "following") {
  follower: DID! @documentAccount # The controller of the document is the follower
  following: DID!
}

# Automatically generated object to represent the account relations
type CeramicAccount {
  # "inverse" account relations must be explicitly defined
  followers: [Follow] @accountRelationTo(field: "following")

}

Account to document

# Add PostMeta model

type Post @loadModel(id: "...") {}

type PostMeta @createModel(accountRelation: SET, accountRelationField: "postID") {
  postID: StreamID! @documentReference(model: "Post")
  coverImage: URI!
}

# Add view to existing model

type Post @loadModel(id: "...") {
  # This is a 0-1 relation with the account DID provided at runtime
  meta: PostMeta @accountRelationFrom(field: "postID")
}

Model definition

type ModelAccountRelation =
  // Existing types
  | { type: 'single' }
  | { type: 'list' }
  // Added type
  | { type: 'set', field: string }

type ModelDefinition = {
  // ... existing fields
  // changed field:
  accountRelation: ModelAccountRelation
}

Stream handling logic

Generated GraphQL schema

type Follow {
  follower: CeramicAccount!
  following: CeramicAccount!
}

type FollowEdge {
  cursor: String!
  node: Follow
}

type FollowConnection {
  edges: [FollowEdge]
  pageInfo: ...
}

type Post {
  text: String!
  meta: PostMeta
}

type PostMeta {
  postID: StreamID!
  coverImage: URI!
}

type CeramicAccount {
  # Automatically generated based on the account relation
  followSet: FollowConnection!
  # Explicitly added to the composite
  followers: FollowConnection!
  follows: Follow
}

Example queries

query NewFollowers($did: DID!) {
  node(id: $did) {
    ...on CeramicAccount {
      followers(last: 10) {
        edges {
          node {
            id # Follower DID
            # Query if the follower also follows a known DID
            followsBob: follows(account: "did:pkh:...") {
              id
            }
            # "viewer" is replaced by the viewer DID in the client
            followsMe: follows(account: "viewer") {
              id
            }
          }
        }
      }
    }
  }
}

query PostWithMeta($postID: ID!) {
  node(id: $postID) {
    ...on Post {
      text
      # "documentAccount" is replaced by the document controller DID in the client
      meta(account: "documentAccount") {
        coverImage
      }
    }
  }
}