Browse Source

Forum initial schema

Leszek Wiesner 3 years ago
parent
commit
719ceffc0e

+ 56 - 1
query-node/manifest.yml

@@ -12,6 +12,7 @@ typegen:
   metadata:
     source: ${TYPEGEN_WS_URI}
   events:
+    # Membership
     - members.MembershipBought
     - members.MemberProfileUpdated
     - members.MemberAccountsUpdated
@@ -26,7 +27,7 @@ typegen:
     - members.ReferralCutUpdated
     - members.InitialInvitationBalanceUpdated
     - members.LeaderInvitationQuotaUpdated
-    # Use Storage Working Group as a reference group (all groups emit the same events)
+    # Working groups - use Storage Working Group as a reference group (all groups emit the same events)
     - storageWorkingGroup.OpeningAdded
     - storageWorkingGroup.AppliedOnOpening
     - storageWorkingGroup.OpeningFilled
@@ -49,6 +50,25 @@ typegen:
     - storageWorkingGroup.BudgetSpending
     - storageWorkingGroup.RewardPaid
     - storageWorkingGroup.NewMissedRewardLevelReached
+  # Forum
+  # FIXME: https://github.com/Joystream/hydra/issues/373
+    # - forum.CategoryCreated
+    # - forum.CategoryUpdated
+    # - forum.CategoryDeleted
+    # - forum.ThreadCreated
+    # - forum.ThreadModerated
+    # - forum.ThreadUpdated
+    # - forum.ThreadTitleUpdated
+    # - forum.ThreadDeleted
+    # - forum.ThreadMoved
+    # - forum.PostAdded
+    # - forum.PostModerated
+    # - forum.PostDeleted
+    # - forum.PostTextUpdated
+    # - forum.PostReacted
+    # - forum.VoteOnPoll
+    # - forum.CategoryStickyThreadUpdate
+    # - forum.CategoryMembershipOfModeratorUpdated
   calls:
     - members.updateProfile
     - members.updateAccounts
@@ -276,6 +296,41 @@ mappings:
       handler: workingGroups_NewMissedRewardLevelReached(DatabaseManager, SubstrateEvent)
     - event: contentDirectoryWorkingGroup.WorkerStartedLeaving
       handler: workingGroups_WorkerStartedLeaving(DatabaseManager, SubstrateEvent)
+    # Forum
+    - event: forum.CategoryCreated
+      handler: forum_CategoryCreated(DatabaseManager, SubstrateEvent)
+    - event: forum.CategoryUpdated
+      handler: forum_CategoryUpdated(DatabaseManager, SubstrateEvent)
+    - event: forum.CategoryDeleted
+      handler: forum_CategoryDeleted(DatabaseManager, SubstrateEvent)
+    - event: forum.ThreadCreated
+      handler: forum_ThreadCreated(DatabaseManager, SubstrateEvent)
+    - event: forum.ThreadModerated
+      handler: forum_ThreadModerated(DatabaseManager, SubstrateEvent)
+    - event: forum.ThreadUpdated
+      handler: forum_ThreadUpdated(DatabaseManager, SubstrateEvent)
+    - event: forum.ThreadTitleUpdated
+      handler: forum_ThreadTitleUpdated(DatabaseManager, SubstrateEvent)
+    - event: forum.ThreadDeleted
+      handler: forum_ThreadDeleted(DatabaseManager, SubstrateEvent)
+    - event: forum.ThreadMoved
+      handler: forum_ThreadMoved(DatabaseManager, SubstrateEvent)
+    - event: forum.PostAdded
+      handler: forum_PostAdded(DatabaseManager, SubstrateEvent)
+    - event: forum.PostModerated
+      handler: forum_PostModerated(DatabaseManager, SubstrateEvent)
+    - event: forum.PostDeleted
+      handler: forum_PostDeleted(DatabaseManager, SubstrateEvent)
+    - event: forum.PostTextUpdated
+      handler: forum_PostTextUpdated(DatabaseManager, SubstrateEvent)
+    - event: forum.PostReacted
+      handler: forum_PostReacted(DatabaseManager, SubstrateEvent)
+    - event: forum.VoteOnPoll
+      handler: forum_VoteOnPoll(DatabaseManager, SubstrateEvent)
+    - event: forum.CategoryStickyThreadUpdate
+      handler: forum_CategoryStickyThreadUpdate(DatabaseManager, SubstrateEvent)
+    - event: forum.CategoryMembershipOfModeratorUpdated
+      handler: forum_CategoryMembershipOfModeratorUpdated(DatabaseManager, SubstrateEvent)
   extrinsicHandlers:
     # infer defaults here
     #- extrinsic: Balances.Transfer

