Browse Source

Working group mappings - test all existing groups + required fixes

Leszek Wiesner 3 years ago
parent
commit
795f6bfb08

+ 118 - 1
query-node/manifest.yml

@@ -93,7 +93,7 @@ mappings:
       handler: members_InitialInvitationBalanceUpdated(DatabaseManager, SubstrateEvent)
     - event: members.LeaderInvitationQuotaUpdated
       handler: members_LeaderInvitationQuotaUpdated(DatabaseManager, SubstrateEvent)
-    # Working Groups module
+    # Storage working group
     - event: storageWorkingGroup.OpeningAdded
       handler: workingGroups_OpeningAdded(DatabaseManager, SubstrateEvent)
     - event: storageWorkingGroup.AppliedOnOpening
@@ -132,6 +132,123 @@ mappings:
       handler: workingGroups_StatusTextChanged(DatabaseManager, SubstrateEvent)
     - event: storageWorkingGroup.BudgetSpending
       handler: workingGroups_BudgetSpending(DatabaseManager, SubstrateEvent)
+    # Forum working group
+    - event: forumWorkingGroup.OpeningAdded
+      handler: workingGroups_OpeningAdded(DatabaseManager, SubstrateEvent)
+    - event: forumWorkingGroup.AppliedOnOpening
+      handler: workingGroups_AppliedOnOpening(DatabaseManager, SubstrateEvent)
+    - event: forumWorkingGroup.OpeningFilled
+      handler: workingGroups_OpeningFilled(DatabaseManager, SubstrateEvent)
+    - event: forumWorkingGroup.LeaderSet
+      handler: workingGroups_LeaderSet(DatabaseManager, SubstrateEvent)
+    - event: forumWorkingGroup.WorkerRoleAccountUpdated
+      handler: workingGroups_WorkerRoleAccountUpdated(DatabaseManager, SubstrateEvent)
+    - event: forumWorkingGroup.LeaderUnset
+      handler: workingGroups_LeaderUnset(DatabaseManager, SubstrateEvent)
+    - event: forumWorkingGroup.WorkerExited
+      handler: workingGroups_WorkerExited(DatabaseManager, SubstrateEvent)
+    - event: forumWorkingGroup.TerminatedWorker
+      handler: workingGroups_TerminatedWorker(DatabaseManager, SubstrateEvent)
+    - event: forumWorkingGroup.TerminatedLeader
+      handler: workingGroups_TerminatedLeader(DatabaseManager, SubstrateEvent)
+    - event: forumWorkingGroup.StakeSlashed
+      handler: workingGroups_StakeSlashed(DatabaseManager, SubstrateEvent)
+    - event: forumWorkingGroup.StakeDecreased
+      handler: workingGroups_StakeDecreased(DatabaseManager, SubstrateEvent)
+    - event: forumWorkingGroup.StakeIncreased
+      handler: workingGroups_StakeIncreased(DatabaseManager, SubstrateEvent)
+    - event: forumWorkingGroup.ApplicationWithdrawn
+      handler: workingGroups_ApplicationWithdrawn(DatabaseManager, SubstrateEvent)
+    - event: forumWorkingGroup.OpeningCanceled
+      handler: workingGroups_OpeningCanceled(DatabaseManager, SubstrateEvent)
+    - event: forumWorkingGroup.BudgetSet
+      handler: workingGroups_BudgetSet(DatabaseManager, SubstrateEvent)
+    - event: forumWorkingGroup.WorkerRewardAccountUpdated
+      handler: workingGroups_WorkerRewardAccountUpdated(DatabaseManager, SubstrateEvent)
+    - event: forumWorkingGroup.WorkerRewardAmountUpdated
+      handler: workingGroups_WorkerRewardAmountUpdated(DatabaseManager, SubstrateEvent)
+    - event: forumWorkingGroup.StatusTextChanged
+      handler: workingGroups_StatusTextChanged(DatabaseManager, SubstrateEvent)
+    - event: forumWorkingGroup.BudgetSpending
+      handler: workingGroups_BudgetSpending(DatabaseManager, SubstrateEvent)
+    # Membership working group
+    - event: membershipWorkingGroup.OpeningAdded
+      handler: workingGroups_OpeningAdded(DatabaseManager, SubstrateEvent)
+    - event: membershipWorkingGroup.AppliedOnOpening
+      handler: workingGroups_AppliedOnOpening(DatabaseManager, SubstrateEvent)
+    - event: membershipWorkingGroup.OpeningFilled
+      handler: workingGroups_OpeningFilled(DatabaseManager, SubstrateEvent)
+    - event: membershipWorkingGroup.LeaderSet
+      handler: workingGroups_LeaderSet(DatabaseManager, SubstrateEvent)
+    - event: membershipWorkingGroup.WorkerRoleAccountUpdated
+      handler: workingGroups_WorkerRoleAccountUpdated(DatabaseManager, SubstrateEvent)
+    - event: membershipWorkingGroup.LeaderUnset
+      handler: workingGroups_LeaderUnset(DatabaseManager, SubstrateEvent)
+    - event: membershipWorkingGroup.WorkerExited
+      handler: workingGroups_WorkerExited(DatabaseManager, SubstrateEvent)
+    - event: membershipWorkingGroup.TerminatedWorker
+      handler: workingGroups_TerminatedWorker(DatabaseManager, SubstrateEvent)
+    - event: membershipWorkingGroup.TerminatedLeader
+      handler: workingGroups_TerminatedLeader(DatabaseManager, SubstrateEvent)
+    - event: membershipWorkingGroup.StakeSlashed
+      handler: workingGroups_StakeSlashed(DatabaseManager, SubstrateEvent)
+    - event: membershipWorkingGroup.StakeDecreased
+      handler: workingGroups_StakeDecreased(DatabaseManager, SubstrateEvent)
+    - event: membershipWorkingGroup.StakeIncreased
+      handler: workingGroups_StakeIncreased(DatabaseManager, SubstrateEvent)
+    - event: membershipWorkingGroup.ApplicationWithdrawn
+      handler: workingGroups_ApplicationWithdrawn(DatabaseManager, SubstrateEvent)
+    - event: membershipWorkingGroup.OpeningCanceled
+      handler: workingGroups_OpeningCanceled(DatabaseManager, SubstrateEvent)
+    - event: membershipWorkingGroup.BudgetSet
+      handler: workingGroups_BudgetSet(DatabaseManager, SubstrateEvent)
+    - event: membershipWorkingGroup.WorkerRewardAccountUpdated
+      handler: workingGroups_WorkerRewardAccountUpdated(DatabaseManager, SubstrateEvent)
+    - event: membershipWorkingGroup.WorkerRewardAmountUpdated
+      handler: workingGroups_WorkerRewardAmountUpdated(DatabaseManager, SubstrateEvent)
+    - event: membershipWorkingGroup.StatusTextChanged
+      handler: workingGroups_StatusTextChanged(DatabaseManager, SubstrateEvent)
+    - event: membershipWorkingGroup.BudgetSpending
+      handler: workingGroups_BudgetSpending(DatabaseManager, SubstrateEvent)
+    # Content directory working group
+    - event: contentDirectoryWorkingGroup.OpeningAdded
+      handler: workingGroups_OpeningAdded(DatabaseManager, SubstrateEvent)
+    - event: contentDirectoryWorkingGroup.AppliedOnOpening
+      handler: workingGroups_AppliedOnOpening(DatabaseManager, SubstrateEvent)
+    - event: contentDirectoryWorkingGroup.OpeningFilled
+      handler: workingGroups_OpeningFilled(DatabaseManager, SubstrateEvent)
+    - event: contentDirectoryWorkingGroup.LeaderSet
+      handler: workingGroups_LeaderSet(DatabaseManager, SubstrateEvent)
+    - event: contentDirectoryWorkingGroup.WorkerRoleAccountUpdated
+      handler: workingGroups_WorkerRoleAccountUpdated(DatabaseManager, SubstrateEvent)
+    - event: contentDirectoryWorkingGroup.LeaderUnset
+      handler: workingGroups_LeaderUnset(DatabaseManager, SubstrateEvent)
+    - event: contentDirectoryWorkingGroup.WorkerExited
+      handler: workingGroups_WorkerExited(DatabaseManager, SubstrateEvent)
+    - event: contentDirectoryWorkingGroup.TerminatedWorker
+      handler: workingGroups_TerminatedWorker(DatabaseManager, SubstrateEvent)
+    - event: contentDirectoryWorkingGroup.TerminatedLeader
+      handler: workingGroups_TerminatedLeader(DatabaseManager, SubstrateEvent)
+    - event: contentDirectoryWorkingGroup.StakeSlashed
+      handler: workingGroups_StakeSlashed(DatabaseManager, SubstrateEvent)
+    - event: contentDirectoryWorkingGroup.StakeDecreased
+      handler: workingGroups_StakeDecreased(DatabaseManager, SubstrateEvent)
+    - event: contentDirectoryWorkingGroup.StakeIncreased
+      handler: workingGroups_StakeIncreased(DatabaseManager, SubstrateEvent)
+    - event: contentDirectoryWorkingGroup.ApplicationWithdrawn
+      handler: workingGroups_ApplicationWithdrawn(DatabaseManager, SubstrateEvent)
+    - event: contentDirectoryWorkingGroup.OpeningCanceled
+      handler: workingGroups_OpeningCanceled(DatabaseManager, SubstrateEvent)
+    - event: contentDirectoryWorkingGroup.BudgetSet
+      handler: workingGroups_BudgetSet(DatabaseManager, SubstrateEvent)
+    - event: contentDirectoryWorkingGroup.WorkerRewardAccountUpdated
+      handler: workingGroups_WorkerRewardAccountUpdated(DatabaseManager, SubstrateEvent)
+    - event: contentDirectoryWorkingGroup.WorkerRewardAmountUpdated
+      handler: workingGroups_WorkerRewardAmountUpdated(DatabaseManager, SubstrateEvent)
+    - event: contentDirectoryWorkingGroup.StatusTextChanged
+      handler: workingGroups_StatusTextChanged(DatabaseManager, SubstrateEvent)
+    - event: contentDirectoryWorkingGroup.BudgetSpending
+      handler: workingGroups_BudgetSpending(DatabaseManager, SubstrateEvent)
   extrinsicHandlers:
     # infer defaults here
     #- extrinsic: Balances.Transfer

