index.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. import { getTypeRegistry, Null, u128, u64, u32, Vec, Option, Text } from '@polkadot/types';
  2. import { Enum } from '@polkadot/types/codec';
  3. import { BlockNumber, Balance } from '@polkadot/types/interfaces';
  4. import { JoyStruct, JoyEnum } from '../common';
  5. import { StakeId } from '../stake';
  6. import { GenericJoyStreamRoleSchema } from './schemas/role.schema.typings'
  7. import ajv from 'ajv'
  8. export class ApplicationId extends u64 { };
  9. export class OpeningId extends u64 { };
  10. export class CurrentBlock extends Null { };
  11. export class ExactBlock extends u32 { }; // BlockNumber
  12. export class ActivateOpeningAt extends Enum {
  13. constructor(value?: any, index?: number) {
  14. super(
  15. {
  16. CurrentBlock,
  17. ExactBlock,
  18. },
  19. value, index);
  20. }
  21. }
  22. export enum ApplicationDeactivationCauseKeys {
  23. External = 'External',
  24. Hired = 'Hired',
  25. NotHired = 'NotHired',
  26. CrowdedOut = 'CrowdedOut',
  27. OpeningCancelled = 'OpeningCancelled',
  28. ReviewPeriodExpired = 'ReviewPeriodExpired',
  29. OpeningFilled = 'OpeningFilled',
  30. }
  31. export class ApplicationDeactivationCause extends Enum {
  32. constructor(value?: any, index?: number) {
  33. super(
  34. [
  35. ApplicationDeactivationCauseKeys.External,
  36. ApplicationDeactivationCauseKeys.Hired,
  37. ApplicationDeactivationCauseKeys.NotHired,
  38. ApplicationDeactivationCauseKeys.CrowdedOut,
  39. ApplicationDeactivationCauseKeys.OpeningCancelled,
  40. ApplicationDeactivationCauseKeys.ReviewPeriodExpired,
  41. ApplicationDeactivationCauseKeys.OpeningFilled,
  42. ],
  43. value, index);
  44. }
  45. };
  46. export type UnstakingApplicationStageType = {
  47. deactivation_initiated: BlockNumber,
  48. cause: ApplicationDeactivationCause
  49. };
  50. export class UnstakingApplicationStage extends JoyStruct<UnstakingApplicationStageType> {
  51. constructor(value?: UnstakingApplicationStageType) {
  52. super({
  53. deactivation_initiated: u32, // BlockNumber
  54. cause: ApplicationDeactivationCause,
  55. }, value);
  56. }
  57. get cause(): ApplicationDeactivationCause {
  58. return this.getField<ApplicationDeactivationCause>('cause')
  59. }
  60. };
  61. export type InactiveApplicationStageType = {
  62. deactivation_initiated: BlockNumber,
  63. deactivated: BlockNumber,
  64. cause: ApplicationDeactivationCause
  65. };
  66. export class InactiveApplicationStage extends JoyStruct<InactiveApplicationStageType> {
  67. constructor(value?: InactiveApplicationStageType) {
  68. super({
  69. deactivation_initiated: u32, // BlockNumber
  70. deactivated: u32,
  71. cause: ApplicationDeactivationCause,
  72. }, value);
  73. }
  74. get cause(): ApplicationDeactivationCause {
  75. return this.getField<ApplicationDeactivationCause>('cause')
  76. }
  77. };
  78. export class ActiveApplicationStage extends Null { };
  79. export enum ApplicationStageKeys {
  80. Active = 'Active',
  81. Unstaking = 'Unstaking',
  82. Inactive = 'Inactive',
  83. }
  84. export class ApplicationStage extends Enum {
  85. constructor(value?: any, index?: number) {
  86. super(
  87. {
  88. [ApplicationStageKeys.Active]: ActiveApplicationStage,
  89. [ApplicationStageKeys.Unstaking]: UnstakingApplicationStage,
  90. [ApplicationStageKeys.Inactive]: InactiveApplicationStage,
  91. },
  92. value, index);
  93. }
  94. }
  95. export type IApplicationRationingPolicy = {
  96. max_active_applicants: u32,
  97. };
  98. export class ApplicationRationingPolicy extends JoyStruct<IApplicationRationingPolicy> {
  99. constructor(value?: IApplicationRationingPolicy) {
  100. super({
  101. max_active_applicants: u32,
  102. }, value);
  103. }
  104. get max_active_applicants(): u32 {
  105. return this.getField<u32>('max_active_applicants')
  106. }
  107. };
  108. export type WaitingToBeingOpeningStageVariantType = {
  109. begins_at_block: BlockNumber,
  110. };
  111. export class WaitingToBeingOpeningStageVariant extends JoyStruct<WaitingToBeingOpeningStageVariantType> {
  112. constructor(value?: WaitingToBeingOpeningStageVariantType) {
  113. super({
  114. begins_at_block: u32,
  115. }, value);
  116. }
  117. get begins_at_block(): BlockNumber {
  118. return this.getField<BlockNumber>('begins_at_block')
  119. }
  120. };
  121. // TODO: Find usages and replace them with JoyEnum helpers
  122. export enum OpeningDeactivationCauseKeys {
  123. CancelledBeforeActivation = 'CancelledBeforeActivation',
  124. CancelledAcceptingApplications = 'CancelledAcceptingApplications',
  125. CancelledInReviewPeriod = 'CancelledInReviewPeriod',
  126. ReviewPeriodExpired = 'ReviewPeriodExpired',
  127. Filled = 'Filled',
  128. }
  129. class OpeningDeactivationCause_CancelledBeforeActivation extends Null { };
  130. class OpeningDeactivationCause_CancelledAcceptingApplications extends Null { };
  131. class OpeningDeactivationCause_CancelledInReviewPeriod extends Null { };
  132. class OpeningDeactivationCause_ReviewPeriodExpired extends Null { };
  133. class OpeningDeactivationCause_Filled extends Null { };
  134. export class OpeningDeactivationCause extends JoyEnum({
  135. 'CancelledBeforeActivation': OpeningDeactivationCause_CancelledBeforeActivation,
  136. 'CancelledAcceptingApplications': OpeningDeactivationCause_CancelledAcceptingApplications,
  137. 'CancelledInReviewPeriod': OpeningDeactivationCause_CancelledInReviewPeriod,
  138. 'ReviewPeriodExpired': OpeningDeactivationCause_ReviewPeriodExpired,
  139. 'Filled': OpeningDeactivationCause_Filled,
  140. } as const) { };
  141. export type IAcceptingApplications = {
  142. started_accepting_applicants_at_block: BlockNumber,
  143. };
  144. export class AcceptingApplications extends JoyStruct<IAcceptingApplications> {
  145. constructor(value?: IAcceptingApplications) {
  146. super({
  147. started_accepting_applicants_at_block: u32,
  148. }, value);
  149. }
  150. get started_accepting_applicants_at_block(): BlockNumber {
  151. return this.getField<BlockNumber>('started_accepting_applicants_at_block')
  152. }
  153. };
  154. export type IReviewPeriod = {
  155. started_accepting_applicants_at_block: BlockNumber,
  156. started_review_period_at_block: BlockNumber,
  157. };
  158. export class ReviewPeriod extends JoyStruct<IReviewPeriod> {
  159. constructor(value?: IReviewPeriod) {
  160. super({
  161. started_accepting_applicants_at_block: u32,
  162. started_review_period_at_block: u32,
  163. }, value);
  164. }
  165. get started_accepting_applicants_at_block(): BlockNumber {
  166. return this.getField<BlockNumber>('started_accepting_applicants_at_block')
  167. }
  168. get started_review_period_at_block(): BlockNumber {
  169. return this.getField<BlockNumber>('started_review_period_at_block')
  170. }
  171. };
  172. export type IDeactivated = {
  173. cause: OpeningDeactivationCause,
  174. deactivated_at_block: BlockNumber,
  175. started_accepting_applicants_at_block: BlockNumber,
  176. started_review_period_at_block: Option<BlockNumber>,
  177. };
  178. export class Deactivated extends JoyStruct<IDeactivated> {
  179. constructor(value?: IDeactivated) {
  180. super({
  181. cause: OpeningDeactivationCause,
  182. deactivated_at_block: u32,
  183. started_accepting_applicants_at_block: u32,
  184. started_review_period_at_block: Option.with(u32),
  185. }, value);
  186. }
  187. get cause(): OpeningDeactivationCause {
  188. return this.getField<OpeningDeactivationCause>('cause')
  189. }
  190. get deactivated_at_block(): BlockNumber {
  191. return this.getField<BlockNumber>('deactivated_at_block')
  192. }
  193. get started_accepting_applicants_at_block(): BlockNumber {
  194. return this.getField<BlockNumber>('started_accepting_applicants_at_block')
  195. }
  196. get started_review_period_at_block(): BlockNumber {
  197. return this.getField<BlockNumber>('started_review_period_at_block')
  198. }
  199. };
  200. // TODO: Find usages and replace them with JoyEnum helpers
  201. export enum ActiveOpeningStageKeys {
  202. AcceptingApplications = 'AcceptingApplications',
  203. ReviewPeriod = 'ReviewPeriod',
  204. Deactivated = 'Deactivated',
  205. }
  206. export class ActiveOpeningStage extends JoyEnum({AcceptingApplications, ReviewPeriod, Deactivated} as const) { }
  207. export type ActiveOpeningStageVariantType = {
  208. stage: ActiveOpeningStage,
  209. applications_added: Vec<ApplicationId>,//BTreeSet<ApplicationId>,
  210. active_application_count: u32,
  211. unstaking_application_count: u32,
  212. deactivated_application_count: u32,
  213. }
  214. export class ActiveOpeningStageVariant extends JoyStruct<ActiveOpeningStageVariantType> {
  215. constructor(value?: ActiveOpeningStageVariantType) {
  216. super({
  217. stage: ActiveOpeningStage,
  218. applications_added: Vec.with(ApplicationId),//BTreeSet<ApplicationId>,
  219. active_application_count: u32,
  220. unstaking_application_count: u32,
  221. deactivated_application_count: u32,
  222. }, value);
  223. }
  224. get stage(): ActiveOpeningStage {
  225. return this.getField<ActiveOpeningStage>('stage')
  226. }
  227. get is_active(): boolean {
  228. switch (this.stage.type) {
  229. case ActiveOpeningStageKeys.AcceptingApplications:
  230. return true
  231. }
  232. return false
  233. }
  234. }
  235. // TODO: Find usages and replace them with JoyEnum helpers
  236. export enum OpeningStageKeys {
  237. WaitingToBegin = 'WaitingToBegin',
  238. Active = 'Active',
  239. }
  240. export class OpeningStage extends JoyEnum({
  241. 'WaitingToBegin': WaitingToBeingOpeningStageVariant,
  242. 'Active': ActiveOpeningStageVariant
  243. } as const) { };
  244. export enum StakingAmountLimitModeKeys {
  245. AtLeast = 'AtLeast',
  246. Exact = 'Exact',
  247. }
  248. export class StakingAmountLimitMode extends Enum {
  249. constructor(value?: any, index?: number) {
  250. super(
  251. [
  252. StakingAmountLimitModeKeys.AtLeast,
  253. StakingAmountLimitModeKeys.Exact,
  254. ],
  255. value, index);
  256. }
  257. };
  258. export type IStakingPolicy = {
  259. amount: Balance,
  260. amount_mode: StakingAmountLimitMode,
  261. crowded_out_unstaking_period_length: Option<BlockNumber>,
  262. review_period_expired_unstaking_period_length: Option<BlockNumber>,
  263. };
  264. export class StakingPolicy extends JoyStruct<IStakingPolicy> {
  265. constructor(value?: IStakingPolicy) {
  266. super({
  267. amount: u128,
  268. amount_mode: StakingAmountLimitMode,
  269. crowded_out_unstaking_period_length: Option.with(u32),
  270. review_period_expired_unstaking_period_length: Option.with(u32),
  271. }, value);
  272. }
  273. get amount(): u128 {
  274. return this.getField<u128>('amount')
  275. }
  276. get amount_mode(): StakingAmountLimitMode {
  277. return this.getField<StakingAmountLimitMode>('amount_mode')
  278. }
  279. get crowded_out_unstaking_period_length(): Option<u32> {
  280. return this.getField<Option<u32>>('crowded_out_unstaking_period_length')
  281. }
  282. get review_period_expired_unstaking_period_length(): Option<u32> {
  283. return this.getField<Option<u32>>('review_period_expired_unstaking_period_length')
  284. }
  285. };
  286. import * as role_schema_json from './schemas/role.schema.json'
  287. export const schemaValidator: ajv.ValidateFunction = new ajv({ allErrors: true }).compile(role_schema_json);
  288. const OpeningHRTFallback: GenericJoyStreamRoleSchema = {
  289. version: 1,
  290. headline: "Unknown",
  291. job: {
  292. title: "Unknown",
  293. description: "Unknown"
  294. },
  295. application: {},
  296. reward: "Unknown",
  297. creator: {
  298. membership: {
  299. handle: "Unknown"
  300. }
  301. }
  302. };
  303. export type IOpening = {
  304. created: BlockNumber,
  305. stage: OpeningStage,
  306. max_review_period_length: BlockNumber,
  307. application_rationing_policy: Option<ApplicationRationingPolicy>,
  308. application_staking_policy: Option<StakingPolicy>,
  309. role_staking_policy: Option<StakingPolicy>,
  310. human_readable_text: Text, // Vec<u8>,
  311. };
  312. export class Opening extends JoyStruct<IOpening> {
  313. constructor(value?: IOpening) {
  314. super({
  315. created: u32,
  316. stage: OpeningStage,
  317. max_review_period_length: u32,
  318. application_rationing_policy: Option.with(ApplicationRationingPolicy),
  319. application_staking_policy: Option.with(StakingPolicy),
  320. role_staking_policy: Option.with(StakingPolicy),
  321. human_readable_text: Text, // Vec.with(u8),
  322. }, value);
  323. }
  324. parse_human_readable_text(): GenericJoyStreamRoleSchema | string | undefined {
  325. const hrt = this.getField<Text>('human_readable_text')
  326. if (!hrt) {
  327. return undefined
  328. }
  329. const str = hrt.toString()
  330. try {
  331. const obj = JSON.parse(str)
  332. if (schemaValidator(obj) === true) {
  333. return obj as unknown as GenericJoyStreamRoleSchema
  334. }
  335. console.log("parse_human_readable_text JSON schema validation failed:", schemaValidator.errors);
  336. } catch (e) {
  337. console.log("parse_human_readable_text JSON schema validation failed:", e.toString())
  338. }
  339. return str
  340. }
  341. parse_human_readable_text_with_fallback(): GenericJoyStreamRoleSchema {
  342. const hrt = this.parse_human_readable_text();
  343. if (typeof hrt !== 'object') {
  344. return OpeningHRTFallback;
  345. }
  346. return hrt;
  347. }
  348. get created(): BlockNumber {
  349. return this.getField<BlockNumber>('created')
  350. }
  351. get stage(): OpeningStage {
  352. return this.getField<OpeningStage>('stage')
  353. }
  354. get max_review_period_length(): BlockNumber {
  355. return this.getField<BlockNumber>('max_review_period_length')
  356. }
  357. get application_rationing_policy(): Option<ApplicationRationingPolicy> {
  358. return this.getField<Option<ApplicationRationingPolicy>>('application_rationing_policy')
  359. }
  360. get application_staking_policy(): Option<StakingPolicy> {
  361. return this.getField<Option<StakingPolicy>>('application_staking_policy')
  362. }
  363. get role_staking_policy(): Option<StakingPolicy> {
  364. return this.getField<Option<StakingPolicy>>('role_staking_policy')
  365. }
  366. get human_readable_text(): Text {
  367. return this.getField<Text>('human_readable_text');
  368. }
  369. get max_applicants(): number {
  370. const appPolicy = this.application_rationing_policy
  371. if (appPolicy.isNone) {
  372. return 0
  373. }
  374. return appPolicy.unwrap().max_active_applicants.toNumber()
  375. }
  376. get is_active(): boolean {
  377. switch (this.stage.type) {
  378. case OpeningStageKeys.WaitingToBegin:
  379. return true
  380. case OpeningStageKeys.Active:
  381. return (this.stage.value as ActiveOpeningStageVariant).is_active
  382. }
  383. return false
  384. }
  385. }
  386. export type IApplication = {
  387. opening_id: OpeningId,
  388. application_index_in_opening: u32,
  389. add_to_opening_in_block: BlockNumber,
  390. active_role_staking_id: Option<StakeId>,
  391. active_application_staking_id: Option<StakeId>,
  392. stage: ApplicationStage,
  393. human_readable_text: Text,
  394. }
  395. export class Application extends JoyStruct<IApplication> {
  396. constructor(value?: IOpening) {
  397. super({
  398. opening_id: OpeningId,
  399. application_index_in_opening: u32,
  400. add_to_opening_in_block: u32,
  401. active_role_staking_id: Option.with(StakeId),
  402. active_application_staking_id: Option.with(StakeId),
  403. stage: ApplicationStage,
  404. human_readable_text: Text,
  405. }, value);
  406. }
  407. get stage(): ApplicationStage {
  408. return this.getField<ApplicationStage>('stage')
  409. }
  410. get active_role_staking_id(): Option<StakeId> {
  411. return this.getField<Option<StakeId>>('active_role_staking_id')
  412. }
  413. get active_application_staking_id(): Option<StakeId> {
  414. return this.getField<Option<StakeId>>('active_application_staking_id')
  415. }
  416. get human_readable_text(): Text {
  417. return this.getField<Text>('human_readable_text')
  418. }
  419. }
  420. export function registerHiringTypes() {
  421. try {
  422. getTypeRegistry().register({
  423. ApplicationId: 'u64',
  424. OpeningId: 'u64',
  425. Application,
  426. ApplicationStage,
  427. // why the prefix? is there some other identically named type?
  428. 'hiring::ActivateOpeningAt': ActivateOpeningAt,
  429. ApplicationRationingPolicy,
  430. OpeningStage,
  431. StakingPolicy,
  432. Opening,
  433. });
  434. } catch (err) {
  435. console.error('Failed to register custom types of hiring module', err);
  436. }
  437. }