+ 76 - 0
query-node/mappings/forum.ts

@@ -0,0 +1,76 @@
+/*
+eslint-disable @typescript-eslint/naming-convention
+*/
+import { SubstrateEvent } from '@dzlzv/hydra-common'
+import { DatabaseManager } from '@dzlzv/hydra-db-utils'
+
+export async function forum_CategoryCreated(db: DatabaseManager, event_: SubstrateEvent): Promise<void> {
+  // TODO
+}
+
+export async function forum_CategoryUpdated(db: DatabaseManager, event_: SubstrateEvent): Promise<void> {
+  // TODO
+}
+
+export async function forum_CategoryDeleted(db: DatabaseManager, event_: SubstrateEvent): Promise<void> {
+  // TODO
+}
+
+export async function forum_ThreadCreated(db: DatabaseManager, event_: SubstrateEvent): Promise<void> {
+  // TODO
+}
+
+export async function forum_ThreadModerated(db: DatabaseManager, event_: SubstrateEvent): Promise<void> {
+  // TODO
+}
+
+export async function forum_ThreadUpdated(db: DatabaseManager, event_: SubstrateEvent): Promise<void> {
+  // TODO
+}
+
+export async function forum_ThreadTitleUpdated(db: DatabaseManager, event_: SubstrateEvent): Promise<void> {
+  // TODO
+}
+
+export async function forum_ThreadDeleted(db: DatabaseManager, event_: SubstrateEvent): Promise<void> {
+  // TODO
+}
+
+export async function forum_ThreadMoved(db: DatabaseManager, event_: SubstrateEvent): Promise<void> {
+  // TODO
+}
+
+export async function forum_PostAdded(db: DatabaseManager, event_: SubstrateEvent): Promise<void> {
+  // TODO
+}
+
+export async function forum_PostModerated(db: DatabaseManager, event_: SubstrateEvent): Promise<void> {
+  // TODO
+}
+
+export async function forum_PostDeleted(db: DatabaseManager, event_: SubstrateEvent): Promise<void> {
+  // TODO
+}
+
+export async function forum_PostTextUpdated(db: DatabaseManager, event_: SubstrateEvent): Promise<void> {
+  // TODO
+}
+
+export async function forum_PostReacted(db: DatabaseManager, event_: SubstrateEvent): Promise<void> {
+  // TODO
+}
+
+export async function forum_VoteOnPoll(db: DatabaseManager, event_: SubstrateEvent): Promise<void> {
+  // TODO
+}
+
+export async function forum_CategoryStickyThreadUpdate(db: DatabaseManager, event_: SubstrateEvent): Promise<void> {
+  // TODO
+}
+
+export async function forum_CategoryMembershipOfModeratorUpdated(
+  db: DatabaseManager,
+  event_: SubstrateEvent
+): Promise<void> {
+  // TODO
+}

+ 1 - 0
query-node/mappings/index.ts

@@ -1,2 +1,3 @@
 export * from './membership'
 export * from './workingGroups'
+export * from './forum'

+ 18 - 0
query-node/schemas/common.graphql

