Browse Source

ForumThread.initialPost and Proposal.isFinalized

Leszek Wiesner 3 years ago
parent
commit
386af1c51a

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

@@ -347,6 +347,9 @@ export async function forum_ThreadCreated(ctx: EventContext & StoreContext): Pro
     origin: postOrigin,
   })
   await store.save<ForumPost>(initialPost)
+
+  thread.initialPost = initialPost
+  await store.save<ForumThread>(thread)
 }
 
 export async function forum_ThreadModerated(ctx: EventContext & StoreContext): Promise<void> {

+ 4 - 0
query-node/mappings/proposals.ts

@@ -332,6 +332,7 @@ export async function proposalsCodex_ProposalCreated({ store, event }: EventCont
     exactExecutionBlock: generalProposalParameters.exact_execution_block.unwrapOr(undefined)?.toNumber(),
     stakingAccount: generalProposalParameters.staking_account_id.toString(),
     status: new ProposalStatusDeciding(),
+    isFinalized: false,
     statusSetAtBlock: event.blockNumber,
     statusSetAtTime: eventTime,
   })
@@ -436,6 +437,7 @@ export async function proposalsEngine_ProposalDecisionMade({
       | ProposalStatusSlashed
       | ProposalStatusVetoed).proposalDecisionMadeEventId = proposalDecisionMadeEvent.id
     proposal.status = decisionStatus
+    proposal.isFinalized = true
     proposal.statusSetAtBlock = event.blockNumber
     proposal.statusSetAtTime = eventTime
     proposal.updatedAt = eventTime
@@ -468,6 +470,7 @@ export async function proposalsEngine_ProposalExecuted({ store, event }: EventCo
 
   newStatus.proposalExecutedEventId = proposalExecutedEvent.id
   proposal.status = newStatus
+  proposal.isFinalized = true
   proposal.statusSetAtBlock = event.blockNumber
   proposal.statusSetAtTime = eventTime
   proposal.updatedAt = eventTime
@@ -516,6 +519,7 @@ export async function proposalsEngine_ProposalCancelled({ store, event }: EventC
   await store.save<ProposalCancelledEvent>(proposalCancelledEvent)
 
   proposal.status = new ProposalStatusCancelled()
+  proposal.isFinalized = true
   proposal.status.cancelledInEventId = proposalCancelledEvent.id
   proposal.statusSetAtBlock = event.blockNumber
   proposal.statusSetAtTime = eventTime

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

@@ -83,6 +83,9 @@ type ForumThread @entity {
   "All posts in the thread"
   posts: [ForumPost!] @derivedFrom(field: "thread")
 
+  "The intial post created along with the thread"
+  initialPost: ForumPost
+
   "Number of non-deleted posts in the thread"
   visiblePostsCount: Int!
 

+ 3 - 0
query-node/schemas/proposals.graphql

@@ -138,6 +138,9 @@ type Proposal @entity {
   "Current proposal status"
   status: ProposalStatus!
 
+  "If true then the proposal status is final and will not change form this point"
+  isFinalized: Boolean
+
   # Additional fileds to avoid the need for complex filtering through status variant relations:
 
   "Number of the block the current status was set at"

+ 9 - 10
tests/integration-tests/src/QueryNodeApi.ts

@@ -187,10 +187,10 @@ import {
   GetThreadDeletedEventsByEventIdsQuery,
   GetThreadDeletedEventsByEventIdsQueryVariables,
   GetThreadDeletedEventsByEventIds,
-  ForumThreadWithPostsFieldsFragment,
-  GetThreadsWithPostsByIdsQuery,
-  GetThreadsWithPostsByIdsQueryVariables,
-  GetThreadsWithPostsByIds,
+  ForumThreadWithInitialPostFragment,
+  GetThreadsWithInitialPostsByIds,
+  GetThreadsWithInitialPostsByIdsQuery,
+  GetThreadsWithInitialPostsByIdsQueryVariables,
   GetMembershipBoughtEventsByEventIdsQuery,
   GetMembershipBoughtEventsByEventIdsQueryVariables,
   GetMembershipBoughtEventsByEventIds,
@@ -844,12 +844,11 @@ export class QueryNodeApi {
     >(GetThreadMetadataUpdatedEventsByEventIds, { eventIds }, 'threadMetadataUpdatedEvents')
   }
 
-  public async getThreadsWithPostsByIds(ids: ThreadId[]): Promise<ForumThreadWithPostsFieldsFragment[]> {
-    return this.multipleEntitiesQuery<GetThreadsWithPostsByIdsQuery, GetThreadsWithPostsByIdsQueryVariables>(
-      GetThreadsWithPostsByIds,
-      { ids: ids.map((id) => id.toString()) },
-      'forumThreads'
-    )
+  public async getThreadsWithInitialPostsByIds(ids: ThreadId[]): Promise<ForumThreadWithInitialPostFragment[]> {
+    return this.multipleEntitiesQuery<
+      GetThreadsWithInitialPostsByIdsQuery,
+      GetThreadsWithInitialPostsByIdsQueryVariables
+    >(GetThreadsWithInitialPostsByIds, { ids: ids.map((id) => id.toString()) }, 'forumThreads')
   }
 
   public async getVoteOnPollEvents(events: EventDetails[]): Promise<VoteOnPollEventFieldsFragment[]> {

+ 5 - 5
tests/integration-tests/src/fixtures/forum/CreateThreadsFixture.ts

@@ -4,7 +4,7 @@ import { MetadataInput, ThreadCreatedEventDetails } from '../../types'
 import { SubmittableExtrinsic } from '@polkadot/api/types'
 import { Utils } from '../../utils'
 import { ISubmittableResult } from '@polkadot/types/types/'
-import { ForumThreadWithPostsFieldsFragment, ThreadCreatedEventFieldsFragment } from '../../graphql/generated/queries'
+import { ForumThreadWithInitialPostFragment, ThreadCreatedEventFieldsFragment } from '../../graphql/generated/queries'
 import { assert } from 'chai'
 import { StandardizedFixture } from '../../Fixture'
 import { CategoryId, PollInput } from '@joystream/types/forum'
@@ -95,7 +95,7 @@ export class CreateThreadsFixture extends StandardizedFixture {
   }
 
   protected assertQueriedThreadsAreValid(
-    qThreads: ForumThreadWithPostsFieldsFragment[],
+    qThreads: ForumThreadWithInitialPostFragment[],
     qEvents: ThreadCreatedEventFieldsFragment[]
   ): void {
     this.events.map((e, i) => {
@@ -111,8 +111,8 @@ export class CreateThreadsFixture extends StandardizedFixture {
       assert.equal(qThread.status.__typename, 'ThreadStatusActive')
       assert.equal(qThread.isSticky, false)
       assert.equal(qThread.createdInEvent.id, qEvent.id)
-      const initialPost = qThread.posts.find((p) => p.origin.__typename === 'PostOriginThreadInitial')
-      Utils.assert(initialPost, "Query node: Thread's initial post not found!")
+      const { initialPost } = qThread
+      Utils.assert(initialPost, "Query node: Thread's initial post is empty!")
       assert.equal(initialPost.id, e.postId.toString())
       assert.equal(initialPost.text, threadParams.text)
       Utils.assert(initialPost.origin.__typename === 'PostOriginThreadInitial')
@@ -154,7 +154,7 @@ export class CreateThreadsFixture extends StandardizedFixture {
     )
 
     // Query the threads
-    const qThreads = await this.query.getThreadsWithPostsByIds(this.events.map((e) => e.threadId))
+    const qThreads = await this.query.getThreadsWithInitialPostsByIds(this.events.map((e) => e.threadId))
     this.assertQueriedThreadsAreValid(qThreads, qEvents)
   }
 }

+ 3 - 3
tests/integration-tests/src/fixtures/forum/DeleteThreadsFixture.ts

@@ -4,7 +4,7 @@ import { EventDetails, MemberContext } from '../../types'
 import { SubmittableExtrinsic } from '@polkadot/api/types'
 import { Utils } from '../../utils'
 import { ISubmittableResult } from '@polkadot/types/types/'
-import { ForumThreadWithPostsFieldsFragment, ThreadDeletedEventFieldsFragment } from '../../graphql/generated/queries'
+import { ForumThreadWithInitialPostFragment, ThreadDeletedEventFieldsFragment } from '../../graphql/generated/queries'
 import { assert } from 'chai'
 import { StandardizedFixture } from '../../Fixture'
 import { CategoryId } from '@joystream/types/forum'
@@ -60,7 +60,7 @@ export class DeleteThreadsFixture extends StandardizedFixture {
   }
 
   protected assertQueriedThreadsAreValid(
-    qThreads: ForumThreadWithPostsFieldsFragment[],
+    qThreads: ForumThreadWithInitialPostFragment[],
     qEvents: ThreadDeletedEventFieldsFragment[]
   ): void {
     this.events.map((e, i) => {
@@ -89,7 +89,7 @@ export class DeleteThreadsFixture extends StandardizedFixture {
     )
 
     // Query the threads
-    const qThreads = await this.query.getThreadsWithPostsByIds(this.removals.map((r) => r.threadId))
+    const qThreads = await this.query.getThreadsWithInitialPostsByIds(this.removals.map((r) => r.threadId))
     this.assertQueriedThreadsAreValid(qThreads, qEvents)
   }
 }

+ 3 - 3
tests/integration-tests/src/fixtures/forum/ModerateThreadsFixture.ts

@@ -5,7 +5,7 @@ import { WorkerId } from '@joystream/types/working-group'
 import { SubmittableExtrinsic } from '@polkadot/api/types'
 import { Utils } from '../../utils'
 import { ISubmittableResult } from '@polkadot/types/types/'
-import { ForumThreadWithPostsFieldsFragment, ThreadModeratedEventFieldsFragment } from '../../graphql/generated/queries'
+import { ForumThreadWithInitialPostFragment, ThreadModeratedEventFieldsFragment } from '../../graphql/generated/queries'
 import { assert } from 'chai'
 import { CategoryId } from '@joystream/types/forum'
 import { WithForumWorkersFixture } from './WithForumWorkersFixture'
@@ -48,7 +48,7 @@ export class ModerateThreadsFixture extends WithForumWorkersFixture {
   }
 
   protected assertQueriedThreadsAreValid(
-    qThreads: ForumThreadWithPostsFieldsFragment[],
+    qThreads: ForumThreadWithInitialPostFragment[],
     qEvents: ThreadModeratedEventFieldsFragment[]
   ): void {
     this.events.map((e, i) => {
@@ -78,7 +78,7 @@ export class ModerateThreadsFixture extends WithForumWorkersFixture {
     )
 
     // Query the threads
-    const qThreads = await this.query.getThreadsWithPostsByIds(this.moderations.map((m) => m.threadId))
+    const qThreads = await this.query.getThreadsWithInitialPostsByIds(this.moderations.map((m) => m.threadId))
     this.assertQueriedThreadsAreValid(qThreads, qEvents)
   }
 }

+ 3 - 3
tests/integration-tests/src/fixtures/forum/MoveThreadsFixture.ts

@@ -4,7 +4,7 @@ import { EventDetails } from '../../types'
 import { SubmittableExtrinsic } from '@polkadot/api/types'
 import { Utils } from '../../utils'
 import { ISubmittableResult } from '@polkadot/types/types/'
-import { ForumThreadWithPostsFieldsFragment, ThreadMovedEventFieldsFragment } from '../../graphql/generated/queries'
+import { ForumThreadWithInitialPostFragment, ThreadMovedEventFieldsFragment } from '../../graphql/generated/queries'
 import { assert } from 'chai'
 import { CategoryId } from '@joystream/types/forum'
 import { ThreadId } from '@joystream/types/common'
@@ -47,7 +47,7 @@ export class MoveThreadsFixture extends WithForumWorkersFixture {
   }
 
   protected assertQueriedThreadsAreValid(
-    qThreads: ForumThreadWithPostsFieldsFragment[],
+    qThreads: ForumThreadWithInitialPostFragment[],
     qEvents: ThreadMovedEventFieldsFragment[]
   ): void {
     // Check movedInEvents array
@@ -87,7 +87,7 @@ export class MoveThreadsFixture extends WithForumWorkersFixture {
     )
 
     // Query the threads
-    const qThreads = await this.query.getThreadsWithPostsByIds(this.updates.map((u) => u.threadId))
+    const qThreads = await this.query.getThreadsWithInitialPostsByIds(this.updates.map((u) => u.threadId))
     this.assertQueriedThreadsAreValid(qThreads, qEvents)
   }
 }

+ 3 - 3
tests/integration-tests/src/fixtures/forum/UpdateThreadsMetadataFixture.ts

@@ -5,7 +5,7 @@ import { SubmittableExtrinsic } from '@polkadot/api/types'
 import { Utils } from '../../utils'
 import { ISubmittableResult } from '@polkadot/types/types/'
 import {
-  ForumThreadWithPostsFieldsFragment,
+  ForumThreadWithInitialPostFragment,
   ThreadMetadataUpdatedEventFieldsFragment,
 } from '../../graphql/generated/queries'
 import { assert } from 'chai'
@@ -81,7 +81,7 @@ export class UpdateThreadsMetadataFixture extends StandardizedFixture {
   }
 
   protected assertQueriedThreadsAreValid(
-    qThreads: ForumThreadWithPostsFieldsFragment[],
+    qThreads: ForumThreadWithInitialPostFragment[],
     qEvents: ThreadMetadataUpdatedEventFieldsFragment[]
   ): void {
     // Check metadataUpdates array
@@ -129,7 +129,7 @@ export class UpdateThreadsMetadataFixture extends StandardizedFixture {
     )
 
     // Query the threads
-    const qThreads = await this.query.getThreadsWithPostsByIds(this.updates.map((u) => u.threadId))
+    const qThreads = await this.query.getThreadsWithInitialPostsByIds(this.updates.map((u) => u.threadId))
     this.assertQueriedThreadsAreValid(qThreads, qEvents)
   }
 }

+ 3 - 3
tests/integration-tests/src/fixtures/forum/VoteOnPollFixture.ts

@@ -3,7 +3,7 @@ import { QueryNodeApi } from '../../QueryNodeApi'
 import { EventDetails } from '../../types'
 import { SubmittableExtrinsic } from '@polkadot/api/types'
 import { ISubmittableResult } from '@polkadot/types/types/'
-import { ForumThreadWithPostsFieldsFragment, VoteOnPollEventFieldsFragment } from '../../graphql/generated/queries'
+import { ForumThreadWithInitialPostFragment, VoteOnPollEventFieldsFragment } from '../../graphql/generated/queries'
 import { assert } from 'chai'
 import { StandardizedFixture } from '../../Fixture'
 import { CategoryId } from '@joystream/types/forum'
@@ -49,7 +49,7 @@ export class VoteOnPollFixture extends StandardizedFixture {
     assert.equal(qEvent.votingMember.id, this.votes[i].asMember.toString())
   }
 
-  protected assertQueriedThreadsAreValid(qThreads: ForumThreadWithPostsFieldsFragment[]): void {
+  protected assertQueriedThreadsAreValid(qThreads: ForumThreadWithInitialPostFragment[]): void {
     this.votes.forEach(({ asMember, threadId, index }) => {
       const qThread = qThreads.find((t) => t.id === threadId.toString())
       Utils.assert(qThread, 'Query node: Thread not found')
@@ -69,7 +69,7 @@ export class VoteOnPollFixture extends StandardizedFixture {
     )
 
     // Query the threads
-    const qThreads = await this.query.getThreadsWithPostsByIds(this.votes.map((v) => v.threadId))
+    const qThreads = await this.query.getThreadsWithInitialPostsByIds(this.votes.map((v) => v.threadId))
     this.assertQueriedThreadsAreValid(qThreads)
   }
 }

+ 1 - 0
tests/integration-tests/src/fixtures/proposals/CancelProposalsFixture.ts

@@ -43,6 +43,7 @@ export class CancelProposalsFixture extends StandardizedFixture {
       Utils.assert(qProposal, 'Query node: Proposal not found')
       Utils.assert(qProposal.status.__typename === 'ProposalStatusCancelled', 'Invalid proposal status')
       assert.equal(qProposal.status.cancelledInEvent?.id, qEvent.id)
+      assert.equal(qProposal.isFinalized, true)
     })
   }
 

+ 1 - 0
tests/integration-tests/src/fixtures/proposals/CreateProposalsFixture.ts

@@ -316,6 +316,7 @@ export class CreateProposalsFixture extends StandardizedFixture {
       assert.equal(new Date(qProposal.statusSetAtTime).getTime(), e.blockTimestamp)
       assert.equal(qProposal.createdInEvent.inBlock, e.blockNumber)
       assert.equal(qProposal.createdInEvent.inExtrinsic, this.extrinsics[i].hash.toString())
+      assert.equal(qProposal.isFinalized, false)
       this.assertProposalDetailsAreValid(proposalParams, qProposal)
     })
   }

+ 4 - 0
tests/integration-tests/src/fixtures/proposals/DecideOnProposalStatusFixture.ts

@@ -124,12 +124,14 @@ export class DecideOnProposalStatusFixture extends BaseQueryNodeFixture {
       ) {
         Utils.assert(qProposal.status.proposalExecutedEvent?.id, 'Missing proposalExecutedEvent reference')
         assert.equal(qProposal.status.proposalExecutedEvent?.executionStatus.__typename, qProposal.status.__typename)
+        assert.equal(qProposal.isFinalized, true)
       } else if (
         qProposal.status.__typename === 'ProposalStatusDormant' ||
         qProposal.status.__typename === 'ProposalStatusGracing'
       ) {
         Utils.assert(qProposal.status.proposalStatusUpdatedEvent?.id, 'Missing proposalStatusUpdatedEvent reference')
         assert.equal(qProposal.status.proposalStatusUpdatedEvent?.newStatus.__typename, qProposal.status.__typename)
+        assert.equal(qProposal.isFinalized, false)
         assert.include(
           qProposal.proposalStatusUpdates.map((u) => u.id),
           qProposal.status.proposalStatusUpdatedEvent?.id
@@ -137,6 +139,7 @@ export class DecideOnProposalStatusFixture extends BaseQueryNodeFixture {
       } else {
         Utils.assert(qProposal.status.proposalDecisionMadeEvent?.id, 'Missing proposalDecisionMadeEvent reference')
         assert.equal(qProposal.status.proposalDecisionMadeEvent?.decisionStatus.__typename, qProposal.status.__typename)
+        assert.equal(qProposal.isFinalized, true)
       }
     })
   }
@@ -149,6 +152,7 @@ export class DecideOnProposalStatusFixture extends BaseQueryNodeFixture {
       qProposal.status.__typename,
       params.expectExecutionFailure ? 'ProposalStatusExecutionFailed' : 'ProposalStatusExecuted'
     )
+    assert.equal(qProposal.isFinalized, true)
     if (proposal.exactExecutionBlock.isSome) {
       assert.equal(qProposal.statusSetAtBlock, proposal.exactExecutionBlock.unwrap().toNumber())
     } else if (proposal.parameters.gracePeriod.toNumber()) {

+ 1 - 0
tests/integration-tests/src/fixtures/proposals/ExpireProposalsFixture.ts

@@ -37,6 +37,7 @@ export class ExpireProposalsFixture extends BaseQueryNodeFixture {
       )
       Utils.assert(qProposal.status.proposalDecisionMadeEvent, 'Missing proposalDecisionMadeEvent relation')
       assert.equal(qProposal.status.proposalDecisionMadeEvent.decisionStatus.__typename, 'ProposalStatusExpired')
+      assert.equal(qProposal.isFinalized, true)
     })
   }
 

+ 2 - 2
tests/integration-tests/src/flows/proposals/expireProposal.ts

@@ -29,8 +29,8 @@ export default async function expireProposal({ api, query, lock }: FlowProps): P
   await new FixtureRunner(createProposalFixture).run()
   const [proposalId] = createProposalFixture.getCreatedProposalsIds()
 
-  const approveProposalFixture = new ExpireProposalsFixture(api, query, [proposalId])
-  await new FixtureRunner(approveProposalFixture).runWithQueryNodeChecks()
+  const expireProposalFixture = new ExpireProposalsFixture(api, query, [proposalId])
+  await new FixtureRunner(expireProposalFixture).runWithQueryNodeChecks()
 
   unlock()
 

+ 14 - 12
tests/integration-tests/src/graphql/generated/queries.ts

@@ -37,7 +37,7 @@ export type ForumPostFieldsFragment = {
   reactions: Array<{ id: string; reaction: Types.PostReaction; member: { id: string } }>
 }
 
-export type ForumThreadWithPostsFieldsFragment = {
+export type ForumThreadWithInitialPostFragment = {
   id: string
   createdAt: any
   updatedAt?: Types.Maybe<any>
@@ -45,7 +45,7 @@ export type ForumThreadWithPostsFieldsFragment = {
   isSticky: boolean
   author: { id: string }
   category: { id: string }
-  posts: Array<ForumPostFieldsFragment>
+  initialPost?: Types.Maybe<ForumPostFieldsFragment>
   poll?: Types.Maybe<{
     description: string
     endTime: any
@@ -68,11 +68,11 @@ export type GetCategoriesByIdsQueryVariables = Types.Exact<{
 
 export type GetCategoriesByIdsQuery = { forumCategories: Array<ForumCategoryFieldsFragment> }
 
-export type GetThreadsWithPostsByIdsQueryVariables = Types.Exact<{
+export type GetThreadsWithInitialPostsByIdsQueryVariables = Types.Exact<{
   ids?: Types.Maybe<Array<Types.Scalars['ID']> | Types.Scalars['ID']>
 }>
 
-export type GetThreadsWithPostsByIdsQuery = { forumThreads: Array<ForumThreadWithPostsFieldsFragment> }
+export type GetThreadsWithInitialPostsByIdsQuery = { forumThreads: Array<ForumThreadWithInitialPostFragment> }
 
 export type GetPostsByIdsQueryVariables = Types.Exact<{
   ids?: Types.Maybe<Array<Types.Scalars['ID']> | Types.Scalars['ID']>
@@ -162,7 +162,7 @@ export type ThreadMetadataUpdatedEventFieldsFragment = {
   network: Types.Network
   inExtrinsic?: Types.Maybe<string>
   indexInBlock: number
-  newTitle: string
+  newTitle?: Types.Maybe<string>
   thread: { id: string }
 }
 
@@ -988,6 +988,7 @@ export type ProposalFieldsFragment = {
   councilApprovals: number
   statusSetAtBlock: number
   statusSetAtTime: any
+  isFinalized?: Types.Maybe<boolean>
   details:
     | ProposalDetailsFields_SignalProposalDetails_Fragment
     | ProposalDetailsFields_RuntimeUpgradeProposalDetails_Fragment
@@ -1816,8 +1817,8 @@ export const ForumPostFields = gql`
     }
   }
 `
-export const ForumThreadWithPostsFields = gql`
-  fragment ForumThreadWithPostsFields on ForumThread {
+export const ForumThreadWithInitialPost = gql`
+  fragment ForumThreadWithInitialPost on ForumThread {
     id
     createdAt
     updatedAt
@@ -1828,7 +1829,7 @@ export const ForumThreadWithPostsFields = gql`
       id
     }
     title
-    posts {
+    initialPost {
       ...ForumPostFields
     }
     poll {
@@ -2654,6 +2655,7 @@ export const ProposalFields = gql`
     }
     statusSetAtBlock
     statusSetAtTime
+    isFinalized
     createdInEvent {
       id
       inBlock
@@ -3318,13 +3320,13 @@ export const GetCategoriesByIds = gql`
   }
   ${ForumCategoryFields}
 `
-export const GetThreadsWithPostsByIds = gql`
-  query getThreadsWithPostsByIds($ids: [ID!]) {
+export const GetThreadsWithInitialPostsByIds = gql`
+  query getThreadsWithInitialPostsByIds($ids: [ID!]) {
     forumThreads(where: { id_in: $ids }) {
-      ...ForumThreadWithPostsFields
+      ...ForumThreadWithInitialPost
     }
   }
-  ${ForumThreadWithPostsFields}
+  ${ForumThreadWithInitialPost}
 `
 export const GetPostsByIds = gql`
   query getPostsByIds($ids: [ID!]) {

+ 24 - 3
tests/integration-tests/src/graphql/generated/schema.ts

@@ -3386,6 +3386,7 @@ export type ForumPost = BaseGraphQlObject & {
   deletedInEvent?: Maybe<PostDeletedEvent>
   deletedInEventId?: Maybe<Scalars['String']>
   forumpostrepliesTo?: Maybe<Array<ForumPost>>
+  forumthreadinitialPost?: Maybe<Array<ForumThread>>
   postaddedeventpost?: Maybe<Array<PostAddedEvent>>
   postmoderatedeventpost?: Maybe<Array<PostModeratedEvent>>
   postreactedeventpost?: Maybe<Array<PostReactedEvent>>
@@ -3590,6 +3591,9 @@ export type ForumPostWhereInput = {
   forumpostrepliesTo_none?: Maybe<ForumPostWhereInput>
   forumpostrepliesTo_some?: Maybe<ForumPostWhereInput>
   forumpostrepliesTo_every?: Maybe<ForumPostWhereInput>
+  forumthreadinitialPost_none?: Maybe<ForumThreadWhereInput>
+  forumthreadinitialPost_some?: Maybe<ForumThreadWhereInput>
+  forumthreadinitialPost_every?: Maybe<ForumThreadWhereInput>
   postaddedeventpost_none?: Maybe<PostAddedEventWhereInput>
   postaddedeventpost_some?: Maybe<PostAddedEventWhereInput>
   postaddedeventpost_every?: Maybe<PostAddedEventWhereInput>
@@ -3623,6 +3627,8 @@ export type ForumThread = BaseGraphQlObject & {
   /** Thread title */
   title: Scalars['String']
   posts: Array<ForumPost>
+  initialPost?: Maybe<ForumPost>
+  initialPostId?: Maybe<Scalars['String']>
   /** Number of non-deleted posts in the thread */
   visiblePostsCount: Scalars['Int']
   poll?: Maybe<ForumPoll>
@@ -3649,6 +3655,7 @@ export type ForumThreadCreateInput = {
   author: Scalars['ID']
   category: Scalars['ID']
   title: Scalars['String']
+  initialPost?: Maybe<Scalars['ID']>
   visiblePostsCount: Scalars['Float']
   isSticky: Scalars['Boolean']
   status: Scalars['JSONObject']
@@ -3672,6 +3679,8 @@ export enum ForumThreadOrderByInput {
   CategoryDesc = 'category_DESC',
   TitleAsc = 'title_ASC',
   TitleDesc = 'title_DESC',
+  InitialPostAsc = 'initialPost_ASC',
+  InitialPostDesc = 'initialPost_DESC',
   VisiblePostsCountAsc = 'visiblePostsCount_ASC',
   VisiblePostsCountDesc = 'visiblePostsCount_DESC',
   IsStickyAsc = 'isSticky_ASC',
@@ -3688,7 +3697,7 @@ export type ForumThreadTag = BaseGraphQlObject & {
   deletedById?: Maybe<Scalars['String']>
   version: Scalars['Int']
   threads: Array<ForumThread>
-  /** Number of non-deleted threads currently assigned to the tag */
+  /** Number of non-removed threads currently assigned to the tag */
   visibleThreadsCount: Scalars['Int']
 }
 
@@ -3768,6 +3777,7 @@ export type ForumThreadUpdateInput = {
   author?: Maybe<Scalars['ID']>
   category?: Maybe<Scalars['ID']>
   title?: Maybe<Scalars['String']>
+  initialPost?: Maybe<Scalars['ID']>
   visiblePostsCount?: Maybe<Scalars['Float']>
   isSticky?: Maybe<Scalars['Boolean']>
   status?: Maybe<Scalars['JSONObject']>
@@ -3807,6 +3817,8 @@ export type ForumThreadWhereInput = {
   title_startsWith?: Maybe<Scalars['String']>
   title_endsWith?: Maybe<Scalars['String']>
   title_in?: Maybe<Array<Scalars['String']>>
+  initialPost_eq?: Maybe<Scalars['ID']>
+  initialPost_in?: Maybe<Array<Scalars['ID']>>
   visiblePostsCount_eq?: Maybe<Scalars['Int']>
   visiblePostsCount_gt?: Maybe<Scalars['Int']>
   visiblePostsCount_gte?: Maybe<Scalars['Int']>
@@ -3821,6 +3833,7 @@ export type ForumThreadWhereInput = {
   posts_none?: Maybe<ForumPostWhereInput>
   posts_some?: Maybe<ForumPostWhereInput>
   posts_every?: Maybe<ForumPostWhereInput>
+  initialPost?: Maybe<ForumPostWhereInput>
   poll?: Maybe<ForumPollWhereInput>
   createdInEvent?: Maybe<ThreadCreatedEventWhereInput>
   metadataUpdates_none?: Maybe<ThreadMetadataUpdatedEventWhereInput>
@@ -8280,6 +8293,8 @@ export type Proposal = BaseGraphQlObject & {
   votes: Array<ProposalVotedEvent>
   /** Current proposal status */
   status: ProposalStatus
+  /** If true then the proposal status is final and will not change form this point */
+  isFinalized?: Maybe<Scalars['Boolean']>
   /** Number of the block the current status was set at */
   statusSetAtBlock: Scalars['Int']
   /** Time the current status was set at (based on block timestamp) */
@@ -8554,6 +8569,7 @@ export type ProposalCreateInput = {
   exactExecutionBlock?: Maybe<Scalars['Float']>
   councilApprovals: Scalars['Float']
   status: Scalars['JSONObject']
+  isFinalized?: Maybe<Scalars['Boolean']>
   statusSetAtBlock: Scalars['Float']
   statusSetAtTime: Scalars['DateTime']
 }
@@ -9972,6 +9988,8 @@ export enum ProposalOrderByInput {
   ExactExecutionBlockDesc = 'exactExecutionBlock_DESC',
   CouncilApprovalsAsc = 'councilApprovals_ASC',
   CouncilApprovalsDesc = 'councilApprovals_DESC',
+  IsFinalizedAsc = 'isFinalized_ASC',
+  IsFinalizedDesc = 'isFinalized_DESC',
   StatusSetAtBlockAsc = 'statusSetAtBlock_ASC',
   StatusSetAtBlockDesc = 'statusSetAtBlock_DESC',
   StatusSetAtTimeAsc = 'statusSetAtTime_ASC',
@@ -10251,6 +10269,7 @@ export type ProposalUpdateInput = {
   exactExecutionBlock?: Maybe<Scalars['Float']>
   councilApprovals?: Maybe<Scalars['Float']>
   status?: Maybe<Scalars['JSONObject']>
+  isFinalized?: Maybe<Scalars['Boolean']>
   statusSetAtBlock?: Maybe<Scalars['Float']>
   statusSetAtTime?: Maybe<Scalars['DateTime']>
 }
@@ -10487,6 +10506,8 @@ export type ProposalWhereInput = {
   councilApprovals_lte?: Maybe<Scalars['Int']>
   councilApprovals_in?: Maybe<Array<Scalars['Int']>>
   status_json?: Maybe<Scalars['JSONObject']>
+  isFinalized_eq?: Maybe<Scalars['Boolean']>
+  isFinalized_in?: Maybe<Array<Scalars['Boolean']>>
   statusSetAtBlock_eq?: Maybe<Scalars['Int']>
   statusSetAtBlock_gt?: Maybe<Scalars['Int']>
   statusSetAtBlock_gte?: Maybe<Scalars['Int']>
@@ -15522,7 +15543,7 @@ export type ThreadMetadataUpdatedEvent = BaseGraphQlObject & {
   thread: ForumThread
   threadId: Scalars['String']
   /** New title of the thread */
-  newTitle: Scalars['String']
+  newTitle?: Maybe<Scalars['String']>
 }
 
 export type ThreadMetadataUpdatedEventConnection = {
@@ -15537,7 +15558,7 @@ export type ThreadMetadataUpdatedEventCreateInput = {
   network: Network
   indexInBlock: Scalars['Float']
   thread: Scalars['ID']
-  newTitle: Scalars['String']
+  newTitle?: Maybe<Scalars['String']>
 }
 
 export type ThreadMetadataUpdatedEventEdge = {

+ 4 - 4
tests/integration-tests/src/graphql/queries/forum.graphql

@@ -90,7 +90,7 @@ fragment ForumPostFields on ForumPost {
   }
 }
 
-fragment ForumThreadWithPostsFields on ForumThread {
+fragment ForumThreadWithInitialPost on ForumThread {
   id
   createdAt
   updatedAt
@@ -101,7 +101,7 @@ fragment ForumThreadWithPostsFields on ForumThread {
     id
   }
   title
-  posts {
+  initialPost {
     ...ForumPostFields
   }
   poll {
@@ -158,9 +158,9 @@ query getCategoriesByIds($ids: [ID!]) {
   }
 }
 
-query getThreadsWithPostsByIds($ids: [ID!]) {
+query getThreadsWithInitialPostsByIds($ids: [ID!]) {
   forumThreads(where: { id_in: $ids }) {
-    ...ForumThreadWithPostsFields
+    ...ForumThreadWithInitialPost
   }
 }
 

+ 1 - 0
tests/integration-tests/src/graphql/queries/proposals.graphql

@@ -264,6 +264,7 @@ fragment ProposalFields on Proposal {
   }
   statusSetAtBlock
   statusSetAtTime
+  isFinalized
   createdInEvent {
     id
     inBlock