workingGroups.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. import { Option } from '@polkadot/types/';
  2. import { Balance } from '@polkadot/types/interfaces';
  3. import BaseTransport from './base';
  4. import { ApiPromise } from '@polkadot/api';
  5. import MembersTransport from './members';
  6. import { SingleLinkedMapEntry } from '../index';
  7. import { Worker, WorkerId, Opening as WGOpening, Application as WGApplication, OpeningTypeKey } from '@joystream/types/working-group';
  8. import { apiModuleByGroup } from '../consts/workingGroups';
  9. import { WorkingGroupKey } from '@joystream/types/common';
  10. import { WorkerData, OpeningData, ParsedApplication } from '../types/workingGroups';
  11. import { OpeningId, ApplicationId, Opening, Application, ActiveOpeningStageKey } from '@joystream/types/hiring';
  12. import { MultipleLinkedMapEntry } from '../LinkedMapEntry';
  13. import { Stake, StakeId } from '@joystream/types/stake';
  14. import { RewardRelationshipId, RewardRelationship } from '@joystream/types/recurring-rewards';
  15. import { APIQueryCache } from '../APIQueryCache';
  16. export default class WorkingGroupsTransport extends BaseTransport {
  17. private membersT: MembersTransport;
  18. constructor (api: ApiPromise, cacheApi: APIQueryCache, membersTransport: MembersTransport) {
  19. super(api, cacheApi);
  20. this.membersT = membersTransport;
  21. }
  22. protected queryByGroup (group: WorkingGroupKey) {
  23. const module = apiModuleByGroup[group];
  24. return this.cacheApi.query[module];
  25. }
  26. public async groupMemberById (group: WorkingGroupKey, workerId: number): Promise<WorkerData | null> {
  27. const workerLink = new SingleLinkedMapEntry(
  28. Worker,
  29. await this.queryByGroup(group).workerById(workerId)
  30. );
  31. const worker = workerLink.value;
  32. if (!worker.is_active) {
  33. return null;
  34. }
  35. const stake = worker.role_stake_profile.isSome
  36. ? (await this.stakeValue(worker.role_stake_profile.unwrap().stake_id)).toNumber()
  37. : undefined;
  38. const reward = worker.reward_relationship.isSome
  39. ? (await this.rewardRelationship(worker.reward_relationship.unwrap()))
  40. : undefined;
  41. const profile = await this.membersT.expectedMembership(worker.member_id);
  42. return { group, workerId, worker, profile, stake, reward };
  43. }
  44. public async currentLead (group: WorkingGroupKey): Promise<WorkerData | null> {
  45. const optLeadId = (await this.queryByGroup(group).currentLead()) as Option<WorkerId>;
  46. if (!optLeadId.isSome) {
  47. return null;
  48. }
  49. const leadWorkerId = optLeadId.unwrap().toNumber();
  50. return this.groupMemberById(group, leadWorkerId);
  51. }
  52. public async allOpenings (group: WorkingGroupKey, type?: OpeningTypeKey): Promise<OpeningData[]> {
  53. const nextId = (await this.queryByGroup(group).nextOpeningId()) as OpeningId;
  54. if (nextId.eq(0)) {
  55. return [];
  56. }
  57. const query = this.queryByGroup(group).openingById();
  58. const result = new MultipleLinkedMapEntry(OpeningId, WGOpening, await query);
  59. const { linked_keys: openingIds, linked_values: openings } = result;
  60. return (await Promise.all(openings.map(opening => this.hiring.openingById(opening.hiring_opening_id))))
  61. .map((hiringOpeningRes, index) => {
  62. const id = openingIds[index];
  63. const opening = openings[index];
  64. const hiringOpening = (new SingleLinkedMapEntry(Opening, hiringOpeningRes)).value;
  65. return { id, opening, hiringOpening };
  66. })
  67. .filter(openingData => !type || openingData.opening.opening_type.isOfType(type));
  68. }
  69. public async activeOpenings (group: WorkingGroupKey, substage?: ActiveOpeningStageKey, type?: OpeningTypeKey) {
  70. return (await this.allOpenings(group, type))
  71. .filter(od =>
  72. od.hiringOpening.stage.isOfType('Active') &&
  73. (!substage || od.hiringOpening.stage.asType('Active').stage.isOfType(substage))
  74. );
  75. }
  76. async wgApplicationById (group: WorkingGroupKey, wgApplicationId: number | ApplicationId): Promise<WGApplication> {
  77. const nextAppId = await this.queryByGroup(group).nextApplicationId() as ApplicationId;
  78. if (wgApplicationId < 0 || wgApplicationId >= nextAppId.toNumber()) {
  79. throw new Error(`Invalid working group application ID (${wgApplicationId})!`);
  80. }
  81. return new SingleLinkedMapEntry(
  82. WGApplication,
  83. await this.queryByGroup(group).applicationById(wgApplicationId)
  84. ).value;
  85. }
  86. protected async hiringApplicationById (id: number | ApplicationId): Promise<Application> {
  87. return new SingleLinkedMapEntry(
  88. Application,
  89. await this.hiring.applicationById(id)
  90. ).value;
  91. }
  92. protected async stakeValue (stakeId: StakeId): Promise<Balance> {
  93. return new SingleLinkedMapEntry(
  94. Stake,
  95. await this.stake.stakes(stakeId)
  96. ).value.value;
  97. }
  98. protected async rewardRelationship (relationshipId: RewardRelationshipId): Promise<RewardRelationship> {
  99. return new SingleLinkedMapEntry(
  100. RewardRelationship,
  101. await this.recurringRewards.rewardRelationships(relationshipId)
  102. ).value;
  103. }
  104. protected async parseApplication (wgApplicationId: number, wgApplication: WGApplication): Promise<ParsedApplication> {
  105. const appId = wgApplication.application_id;
  106. const application = await this.hiringApplicationById(appId);
  107. const { active_role_staking_id: roleStakingId, active_application_staking_id: appStakingId } = application;
  108. return {
  109. wgApplicationId,
  110. applicationId: appId.toNumber(),
  111. member: await this.membersT.expectedMembership(wgApplication.member_id),
  112. roleAccout: wgApplication.role_account_id,
  113. stakes: {
  114. application: appStakingId.isSome ? (await this.stakeValue(appStakingId.unwrap())).toNumber() : 0,
  115. role: roleStakingId.isSome ? (await this.stakeValue(roleStakingId.unwrap())).toNumber() : 0
  116. },
  117. humanReadableText: application.human_readable_text.toString(),
  118. stage: application.stage
  119. };
  120. }
  121. async parsedApplicationById (group: WorkingGroupKey, wgApplicationId: number): Promise<ParsedApplication> {
  122. const wgApplication = await this.wgApplicationById(group, wgApplicationId);
  123. return this.parseApplication(wgApplicationId, wgApplication);
  124. }
  125. async openingApplications (group: WorkingGroupKey, wgOpeningId: number): Promise<ParsedApplication[]> {
  126. const applications: ParsedApplication[] = [];
  127. const nextAppId = await this.queryByGroup(group).nextApplicationId() as ApplicationId;
  128. for (let i = 0; i < nextAppId.toNumber(); i++) {
  129. const wgApplication = await this.wgApplicationById(group, i);
  130. if (wgApplication.opening_id.toNumber() !== wgOpeningId) {
  131. continue;
  132. }
  133. applications.push(await this.parseApplication(i, wgApplication));
  134. }
  135. return applications;
  136. }
  137. async openingActiveApplications (group: WorkingGroupKey, wgOpeningId: number): Promise<ParsedApplication[]> {
  138. return (await this.openingApplications(group, wgOpeningId))
  139. .filter(a => a.stage.isOfType('Active'));
  140. }
  141. }