@@ -44,6 +44,24 @@ enum EventType {
   BudgetSpending
   RewardPaid
   NewMissedRewardLevelReached
+  # Forum
+  CategoryCreated
+  CategoryUpdated
+  CategoryDeleted
+  ThreadCreated
+  ThreadModerated
+  ThreadUpdated
+  ThreadTitleUpdated
+  ThreadDeleted
+  ThreadMoved
+  PostAdded
+  PostModerated
+  PostDeleted
+  PostTextUpdated
+  PostReacted
+  VoteOnPoll
+  CategoryStickyThreadUpdate
+  CategoryMembershipOfModeratorUpdated
 }
 
 type Block @entity {

+ 227 - 0
query-node/schemas/forum.graphql

@@ -0,0 +1,227 @@
+type ForumModerator @entity {
+  "Moderator worker"
+  worker: Worker!
+
+  "Categories managed by the moderator"
+  categories: [ForumCategory!] @derivedFrom(field: "moderators")
+}
+
+type CategoryStatusActive @variant {
+  # No additional information required
+  _phantom: Int
+}
+
+type CategoryStatusArchived @variant {
+  # TODO: Variant relationship
+  "Id of the event the category was archived in"
+  categoryUpdatedEventId: ID!
+}
+
+type CategoryStatusRemoved @variant {
+  # TODO: Variant relationship
+  "Id of the event the category was deleted in"
+  categoryDeletedEventId: ID!
+}
+
+union CategoryStatus = CategoryStatusActive | CategoryStatusArchived | CategoryStatusRemoved
+
+type ForumCategory @entity {
+  "Runtime category id"
+  id: ID!
+
+  "Parent category (if none - this is a root category)"
+  parent: ForumCategory
+
+  "Category title"
+  title: String!
+
+  "Category description"
+  description: String!
+
+  "List of all threads in the category"
+  threads: [ForumThread!] @derivedFrom(field: "category")
+
+  "List of all moderators managing this category"
+  moderators: [ForumModerator!]
+
+  "The event the category was created in"
+  createdInEvent: CategoryCreatedEvent! @derivedFrom(field: "category")
+
+  "Current category status"
+  status: CategoryStatus!
+}
+
+"The thread is visible and editable"
+type ThreadStatusActive @variant {
+  # No additional information required
+  _phantom: Int
+}
+
+"The thread is visible, but not editable - it was removed by the author from the runtime state, but the `hide` flag was set to FALSE"
+type ThreadStatusLocked @variant {
+  # TODO: Variant relationship
+  # TODO: May become optional if threads can be created as non-editable
+  "Id of the event the thread was deleted (locked) in"
+  threadDeletedEventId: ID!
+}
+
+"The thread is hidden - it was removed by the moderator and the associated stake was slashed"
+type ThreadStatusModerated @variant {
+  # TODO: Variant relationship
+  "Id of the event the thread was moderated in"
+  threadModeratedEventId: ID!
+}
+
+"The thread is hidden - it was removed by the author and the `hide` flag was set to TRUE"
+type ThreadStatusRemoved @variant {
+  # TODO: Variant relationship
+  "Id of the event the thread was removed in"
+  threadDeletedEvent: ID!
+}
+
+union ThreadStatus = ThreadStatusActive | ThreadStatusLocked | ThreadStatusModerated | ThreadStatusRemoved
+
+type ForumThread @entity {
+  "Runtime thread id"
+  id: ID!
+
+  "Author of the forum thread"
+  author: Membership!
+
+  "Category the thread belongs to"
+  category: ForumCategory!
+
+  "Thread title"
+  title: String! @fulltext(query: "threadsByTitle")
+
+  "Thread text (md-formatted)"
+  text: String! @fulltext(query: "threadsByText")
+
+  "All posts in the thread"
+  posts: [ForumPost!] @derivedFrom(field: "thread")
+
+  "Optional poll associated with the thread"
+  poll: ForumPoll @derivedFrom(field: "thread")
+
+  "Whether the thread is sticky in the category"
+  isSticky: Boolean!
+
+  "The event the thread was created in"
+  createdInEvent: ThreadCreatedEvent!
+
+  "Current thread status"
+  status: ThreadStatus!
+
+  # Required to create Many-to-Many relation
+  "The events the thred was made sticky in"
+  madeStickyInEvents: [CategoryStickyThreadUpdateEvent!] @derivedFrom(field: "newStickyThreads")
+
+  "List of events that moved the thread to a different category"
+  movedInEvents: [ThreadMovedEvent!] @derivedFrom(field: "thread")
+}
+
+type ForumPoll @entity {
+  "The thread the poll belongs to"
+  thread: ForumThread!
+
+  "Poll description"
+  description: String!
+
+  "The time at which the poll ends"
+  endTime: DateTime!
+
+  "List of poll alternatives"
+  pollAlternatives: [ForumPollAlternative!] @derivedFrom(field: "poll")
+}
+
+type ForumPollAlternative @entity {
+  "The related poll"
+  poll: ForumPoll!
+
+  "The alternative text"
+  text: String!
+
+  "List of all associated vote events"
+  votes: [VoteOnPollEvent!] @derivedFrom(field: "pollAlternative")
+}
+
+enum PostReaction {
+  LIKE
+  # We may support some other ones in the future...
+}
+
+type ForumPostReaction @entity {
+  "{memberId}-{postId}"
+  id: ID!
+
+  "The member that reacted"
+  member: Membership!
+
+  "The post that has been reacted to"
+  post: ForumPost!
+
+  "The reaction"
+  reaction: PostReaction!
+}
+
+"The post is visible and editable"
+type PostStatusActive @variant {
+  # No additional information required
+  _phantom: Int
+}
+
+"The post is visible but not editable - either it wasn't editable to begin with or it was removed from the runtime state, but with `hide` flag beeing set to FALSE"
+type PostStatusLocked @variant {
+  # TODO: Variant relationship
+  "Post deleted event id in case the post became locked through runtime removal"
+  postDeletedEventId: ID
+}
+
+"The post is hidden - it was removed by the moderator and the associated stake was slashed"
+type PostStatusModerated @variant {
+  # TODO: Variant relationship
+  "Id of the event the post was moderated in"
+  postModeratedEventId: ID!
+}
+
+"The post is hidden - it was removed from the runtime state by the author and the `hide` flag was set to TRUE"
+type PostStatusRemoved @variant {
+  # TODO: Variant relationship
+  "Id of the event the post was removed in"
+  postDeletedEventId: ID!
+}
+
+union PostStatus = PostStatusActive | PostStatusLocked | PostStatusModerated | PostStatusRemoved
+
+type ForumPost @entity {
+  "Runtime post id"
+  id: ID!
+
+  "Author of the forum post"
+  author: Membership!
+
+  "Thread the post was submitted in"
+  thread: ForumThread!
+
+  "Content of the post (md-formatted)"
+  text: String! @fulltext(query: "postsByText")
+
+  "A post that this post replies to (if any)"
+  repliesTo: ForumPost
+
+  "Current post status"
+  status: PostStatus!
+
+  "The event the post was created in"
+  createdInEvent: PostAddedEvent! @derivedFrom(field: "post")
+
+  "List of all text update events (edits)"
+  edits: [PostTextUpdatedEvent!] @derivedFrom(field: "post")
+
+  "List of all current post reactions"
+  reactions: [ForumPostReaction!] @derivedFrom(field: "post")
+
+  # Required for PostDeletedEvent One-to-Many relation
+  "The event the post was deleted in (if any)"
+  deletedInEvent: PostDeletedEvent
+}

+ 221 - 0
query-node/schemas/forumEvents.graphql

@@ -0,0 +1,221 @@
+type CategoryCreatedEvent @entity {
+  "Generic event data"
+  event: Event!
+
+  "The created category"
+  category: ForumCategory!
+
+  "The moderator (possibly lead) responsible for creating the category"
+  actor: ForumModerator!
+}
+
+type CategoryUpdatedEvent @entity {
+  "Generic event data"
+  event: Event!
+
+  "The category beeing updated"
+  category: ForumCategory!
+
+  "The new archival status of the category (true = archived)"
+  newArchivalStatus: Boolean!
+
+  "The moderator (possibly lead) responsible for updating the category"
+  actor: ForumModerator!
+}
+
+type CategoryDeletedEvent @entity {
+  "Generic event data"
+  event: Event!
+
+  "Category beeing deleted"
+  category: ForumCategory!
+
+  "The moderator (possibly lead) responsible for deleting the category"
+  actor: ForumModerator!
+}
+
+type ThreadCreatedEvent @entity {
+  "Generic event data"
+  event: Event!
+
+  "The thread that was created"
+  thread: ForumThread!
+
+  # The author is already part of the Thread entity itself and is immutable
+}
+
+type ThreadModeratedEvent @entity {
+  "Generic event data"
+  event: Event!
+
+  "The thread beeing moderated"
+  thread: ForumThread!
+
+  "Rationale behind the moderation"
+  rationale: String!
+
+  "Actor responsible for the moderation"
+  actor: ForumModerator!
+}
+
+# FIXME: Not emitted by the runtime
+# type ThreadUpdatedEvent @entity {
+#   "Generic event data"
+#   event: Event!
+
+#   "The thread beeing updated"
+#   thread: ForumThread!
+
+#   "The new archival status of the thread (true = archived)"
+#   newArchivalStatus: Boolean!
+
+#   "Actor responsible for the update"
+#   actor: ForumModerator!
+# }
+
+type ThreadTitleUpdatedEvent @entity {
+  "Generic event data"
+  event: Event!
+
+  "The thread beeing updated"
+  thread: ForumThread!
+
+  "New title of the thread"
+  newTitle: String!
+
+  # Only author can update the thread title, so no actor information required
+}
+
+type ThreadDeletedEvent @entity {
+  "Generic event data"
+  event: Event!
+
+  "The thread beeing deleted"
+  thread: ForumThread!
+
+  # Only author can delete the thread, so no actor information required
+}
+
+type ThreadMovedEvent @entity {
+  "Generic event data"
+  event: Event!
+
+  "The thread beeing moved"
+  thread: ForumThread!
+
+  "Thread's previous category"
+  oldCategory: ForumCategory!
+
+  "Thread's new category"
+  newCategory: ForumCategory!
+
+  "The actor performing the transfer"
+  actor: ForumModerator!
+}
+
+type PostAddedEvent @entity {
+  "Generic event data"
+  event: Event!
+
+  "The post that was added"
+  post: ForumPost!
+
+  "Whether the added post is editable"
+  isEditable: Boolean
+
+  "Post's original text"
+  text: String!
+}
+
+type PostModeratedEvent @entity {
+  "Generic event data"
+  event: Event!
+
+  "The post beeing moderated"
+  post: ForumPost!
+
+  "The rationale behind the moderation"
+  rationale: String!
+
+  "The actor responsible for the moderation"
+  actor: ForumModerator!
+}
+
+type PostDeletedEvent @entity {
+  "Generic event data"
+  event: Event!
+
+  "List of deleted posts"
+  posts: [ForumPost!] @derivedFrom(field: "deletedInEvent")
+
+  "The actor responsible for the removal"
+  actor: Membership!
+}
+
+type PostTextUpdatedEvent @entity {
+  "Generic event data"
+  event: Event!
+
+  "The post beeing updated"
+  post: ForumPost!
+
+  "New post text"
+  newText: String!
+
+  # Only author can edit the post, so no actor context required
+}
+
+type PostReactedEvent @entity {
+  "Generic event data"
+  event: Event!
+
+  "The post beeing reacted to"
+  post: ForumPost!
+
+  "The reaction. Overrides the previous reaction. If empty - cancels the previous reaction."
+  reaction: PostReaction
+
+  "The member reacting to the post"
+  reactingMember: Membership!
+}
+
+type VoteOnPollEvent @entity {
+  "Generic event data"
+  event: Event!
+
+  "Poll alternative beeing voted on"
+  pollAlternative: ForumPollAlternative!
+
+  "The member that casted the vote"
+  votingMember: Membership!
+}
+
+type CategoryStickyThreadUpdateEvent @entity {
+  "Generic event data"
+  event: Event!
+
+  "The related category"
+  category: ForumCategory!
+
+  "List of the threads beeing made sticky"
+  newStickyThreads: [ForumThread!]
+
+  "The actor responsible for making the threads sticky"
+  actor: ForumModerator!
+}
+
+type CategoryMembershipOfModeratorUpdated @entity {
+  "Generic event data"
+  event: Event!
+
+  "The moderator in question"
+  moderator: ForumModerator!
+
+  "The category in question"
+  category: ForumCategory!
+
+  "The flag indicating whether the permissions to moderate the category are granted or revoked"
+  newCanModerateValue: Boolean!
+
+  # Actor is always lead
+}