+ 34 - 25
query-node/mappings/workingGroups.ts

@@ -31,7 +31,6 @@ import {
   OpeningFilledEvent,
   OpeningStatusFilled,
 } from 'query-node/dist/model'
-import { ApplicationId, OpeningId } from '@joystream/types/working-group'
 import { createType } from '@joystream/types'
 
 // Shortcuts
@@ -51,34 +50,37 @@ async function getWorkingGroup(db: DatabaseManager, event_: SubstrateEvent): Pro
 
 async function getOpening(
   db: DatabaseManager,
-  id: OpeningId | string,
+  openingDbId: string,
   relations: string[] = []
 ): Promise<WorkingGroupOpening> {
-  const opening = await db.get(WorkingGroupOpening, { where: { id: id.toString() }, relations })
+  const opening = await db.get(WorkingGroupOpening, { where: { id: openingDbId }, relations })
   if (!opening) {
-    throw new Error(`Opening not found by id ${id.toString()}`)
+    throw new Error(`Opening not found by id ${openingDbId}`)
   }
 
   return opening
 }
 
-async function getApplication(db: DatabaseManager, id: ApplicationId): Promise<WorkingGroupApplication> {
-  const application = await db.get(WorkingGroupApplication, { where: { id: id.toString() } })
+async function getApplication(db: DatabaseManager, applicationDbId: string): Promise<WorkingGroupApplication> {
+  const application = await db.get(WorkingGroupApplication, { where: { id: applicationDbId } })
   if (!application) {
-    throw new Error(`Application not found by id ${id.toString()}`)
+    throw new Error(`Application not found by id ${applicationDbId}`)
   }
 
   return application
 }
 
-async function getApplicationFormQuestions(db: DatabaseManager, openingId: string): Promise<ApplicationFormQuestion[]> {
-  const openingWithQuestions = await getOpening(db, openingId, ['metadata', 'metadata.applicationFormQuestions'])
+async function getApplicationFormQuestions(
+  db: DatabaseManager,
+  openingDbId: string
+): Promise<ApplicationFormQuestion[]> {
+  const openingWithQuestions = await getOpening(db, openingDbId, ['metadata', 'metadata.applicationFormQuestions'])
 
   if (!openingWithQuestions) {
-    throw new Error(`Opening not found by id: ${openingId}`)
+    throw new Error(`Opening not found by id: ${openingDbId}`)
   }
   if (!openingWithQuestions.metadata.applicationFormQuestions) {
-    throw new Error(`Application form questions not found for opening: ${openingId}`)
+    throw new Error(`Application form questions not found for opening: ${openingDbId}`)
   }
   return openingWithQuestions.metadata.applicationFormQuestions
 }
@@ -163,7 +165,7 @@ export async function workingGroups_OpeningAdded(db: DatabaseManager, event_: Su
   const {
     balance: rewardPerBlock,
     bytes: metadataBytes,
-    openingId,
+    openingId: openingRuntimeId,
     openingType,
     stakePolicy,
   } = new WorkingGroups.OpeningAddedEvent(event_).data
@@ -174,7 +176,8 @@ export async function workingGroups_OpeningAdded(db: DatabaseManager, event_: Su
     createdAt: new Date(event_.blockTimestamp.toNumber()),
     updatedAt: new Date(event_.blockTimestamp.toNumber()),
     createdAtBlock: event_.blockNumber,
-    id: openingId.toString(),
+    id: `${group.name}-${openingRuntimeId.toString()}`,
+    runtimeId: openingRuntimeId.toNumber(),
     applications: [],
     group,
     metadata,
@@ -201,9 +204,9 @@ export async function workingGroups_OpeningAdded(db: DatabaseManager, event_: Su
 export async function workingGroups_AppliedOnOpening(db: DatabaseManager, event_: SubstrateEvent): Promise<void> {
   event_.blockTimestamp = new BN(event_.blockTimestamp) // FIXME: Temporary fix for wrong blockTimestamp type
   const {
-    applicationId,
+    applicationId: applicationRuntimeId,
     applyOnOpeningParameters: {
-      opening_id: openingId,
+      opening_id: openingRuntimeId,
       description: metadataBytes,
       member_id: memberId,
       reward_account_id: rewardAccount,
@@ -212,13 +215,15 @@ export async function workingGroups_AppliedOnOpening(db: DatabaseManager, event_
     },
   } = new WorkingGroups.AppliedOnOpeningEvent(event_).data
   const group = await getWorkingGroup(db, event_)
+  const openingDbId = `${group.name}-${openingRuntimeId.toString()}`
 
   const application = new WorkingGroupApplication({
     createdAt: new Date(event_.blockTimestamp.toNumber()),
     updatedAt: new Date(event_.blockTimestamp.toNumber()),
     createdAtBlock: event_.blockNumber,
-    id: applicationId.toString(),
-    opening: new WorkingGroupOpening({ id: openingId.toString() }),
+    id: `${group.name}-${applicationRuntimeId.toString()}`,
+    runtimeId: applicationRuntimeId.toNumber(),
+    opening: new WorkingGroupOpening({ id: openingDbId }),
     applicant: new Membership({ id: memberId.toString() }),
     rewardAccount: rewardAccount.toString(),
     roleAccount: roleAccout.toString(),
@@ -237,7 +242,7 @@ export async function workingGroups_AppliedOnOpening(db: DatabaseManager, event_
     updatedAt: new Date(event_.blockTimestamp.toNumber()),
     event,
     group,
-    opening: new WorkingGroupOpening({ id: openingId.toString() }),
+    opening: new WorkingGroupOpening({ id: openingDbId }),
     application,
   })
 
@@ -248,13 +253,16 @@ export async function workingGroups_AppliedOnOpening(db: DatabaseManager, event_
 export async function workingGroups_OpeningFilled(db: DatabaseManager, event_: SubstrateEvent): Promise<void> {
   event_.blockTimestamp = new BN(event_.blockTimestamp) // FIXME: Temporary fix for wrong blockTimestamp type
   const {
-    openingId,
+    openingId: openingRuntimeId,
     applicationId: applicationIdsSet,
     applicationIdToWorkerIdMap,
   } = new WorkingGroups.OpeningFilledEvent(event_).data
 
   const group = await getWorkingGroup(db, event_)
-  const opening = await getOpening(db, openingId, ['applications', 'applications.applicant'])
+  const opening = await getOpening(db, `${group.name}-${openingRuntimeId.toString()}`, [
+    'applications',
+    'applications.applicant',
+  ])
   const acceptedApplicationIds = createType('Vec<ApplicationId>', applicationIdsSet.toHex() as any)
 
   // Save the event
@@ -273,16 +281,16 @@ export async function workingGroups_OpeningFilled(db: DatabaseManager, event_: S
   // Update applications and create new workers
   await Promise.all(
     (opening.applications || []).map(async (application) => {
-      const isAccepted = acceptedApplicationIds.some((id) => id.toString() === application.id)
+      const isAccepted = acceptedApplicationIds.some((runtimeId) => runtimeId.toNumber() === application.runtimeId)
       application.status = isAccepted ? new ApplicationStatusAccepted() : new ApplicationStatusRejected()
       if (isAccepted) {
         // Cannot use "applicationIdToWorkerIdMap.get" here,
         // it only works if the passed instance is identical to BTreeMap key instance (=== instead of .eq)
-        const [, workerId] =
+        const [, workerRuntimeId] =
           Array.from(applicationIdToWorkerIdMap.entries()).find(
-            ([applicationId]) => applicationId.toString() === application.id
+            ([applicationRuntimeId]) => applicationRuntimeId.toNumber() === application.runtimeId
           ) || []
-        if (!workerId) {
+        if (!workerRuntimeId) {
           throw new Error(
             `Fatal: No worker id found by accepted application id ${application.id} when handling OpeningFilled event!`
           )
@@ -290,7 +298,8 @@ export async function workingGroups_OpeningFilled(db: DatabaseManager, event_: S
         const worker = new Worker({
           createdAt: new Date(event_.blockTimestamp.toNumber()),
           updatedAt: new Date(event_.blockTimestamp.toNumber()),
-          id: workerId.toString(),
+          id: `${group.name}-${workerRuntimeId.toString()}`,
+          runtimeId: workerRuntimeId.toNumber(),
           hiredAtBlock: event_.blockNumber,
           hiredAtTime: new Date(event_.blockTimestamp.toNumber()),
           application,

+ 14 - 5
query-node/schemas/workingGroups.graphql

@@ -24,6 +24,12 @@ union WorkerStatus = WorkerStatusActive | WorkerStatusLeft | WorkerStatusTermina
 
 # Working Groups
 type Worker @entity {
+  "Worker id ({workingGroupName}-{workerId})"
+  id: ID!
+
+  "WorkerId in specific working group module"
+  runtimeId: Int!
+
   "The group that the worker belongs to"
   group: WorkingGroup!
 
@@ -89,9 +95,6 @@ type WorkingGroupMetadata @entity {
 }
 
 type WorkingGroup @entity {
-  "Working group runtime id"
-  id: ID!
-
   "Working group name"
   name: String!
 
@@ -156,9 +159,12 @@ type WorkingGroupOpeningMetadata @entity {
 }
 
 type WorkingGroupOpening @entity {
-  "Opening runtime id"
+  "Opening id ({workingGroupName}-{openingId})"
   id: ID!
 
+  "OpeningId in specific working group module"
+  runtimeId: Int!
+
   "Related working group"
   group: WorkingGroup!
 
@@ -217,9 +223,12 @@ union WorkingGroupApplicationStatus =
   | ApplicationStatusWithdrawn
 
 type WorkingGroupApplication @entity {
-  "Application runtime id"
+  "Application id ({workingGroupName}-{applicationId})"
   id: ID!
 
+  "ApplicationId in specific working group module"
+  runtimeId: Int!
+
   "Related working group opening"
   opening: WorkingGroupOpening!
 

+ 21 - 6
tests/integration-tests/src/QueryNodeApi.ts

@@ -13,6 +13,7 @@ import {
 } from './QueryNodeApiSchema.generated'
 import Debugger from 'debug'
 import { ApplicationId, OpeningId } from '@joystream/types/working-group'
+import { WorkingGroupModuleName } from './types'
 
 export class QueryNodeApi {
   private readonly queryNodeProvider: ApolloClient<NormalizedCacheObject>
@@ -497,17 +498,20 @@ export class QueryNodeApi {
   }
 
   public async getOpeningById(
-    id: OpeningId
+    id: OpeningId,
+    group: WorkingGroupModuleName
   ): Promise<ApolloQueryResult<Pick<Query, 'workingGroupOpeningByUniqueInput'>>> {
     const OPENING_BY_ID = gql`
       query($openingId: ID!) {
         workingGroupOpeningByUniqueInput(where: { id: $openingId }) {
           id
+          runtimeId
           group {
             name
           }
           applications {
             id
+            runtimeId
             status {
               __typename
             }
@@ -537,25 +541,29 @@ export class QueryNodeApi {
       }
     `
 
-    this.queryDebug(`Executing getOpeningById(${id.toString()})`)
+    const openingId = `${group}-${id.toString()}`
+    this.queryDebug(`Executing getOpeningById(${openingId})`)
 
     return this.queryNodeProvider.query<Pick<Query, 'workingGroupOpeningByUniqueInput'>>({
       query: OPENING_BY_ID,
-      variables: { openingId: id.toString() },
+      variables: { openingId },
     })
   }
 
   public async getApplicationById(
-    id: ApplicationId
+    id: ApplicationId,
+    group: WorkingGroupModuleName
   ): Promise<ApolloQueryResult<Pick<Query, 'workingGroupApplicationByUniqueInput'>>> {
     const APPLICATION_BY_ID = gql`
       query($applicationId: ID!) {
         workingGroupApplicationByUniqueInput(where: { id: $applicationId }) {
           id
+          runtimeId
           createdAtBlock
           createdAt
           opening {
             id
+            runtimeId
           }
           applicant {
             id
@@ -577,11 +585,12 @@ export class QueryNodeApi {
       }
     `
 
-    this.queryDebug(`Executing getApplicationById(${id.toString()})`)
+    const applicationId = `${group}-${id.toString()}`
+    this.queryDebug(`Executing getApplicationById(${applicationId})`)
 
     return this.queryNodeProvider.query<Pick<Query, 'workingGroupApplicationByUniqueInput'>>({
       query: APPLICATION_BY_ID,
-      variables: { applicationId: id.toString() },
+      variables: { applicationId },
     })
   }
 
@@ -603,9 +612,11 @@ export class QueryNodeApi {
           }
           opening {
             id
+            runtimeId
           }
           application {
             id
+            runtimeId
           }
         }
       }
@@ -637,6 +648,7 @@ export class QueryNodeApi {
           }
           opening {
             id
+            runtimeId
           }
         }
       }
@@ -671,9 +683,11 @@ export class QueryNodeApi {
           }
           opening {
             id
+            runtimeId
           }
           workersHired {
             id
+            runtimeId
             group {
               name
             }
@@ -695,6 +709,7 @@ export class QueryNodeApi {
             hiredAtTime
             application {
               id
+              runtimeId
             }
             storage
           }

+ 36 - 0
tests/integration-tests/src/QueryNodeApiSchema.generated.ts

@@ -5879,6 +5879,8 @@ export type Worker = BaseGraphQlObject & {
   deletedAt?: Maybe<Scalars['DateTime']>
   deletedById?: Maybe<Scalars['String']>
   version: Scalars['Int']
+  /** WorkerId in specific working group module */
+  runtimeId: Scalars['Int']
   group: WorkingGroup
   groupId: Scalars['String']
   membership: Membership
@@ -5928,6 +5930,7 @@ export type WorkerConnection = {
 }
 
 export type WorkerCreateInput = {
+  runtimeId: Scalars['Float']
   groupId: Scalars['ID']
   membershipId: Scalars['ID']
   roleAccount: Scalars['String']
@@ -6051,6 +6054,8 @@ export enum WorkerOrderByInput {
   UpdatedAtDesc = 'updatedAt_DESC',
   DeletedAtAsc = 'deletedAt_ASC',
   DeletedAtDesc = 'deletedAt_DESC',
+  RuntimeIdAsc = 'runtimeId_ASC',
+  RuntimeIdDesc = 'runtimeId_DESC',
   GroupIdAsc = 'groupId_ASC',
   GroupIdDesc = 'groupId_DESC',
   MembershipIdAsc = 'membershipId_ASC',
@@ -6674,6 +6679,7 @@ export type WorkerStatusTerminatedWhereUniqueInput = {
 }
 
 export type WorkerUpdateInput = {
+  runtimeId?: Maybe<Scalars['Float']>
   groupId?: Maybe<Scalars['ID']>
   membershipId?: Maybe<Scalars['ID']>
   roleAccount?: Maybe<Scalars['String']>
@@ -6714,6 +6720,12 @@ export type WorkerWhereInput = {
   deletedAt_gte?: Maybe<Scalars['DateTime']>
   deletedById_eq?: Maybe<Scalars['ID']>
   deletedById_in?: Maybe<Array<Scalars['ID']>>
+  runtimeId_eq?: Maybe<Scalars['Int']>
+  runtimeId_gt?: Maybe<Scalars['Int']>
+  runtimeId_gte?: Maybe<Scalars['Int']>
+  runtimeId_lt?: Maybe<Scalars['Int']>
+  runtimeId_lte?: Maybe<Scalars['Int']>
+  runtimeId_in?: Maybe<Array<Scalars['Int']>>
   groupId_eq?: Maybe<Scalars['ID']>
   groupId_in?: Maybe<Array<Scalars['ID']>>
   membershipId_eq?: Maybe<Scalars['ID']>
@@ -6820,6 +6832,8 @@ export type WorkingGroupApplication = BaseGraphQlObject & {
   deletedAt?: Maybe<Scalars['DateTime']>
   deletedById?: Maybe<Scalars['String']>
   version: Scalars['Int']
+  /** ApplicationId in specific working group module */
+  runtimeId: Scalars['Int']
   opening: WorkingGroupOpening
   openingId: Scalars['String']
   applicant: Membership
@@ -6851,6 +6865,7 @@ export type WorkingGroupApplicationConnection = {
 
 export type WorkingGroupApplicationCreateInput = {
   createdAt: Scalars['DateTime']
+  runtimeId: Scalars['Float']
   openingId: Scalars['ID']
   applicantId: Scalars['ID']
   stake: Scalars['BigInt']
@@ -6874,6 +6889,8 @@ export enum WorkingGroupApplicationOrderByInput {
   UpdatedAtDesc = 'updatedAt_DESC',
   DeletedAtAsc = 'deletedAt_ASC',
   DeletedAtDesc = 'deletedAt_DESC',
+  RuntimeIdAsc = 'runtimeId_ASC',
+  RuntimeIdDesc = 'runtimeId_DESC',
   OpeningIdAsc = 'openingId_ASC',
   OpeningIdDesc = 'openingId_DESC',
   ApplicantIdAsc = 'applicantId_ASC',
@@ -6898,6 +6915,7 @@ export type WorkingGroupApplicationStatus =
 
 export type WorkingGroupApplicationUpdateInput = {
   createdAt?: Maybe<Scalars['DateTime']>
+  runtimeId?: Maybe<Scalars['Float']>
   openingId?: Maybe<Scalars['ID']>
   applicantId?: Maybe<Scalars['ID']>
   stake?: Maybe<Scalars['BigInt']>
@@ -6933,6 +6951,12 @@ export type WorkingGroupApplicationWhereInput = {
   deletedAt_gte?: Maybe<Scalars['DateTime']>
   deletedById_eq?: Maybe<Scalars['ID']>
   deletedById_in?: Maybe<Array<Scalars['ID']>>
+  runtimeId_eq?: Maybe<Scalars['Int']>
+  runtimeId_gt?: Maybe<Scalars['Int']>
+  runtimeId_gte?: Maybe<Scalars['Int']>
+  runtimeId_lt?: Maybe<Scalars['Int']>
+  runtimeId_lte?: Maybe<Scalars['Int']>
+  runtimeId_in?: Maybe<Array<Scalars['Int']>>
   openingId_eq?: Maybe<Scalars['ID']>
   openingId_in?: Maybe<Array<Scalars['ID']>>
   applicantId_eq?: Maybe<Scalars['ID']>
@@ -7142,6 +7166,8 @@ export type WorkingGroupOpening = BaseGraphQlObject & {
   deletedAt?: Maybe<Scalars['DateTime']>
   deletedById?: Maybe<Scalars['String']>
   version: Scalars['Int']
+  /** OpeningId in specific working group module */
+  runtimeId: Scalars['Int']
   group: WorkingGroup
   groupId: Scalars['String']
   applications: Array<WorkingGroupApplication>
@@ -7174,6 +7200,7 @@ export type WorkingGroupOpeningConnection = {
 
 export type WorkingGroupOpeningCreateInput = {
   createdAt: Scalars['DateTime']
+  runtimeId: Scalars['Float']
   groupId: Scalars['ID']
   type: WorkingGroupOpeningType
   status: Scalars['JSONObject']
@@ -7326,6 +7353,8 @@ export enum WorkingGroupOpeningOrderByInput {
   UpdatedAtDesc = 'updatedAt_DESC',
   DeletedAtAsc = 'deletedAt_ASC',
   DeletedAtDesc = 'deletedAt_DESC',
+  RuntimeIdAsc = 'runtimeId_ASC',
+  RuntimeIdDesc = 'runtimeId_DESC',
   GroupIdAsc = 'groupId_ASC',
   GroupIdDesc = 'groupId_DESC',
   TypeAsc = 'type_ASC',
@@ -7351,6 +7380,7 @@ export enum WorkingGroupOpeningType {
 
 export type WorkingGroupOpeningUpdateInput = {
   createdAt?: Maybe<Scalars['DateTime']>
+  runtimeId?: Maybe<Scalars['Float']>
   groupId?: Maybe<Scalars['ID']>
   type?: Maybe<WorkingGroupOpeningType>
   status?: Maybe<Scalars['JSONObject']>
@@ -7386,6 +7416,12 @@ export type WorkingGroupOpeningWhereInput = {
   deletedAt_gte?: Maybe<Scalars['DateTime']>
   deletedById_eq?: Maybe<Scalars['ID']>
   deletedById_in?: Maybe<Array<Scalars['ID']>>
+  runtimeId_eq?: Maybe<Scalars['Int']>
+  runtimeId_gt?: Maybe<Scalars['Int']>
+  runtimeId_gte?: Maybe<Scalars['Int']>
+  runtimeId_lt?: Maybe<Scalars['Int']>
+  runtimeId_lte?: Maybe<Scalars['Int']>
+  runtimeId_in?: Maybe<Array<Scalars['Int']>>
   groupId_eq?: Maybe<Scalars['ID']>
   groupId_in?: Maybe<Array<Scalars['ID']>>
   type_eq?: Maybe<WorkingGroupOpeningType>

+ 13 - 13
tests/integration-tests/src/fixtures/workingGroupsModule.ts

@@ -125,7 +125,7 @@ export class SudoCreateLeadOpeningFixture extends BaseFixture {
     if (!qOpening) {
       throw new Error('Query node: Opening not found')
     }
-    assert.equal(qOpening.id, eventDetails.openingId.toString())
+    assert.equal(qOpening.runtimeId, eventDetails.openingId.toNumber())
     assert.equal(qOpening.createdAtBlock, eventDetails.blockNumber)
     assert.equal(qOpening.group.name, this.group)
     assert.equal(qOpening.rewardPerBlock, this.openingParams.reward.toString())
@@ -164,7 +164,7 @@ export class SudoCreateLeadOpeningFixture extends BaseFixture {
     assert.equal(qEvent.event.inExtrinsic, txHash)
     assert.equal(qEvent.event.type, EventType.OpeningAdded)
     assert.equal(qEvent.group.name, this.group)
-    assert.equal(qEvent.opening.id, eventDetails.openingId.toString())
+    assert.equal(qEvent.opening.runtimeId, eventDetails.openingId.toNumber())
   }
 
   async execute(): Promise<void> {
@@ -189,7 +189,7 @@ export class SudoCreateLeadOpeningFixture extends BaseFixture {
     const tx = this.tx!
     // Query the opening
     await this.query.tryQueryWithTimeout(
-      () => this.query.getOpeningById(eventDetails.openingId),
+      () => this.query.getOpeningById(eventDetails.openingId, this.group),
       (r) => this.assertOpeningMatchQueriedResult(eventDetails, r.data.workingGroupOpeningByUniqueInput)
     )
     // Query the event
@@ -255,9 +255,9 @@ export class ApplyOnOpeningHappyCaseFixture extends BaseFixture {
     if (!qApplication) {
       throw new Error('Application not found')
     }
-    assert.equal(qApplication.id, eventDetails.applicationId.toString())
+    assert.equal(qApplication.runtimeId, eventDetails.applicationId.toNumber())
     assert.equal(qApplication.createdAtBlock, eventDetails.blockNumber)
-    assert.equal(qApplication.opening.id, this.openingId.toString())
+    assert.equal(qApplication.opening.runtimeId, this.openingId.toNumber())
     assert.equal(qApplication.applicant.id, this.applicant.memberId.toString())
     assert.equal(qApplication.roleAccount, this.applicant.account)
     assert.equal(qApplication.rewardAccount, this.applicant.account)
@@ -286,8 +286,8 @@ export class ApplyOnOpeningHappyCaseFixture extends BaseFixture {
     assert.equal(qEvent.event.inExtrinsic, txHash)
     assert.equal(qEvent.event.type, EventType.AppliedOnOpening)
     assert.equal(qEvent.group.name, this.group)
-    assert.equal(qEvent.opening.id, this.openingId.toString())
-    assert.equal(qEvent.application.id, eventDetails.applicationId.toString())
+    assert.equal(qEvent.opening.runtimeId, this.openingId.toNumber())
+    assert.equal(qEvent.application.runtimeId, eventDetails.applicationId.toNumber())
   }
 
   async execute(): Promise<void> {
@@ -321,7 +321,7 @@ export class ApplyOnOpeningHappyCaseFixture extends BaseFixture {
     const tx = this.tx!
     // Query the application
     await this.query.tryQueryWithTimeout(
-      () => this.query.getApplicationById(eventDetails.applicationId),
+      () => this.query.getApplicationById(eventDetails.applicationId, this.group),
       (r) => this.assertApplicationMatchQueriedResult(eventDetails, r.data.workingGroupApplicationByUniqueInput)
     )
     // Query the event
@@ -390,7 +390,7 @@ export class SudoFillLeadOpening extends BaseFixture {
     }
     assert.equal(qEvent.event.inExtrinsic, txHash)
     assert.equal(qEvent.event.type, EventType.OpeningFilled)
-    assert.equal(qEvent.opening.id, this.openingId.toString())
+    assert.equal(qEvent.opening.runtimeId, this.openingId.toNumber())
     assert.equal(qEvent.group.name, this.group)
     this.acceptedApplicationIds.forEach((acceptedApplId, i) => {
       // Cannot use "applicationIdToWorkerIdMap.get" here,
@@ -402,7 +402,7 @@ export class SudoFillLeadOpening extends BaseFixture {
       if (!workerId) {
         throw new Error(`WorkerId for application id ${acceptedApplId.toString()} not found in OpeningFilled event!`)
       }
-      const qWorker = qEvent.workersHired.find((w) => w.id === workerId.toString())
+      const qWorker = qEvent.workersHired.find((w) => w.runtimeId === workerId.toNumber())
       if (!qWorker) {
         throw new Error(`Query node: Worker not found in OpeningFilled.hiredWorkers (id: ${workerId.toString()})`)
       }
@@ -432,7 +432,7 @@ export class SudoFillLeadOpening extends BaseFixture {
     assert.equal(qWorker.isLead, true)
     assert.equal(qWorker.stake, applicationStake.toString())
     assert.equal(qWorker.hiredAtBlock, eventDetails.blockNumber)
-    assert.equal(qWorker.application.id, applicationId.toString())
+    assert.equal(qWorker.application.runtimeId, applicationId.toNumber())
   }
 
   async runQueryNodeChecks(): Promise<void> {
@@ -448,7 +448,7 @@ export class SudoFillLeadOpening extends BaseFixture {
     // Check opening status
     const {
       data: { workingGroupOpeningByUniqueInput: qOpening },
-    } = await this.query.getOpeningById(this.openingId)
+    } = await this.query.getOpeningById(this.openingId, this.group)
     if (!qOpening) {
       throw new Error(`Query node: Opening ${this.openingId.toString()} not found!`)
     }
@@ -456,7 +456,7 @@ export class SudoFillLeadOpening extends BaseFixture {
 
     // Check application statuses
     const acceptedApplications = this.acceptedApplicationIds.map((id) => {
-      const application = qOpening.applications.find((a) => a.id === id.toString())
+      const application = qOpening.applications.find((a) => a.runtimeId === id.toNumber())
       if (!application) {
         throw new Error(`Application not found by id ${id.toString()} in opening ${qOpening.id}`)
       }

+ 49 - 44
tests/integration-tests/src/flows/working-groups/leadOpening.ts

@@ -8,53 +8,58 @@ import {
 import Debugger from 'debug'
 import { FixtureRunner } from '../../Fixture'
 import { AddStakingAccountsHappyCaseFixture, BuyMembershipHappyCaseFixture } from '../../fixtures/membershipModule'
+import { workingGroups } from '../../types'
 
 export default async function leadOpening({ api, query, env }: FlowProps): Promise<void> {
-  const debug = Debugger('flow:lead-opening')
-  debug('Started')
-  api.enableDebugTxLogs()
-
-  const sudoLeadOpeningFixture = new SudoCreateLeadOpeningFixture(api, query, 'storageWorkingGroup')
-  const openingRunner = new FixtureRunner(sudoLeadOpeningFixture)
-  await openingRunner.run()
-  const openingId = sudoLeadOpeningFixture.getCreatedOpeningId()
-  const openingParams = sudoLeadOpeningFixture.getDefaultOpeningParams()
-
-  const [applicantAcc, stakingAcc] = (await api.createKeyPairs(2)).map((kp) => kp.address)
-  const buyMembershipFixture = new BuyMembershipHappyCaseFixture(api, query, [applicantAcc])
-  await new FixtureRunner(buyMembershipFixture).run()
-  const [applicantMemberId] = buyMembershipFixture.getCreatedMembers()
-
-  const applicantContext = {
-    account: applicantAcc,
-    memberId: applicantMemberId,
-  }
-
-  const addStakingAccFixture = new AddStakingAccountsHappyCaseFixture(api, query, applicantContext, [stakingAcc])
-  await new FixtureRunner(addStakingAccFixture).run()
-
-  await api.treasuryTransferBalance(stakingAcc, openingParams.stake)
-
-  const applyOnOpeningFixture = new ApplyOnOpeningHappyCaseFixture(
-    api,
-    query,
-    'storageWorkingGroup',
-    applicantContext,
-    stakingAcc,
-    openingId,
-    openingParams.metadata
-  )
-  const applicationRunner = new FixtureRunner(applyOnOpeningFixture)
-  await applicationRunner.run()
-  const applicationId = applyOnOpeningFixture.getCreatedApplicationId()
+  await Promise.all(
+    workingGroups.map(async (group) => {
+      const debug = Debugger(`flow:lead-opening:${group}`)
+      debug('Started')
+      api.enableDebugTxLogs()
+
+      const sudoLeadOpeningFixture = new SudoCreateLeadOpeningFixture(api, query, group)
+      const openingRunner = new FixtureRunner(sudoLeadOpeningFixture)
+      await openingRunner.run()
+      const openingId = sudoLeadOpeningFixture.getCreatedOpeningId()
+      const openingParams = sudoLeadOpeningFixture.getDefaultOpeningParams()
+
+      const [applicantAcc, stakingAcc] = (await api.createKeyPairs(2)).map((kp) => kp.address)
+      const buyMembershipFixture = new BuyMembershipHappyCaseFixture(api, query, [applicantAcc])
+      await new FixtureRunner(buyMembershipFixture).run()
+      const [applicantMemberId] = buyMembershipFixture.getCreatedMembers()
+
+      const applicantContext = {
+        account: applicantAcc,
+        memberId: applicantMemberId,
+      }
 
-  // Run query node checks once this part of the flow is done
-  await openingRunner.runQueryNodeChecks()
-  await applicationRunner.runQueryNodeChecks()
+      const addStakingAccFixture = new AddStakingAccountsHappyCaseFixture(api, query, applicantContext, [stakingAcc])
+      await new FixtureRunner(addStakingAccFixture).run()
 
-  // Fill opening
-  const fillOpeningFixture = new SudoFillLeadOpening(api, query, 'storageWorkingGroup', openingId, [applicationId])
-  await new FixtureRunner(fillOpeningFixture).runWithQueryNodeChecks()
+      await api.treasuryTransferBalance(stakingAcc, openingParams.stake)
 
-  debug('Done')
+      const applyOnOpeningFixture = new ApplyOnOpeningHappyCaseFixture(
+        api,
+        query,
+        group,
+        applicantContext,
+        stakingAcc,
+        openingId,
+        openingParams.metadata
+      )
+      const applicationRunner = new FixtureRunner(applyOnOpeningFixture)
+      await applicationRunner.run()
+      const applicationId = applyOnOpeningFixture.getCreatedApplicationId()
+
+      // Run query node checks once this part of the flow is done
+      await openingRunner.runQueryNodeChecks()
+      await applicationRunner.runQueryNodeChecks()
+
+      // Fill opening
+      const fillOpeningFixture = new SudoFillLeadOpening(api, query, group, openingId, [applicationId])
+      await new FixtureRunner(fillOpeningFixture).runWithQueryNodeChecks()
+
+      debug('Done')
+    })
+  )
 }

+ 7 - 0
tests/integration-tests/src/types.ts

@@ -81,6 +81,13 @@ export type WorkingGroupModuleName =
   | 'forumWorkingGroup'
   | 'membershipWorkingGroup'
 
+export const workingGroups: WorkingGroupModuleName[] = [
+  'storageWorkingGroup',
+  'contentDirectoryWorkingGroup',
+  'forumWorkingGroup',
+  'membershipWorkingGroup',
+]
+
 export const lockIdByWorkingGroup: { [K in WorkingGroupModuleName]: string } = {
   storageWorkingGroup: '0x0606060606060606',
   contentDirectoryWorkingGroup: '0x0707070707070707',