Pārlūkot izejas kodu

Merge branch 'iznik' into migrate_runtime_module1

# Conflicts:
#	Cargo.lock
#	runtime-modules/hiring/src/mock.rs
#	runtime-modules/recurring-reward/src/mock/mod.rs
#	runtime-modules/token-minting/src/mock.rs
Shamil Gadelshin 4 gadi atpakaļ
vecāks
revīzija
29027390c5
100 mainītis faili ar 18570 papildinājumiem un 3974 dzēšanām
  1. 6 6
      .github/workflows/joystream-cli.yml
  2. 7 0
      cli/.eslintrc.js
  3. 1 1
      cli/package.json
  4. 384 355
      cli/src/Api.ts
  5. 11 11
      cli/src/ExitCodes.ts
  6. 301 248
      cli/src/Types.ts
  7. 213 224
      cli/src/base/AccountsCommandBase.ts
  8. 374 343
      cli/src/base/ApiCommandBase.ts
  9. 84 73
      cli/src/base/DefaultCommandBase.ts
  10. 94 95
      cli/src/base/StateAwareCommandBase.ts
  11. 241 160
      cli/src/base/WorkingGroupsCommandBase.ts
  12. 25 25
      cli/src/commands/account/choose.ts
  13. 35 35
      cli/src/commands/account/create.ts
  14. 34 34
      cli/src/commands/account/current.ts
  15. 62 61
      cli/src/commands/account/export.ts
  16. 23 21
      cli/src/commands/account/forget.ts
  17. 34 36
      cli/src/commands/account/import.ts
  18. 53 54
      cli/src/commands/account/transferTokens.ts
  19. 7 8
      cli/src/commands/api/getUri.ts
  20. 210 203
      cli/src/commands/api/inspect.ts
  21. 22 22
      cli/src/commands/api/setUri.ts
  22. 48 49
      cli/src/commands/council/info.ts
  23. 31 32
      cli/src/commands/working-groups/application.ts
  24. 77 84
      cli/src/commands/working-groups/createOpening.ts
  25. 56 0
      cli/src/commands/working-groups/decreaseWorkerStake.ts
  26. 56 0
      cli/src/commands/working-groups/evictWorker.ts
  27. 49 55
      cli/src/commands/working-groups/fillOpening.ts
  28. 46 0
      cli/src/commands/working-groups/increaseStake.ts
  29. 28 0
      cli/src/commands/working-groups/leaveRole.ts
  30. 67 65
      cli/src/commands/working-groups/opening.ts
  31. 18 17
      cli/src/commands/working-groups/openings.ts
  32. 34 32
      cli/src/commands/working-groups/overview.ts
  33. 55 0
      cli/src/commands/working-groups/slashWorker.ts
  34. 37 43
      cli/src/commands/working-groups/startAcceptingApplications.ts
  35. 35 43
      cli/src/commands/working-groups/startReviewPeriod.ts
  36. 35 42
      cli/src/commands/working-groups/terminateApplication.ts
  37. 48 0
      cli/src/commands/working-groups/updateRewardAccount.ts
  38. 58 0
      cli/src/commands/working-groups/updateRoleAccount.ts
  39. 67 0
      cli/src/commands/working-groups/updateWorkerReward.ts
  40. 52 47
      cli/src/helpers/display.ts
  41. 30 0
      cli/src/helpers/promptOptions.ts
  42. 14 14
      cli/src/helpers/validation.ts
  43. 1 1
      cli/src/index.ts
  44. 44 0
      cli/src/promptOptions/addWorkerOpening.ts
  45. 51 0
      cli/src/validators/common.ts
  46. 7 7
      cli/test/commands/council/info.test.ts
  47. 1 3
      cli/test/tsconfig.json
  48. 2 1
      cli/tsconfig.json
  49. 12 0
      devops/ansible/build-and-run-tests-single-node-playbook.yml
  50. 4 0
      devops/ansible/build-image-tasklist.yml
  51. 34 0
      devops/ansible/docker-compose.yml
  52. 2 0
      devops/ansible/hosts
  53. 15 0
      devops/ansible/install-dependencies-playbook.yml
  54. 24 0
      devops/ansible/run-tests-single-node-playbook.yml
  55. 24 0
      devops/ansible/run-tests-two-nodes-playbook.yml
  56. 37 0
      devops/dockerfiles/ansible-node/Dockerfile
  57. 766 0
      devops/vstore/classes.json
  58. 12694 0
      devops/vstore/entities.json
  59. 1 1
      node/src/chain_spec.rs
  60. 1 1
      package.json
  61. 53 3
      pioneer/packages/joy-proposals/src/Proposal/Body.tsx
  62. 1 1
      pioneer/packages/joy-proposals/src/Proposal/Votes.tsx
  63. 357 0
      pioneer/packages/joy-proposals/src/forms/AddWorkingGroupOpeningForm.tsx
  64. 48 10
      pioneer/packages/joy-proposals/src/forms/GenericProposalForm.tsx
  65. 84 0
      pioneer/packages/joy-proposals/src/forms/GenericWorkingGroupProposalForm.tsx
  66. 1 1
      pioneer/packages/joy-proposals/src/forms/errorHandling.ts
  67. 1 0
      pioneer/packages/joy-proposals/src/forms/index.ts
  68. 3 1
      pioneer/packages/joy-proposals/src/index.tsx
  69. 105 1
      pioneer/packages/joy-proposals/src/validationSchema.ts
  70. 1 1
      pioneer/packages/joy-roles/src/index.tsx
  71. 72 113
      pioneer/packages/joy-roles/src/tabs/Admin.controller.tsx
  72. 4 0
      pioneer/packages/joy-roles/src/tabs/MyRoles.tsx
  73. 24 10
      pioneer/packages/joy-utils/src/MemberProfilePreview.tsx
  74. 2 2
      pioneer/packages/joy-utils/src/MyAccount.tsx
  75. 57 1
      pioneer/packages/joy-utils/src/consts/proposals.ts
  76. 4 0
      pioneer/packages/joy-utils/src/consts/workingGroups.ts
  77. 4 2
      pioneer/packages/joy-utils/src/react/hooks/usePromise.tsx
  78. 3 0
      pioneer/packages/joy-utils/src/transport/index.ts
  79. 5 0
      pioneer/packages/joy-utils/src/transport/members.ts
  80. 10 28
      pioneer/packages/joy-utils/src/transport/proposals.ts
  81. 47 0
      pioneer/packages/joy-utils/src/transport/workingGroups.ts
  82. 1 0
      pioneer/packages/joy-utils/src/types/common.ts
  83. 2 1
      pioneer/packages/joy-utils/src/types/proposals.ts
  84. 7 0
      pioneer/packages/joy-utils/src/types/workingGroups.ts
  85. 1 1
      runtime-modules/content-working-group/Cargo.toml
  86. 42 238
      runtime-modules/content-working-group/src/lib.rs
  87. 6 30
      runtime-modules/content-working-group/src/mock.rs
  88. 21 23
      runtime-modules/content-working-group/src/tests.rs
  89. 2 4
      runtime-modules/governance/src/election.rs
  90. 2 4
      runtime-modules/governance/src/mock.rs
  91. 1 1
      runtime-modules/membership/Cargo.toml
  92. 3 4
      runtime-modules/membership/src/genesis.rs
  93. 647 3
      runtime-modules/membership/src/lib.rs
  94. 0 725
      runtime-modules/membership/src/members.rs
  95. 6 8
      runtime-modules/membership/src/mock.rs
  96. 0 64
      runtime-modules/membership/src/role_types.rs
  97. 55 135
      runtime-modules/membership/src/tests.rs
  98. 2 2
      runtime-modules/proposals/codex/src/lib.rs
  99. 1 2
      runtime-modules/proposals/codex/src/tests/mock.rs
  100. 3 3
      runtime-modules/proposals/discussion/src/lib.rs

+ 6 - 6
.github/workflows/joystream-cli.yml

@@ -3,7 +3,7 @@ on: [pull_request, push]
 
 jobs:
   cli_build_ubuntu:
-    name: Ubuntu Build
+    name: Ubuntu Checks
     runs-on: ubuntu-latest
     strategy:
       matrix:
@@ -14,14 +14,14 @@ jobs:
       uses: actions/setup-node@v1
       with:
         node-version: ${{ matrix.node-version }}
-    - name: build
+    - name: checks
       run: |
         yarn install --frozen-lockfile
         yarn madge --circular types/
-        yarn workspace joystream-cli build
+        yarn workspace joystream-cli checks
 
   cli_build_osx:
-    name: MacOS Build
+    name: MacOS Checks
     runs-on: macos-latest
     strategy:
       matrix:
@@ -32,8 +32,8 @@ jobs:
       uses: actions/setup-node@v1
       with:
         node-version: ${{ matrix.node-version }}
-    - name: build
+    - name: checks
       run: |
         yarn install --frozen-lockfile --network-timeout 120000
         yarn madge --circular types/
-        yarn workspace joystream-cli build
+        yarn workspace joystream-cli checks

+ 7 - 0
cli/.eslintrc.js

@@ -1,4 +1,7 @@
 module.exports = {
+  env: {
+    mocha: true,
+  },
   extends: [
     // The oclif rules have some code-style/formatting rules which may conflict with
     // our prettier global settings. Disabling for now
@@ -7,4 +10,8 @@ module.exports = {
     // "oclif",
     // "oclif-typescript",
   ],
+  rules: {
+    "no-unused-vars": "off", // Required by the typescript rule below
+    "@typescript-eslint/no-unused-vars": ["error"]
+  }
 }

+ 1 - 1
cli/package.json

@@ -90,7 +90,7 @@
     "test": "nyc --extension .ts mocha --forbid-only \"test/**/*.test.ts\"",
     "build": "tsc --build tsconfig.json",
     "version": "oclif-dev readme && git add README.md",
-    "lint": "eslint ./src/ --quiet --ext .ts",
+    "lint": "eslint ./ --quiet --ext .ts",
     "checks": "yarn lint && tsc --noEmit --pretty && prettier ./ --check",
     "format": "prettier ./ --write"
   },

+ 384 - 355
cli/src/Api.ts

@@ -1,440 +1,469 @@
-import BN from 'bn.js';
-import { registerJoystreamTypes } from '@joystream/types/';
-import { ApiPromise, WsProvider } from '@polkadot/api';
-import { QueryableStorageMultiArg } from '@polkadot/api/types';
-import { formatBalance } from '@polkadot/util';
-import { Hash, Balance } from '@polkadot/types/interfaces';
-import { KeyringPair } from '@polkadot/keyring/types';
-import { Codec } from '@polkadot/types/types';
-import { Option, Vec } from '@polkadot/types';
-import { u32 } from '@polkadot/types/primitive';
+import BN from 'bn.js'
+import { registerJoystreamTypes } from '@joystream/types/'
+import { ApiPromise, WsProvider } from '@polkadot/api'
+import { QueryableStorageMultiArg } from '@polkadot/api/types'
+import { formatBalance } from '@polkadot/util'
+import { Hash, Balance, Moment } from '@polkadot/types/interfaces'
+import { KeyringPair } from '@polkadot/keyring/types'
+import { Codec } from '@polkadot/types/types'
+import { Option, Vec } from '@polkadot/types'
+import { u32 } from '@polkadot/types/primitive'
 import {
-    AccountSummary,
-    CouncilInfoObj, CouncilInfoTuple, createCouncilInfoObj,
-    WorkingGroups,
-    GroupMember,
-    OpeningStatus,
-    GroupOpeningStage,
-    GroupOpening,
-    GroupApplication
-} from './Types';
-import { DerivedFees, DerivedBalances } from '@polkadot/api-derive/types';
-import { CLIError } from '@oclif/errors';
-import ExitCodes from './ExitCodes';
+  AccountSummary,
+  CouncilInfoObj,
+  CouncilInfoTuple,
+  createCouncilInfoObj,
+  WorkingGroups,
+  Reward,
+  GroupMember,
+  OpeningStatus,
+  GroupOpeningStage,
+  GroupOpening,
+  GroupApplication,
+} from './Types'
+import { DerivedFees, DerivedBalances } from '@polkadot/api-derive/types'
+import { CLIError } from '@oclif/errors'
+import ExitCodes from './ExitCodes'
 import {
-    Worker, WorkerId,
-    RoleStakeProfile,
-    Opening as WGOpening,
-    Application as WGApplication
-} from '@joystream/types/working-group';
+  Worker,
+  WorkerId,
+  RoleStakeProfile,
+  Opening as WGOpening,
+  Application as WGApplication,
+} from '@joystream/types/working-group'
 import {
-    Opening,
-    Application,
-    OpeningStage,
-    ApplicationStageKeys,
-    ApplicationId,
-    OpeningId
-} from '@joystream/types/hiring';
-import { MemberId, Profile } from '@joystream/types/members';
-import { RewardRelationship, RewardRelationshipId } from '@joystream/types/recurring-rewards';
-import { Stake, StakeId } from '@joystream/types/stake';
-import { LinkageResult } from '@polkadot/types/codec/Linkage';
-import { Moment } from '@polkadot/types/interfaces';
-
-export const DEFAULT_API_URI = 'wss://rome-rpc-endpoint.joystream.org:9944/';
-const DEFAULT_DECIMALS = new u32(12);
+  Opening,
+  Application,
+  OpeningStage,
+  ApplicationStageKeys,
+  ApplicationId,
+  OpeningId,
+} from '@joystream/types/hiring'
+import { MemberId, Profile } from '@joystream/types/members'
+import { RewardRelationship, RewardRelationshipId } from '@joystream/types/recurring-rewards'
+import { Stake, StakeId } from '@joystream/types/stake'
+import { LinkageResult } from '@polkadot/types/codec/Linkage'
+
+import { InputValidationLengthConstraint } from '@joystream/types/common'
+
+export const DEFAULT_API_URI = 'wss://rome-rpc-endpoint.joystream.org:9944/'
+const DEFAULT_DECIMALS = new u32(12)
 
 // Mapping of working group to api module
 export const apiModuleByGroup: { [key in WorkingGroups]: string } = {
-    [WorkingGroups.StorageProviders]: 'storageWorkingGroup'
-};
+  [WorkingGroups.StorageProviders]: 'storageWorkingGroup',
+}
 
 // Api wrapper for handling most common api calls and allowing easy API implementation switch in the future
 export default class Api {
-    private _api: ApiPromise;
+  private _api: ApiPromise
 
-    private constructor(originalApi: ApiPromise) {
-        this._api = originalApi;
-    }
+  private constructor(originalApi: ApiPromise) {
+    this._api = originalApi
+  }
 
-    public getOriginalApi(): ApiPromise {
-        return this._api;
-    }
+  public getOriginalApi(): ApiPromise {
+    return this._api
+  }
 
-    private static async initApi(apiUri: string = DEFAULT_API_URI): Promise<ApiPromise> {
-        const wsProvider: WsProvider = new WsProvider(apiUri);
-        registerJoystreamTypes();
-        const api = await ApiPromise.create({ provider: wsProvider });
+  private static async initApi(apiUri: string = DEFAULT_API_URI): Promise<ApiPromise> {
+    const wsProvider: WsProvider = new WsProvider(apiUri)
+    registerJoystreamTypes()
+    const api = await ApiPromise.create({ provider: wsProvider })
 
-        // Initializing some api params based on pioneer/packages/react-api/Api.tsx
-        const [properties] = await Promise.all([
-            api.rpc.system.properties()
-        ]);
+    // Initializing some api params based on pioneer/packages/react-api/Api.tsx
+    const [properties] = await Promise.all([api.rpc.system.properties()])
 
-        const tokenSymbol = properties.tokenSymbol.unwrapOr('DEV').toString();
-        const tokenDecimals = properties.tokenDecimals.unwrapOr(DEFAULT_DECIMALS).toNumber();
+    const tokenSymbol = properties.tokenSymbol.unwrapOr('DEV').toString()
+    const tokenDecimals = properties.tokenDecimals.unwrapOr(DEFAULT_DECIMALS).toNumber()
 
-        // formatBlanace config
-        formatBalance.setDefaults({
-            decimals: tokenDecimals,
-            unit: tokenSymbol
-        });
+    // formatBlanace config
+    formatBalance.setDefaults({
+      decimals: tokenDecimals,
+      unit: tokenSymbol,
+    })
 
-        return api;
-    }
+    return api
+  }
 
-    static async create(apiUri: string = DEFAULT_API_URI): Promise<Api> {
-        const originalApi: ApiPromise = await Api.initApi(apiUri);
-        return new Api(originalApi);
-    }
+  static async create(apiUri: string = DEFAULT_API_URI): Promise<Api> {
+    const originalApi: ApiPromise = await Api.initApi(apiUri)
+    return new Api(originalApi)
+  }
 
-    private async queryMultiOnce(queries: Parameters<typeof ApiPromise.prototype.queryMulti>[0]): Promise<Codec[]> {
-        let results: Codec[] = [];
+  private async queryMultiOnce(queries: Parameters<typeof ApiPromise.prototype.queryMulti>[0]): Promise<Codec[]> {
+    let results: Codec[] = []
 
-        const unsub = await this._api.queryMulti(
-            queries,
-            (res) => { results = res }
-        );
-        unsub();
+    const unsub = await this._api.queryMulti(queries, (res) => {
+      results = res
+    })
+    unsub()
 
-        if (!results.length || results.length !== queries.length) {
-            throw new CLIError('API querying issue', { exit: ExitCodes.ApiError });
-        }
-
-        return results;
+    if (!results.length || results.length !== queries.length) {
+      throw new CLIError('API querying issue', { exit: ExitCodes.ApiError })
     }
 
-    async getAccountsBalancesInfo(accountAddresses: string[]): Promise<DerivedBalances[]> {
-        let accountsBalances: DerivedBalances[] = await this._api.derive.balances.votingBalances(accountAddresses);
-
-        return accountsBalances;
+    return results
+  }
+
+  async getAccountsBalancesInfo(accountAddresses: string[]): Promise<DerivedBalances[]> {
+    const accountsBalances: DerivedBalances[] = await this._api.derive.balances.votingBalances(accountAddresses)
+
+    return accountsBalances
+  }
+
+  // Get on-chain data related to given account.
+  // For now it's just account balances
+  async getAccountSummary(accountAddresses: string): Promise<AccountSummary> {
+    const balances: DerivedBalances = (await this.getAccountsBalancesInfo([accountAddresses]))[0]
+    // TODO: Some more information can be fetched here in the future
+
+    return { balances }
+  }
+
+  async getCouncilInfo(): Promise<CouncilInfoObj> {
+    const queries: { [P in keyof CouncilInfoObj]: QueryableStorageMultiArg<'promise'> } = {
+      activeCouncil: this._api.query.council.activeCouncil,
+      termEndsAt: this._api.query.council.termEndsAt,
+      autoStart: this._api.query.councilElection.autoStart,
+      newTermDuration: this._api.query.councilElection.newTermDuration,
+      candidacyLimit: this._api.query.councilElection.candidacyLimit,
+      councilSize: this._api.query.councilElection.councilSize,
+      minCouncilStake: this._api.query.councilElection.minCouncilStake,
+      minVotingStake: this._api.query.councilElection.minVotingStake,
+      announcingPeriod: this._api.query.councilElection.announcingPeriod,
+      votingPeriod: this._api.query.councilElection.votingPeriod,
+      revealingPeriod: this._api.query.councilElection.revealingPeriod,
+      round: this._api.query.councilElection.round,
+      stage: this._api.query.councilElection.stage,
     }
+    const results: CouncilInfoTuple = (await this.queryMultiOnce(Object.values(queries))) as CouncilInfoTuple
 
-    // Get on-chain data related to given account.
-    // For now it's just account balances
-    async getAccountSummary(accountAddresses: string): Promise<AccountSummary> {
-        const balances: DerivedBalances = (await this.getAccountsBalancesInfo([accountAddresses]))[0];
-        // TODO: Some more information can be fetched here in the future
+    return createCouncilInfoObj(...results)
+  }
 
-        return { balances };
-    }
+  // TODO: This formula is probably not too good, so some better implementation will be required in the future
+  async estimateFee(account: KeyringPair, recipientAddr: string, amount: BN): Promise<BN> {
+    const transfer = this._api.tx.balances.transfer(recipientAddr, amount)
+    const signature = account.sign(transfer.toU8a())
+    const transactionByteSize: BN = new BN(transfer.encodedLength + signature.length)
 
-    async getCouncilInfo(): Promise<CouncilInfoObj> {
-        const queries: { [P in keyof CouncilInfoObj]: QueryableStorageMultiArg<"promise"> } = {
-            activeCouncil: this._api.query.council.activeCouncil,
-            termEndsAt: this._api.query.council.termEndsAt,
-            autoStart: this._api.query.councilElection.autoStart,
-            newTermDuration: this._api.query.councilElection.newTermDuration,
-            candidacyLimit: this._api.query.councilElection.candidacyLimit,
-            councilSize: this._api.query.councilElection.councilSize,
-            minCouncilStake: this._api.query.councilElection.minCouncilStake,
-            minVotingStake: this._api.query.councilElection.minVotingStake,
-            announcingPeriod: this._api.query.councilElection.announcingPeriod,
-            votingPeriod: this._api.query.councilElection.votingPeriod,
-            revealingPeriod: this._api.query.councilElection.revealingPeriod,
-            round: this._api.query.councilElection.round,
-            stage: this._api.query.councilElection.stage
-        }
-        const results: CouncilInfoTuple = <CouncilInfoTuple>await this.queryMultiOnce(Object.values(queries));
-
-        return createCouncilInfoObj(...results);
-    }
+    const fees: DerivedFees = await this._api.derive.balances.fees()
 
-    // TODO: This formula is probably not too good, so some better implementation will be required in the future
-    async estimateFee(account: KeyringPair, recipientAddr: string, amount: BN): Promise<BN> {
-        const transfer = this._api.tx.balances.transfer(recipientAddr, amount);
-        const signature = account.sign(transfer.toU8a());
-        const transactionByteSize: BN = new BN(transfer.encodedLength + signature.length);
+    const estimatedFee = fees.transactionBaseFee.add(fees.transactionByteFee.mul(transactionByteSize))
 
-        const fees: DerivedFees = await this._api.derive.balances.fees();
+    return estimatedFee
+  }
 
-        const estimatedFee = fees.transactionBaseFee.add(fees.transactionByteFee.mul(transactionByteSize));
+  async transfer(account: KeyringPair, recipientAddr: string, amount: BN): Promise<Hash> {
+    const txHash = await this._api.tx.balances.transfer(recipientAddr, amount).signAndSend(account)
+    return txHash
+  }
 
-        return estimatedFee;
-    }
+  // Working groups
+  // TODO: This is a lot of repeated logic from "/pioneer/joy-roles/src/transport.substrate.ts"
+  // (although simplified a little bit)
+  // Hopefully this will be refactored to "joystream-js" soon
+  protected singleLinkageResult<T extends Codec>(result: LinkageResult) {
+    return result[0] as T
+  }
 
-    async transfer(account: KeyringPair, recipientAddr: string, amount: BN): Promise<Hash> {
-        const txHash = await this._api.tx.balances
-            .transfer(recipientAddr, amount)
-            .signAndSend(account);
-        return txHash;
-    }
+  protected multiLinkageResult<K extends Codec, V extends Codec>(result: LinkageResult): [Vec<K>, Vec<V>] {
+    return [result[0] as Vec<K>, result[1] as Vec<V>]
+  }
 
-    // Working groups
-    // TODO: This is a lot of repeated logic from "/pioneer/joy-roles/src/transport.substrate.ts"
-    // (although simplified a little bit)
-    // Hopefully this will be refactored to "joystream-js" soon
-    protected singleLinkageResult<T extends Codec>(result: LinkageResult) {
-        return result[0] as T;
-    }
+  protected async blockHash(height: number): Promise<string> {
+    const blockHash = await this._api.rpc.chain.getBlockHash(height)
 
-    protected multiLinkageResult<K extends Codec, V extends Codec>(result: LinkageResult): [Vec<K>, Vec<V>] {
-        return [result[0] as Vec<K>, result[1] as Vec<V>];
-    }
+    return blockHash.toString()
+  }
 
-    protected async blockHash(height: number): Promise<string> {
-        const blockHash = await this._api.rpc.chain.getBlockHash(height);
+  protected async blockTimestamp(height: number): Promise<Date> {
+    const blockTime = (await this._api.query.timestamp.now.at(await this.blockHash(height))) as Moment
 
-        return blockHash.toString();
-    }
+    return new Date(blockTime.toNumber())
+  }
 
-    protected async blockTimestamp(height: number): Promise<Date> {
-        const blockTime = (await this._api.query.timestamp.now.at(await this.blockHash(height))) as Moment;
+  protected workingGroupApiQuery(group: WorkingGroups) {
+    const module = apiModuleByGroup[group]
+    return this._api.query[module]
+  }
 
-        return new Date(blockTime.toNumber());
-    }
+  protected async memberProfileById(memberId: MemberId): Promise<Profile | null> {
+    const profile = (await this._api.query.members.memberProfile(memberId)) as Option<Profile>
 
-    protected workingGroupApiQuery(group: WorkingGroups) {
-        const module = apiModuleByGroup[group];
-        return this._api.query[module];
-    }
+    return profile.unwrapOr(null)
+  }
 
-    protected async memberProfileById(memberId: MemberId): Promise<Profile | null> {
-        const profile = await this._api.query.members.memberProfile(memberId) as Option<Profile>;
+  async groupLead(group: WorkingGroups): Promise<GroupMember | null> {
+    const optLeadId = (await this.workingGroupApiQuery(group).currentLead()) as Option<WorkerId>
 
-        return profile.unwrapOr(null);
+    if (!optLeadId.isSome) {
+      return null
     }
 
-    async groupLead(group: WorkingGroups): Promise<GroupMember | null> {
-        const optLeadId = (await this.workingGroupApiQuery(group).currentLead()) as Option<WorkerId>;
+    const leadWorkerId = optLeadId.unwrap()
+    const leadWorker = await this.workerByWorkerId(group, leadWorkerId.toNumber())
 
-        if (!optLeadId.isSome) {
-            return null;
-        }
+    return await this.parseGroupMember(leadWorkerId, leadWorker)
+  }
 
-        const leadWorkerId = optLeadId.unwrap();
-        const leadWorker = this.singleLinkageResult<Worker>(
-            await this.workingGroupApiQuery(group).workerById(leadWorkerId) as LinkageResult
-        );
+  protected async stakeValue(stakeId: StakeId): Promise<Balance> {
+    const stake = this.singleLinkageResult<Stake>((await this._api.query.stake.stakes(stakeId)) as LinkageResult)
+    return stake.value
+  }
 
-        if (!leadWorker.is_active) {
-            return null;
-        }
+  protected async workerStake(stakeProfile: RoleStakeProfile): Promise<Balance> {
+    return this.stakeValue(stakeProfile.stake_id)
+  }
 
-        return await this.groupMember(leadWorkerId, leadWorker);
-    }
+  protected async workerReward(relationshipId: RewardRelationshipId): Promise<Reward> {
+    const rewardRelationship = this.singleLinkageResult<RewardRelationship>(
+      (await this._api.query.recurringRewards.rewardRelationships(relationshipId)) as LinkageResult
+    )
 
-    protected async stakeValue(stakeId: StakeId): Promise<Balance> {
-        const stake = this.singleLinkageResult<Stake>(
-            await this._api.query.stake.stakes(stakeId) as LinkageResult
-        );
-        return stake.value;
+    return {
+      totalRecieved: rewardRelationship.total_reward_received,
+      value: rewardRelationship.amount_per_payout,
+      interval: rewardRelationship.payout_interval.unwrapOr(undefined)?.toNumber(),
+      nextPaymentBlock: rewardRelationship.next_payment_at_block.unwrapOr(new BN(0)).toNumber(),
     }
+  }
 
-    protected async workerStake (stakeProfile: RoleStakeProfile): Promise<Balance> {
-        return this.stakeValue(stakeProfile.stake_id);
+  protected async parseGroupMember(id: WorkerId, worker: Worker): Promise<GroupMember> {
+    const roleAccount = worker.role_account_id
+    const memberId = worker.member_id
+
+    const profile = await this.memberProfileById(memberId)
+
+    if (!profile) {
+      throw new Error(`Group member profile not found! (member id: ${memberId.toNumber()})`)
     }
 
-    protected async workerTotalReward(relationshipId: RewardRelationshipId): Promise<Balance> {
-        const relationship = this.singleLinkageResult<RewardRelationship>(
-            await this._api.query.recurringRewards.rewardRelationships(relationshipId) as LinkageResult
-        );
-        return relationship.total_reward_received;
+    let stake: Balance | undefined
+    if (worker.role_stake_profile && worker.role_stake_profile.isSome) {
+      stake = await this.workerStake(worker.role_stake_profile.unwrap())
     }
 
-    protected async groupMember(
-        id: WorkerId,
-        worker: Worker
-    ): Promise<GroupMember> {
-        const roleAccount = worker.role_account_id;
-        const memberId = worker.member_id;
-
-        const profile = await this.memberProfileById(memberId);
-
-        if (!profile) {
-            throw new Error(`Group member profile not found! (member id: ${memberId.toNumber()})`);
-        }
-
-        let stakeValue: Balance = this._api.createType("Balance", 0);
-        if (worker.role_stake_profile && worker.role_stake_profile.isSome) {
-            stakeValue = await this.workerStake(worker.role_stake_profile.unwrap());
-        }
-
-        let earnedValue: Balance = this._api.createType("Balance", 0);
-        if (worker.reward_relationship && worker.reward_relationship.isSome) {
-            earnedValue = await this.workerTotalReward(worker.reward_relationship.unwrap());
-        }
-
-        return ({
-            workerId: id,
-            roleAccount,
-            memberId,
-            profile,
-            stake: stakeValue,
-            earned: earnedValue
-        });
+    let reward: Reward | undefined
+    if (worker.reward_relationship && worker.reward_relationship.isSome) {
+      reward = await this.workerReward(worker.reward_relationship.unwrap())
     }
 
-    async groupMembers(group: WorkingGroups): Promise<GroupMember[]> {
-        const nextId = (await this.workingGroupApiQuery(group).nextWorkerId()) as WorkerId;
+    return {
+      workerId: id,
+      roleAccount,
+      memberId,
+      profile,
+      stake,
+      reward,
+    }
+  }
 
-        // This is chain specfic, but if next id is still 0, it means no curators have been added yet
-        if (nextId.eq(0)) {
-            return [];
-        }
+  async workerByWorkerId(group: WorkingGroups, workerId: number): Promise<Worker> {
+    const nextId = (await this.workingGroupApiQuery(group).nextWorkerId()) as WorkerId
 
-        const [workerIds, workers] = this.multiLinkageResult<WorkerId, Worker>(
-            (await this.workingGroupApiQuery(group).workerById()) as LinkageResult
-        );
+    // This is chain specfic, but if next id is still 0, it means no workers have been added yet
+    if (workerId < 0 || workerId >= nextId.toNumber()) {
+      throw new CLIError('Invalid worker id!')
+    }
 
-        let groupMembers: GroupMember[] = [];
-        for (let [index, worker] of Object.entries(workers.toArray())) {
-            const workerId = workerIds[parseInt(index)];
-            groupMembers.push(await this.groupMember(workerId, worker));
-        }
+    const worker = this.singleLinkageResult<Worker>(
+      (await this.workingGroupApiQuery(group).workerById(workerId)) as LinkageResult
+    )
 
-        return groupMembers.reverse();
+    if (!worker.is_active) {
+      throw new CLIError('This worker is not active anymore')
     }
 
-    async openingsByGroup(group: WorkingGroups): Promise<GroupOpening[]> {
-        const openings: GroupOpening[] = [];
-        const nextId = (await this.workingGroupApiQuery(group).nextOpeningId()) as OpeningId;
+    return worker
+  }
 
-        // This is chain specfic, but if next id is still 0, it means no openings have been added yet
-        if (!nextId.eq(0)) {
-            const highestId = nextId.toNumber() - 1;
-            for (let i = highestId; i >= 0; i--) {
-                openings.push(await this.groupOpening(group, i));
-            }
-        }
+  async groupMember(group: WorkingGroups, workerId: number) {
+    const worker = await this.workerByWorkerId(group, workerId)
+    return await this.parseGroupMember(new WorkerId(workerId), worker)
+  }
 
-        return openings;
-    }
+  async groupMembers(group: WorkingGroups): Promise<GroupMember[]> {
+    const nextId = (await this.workingGroupApiQuery(group).nextWorkerId()) as WorkerId
 
-    protected async hiringOpeningById(id: number | OpeningId): Promise<Opening> {
-        const result = await this._api.query.hiring.openingById(id) as LinkageResult;
-        return this.singleLinkageResult<Opening>(result);
+    // This is chain specfic, but if next id is still 0, it means no workers have been added yet
+    if (nextId.eq(0)) {
+      return []
     }
 
-    protected async hiringApplicationById(id: number | ApplicationId): Promise<Application> {
-        const result = await this._api.query.hiring.applicationById(id) as LinkageResult;
-        return this.singleLinkageResult<Application>(result);
+    const [workerIds, workers] = this.multiLinkageResult<WorkerId, Worker>(
+      (await this.workingGroupApiQuery(group).workerById()) as LinkageResult
+    )
+
+    const groupMembers: GroupMember[] = []
+    for (const [index, worker] of Object.entries(workers.toArray())) {
+      const workerId = workerIds[parseInt(index)]
+      if (worker.is_active) {
+        groupMembers.push(await this.parseGroupMember(workerId, worker))
+      }
     }
 
-    async wgApplicationById(group: WorkingGroups, wgApplicationId: number): Promise<WGApplication> {
-        const nextAppId = await this.workingGroupApiQuery(group).nextApplicationId() as ApplicationId;
+    return groupMembers.reverse()
+  }
 
-        if (wgApplicationId < 0 || wgApplicationId >= nextAppId.toNumber()) {
-            throw new CLIError('Invalid working group application ID!');
-        }
+  async openingsByGroup(group: WorkingGroups): Promise<GroupOpening[]> {
+    const openings: GroupOpening[] = []
+    const nextId = (await this.workingGroupApiQuery(group).nextOpeningId()) as OpeningId
 
-        return this.singleLinkageResult<WGApplication>(
-            await this.workingGroupApiQuery(group).applicationById(wgApplicationId) as LinkageResult
-        );
+    // This is chain specfic, but if next id is still 0, it means no openings have been added yet
+    if (!nextId.eq(0)) {
+      const highestId = nextId.toNumber() - 1
+      for (let i = highestId; i >= 0; i--) {
+        openings.push(await this.groupOpening(group, i))
+      }
     }
 
-    protected async parseApplication(wgApplicationId: number, wgApplication: WGApplication): Promise<GroupApplication> {
-        const appId = wgApplication.application_id;
-        const application = await this.hiringApplicationById(appId);
-
-        const { active_role_staking_id: roleStakingId, active_application_staking_id: appStakingId } = application;
-
-        return {
-            wgApplicationId,
-            applicationId: appId.toNumber(),
-            member: await this.memberProfileById(wgApplication.member_id),
-            roleAccout: wgApplication.role_account_id,
-            stakes: {
-                application: appStakingId.isSome ? (await this.stakeValue(appStakingId.unwrap())).toNumber() : 0,
-                role: roleStakingId.isSome ? (await this.stakeValue(roleStakingId.unwrap())).toNumber() : 0
-            },
-            humanReadableText: application.human_readable_text.toString(),
-            stage: application.stage.type as ApplicationStageKeys
-        };
+    return openings
+  }
+
+  protected async hiringOpeningById(id: number | OpeningId): Promise<Opening> {
+    const result = (await this._api.query.hiring.openingById(id)) as LinkageResult
+    return this.singleLinkageResult<Opening>(result)
+  }
+
+  protected async hiringApplicationById(id: number | ApplicationId): Promise<Application> {
+    const result = (await this._api.query.hiring.applicationById(id)) as LinkageResult
+    return this.singleLinkageResult<Application>(result)
+  }
+
+  async wgApplicationById(group: WorkingGroups, wgApplicationId: number): Promise<WGApplication> {
+    const nextAppId = (await this.workingGroupApiQuery(group).nextApplicationId()) as ApplicationId
+
+    if (wgApplicationId < 0 || wgApplicationId >= nextAppId.toNumber()) {
+      throw new CLIError('Invalid working group application ID!')
     }
 
-    async groupApplication(group: WorkingGroups, wgApplicationId: number): Promise<GroupApplication> {
-        const wgApplication = await this.wgApplicationById(group, wgApplicationId);
-        return await this.parseApplication(wgApplicationId, wgApplication);
+    return this.singleLinkageResult<WGApplication>(
+      (await this.workingGroupApiQuery(group).applicationById(wgApplicationId)) as LinkageResult
+    )
+  }
+
+  protected async parseApplication(wgApplicationId: number, wgApplication: WGApplication): Promise<GroupApplication> {
+    const appId = wgApplication.application_id
+    const application = await this.hiringApplicationById(appId)
+
+    const { active_role_staking_id: roleStakingId, active_application_staking_id: appStakingId } = application
+
+    return {
+      wgApplicationId,
+      applicationId: appId.toNumber(),
+      wgOpeningId: wgApplication.opening_id.toNumber(),
+      member: await this.memberProfileById(wgApplication.member_id),
+      roleAccout: wgApplication.role_account_id,
+      stakes: {
+        application: appStakingId.isSome ? (await this.stakeValue(appStakingId.unwrap())).toNumber() : 0,
+        role: roleStakingId.isSome ? (await this.stakeValue(roleStakingId.unwrap())).toNumber() : 0,
+      },
+      humanReadableText: application.human_readable_text.toString(),
+      stage: application.stage.type as ApplicationStageKeys,
+    }
+  }
+
+  async groupApplication(group: WorkingGroups, wgApplicationId: number): Promise<GroupApplication> {
+    const wgApplication = await this.wgApplicationById(group, wgApplicationId)
+    return await this.parseApplication(wgApplicationId, wgApplication)
+  }
+
+  protected async groupOpeningApplications(group: WorkingGroups, wgOpeningId: number): Promise<GroupApplication[]> {
+    const applications: GroupApplication[] = []
+
+    const nextAppId = (await this.workingGroupApiQuery(group).nextApplicationId()) as ApplicationId
+    for (let i = 0; i < nextAppId.toNumber(); i++) {
+      const wgApplication = await this.wgApplicationById(group, i)
+      if (wgApplication.opening_id.toNumber() !== wgOpeningId) {
+        continue
+      }
+      applications.push(await this.parseApplication(i, wgApplication))
     }
 
-    protected async groupOpeningApplications(group: WorkingGroups, wgOpeningId: number): Promise<GroupApplication[]> {
-        const applications: GroupApplication[] = [];
+    return applications
+  }
 
-        const nextAppId = await this.workingGroupApiQuery(group).nextApplicationId() as ApplicationId;
-        for (let i = 0; i < nextAppId.toNumber(); i++) {
-            const wgApplication = await this.wgApplicationById(group, i);
-            if (wgApplication.opening_id.toNumber() !== wgOpeningId) {
-                continue;
-            }
-            applications.push(await this.parseApplication(i, wgApplication));
-        }
+  async groupOpening(group: WorkingGroups, wgOpeningId: number): Promise<GroupOpening> {
+    const nextId = ((await this.workingGroupApiQuery(group).nextOpeningId()) as OpeningId).toNumber()
+
+    if (wgOpeningId < 0 || wgOpeningId >= nextId) {
+      throw new CLIError('Invalid working group opening ID!')
+    }
 
+    const groupOpening = this.singleLinkageResult<WGOpening>(
+      (await this.workingGroupApiQuery(group).openingById(wgOpeningId)) as LinkageResult
+    )
+
+    const openingId = groupOpening.hiring_opening_id.toNumber()
+    const opening = await this.hiringOpeningById(openingId)
+    const applications = await this.groupOpeningApplications(group, wgOpeningId)
+    const stage = await this.parseOpeningStage(opening.stage)
+    const type = groupOpening.opening_type
+    const stakes = {
+      application: opening.application_staking_policy.unwrapOr(undefined),
+      role: opening.role_staking_policy.unwrapOr(undefined),
+    }
 
-        return applications;
+    return {
+      wgOpeningId,
+      openingId,
+      opening,
+      stage,
+      stakes,
+      applications,
+      type,
+    }
+  }
+
+  async parseOpeningStage(stage: OpeningStage): Promise<GroupOpeningStage> {
+    let status: OpeningStatus | undefined, stageBlock: number | undefined, stageDate: Date | undefined
+
+    if (stage.isOfType('WaitingToBegin')) {
+      const stageData = stage.asType('WaitingToBegin')
+      const currentBlockNumber = (await this._api.derive.chain.bestNumber()).toNumber()
+      const expectedBlockTime = (this._api.consts.babe.expectedBlockTime as Moment).toNumber()
+      status = OpeningStatus.WaitingToBegin
+      stageBlock = stageData.begins_at_block.toNumber()
+      stageDate = new Date(Date.now() + (stageBlock - currentBlockNumber) * expectedBlockTime)
     }
 
-    async groupOpening(group: WorkingGroups, wgOpeningId: number): Promise<GroupOpening> {
-        const nextId = ((await this.workingGroupApiQuery(group).nextOpeningId()) as OpeningId).toNumber();
-
-        if (wgOpeningId < 0 || wgOpeningId >= nextId) {
-            throw new CLIError('Invalid working group opening ID!');
-        }
-
-        const groupOpening = this.singleLinkageResult<WGOpening>(
-            await this.workingGroupApiQuery(group).openingById(wgOpeningId) as LinkageResult
-        );
-
-        const openingId = groupOpening.hiring_opening_id.toNumber();
-        const opening = await this.hiringOpeningById(openingId);
-        const applications = await this.groupOpeningApplications(group, wgOpeningId);
-        const stage = await this.parseOpeningStage(opening.stage);
-        const stakes = {
-            application: opening.application_staking_policy.unwrapOr(undefined),
-            role: opening.role_staking_policy.unwrapOr(undefined)
-        }
-
-        return ({
-            wgOpeningId,
-            openingId,
-            opening,
-            stage,
-            stakes,
-            applications
-        });
+    if (stage.isOfType('Active')) {
+      const stageData = stage.asType('Active')
+      const substage = stageData.stage
+      if (substage.isOfType('AcceptingApplications')) {
+        status = OpeningStatus.AcceptingApplications
+        stageBlock = substage.asType('AcceptingApplications').started_accepting_applicants_at_block.toNumber()
+      }
+      if (substage.isOfType('ReviewPeriod')) {
+        status = OpeningStatus.InReview
+        stageBlock = substage.asType('ReviewPeriod').started_review_period_at_block.toNumber()
+      }
+      if (substage.isOfType('Deactivated')) {
+        status = substage.asType('Deactivated').cause.isOfType('Filled')
+          ? OpeningStatus.Complete
+          : OpeningStatus.Cancelled
+        stageBlock = substage.asType('Deactivated').deactivated_at_block.toNumber()
+      }
+      if (stageBlock) {
+        stageDate = new Date(await this.blockTimestamp(stageBlock))
+      }
     }
 
-    async parseOpeningStage(stage: OpeningStage): Promise<GroupOpeningStage> {
-        let
-            status: OpeningStatus | undefined,
-            stageBlock: number | undefined,
-            stageDate: Date | undefined;
-
-        if (stage.isOfType('WaitingToBegin')) {
-            const stageData = stage.asType('WaitingToBegin');
-            const currentBlockNumber = (await this._api.derive.chain.bestNumber()).toNumber();
-            const expectedBlockTime = (this._api.consts.babe.expectedBlockTime as Moment).toNumber();
-            status = OpeningStatus.WaitingToBegin;
-            stageBlock = stageData.begins_at_block.toNumber();
-            stageDate = new Date(Date.now() + (stageBlock - currentBlockNumber) * expectedBlockTime);
-        }
-
-        if (stage.isOfType('Active')) {
-            const stageData = stage.asType('Active');
-            const substage = stageData.stage;
-            if (substage.isOfType('AcceptingApplications')) {
-                status = OpeningStatus.AcceptingApplications;
-                stageBlock = substage.asType('AcceptingApplications').started_accepting_applicants_at_block.toNumber();
-            }
-            if (substage.isOfType('ReviewPeriod')) {
-                status = OpeningStatus.InReview;
-                stageBlock = substage.asType('ReviewPeriod').started_review_period_at_block.toNumber();
-            }
-            if (substage.isOfType('Deactivated')) {
-                status = substage.asType('Deactivated').cause.isOfType('Filled')
-                    ? OpeningStatus.Complete
-                    : OpeningStatus.Cancelled;
-                stageBlock = substage.asType('Deactivated').deactivated_at_block.toNumber();
-            }
-            if (stageBlock) {
-                stageDate = new Date(await this.blockTimestamp(stageBlock));
-            }
-        }
-
-        return {
-            status: status || OpeningStatus.Unknown,
-            block: stageBlock,
-            date: stageDate
-        };
+    return {
+      status: status || OpeningStatus.Unknown,
+      block: stageBlock,
+      date: stageDate,
     }
+  }
+
+  async getMemberIdsByControllerAccount(address: string): Promise<MemberId[]> {
+    const ids = (await this._api.query.members.memberIdsByControllerAccountId(address)) as Vec<MemberId>
+    return ids.toArray()
+  }
+
+  async workerExitRationaleConstraint(group: WorkingGroups): Promise<InputValidationLengthConstraint> {
+    return (await this.workingGroupApiQuery(group).workerExitRationaleText()) as InputValidationLengthConstraint
+  }
 }

+ 11 - 11
cli/src/ExitCodes.ts

@@ -1,15 +1,15 @@
 enum ExitCodes {
-    OK = 0,
+  OK = 0,
 
-    InvalidInput = 400,
-    FileNotFound = 401,
-    InvalidFile = 402,
-    NoAccountFound = 403,
-    NoAccountSelected = 404,
-    AccessDenied = 405,
+  InvalidInput = 400,
+  FileNotFound = 401,
+  InvalidFile = 402,
+  NoAccountFound = 403,
+  NoAccountSelected = 404,
+  AccessDenied = 405,
 
-    UnexpectedException = 500,
-    FsOperationFailed = 501,
-    ApiError = 502,
+  UnexpectedException = 500,
+  FsOperationFailed = 501,
+  ApiError = 502,
 }
-export = ExitCodes;
+export = ExitCodes

+ 301 - 248
cli/src/Types.ts

@@ -1,318 +1,371 @@
-import BN from 'bn.js';
-import { ElectionStage, Seat } from '@joystream/types/council';
-import { Option, Text } from '@polkadot/types';
-import { Constructor } from '@polkadot/types/types';
-import { Struct, Vec } from '@polkadot/types/codec';
-import { u32 } from '@polkadot/types/primitive';
-import { BlockNumber, Balance, AccountId } from '@polkadot/types/interfaces';
-import { DerivedBalances } from '@polkadot/api-derive/types';
-import { KeyringPair } from '@polkadot/keyring/types';
-import { WorkerId } from '@joystream/types/working-group';
-import { Profile, MemberId } from '@joystream/types/members';
+import BN from 'bn.js'
+import { ElectionStage, Seat } from '@joystream/types/council'
+import { Option, Text } from '@polkadot/types'
+import { Constructor, Codec } from '@polkadot/types/types'
+import { Struct, Vec } from '@polkadot/types/codec'
+import { u32 } from '@polkadot/types/primitive'
+import { BlockNumber, Balance, AccountId } from '@polkadot/types/interfaces'
+import { DerivedBalances } from '@polkadot/api-derive/types'
+import { KeyringPair } from '@polkadot/keyring/types'
+import { WorkerId, OpeningType } from '@joystream/types/working-group'
+import { Profile, MemberId } from '@joystream/types/members'
 import {
-    GenericJoyStreamRoleSchema,
-    JobSpecifics,
-    ApplicationDetails,
-    QuestionSections,
-    QuestionSection,
-    QuestionsFields,
-    QuestionField,
-    EntryInMembershipModuke,
-    HiringProcess,
-    AdditionalRolehiringProcessDetails,
-    CreatorDetails
-} from '@joystream/types/hiring/schemas/role.schema.typings';
-import ajv from 'ajv';
-import { Opening, StakingPolicy, ApplicationStageKeys } from '@joystream/types/hiring';
+  GenericJoyStreamRoleSchema,
+  JobSpecifics,
+  ApplicationDetails,
+  QuestionSections,
+  QuestionSection,
+  QuestionsFields,
+  QuestionField,
+  EntryInMembershipModuke,
+  HiringProcess,
+  AdditionalRolehiringProcessDetails,
+  CreatorDetails,
+} from '@joystream/types/hiring/schemas/role.schema.typings'
+import ajv from 'ajv'
+import { Opening, StakingPolicy, ApplicationStageKeys } from '@joystream/types/hiring'
+import { Validator } from 'inquirer'
 
 // KeyringPair type extended with mandatory "meta.name"
 // It's used for accounts/keys management within CLI.
 // If not provided in the account json file, the meta.name value is set to "Unnamed Account"
 export type NamedKeyringPair = KeyringPair & {
-    meta: {
-        name: string
-    }
+  meta: {
+    name: string
+  }
 }
 
 // Summary of the account information fetched from the api for "account:current" purposes (currently just balances)
 export type AccountSummary = {
-    balances: DerivedBalances
+  balances: DerivedBalances
 }
 
-// Object/Tuple containing council/councilElection information (council:info).
-// The tuple is useful, because that's how api.queryMulti returns the results.
-export type CouncilInfoTuple = Parameters<typeof createCouncilInfoObj>;
-export type CouncilInfoObj = ReturnType<typeof createCouncilInfoObj>;
 // This function allows us to easily transform the tuple into the object
-// and simplifies the creation of consitent Object and Tuple types (seen above).
+// and simplifies the creation of consitent Object and Tuple types (seen below).
 export function createCouncilInfoObj(
-    activeCouncil: Seat[],
-    termEndsAt: BlockNumber,
-    autoStart: Boolean,
-    newTermDuration: BN,
-    candidacyLimit: BN,
-    councilSize: BN,
-    minCouncilStake: Balance,
-    minVotingStake: Balance,
-    announcingPeriod: BlockNumber,
-    votingPeriod: BlockNumber,
-    revealingPeriod: BlockNumber,
-    round: BN,
-    stage: Option<ElectionStage>
+  activeCouncil: Seat[],
+  termEndsAt: BlockNumber,
+  autoStart: boolean,
+  newTermDuration: BN,
+  candidacyLimit: BN,
+  councilSize: BN,
+  minCouncilStake: Balance,
+  minVotingStake: Balance,
+  announcingPeriod: BlockNumber,
+  votingPeriod: BlockNumber,
+  revealingPeriod: BlockNumber,
+  round: BN,
+  stage: Option<ElectionStage>
 ) {
-    return {
-        activeCouncil,
-        termEndsAt,
-        autoStart,
-        newTermDuration,
-        candidacyLimit,
-        councilSize,
-        minCouncilStake,
-        minVotingStake,
-        announcingPeriod,
-        votingPeriod,
-        revealingPeriod,
-        round,
-        stage
-    };
+  return {
+    activeCouncil,
+    termEndsAt,
+    autoStart,
+    newTermDuration,
+    candidacyLimit,
+    councilSize,
+    minCouncilStake,
+    minVotingStake,
+    announcingPeriod,
+    votingPeriod,
+    revealingPeriod,
+    round,
+    stage,
+  }
 }
+// Object/Tuple containing council/councilElection information (council:info).
+// The tuple is useful, because that's how api.queryMulti returns the results.
+export type CouncilInfoTuple = Parameters<typeof createCouncilInfoObj>
+export type CouncilInfoObj = ReturnType<typeof createCouncilInfoObj>
 
 // Object with "name" and "value" properties, used for rendering simple CLI tables like:
 // Total balance:   100 JOY
 // Free calance:     50 JOY
-export type NameValueObj = { name: string, value: string };
+export type NameValueObj = { name: string; value: string }
 
 // Working groups related types
 export enum WorkingGroups {
-    StorageProviders = 'storageProviders'
+  StorageProviders = 'storageProviders',
 }
 
 // In contrast to Pioneer, currently only StorageProviders group is available in CLI
-export const AvailableGroups: readonly WorkingGroups[] = [
-  WorkingGroups.StorageProviders
-] as const;
+export const AvailableGroups: readonly WorkingGroups[] = [WorkingGroups.StorageProviders] as const
+
+export type Reward = {
+  totalRecieved: Balance
+  value: Balance
+  interval?: number
+  nextPaymentBlock: number // 0 = no incoming payment
+}
 
 // Compound working group types
 export type GroupMember = {
-    workerId: WorkerId;
-    memberId: MemberId;
-    roleAccount: AccountId;
-    profile: Profile;
-    stake: Balance;
-    earned: Balance;
+  workerId: WorkerId
+  memberId: MemberId
+  roleAccount: AccountId
+  profile: Profile
+  stake?: Balance
+  reward?: Reward
 }
 
 export type GroupApplication = {
-    wgApplicationId: number;
-    applicationId: number;
-    member: Profile | null;
-    roleAccout: AccountId;
-    stakes: {
-        application: number;
-        role: number;
-    },
-    humanReadableText: string;
-    stage: ApplicationStageKeys;
+  wgApplicationId: number
+  applicationId: number
+  wgOpeningId: number
+  member: Profile | null
+  roleAccout: AccountId
+  stakes: {
+    application: number
+    role: number
+  }
+  humanReadableText: string
+  stage: ApplicationStageKeys
 }
 
 export enum OpeningStatus {
-    WaitingToBegin = 'WaitingToBegin',
-    AcceptingApplications = 'AcceptingApplications',
-    InReview = 'InReview',
-    Complete = 'Complete',
-    Cancelled = 'Cancelled',
-    Unknown = 'Unknown'
+  WaitingToBegin = 'WaitingToBegin',
+  AcceptingApplications = 'AcceptingApplications',
+  InReview = 'InReview',
+  Complete = 'Complete',
+  Cancelled = 'Cancelled',
+  Unknown = 'Unknown',
 }
 
 export type GroupOpeningStage = {
-    status: OpeningStatus;
-    block?: number;
-    date?: Date;
+  status: OpeningStatus
+  block?: number
+  date?: Date
 }
 
 export type GroupOpeningStakes = {
-    application?: StakingPolicy;
-    role?: StakingPolicy;
+  application?: StakingPolicy
+  role?: StakingPolicy
 }
 
 export type GroupOpening = {
-    wgOpeningId: number;
-    openingId: number;
-    stage: GroupOpeningStage;
-    opening: Opening;
-    stakes: GroupOpeningStakes;
-    applications: GroupApplication[];
+  wgOpeningId: number
+  openingId: number
+  stage: GroupOpeningStage
+  opening: Opening
+  stakes: GroupOpeningStakes
+  applications: GroupApplication[]
+  type: OpeningType
 }
 
 // Some helper structs for generating human_readable_text in working group opening extrinsic
 // Note those types are not part of the runtime etc., we just use them to simplify prompting for values
 // (since there exists functionality that handles that for substrate types like: Struct, Vec etc.)
 interface WithJSONable<T> {
-    toJSON: () => T;
+  toJSON: () => T
 }
 export class HRTJobSpecificsStruct extends Struct implements WithJSONable<JobSpecifics> {
-    constructor (value?: JobSpecifics) {
-        super({
-          title: "Text",
-          description: "Text",
-        }, value);
-    }
-    get title(): string {
-        return (this.get('title') as Text).toString();
-    }
-    get description(): string {
-        return (this.get('description') as Text).toString();
-    }
-    toJSON(): JobSpecifics {
-        const { title, description } = this;
-        return { title, description };
-    }
+  constructor(value?: JobSpecifics) {
+    super(
+      {
+        title: 'Text',
+        description: 'Text',
+      },
+      value
+    )
+  }
+  get title(): string {
+    return (this.get('title') as Text).toString()
+  }
+  get description(): string {
+    return (this.get('description') as Text).toString()
+  }
+  toJSON(): JobSpecifics {
+    const { title, description } = this
+    return { title, description }
+  }
 }
 export class HRTEntryInMembershipModukeStruct extends Struct implements WithJSONable<EntryInMembershipModuke> {
-    constructor (value?: EntryInMembershipModuke) {
-        super({
-          handle: "Text",
-        }, value);
-    }
-    get handle(): string {
-        return (this.get('handle') as Text).toString();
-    }
-    toJSON(): EntryInMembershipModuke {
-        const { handle } = this;
-        return { handle };
-    }
+  constructor(value?: EntryInMembershipModuke) {
+    super(
+      {
+        handle: 'Text',
+      },
+      value
+    )
+  }
+  get handle(): string {
+    return (this.get('handle') as Text).toString()
+  }
+  toJSON(): EntryInMembershipModuke {
+    const { handle } = this
+    return { handle }
+  }
 }
 export class HRTCreatorDetailsStruct extends Struct implements WithJSONable<CreatorDetails> {
-    constructor (value?: CreatorDetails) {
-        super({
-          membership: HRTEntryInMembershipModukeStruct,
-        }, value);
-    }
-    get membership(): EntryInMembershipModuke {
-        return (this.get('membership') as HRTEntryInMembershipModukeStruct).toJSON();
-    }
-    toJSON(): CreatorDetails {
-        const { membership } = this;
-        return { membership };
-    }
+  constructor(value?: CreatorDetails) {
+    super(
+      {
+        membership: HRTEntryInMembershipModukeStruct,
+      },
+      value
+    )
+  }
+  get membership(): EntryInMembershipModuke {
+    return (this.get('membership') as HRTEntryInMembershipModukeStruct).toJSON()
+  }
+  toJSON(): CreatorDetails {
+    const { membership } = this
+    return { membership }
+  }
 }
 export class HRTHiringProcessStruct extends Struct implements WithJSONable<HiringProcess> {
-    constructor (value?: HiringProcess) {
-        super({
-          details: "Vec<Text>",
-        }, value);
-    }
-    get details(): AdditionalRolehiringProcessDetails {
-        return (this.get('details') as Vec<Text>).toArray().map(v => v.toString());
-    }
-    toJSON(): HiringProcess {
-        const { details } = this;
-        return { details };
-    }
+  constructor(value?: HiringProcess) {
+    super(
+      {
+        details: 'Vec<Text>',
+      },
+      value
+    )
+  }
+  get details(): AdditionalRolehiringProcessDetails {
+    return (this.get('details') as Vec<Text>).toArray().map((v) => v.toString())
+  }
+  toJSON(): HiringProcess {
+    const { details } = this
+    return { details }
+  }
 }
 export class HRTQuestionFieldStruct extends Struct implements WithJSONable<QuestionField> {
-    constructor (value?: QuestionField) {
-        super({
-            title: "Text",
-            type: "Text"
-        }, value);
-    }
-    get title(): string {
-        return (this.get('title') as Text).toString();
-    }
-    get type(): string {
-        return (this.get('type') as Text).toString();
-    }
-    toJSON(): QuestionField {
-        const { title, type } = this;
-        return { title, type };
-    }
+  constructor(value?: QuestionField) {
+    super(
+      {
+        title: 'Text',
+        type: 'Text',
+      },
+      value
+    )
+  }
+  get title(): string {
+    return (this.get('title') as Text).toString()
+  }
+  get type(): string {
+    return (this.get('type') as Text).toString()
+  }
+  toJSON(): QuestionField {
+    const { title, type } = this
+    return { title, type }
+  }
 }
 class HRTQuestionsFieldsVec extends Vec.with(HRTQuestionFieldStruct) implements WithJSONable<QuestionsFields> {
-    toJSON(): QuestionsFields {
-        return this.toArray().map(v => v.toJSON());
-    }
+  toJSON(): QuestionsFields {
+    return this.toArray().map((v) => v.toJSON())
+  }
 }
 export class HRTQuestionSectionStruct extends Struct implements WithJSONable<QuestionSection> {
-    constructor (value?: QuestionSection) {
-        super({
-            title: "Text",
-            questions: HRTQuestionsFieldsVec
-        }, value);
-    }
-    get title(): string {
-        return (this.get('title') as Text).toString();
-    }
-    get questions(): QuestionsFields {
-        return (this.get('questions') as HRTQuestionsFieldsVec).toJSON();
-    }
-    toJSON(): QuestionSection {
-        const { title, questions } = this;
-        return { title, questions };
-    }
+  constructor(value?: QuestionSection) {
+    super(
+      {
+        title: 'Text',
+        questions: HRTQuestionsFieldsVec,
+      },
+      value
+    )
+  }
+  get title(): string {
+    return (this.get('title') as Text).toString()
+  }
+  get questions(): QuestionsFields {
+    return (this.get('questions') as HRTQuestionsFieldsVec).toJSON()
+  }
+  toJSON(): QuestionSection {
+    const { title, questions } = this
+    return { title, questions }
+  }
+}
+export class HRTQuestionSectionsVec extends Vec.with(HRTQuestionSectionStruct)
+  implements WithJSONable<QuestionSections> {
+  toJSON(): QuestionSections {
+    return this.toArray().map((v) => v.toJSON())
+  }
 }
-export class HRTQuestionSectionsVec extends Vec.with(HRTQuestionSectionStruct) implements WithJSONable<QuestionSections> {
-    toJSON(): QuestionSections {
-        return this.toArray().map(v => v.toJSON());
-    }
-};
 export class HRTApplicationDetailsStruct extends Struct implements WithJSONable<ApplicationDetails> {
-    constructor (value?: ApplicationDetails) {
-        super({
-            sections: HRTQuestionSectionsVec
-        }, value);
-    }
-    get sections(): QuestionSections {
-        return (this.get('sections') as HRTQuestionSectionsVec).toJSON();
-    }
-    toJSON(): ApplicationDetails {
-        const { sections } = this;
-        return { sections };
-    }
+  constructor(value?: ApplicationDetails) {
+    super(
+      {
+        sections: HRTQuestionSectionsVec,
+      },
+      value
+    )
+  }
+  get sections(): QuestionSections {
+    return (this.get('sections') as HRTQuestionSectionsVec).toJSON()
+  }
+  toJSON(): ApplicationDetails {
+    const { sections } = this
+    return { sections }
+  }
 }
 export class HRTStruct extends Struct implements WithJSONable<GenericJoyStreamRoleSchema> {
-    constructor (value?: GenericJoyStreamRoleSchema) {
-        super({
-            version: "u32",
-            headline: "Text",
-            job: HRTJobSpecificsStruct,
-            application: HRTApplicationDetailsStruct,
-            reward: "Text",
-            creator: HRTCreatorDetailsStruct,
-            process: HRTHiringProcessStruct
-        }, value);
-    }
-    get version(): number {
-        return (this.get('version') as u32).toNumber();
-    }
-    get headline(): string {
-        return (this.get('headline') as Text).toString();
-    }
-    get job(): JobSpecifics {
-        return (this.get('job') as HRTJobSpecificsStruct).toJSON();
-    }
-    get application(): ApplicationDetails {
-        return (this.get('application') as HRTApplicationDetailsStruct).toJSON();
-    }
-    get reward(): string {
-        return (this.get('reward') as Text).toString();
-    }
-    get creator(): CreatorDetails {
-        return (this.get('creator') as HRTCreatorDetailsStruct).toJSON();
-    }
-    get process(): HiringProcess {
-        return (this.get('process') as HRTHiringProcessStruct).toJSON();
-    }
-    toJSON(): GenericJoyStreamRoleSchema {
-        const { version, headline, job, application, reward, creator, process } = this;
-        return { version, headline, job, application, reward, creator, process };
-    }
-};
+  constructor(value?: GenericJoyStreamRoleSchema) {
+    super(
+      {
+        version: 'u32',
+        headline: 'Text',
+        job: HRTJobSpecificsStruct,
+        application: HRTApplicationDetailsStruct,
+        reward: 'Text',
+        creator: HRTCreatorDetailsStruct,
+        process: HRTHiringProcessStruct,
+      },
+      value
+    )
+  }
+  get version(): number {
+    return (this.get('version') as u32).toNumber()
+  }
+  get headline(): string {
+    return (this.get('headline') as Text).toString()
+  }
+  get job(): JobSpecifics {
+    return (this.get('job') as HRTJobSpecificsStruct).toJSON()
+  }
+  get application(): ApplicationDetails {
+    return (this.get('application') as HRTApplicationDetailsStruct).toJSON()
+  }
+  get reward(): string {
+    return (this.get('reward') as Text).toString()
+  }
+  get creator(): CreatorDetails {
+    return (this.get('creator') as HRTCreatorDetailsStruct).toJSON()
+  }
+  get process(): HiringProcess {
+    return (this.get('process') as HRTHiringProcessStruct).toJSON()
+  }
+  toJSON(): GenericJoyStreamRoleSchema {
+    const { version, headline, job, application, reward, creator, process } = this
+    return { version, headline, job, application, reward, creator, process }
+  }
+}
+
+// Api-related
 
-// A mapping of argName to json struct and schemaValidator
-// It is used to map arguments of type "Bytes" that are in fact a json string
-// (and can be validated against a schema)
-export type JSONArgsMapping = { [argName: string]: {
-    struct: Constructor<Struct>,
+// Additional options that can be passed to ApiCommandBase.promptForParam in order to override
+// its default behaviour, change param name, add validation etc.
+export type ApiParamOptions<ParamType = Codec> = {
+  forcedName?: string
+  value?: {
+    default: ParamType
+    locked?: boolean
+  }
+  jsonSchema?: {
+    struct: Constructor<Struct>
     schemaValidator: ajv.ValidateFunction
-} };
+  }
+  validator?: Validator
+  nestedOptions?: ApiParamsOptions // For more complex params, like structs
+}
+export type ApiParamsOptions = {
+  [paramName: string]: ApiParamOptions
+}
+
+export type ApiMethodArg = Codec
+export type ApiMethodNamedArg = {
+  name: string
+  value: ApiMethodArg
+}
+export type ApiMethodNamedArgs = ApiMethodNamedArg[]

+ 213 - 224
cli/src/base/AccountsCommandBase.ts

@@ -1,18 +1,18 @@
-import fs from 'fs';
-import path from 'path';
-import slug from 'slug';
-import inquirer from 'inquirer';
-import ExitCodes from '../ExitCodes';
-import { CLIError } from '@oclif/errors';
-import ApiCommandBase from './ApiCommandBase';
-import { Keyring } from '@polkadot/api';
-import { formatBalance } from '@polkadot/util';
-import { NamedKeyringPair } from '../Types';
-import { DerivedBalances } from '@polkadot/api-derive/types';
-import { toFixedLength } from '../helpers/display';
-
-const ACCOUNTS_DIRNAME = 'accounts';
-const SPECIAL_ACCOUNT_POSTFIX = '__DEV';
+import fs from 'fs'
+import path from 'path'
+import slug from 'slug'
+import inquirer from 'inquirer'
+import ExitCodes from '../ExitCodes'
+import { CLIError } from '@oclif/errors'
+import ApiCommandBase from './ApiCommandBase'
+import { Keyring } from '@polkadot/api'
+import { formatBalance } from '@polkadot/util'
+import { NamedKeyringPair } from '../Types'
+import { DerivedBalances } from '@polkadot/api-derive/types'
+import { toFixedLength } from '../helpers/display'
+
+const ACCOUNTS_DIRNAME = 'accounts'
+const SPECIAL_ACCOUNT_POSTFIX = '__DEV'
 
 /**
  * Abstract base class for account-related commands.
@@ -22,216 +22,205 @@ const SPECIAL_ACCOUNT_POSTFIX = '__DEV';
  * Where: APP_DATA_PATH is provided by StateAwareCommandBase and ACCOUNTS_DIRNAME is a const (see above).
  */
 export default abstract class AccountsCommandBase extends ApiCommandBase {
-    getAccountsDirPath(): string {
-        return path.join(this.getAppDataPath(), ACCOUNTS_DIRNAME);
+  getAccountsDirPath(): string {
+    return path.join(this.getAppDataPath(), ACCOUNTS_DIRNAME)
+  }
+
+  getAccountFilePath(account: NamedKeyringPair, isSpecial = false): string {
+    return path.join(this.getAccountsDirPath(), this.generateAccountFilename(account, isSpecial))
+  }
+
+  generateAccountFilename(account: NamedKeyringPair, isSpecial = false): string {
+    return `${slug(account.meta.name, '_')}__${account.address}${isSpecial ? SPECIAL_ACCOUNT_POSTFIX : ''}.json`
+  }
+
+  private initAccountsFs(): void {
+    if (!fs.existsSync(this.getAccountsDirPath())) {
+      fs.mkdirSync(this.getAccountsDirPath())
+    }
+  }
+
+  saveAccount(account: NamedKeyringPair, password: string, isSpecial = false): void {
+    try {
+      const destPath = this.getAccountFilePath(account, isSpecial)
+      fs.writeFileSync(destPath, JSON.stringify(account.toJson(password)))
+    } catch (e) {
+      throw this.createDataWriteError()
+    }
+  }
+
+  // Add dev "Alice" and "Bob" accounts
+  initSpecialAccounts() {
+    const keyring = new Keyring({ type: 'sr25519' })
+    keyring.addFromUri('//Alice', { name: 'Alice' })
+    keyring.addFromUri('//Bob', { name: 'Bob' })
+    keyring.getPairs().forEach((pair) => this.saveAccount({ ...pair, meta: { name: pair.meta.name } }, '', true))
+  }
+
+  fetchAccountFromJsonFile(jsonBackupFilePath: string): NamedKeyringPair {
+    if (!fs.existsSync(jsonBackupFilePath)) {
+      throw new CLIError('Input file does not exist!', { exit: ExitCodes.FileNotFound })
     }
-
-    getAccountFilePath(account: NamedKeyringPair, isSpecial: boolean = false): string {
-        return path.join(this.getAccountsDirPath(), this.generateAccountFilename(account, isSpecial));
-    }
-
-    generateAccountFilename(account: NamedKeyringPair, isSpecial: boolean = false): string {
-        return `${ slug(account.meta.name, '_') }__${ account.address }${ isSpecial ? SPECIAL_ACCOUNT_POSTFIX : '' }.json`;
-    }
-
-    private initAccountsFs(): void {
-        if (!fs.existsSync(this.getAccountsDirPath())) {
-            fs.mkdirSync(this.getAccountsDirPath());
-        }
+    if (path.extname(jsonBackupFilePath) !== '.json') {
+      throw new CLIError('Invalid input file: File extension should be .json', { exit: ExitCodes.InvalidFile })
     }
-
-    saveAccount(account: NamedKeyringPair, password: string, isSpecial: boolean = false): void {
-        try {
-            const destPath = this.getAccountFilePath(account, isSpecial);
-            fs.writeFileSync(destPath, JSON.stringify(account.toJson(password)));
-        } catch(e) {
-            throw this.createDataWriteError();
-        }
-    }
-
-    // Add dev "Alice" and "Bob" accounts
-    initSpecialAccounts() {
-        const keyring = new Keyring({ type: 'sr25519' });
-        keyring.addFromUri('//Alice', { name: 'Alice' });
-        keyring.addFromUri('//Bob', { name: 'Bob' });
-        keyring.getPairs().forEach(pair => this.saveAccount(
-            { ...pair, meta: { name: pair.meta.name } },
-            '',
-            true
-        ));
-    }
-
-    fetchAccountFromJsonFile(jsonBackupFilePath: string): NamedKeyringPair {
-        if (!fs.existsSync(jsonBackupFilePath)) {
-            throw new CLIError('Input file does not exist!', { exit: ExitCodes.FileNotFound });
-        }
-        if (path.extname(jsonBackupFilePath) !== '.json') {
-            throw new CLIError('Invalid input file: File extension should be .json', { exit: ExitCodes.InvalidFile });
-        }
-        let accountJsonObj: any;
-        try {
-            accountJsonObj = require(jsonBackupFilePath);
-        } catch (e) {
-            throw new CLIError('Provided backup file is not valid or cannot be accessed', { exit: ExitCodes.InvalidFile });
-        }
-        if (typeof accountJsonObj !== 'object' || accountJsonObj === null) {
-            throw new CLIError('Provided backup file is not valid', { exit: ExitCodes.InvalidFile });
-        }
-
-        // Force some default account name if none is provided in the original backup
-        if (!accountJsonObj.meta) accountJsonObj.meta = {};
-        if (!accountJsonObj.meta.name) accountJsonObj.meta.name = 'Unnamed Account';
-
-        let keyring = new Keyring();
-        let account:NamedKeyringPair;
-        try {
-            // Try adding and retrieving the keys in order to validate that the backup file is correct
-            keyring.addFromJson(accountJsonObj);
-            account = <NamedKeyringPair> keyring.getPair(accountJsonObj.address); // We can be sure it's named, because we forced it before
-        } catch (e) {
-            throw new CLIError('Provided backup file is not valid', { exit: ExitCodes.InvalidFile });
-        }
-
-        return account;
-    }
-
-    private fetchAccountOrNullFromFile(jsonFilePath: string): NamedKeyringPair | null {
-        try {
-            return this.fetchAccountFromJsonFile(jsonFilePath);
-        } catch (e) {
-            // Here in case of a typical CLIError we just return null (otherwise we throw)
-            if (!(e instanceof CLIError)) throw e;
-            return null;
-        }
-    }
-
-    fetchAccounts(includeSpecial: boolean = false): NamedKeyringPair[] {
-        let files: string[] = [];
-        const accountDir = this.getAccountsDirPath();
-        try {
-            files = fs.readdirSync(accountDir);
-        }
-        catch(e) {
-        }
-
-        // We have to assert the type, because TS is not aware that we're filtering out the nulls at the end
-        return <NamedKeyringPair[]> files
-            .map(fileName => {
-                const filePath = path.join(accountDir, fileName);
-                if (!includeSpecial && filePath.includes(SPECIAL_ACCOUNT_POSTFIX+'.')) return null;
-                return this.fetchAccountOrNullFromFile(filePath);
-            })
-            .filter(accObj => accObj !== null);
-    }
-
-    getSelectedAccountFilename(): string {
-        return this.getPreservedState().selectedAccountFilename;
-    }
-
-    getSelectedAccount(): NamedKeyringPair | null {
-        const selectedAccountFilename = this.getSelectedAccountFilename();
-
-        if (!selectedAccountFilename) {
-            return null;
-        }
-
-        const account = this.fetchAccountOrNullFromFile(
-            path.join(this.getAccountsDirPath(), selectedAccountFilename)
-        );
-
-        return account;
-    }
-
-    // Use when account usage is required in given command
-    async getRequiredSelectedAccount(promptIfMissing: boolean = true): Promise<NamedKeyringPair> {
-        let selectedAccount: NamedKeyringPair | null = this.getSelectedAccount();
-        if (!selectedAccount) {
-            this.warn('No default account selected! Use account:choose to set the default account!');
-            if (!promptIfMissing) this.exit(ExitCodes.NoAccountSelected);
-            const accounts: NamedKeyringPair[] = this.fetchAccounts();
-            if (!accounts.length) {
-                this.error('There are no accounts available!', { exit: ExitCodes.NoAccountFound });
-            }
-
-            selectedAccount = await this.promptForAccount(accounts);
-        }
-
-        return selectedAccount;
-    }
-
-    async setSelectedAccount(account: NamedKeyringPair): Promise<void> {
-        const accountFilename = fs.existsSync(this.getAccountFilePath(account, true))
-            ? this.generateAccountFilename(account, true)
-            : this.generateAccountFilename(account);
-
-        await this.setPreservedState({ selectedAccountFilename: accountFilename });
-    }
-
-    async promptForPassword(message:string = 'Your account\'s password') {
-        const { password } = await inquirer.prompt([
-            { name: 'password', type: 'password', message }
-        ]);
-
-        return password;
-    }
-
-    async requireConfirmation(message: string = 'Are you sure you want to execute this action?'): Promise<void> {
-        const { confirmed } = await inquirer.prompt([
-            { type: 'confirm', name: 'confirmed', message, default: false }
-        ]);
-        if (!confirmed) this.exit(ExitCodes.OK);
-    }
-
-    async promptForAccount(
-        accounts: NamedKeyringPair[],
-        defaultAccount: NamedKeyringPair | null = null,
-        message: string = 'Select an account',
-        showBalances: boolean = true
-    ): Promise<NamedKeyringPair> {
-        let balances: DerivedBalances[];
-        if (showBalances) {
-            balances = await this.getApi().getAccountsBalancesInfo(accounts.map(acc => acc.address));
-        }
-        const longestAccNameLength: number = accounts.reduce((prev, curr) => Math.max(curr.meta.name.length, prev), 0);
-        const accNameColLength: number = Math.min(longestAccNameLength + 1, 20);
-        const { chosenAccountFilename } = await inquirer.prompt([{
-            name: 'chosenAccountFilename',
-            message,
-            type: 'list',
-            choices: accounts.map((account: NamedKeyringPair, i) => ({
-                name: (
-                    `${ toFixedLength(account.meta.name, accNameColLength) } | `+
-                    `${ account.address } | ` +
-                    ((showBalances || '') && (
-                        `${ formatBalance(balances[i].availableBalance) } / `+
-                        `${ formatBalance(balances[i].votingBalance) }`
-                    ))
-                ),
-                value: this.generateAccountFilename(account),
-                short: `${ account.meta.name } (${ account.address })`
-            })),
-            default: defaultAccount && this.generateAccountFilename(defaultAccount)
-        }]);
-
-        return <NamedKeyringPair> accounts.find(acc => this.generateAccountFilename(acc) === chosenAccountFilename);
-    }
-
-    async requestAccountDecoding(account: NamedKeyringPair): Promise<void> {
-        const password: string = await this.promptForPassword();
-        try {
-            account.decodePkcs8(password);
-        } catch (e) {
-            this.error('Invalid password!', { exit: ExitCodes.InvalidInput });
-        }
-    }
-
-    async init() {
-        await super.init();
-        try {
-            this.initAccountsFs();
-            this.initSpecialAccounts();
-        } catch (e) {
-            throw this.createDataDirInitError();
-        }
+    let accountJsonObj: any
+    try {
+      accountJsonObj = require(jsonBackupFilePath)
+    } catch (e) {
+      throw new CLIError('Provided backup file is not valid or cannot be accessed', { exit: ExitCodes.InvalidFile })
+    }
+    if (typeof accountJsonObj !== 'object' || accountJsonObj === null) {
+      throw new CLIError('Provided backup file is not valid', { exit: ExitCodes.InvalidFile })
     }
+
+    // Force some default account name if none is provided in the original backup
+    if (!accountJsonObj.meta) accountJsonObj.meta = {}
+    if (!accountJsonObj.meta.name) accountJsonObj.meta.name = 'Unnamed Account'
+
+    const keyring = new Keyring()
+    let account: NamedKeyringPair
+    try {
+      // Try adding and retrieving the keys in order to validate that the backup file is correct
+      keyring.addFromJson(accountJsonObj)
+      account = keyring.getPair(accountJsonObj.address) as NamedKeyringPair // We can be sure it's named, because we forced it before
+    } catch (e) {
+      throw new CLIError('Provided backup file is not valid', { exit: ExitCodes.InvalidFile })
+    }
+
+    return account
+  }
+
+  private fetchAccountOrNullFromFile(jsonFilePath: string): NamedKeyringPair | null {
+    try {
+      return this.fetchAccountFromJsonFile(jsonFilePath)
+    } catch (e) {
+      // Here in case of a typical CLIError we just return null (otherwise we throw)
+      if (!(e instanceof CLIError)) throw e
+      return null
+    }
+  }
+
+  fetchAccounts(includeSpecial = false): NamedKeyringPair[] {
+    let files: string[] = []
+    const accountDir = this.getAccountsDirPath()
+    try {
+      files = fs.readdirSync(accountDir)
+    } catch (e) {
+      // Do nothing
+    }
+
+    // We have to assert the type, because TS is not aware that we're filtering out the nulls at the end
+    return files
+      .map((fileName) => {
+        const filePath = path.join(accountDir, fileName)
+        if (!includeSpecial && filePath.includes(SPECIAL_ACCOUNT_POSTFIX + '.')) return null
+        return this.fetchAccountOrNullFromFile(filePath)
+      })
+      .filter((accObj) => accObj !== null) as NamedKeyringPair[]
+  }
+
+  getSelectedAccountFilename(): string {
+    return this.getPreservedState().selectedAccountFilename
+  }
+
+  getSelectedAccount(): NamedKeyringPair | null {
+    const selectedAccountFilename = this.getSelectedAccountFilename()
+
+    if (!selectedAccountFilename) {
+      return null
+    }
+
+    const account = this.fetchAccountOrNullFromFile(path.join(this.getAccountsDirPath(), selectedAccountFilename))
+
+    return account
+  }
+
+  // Use when account usage is required in given command
+  async getRequiredSelectedAccount(promptIfMissing = true): Promise<NamedKeyringPair> {
+    let selectedAccount: NamedKeyringPair | null = this.getSelectedAccount()
+    if (!selectedAccount) {
+      this.warn('No default account selected! Use account:choose to set the default account!')
+      if (!promptIfMissing) this.exit(ExitCodes.NoAccountSelected)
+      const accounts: NamedKeyringPair[] = this.fetchAccounts()
+      if (!accounts.length) {
+        this.error('There are no accounts available!', { exit: ExitCodes.NoAccountFound })
+      }
+
+      selectedAccount = await this.promptForAccount(accounts)
+    }
+
+    return selectedAccount
+  }
+
+  async setSelectedAccount(account: NamedKeyringPair): Promise<void> {
+    const accountFilename = fs.existsSync(this.getAccountFilePath(account, true))
+      ? this.generateAccountFilename(account, true)
+      : this.generateAccountFilename(account)
+
+    await this.setPreservedState({ selectedAccountFilename: accountFilename })
+  }
+
+  async promptForPassword(message = "Your account's password") {
+    const { password } = await inquirer.prompt([{ name: 'password', type: 'password', message }])
+
+    return password
+  }
+
+  async requireConfirmation(message = 'Are you sure you want to execute this action?'): Promise<void> {
+    const { confirmed } = await inquirer.prompt([{ type: 'confirm', name: 'confirmed', message, default: false }])
+    if (!confirmed) this.exit(ExitCodes.OK)
+  }
+
+  async promptForAccount(
+    accounts: NamedKeyringPair[],
+    defaultAccount: NamedKeyringPair | null = null,
+    message = 'Select an account',
+    showBalances = true
+  ): Promise<NamedKeyringPair> {
+    let balances: DerivedBalances[]
+    if (showBalances) {
+      balances = await this.getApi().getAccountsBalancesInfo(accounts.map((acc) => acc.address))
+    }
+    const longestAccNameLength: number = accounts.reduce((prev, curr) => Math.max(curr.meta.name.length, prev), 0)
+    const accNameColLength: number = Math.min(longestAccNameLength + 1, 20)
+    const { chosenAccountFilename } = await inquirer.prompt([
+      {
+        name: 'chosenAccountFilename',
+        message,
+        type: 'list',
+        choices: accounts.map((account: NamedKeyringPair, i) => ({
+          name:
+            `${toFixedLength(account.meta.name, accNameColLength)} | ` +
+            `${account.address} | ` +
+            ((showBalances || '') &&
+              `${formatBalance(balances[i].availableBalance)} / ` + `${formatBalance(balances[i].votingBalance)}`),
+          value: this.generateAccountFilename(account),
+          short: `${account.meta.name} (${account.address})`,
+        })),
+        default: defaultAccount && this.generateAccountFilename(defaultAccount),
+      },
+    ])
+
+    return accounts.find((acc) => this.generateAccountFilename(acc) === chosenAccountFilename) as NamedKeyringPair
+  }
+
+  async requestAccountDecoding(account: NamedKeyringPair): Promise<void> {
+    const password: string = await this.promptForPassword()
+    try {
+      account.decodePkcs8(password)
+    } catch (e) {
+      this.error('Invalid password!', { exit: ExitCodes.InvalidInput })
+    }
+  }
+
+  async init() {
+    await super.init()
+    try {
+      this.initAccountsFs()
+      this.initSpecialAccounts()
+    } catch (e) {
+      throw this.createDataDirInitError()
+    }
+  }
 }

+ 374 - 343
cli/src/base/ApiCommandBase.ts

@@ -1,376 +1,407 @@
-import ExitCodes from '../ExitCodes';
-import { CLIError } from '@oclif/errors';
-import StateAwareCommandBase from './StateAwareCommandBase';
-import Api from '../Api';
-import { JSONArgsMapping } from '../Types';
-import { getTypeDef, createType, Option, Tuple, Bytes } from '@polkadot/types';
-import { Codec, TypeDef, TypeDefInfo, Constructor } from '@polkadot/types/types';
-import { Vec, Struct, Enum } from '@polkadot/types/codec';
-import { ApiPromise } from '@polkadot/api';
-import { KeyringPair } from '@polkadot/keyring/types';
-import chalk from 'chalk';
-import { SubmittableResultImpl } from '@polkadot/api/types';
-import ajv from 'ajv';
-
-export type ApiMethodInputArg = Codec;
-
-class ExtrinsicFailedError extends Error { };
+import ExitCodes from '../ExitCodes'
+import { CLIError } from '@oclif/errors'
+import StateAwareCommandBase from './StateAwareCommandBase'
+import Api from '../Api'
+import { getTypeDef, createType, Option, Tuple, Bytes } from '@polkadot/types'
+import { Codec, TypeDef, TypeDefInfo, Constructor } from '@polkadot/types/types'
+import { Vec, Struct, Enum } from '@polkadot/types/codec'
+import { ApiPromise } from '@polkadot/api'
+import { KeyringPair } from '@polkadot/keyring/types'
+import chalk from 'chalk'
+import { SubmittableResultImpl } from '@polkadot/api/types'
+import ajv from 'ajv'
+import { ApiMethodArg, ApiMethodNamedArgs, ApiParamsOptions, ApiParamOptions } from '../Types'
+import { createParamOptions } from '../helpers/promptOptions'
+
+class ExtrinsicFailedError extends Error {}
 
 /**
  * Abstract base class for commands that require access to the API.
  */
 export default abstract class ApiCommandBase extends StateAwareCommandBase {
-    private api: Api | null = null;
-
-    getApi(): Api {
-        if (!this.api) throw new CLIError('Tried to get API before initialization.', { exit: ExitCodes.ApiError });
-        return this.api;
+  private api: Api | null = null
+
+  getApi(): Api {
+    if (!this.api) throw new CLIError('Tried to get API before initialization.', { exit: ExitCodes.ApiError })
+    return this.api
+  }
+
+  // Get original api for lower-level api calls
+  getOriginalApi(): ApiPromise {
+    return this.getApi().getOriginalApi()
+  }
+
+  async init() {
+    await super.init()
+    const apiUri: string = this.getPreservedState().apiUri
+    this.api = await Api.create(apiUri)
+  }
+
+  // This is needed to correctly handle some structs, enums etc.
+  // Where the main typeDef doesn't provide enough information
+  protected getRawTypeDef(type: string) {
+    const instance = createType(type as any)
+    return getTypeDef(instance.toRawType())
+  }
+
+  // Prettifier for type names which are actually JSON strings
+  protected prettifyJsonTypeName(json: string) {
+    const obj = JSON.parse(json) as { [key: string]: string }
+    return (
+      '{\n' +
+      Object.keys(obj)
+        .map((prop) => `  ${prop}${chalk.white(':' + obj[prop])}`)
+        .join('\n') +
+      '\n}'
+    )
+  }
+
+  // Get param name based on TypeDef object
+  protected paramName(typeDef: TypeDef) {
+    return chalk.green(
+      typeDef.displayName ||
+        typeDef.name ||
+        (typeDef.type.startsWith('{') ? this.prettifyJsonTypeName(typeDef.type) : typeDef.type)
+    )
+  }
+
+  // Prompt for simple/plain value (provided as string) of given type
+  async promptForSimple(typeDef: TypeDef, paramOptions?: ApiParamOptions): Promise<Codec> {
+    const providedValue = await this.simplePrompt({
+      message: `Provide value for ${this.paramName(typeDef)}`,
+      type: 'input',
+      // If not default provided - show default value resulting from providing empty string
+      default: paramOptions?.value?.default?.toString() || createType(typeDef.type as any, '').toString(),
+      validate: paramOptions?.validator,
+    })
+    return createType(typeDef.type as any, providedValue)
+  }
+
+  // Prompt for Option<Codec> value
+  async promptForOption(typeDef: TypeDef, paramOptions?: ApiParamOptions): Promise<Option<Codec>> {
+    const subtype = typeDef.sub as TypeDef // We assume that Opion always has a single subtype
+    const defaultValue = paramOptions?.value?.default as Option<Codec> | undefined
+    const confirmed = await this.simplePrompt({
+      message: `Do you want to provide the optional ${this.paramName(typeDef)} parameter?`,
+      type: 'confirm',
+      default: defaultValue ? defaultValue.isSome : false,
+    })
+
+    if (confirmed) {
+      this.openIndentGroup()
+      const value = await this.promptForParam(
+        subtype.type,
+        createParamOptions(subtype.name, defaultValue?.unwrapOr(undefined))
+      )
+      this.closeIndentGroup()
+      return new Option(subtype.type as any, value)
     }
 
-    // Get original api for lower-level api calls
-    getOriginalApi(): ApiPromise {
-        return this.getApi().getOriginalApi();
-    }
+    return new Option(subtype.type as any, null)
+  }
 
-    async init() {
-        await super.init();
-        const apiUri: string = this.getPreservedState().apiUri;
-        this.api = await Api.create(apiUri);
-    }
+  // Prompt for Tuple
+  // TODO: Not well tested yet
+  async promptForTuple(typeDef: TypeDef, paramOptions?: ApiParamOptions): Promise<Tuple> {
+    console.log(chalk.grey(`Providing values for ${this.paramName(typeDef)} tuple:`))
 
-    // This is needed to correctly handle some structs, enums etc.
-    // Where the main typeDef doesn't provide enough information
-    protected getRawTypeDef(type: string) {
-        const instance = createType(type as any);
-        return getTypeDef(instance.toRawType());
-    }
+    this.openIndentGroup()
+    const result: ApiMethodArg[] = []
+    // We assume that for Tuple there is always at least 1 subtype (pethaps it's even always an array?)
+    const subtypes: TypeDef[] = Array.isArray(typeDef.sub) ? typeDef.sub! : [typeDef.sub!]
+    const defaultValue = paramOptions?.value?.default as Tuple | undefined
 
-    // Prettifier for type names which are actually JSON strings
-    protected prettifyJsonTypeName(json: string) {
-        const obj = JSON.parse(json) as { [key: string]: string };
-        return "{\n"+Object.keys(obj).map(prop => `  ${prop}${chalk.white(':'+obj[prop])}`).join("\n")+"\n}";
+    for (const [index, subtype] of Object.entries(subtypes)) {
+      const entryDefaultVal = defaultValue && defaultValue[parseInt(index)]
+      const inputParam = await this.promptForParam(subtype.type, createParamOptions(subtype.name, entryDefaultVal))
+      result.push(inputParam)
     }
-
-    // Get param name based on TypeDef object
-    protected paramName(typeDef: TypeDef) {
-        return chalk.green(
-            typeDef.displayName ||
-            typeDef.name ||
-            (typeDef.type.startsWith('{') ? this.prettifyJsonTypeName(typeDef.type) : typeDef.type)
-        );
+    this.closeIndentGroup()
+
+    return new Tuple(subtypes.map((subtype) => subtype.type) as any, result)
+  }
+
+  // Prompt for Struct
+  async promptForStruct(typeDef: TypeDef, paramOptions?: ApiParamOptions): Promise<ApiMethodArg> {
+    console.log(chalk.grey(`Providing values for ${this.paramName(typeDef)} struct:`))
+
+    this.openIndentGroup()
+    const structType = typeDef.type
+    const rawTypeDef = this.getRawTypeDef(structType)
+    // We assume struct typeDef always has array of typeDefs inside ".sub"
+    const structSubtypes = rawTypeDef.sub as TypeDef[]
+    const structDefault = paramOptions?.value?.default as Struct | undefined
+
+    const structValues: { [key: string]: ApiMethodArg } = {}
+    for (const subtype of structSubtypes) {
+      const fieldOptions = paramOptions?.nestedOptions && paramOptions.nestedOptions[subtype.name!]
+      const fieldDefaultValue = fieldOptions?.value?.default || (structDefault && structDefault.get(subtype.name!))
+      const finalFieldOptions: ApiParamOptions = {
+        ...fieldOptions,
+        forcedName: subtype.name,
+        value: fieldDefaultValue && { ...fieldOptions?.value, default: fieldDefaultValue },
+      }
+      structValues[subtype.name!] = await this.promptForParam(subtype.type, finalFieldOptions)
     }
-
-    // Prompt for simple/plain value (provided as string) of given type
-    async promptForSimple(typeDef: TypeDef, defaultValue?: Codec): Promise<Codec> {
-        const providedValue = await this.simplePrompt({
-            message: `Provide value for ${ this.paramName(typeDef) }`,
-            type: 'input',
-            default: defaultValue?.toString()
-        });
-        return createType(typeDef.type as any, providedValue);
+    this.closeIndentGroup()
+
+    return createType(structType as any, structValues)
+  }
+
+  // Prompt for Vec
+  async promptForVec(typeDef: TypeDef, paramOptions?: ApiParamOptions): Promise<Vec<Codec>> {
+    console.log(chalk.grey(`Providing values for ${this.paramName(typeDef)} vector:`))
+
+    this.openIndentGroup()
+    // We assume Vec always has one TypeDef as ".sub"
+    const subtype = typeDef.sub as TypeDef
+    const defaultValue = paramOptions?.value?.default as Vec<Codec> | undefined
+    const entries: Codec[] = []
+    let addAnother = false
+    do {
+      addAnother = await this.simplePrompt({
+        message: `Do you want to add another entry to ${this.paramName(typeDef)} vector (currently: ${
+          entries.length
+        })?`,
+        type: 'confirm',
+        default: defaultValue ? entries.length < defaultValue.length : false,
+      })
+      const defaultEntryValue = defaultValue && defaultValue[entries.length]
+      if (addAnother) {
+        entries.push(await this.promptForParam(subtype.type, createParamOptions(subtype.name, defaultEntryValue)))
+      }
+    } while (addAnother)
+    this.closeIndentGroup()
+
+    return new Vec(subtype.type as any, entries)
+  }
+
+  // Prompt for Enum
+  async promptForEnum(typeDef: TypeDef, paramOptions?: ApiParamOptions): Promise<Enum> {
+    const enumType = typeDef.type
+    const rawTypeDef = this.getRawTypeDef(enumType)
+    // We assume enum always has array on TypeDefs inside ".sub"
+    const enumSubtypes = rawTypeDef.sub as TypeDef[]
+    const defaultValue = paramOptions?.value?.default as Enum | undefined
+
+    const enumSubtypeName = await this.simplePrompt({
+      message: `Choose value for ${this.paramName(typeDef)}:`,
+      type: 'list',
+      choices: enumSubtypes.map((subtype) => ({
+        name: subtype.name,
+        value: subtype.name,
+      })),
+      default: defaultValue?.type,
+    })
+
+    const enumSubtype = enumSubtypes.find((st) => st.name === enumSubtypeName)!
+
+    if (enumSubtype.type !== 'Null') {
+      const subtypeOptions = createParamOptions(enumSubtype.name, defaultValue?.value)
+      return createType(enumType as any, {
+        [enumSubtype.name!]: await this.promptForParam(enumSubtype.type, subtypeOptions),
+      })
     }
 
-    // Prompt for Option<Codec> value
-    async promptForOption(typeDef: TypeDef, defaultValue?: Option<Codec>): Promise<Option<Codec>> {
-        const subtype = <TypeDef> typeDef.sub; // We assume that Opion always has a single subtype
-        const confirmed = await this.simplePrompt({
-            message: `Do you want to provide the optional ${ this.paramName(typeDef) } parameter?`,
-            type: 'confirm',
-            default: defaultValue ? defaultValue.isSome : false,
-        });
-
-        if (confirmed) {
-            this.openIndentGroup();
-            const value = await this.promptForParam(subtype.type, subtype.name, defaultValue?.unwrapOr(undefined));
-            this.closeIndentGroup();
-            return new Option(subtype.type as any, value);
-        }
-
-        return new Option(subtype.type as any, null);
-    }
+    return createType(enumType as any, enumSubtype.name)
+  }
 
-    // Prompt for Tuple
-    // TODO: Not well tested yet
-    async promptForTuple(typeDef: TypeDef, defaultValue: Tuple): Promise<Tuple> {
-        console.log(chalk.grey(`Providing values for ${ this.paramName(typeDef) } tuple:`));
+  // Prompt for param based on "paramType" string (ie. Option<MemeberId>)
+  // TODO: This may not yet work for all possible types
+  async promptForParam(
+    paramType: string,
+    paramOptions?: ApiParamOptions // TODO: This is not fully implemented for all types yet
+  ): Promise<ApiMethodArg> {
+    const typeDef = getTypeDef(paramType)
+    const rawTypeDef = this.getRawTypeDef(paramType)
 
-        this.openIndentGroup();
-        const result: ApiMethodInputArg[] = [];
-        // We assume that for Tuple there is always at least 1 subtype (pethaps it's even always an array?)
-        const subtypes: TypeDef[] = Array.isArray(typeDef.sub) ? typeDef.sub! : [ typeDef.sub! ];
-
-        for (const [index, subtype] of Object.entries(subtypes)) {
-            const inputParam = await this.promptForParam(subtype.type, subtype.name, defaultValue[parseInt(index)]);
-            result.push(inputParam);
-        }
-        this.closeIndentGroup();
-
-        return new Tuple((subtypes.map(subtype => subtype.type)) as any, result);
+    if (paramOptions?.forcedName) {
+      typeDef.name = paramOptions.forcedName
     }
 
-    // Prompt for Struct
-    async promptForStruct(typeDef: TypeDef, defaultValue?: Struct): Promise<ApiMethodInputArg> {
-        console.log(chalk.grey(`Providing values for ${ this.paramName(typeDef) } struct:`));
-
-        this.openIndentGroup();
-        const structType = typeDef.type;
-        const rawTypeDef = this.getRawTypeDef(structType);
-        // We assume struct typeDef always has array of typeDefs inside ".sub"
-        const structSubtypes = rawTypeDef.sub as TypeDef[];
-
-        const structValues: { [key: string]: ApiMethodInputArg } = {};
-        for (const subtype of structSubtypes) {
-            structValues[subtype.name!] =
-                await this.promptForParam(subtype.type, subtype.name, defaultValue && defaultValue.get(subtype.name!));
-        }
-        this.closeIndentGroup();
-
-        return createType(structType as any, structValues);
+    if (paramOptions?.value?.locked) {
+      return paramOptions.value.default
     }
 
-    // Prompt for Vec
-    async promptForVec(typeDef: TypeDef, defaultValue?: Vec<Codec>): Promise<Vec<Codec>> {
-        console.log(chalk.grey(`Providing values for ${ this.paramName(typeDef) } vector:`));
-
-        this.openIndentGroup();
-        // We assume Vec always has one TypeDef as ".sub"
-        const subtype = typeDef.sub as TypeDef;
-        let entries: Codec[] = [];
-        let addAnother = false;
-        do {
-            addAnother = await this.simplePrompt({
-                message: `Do you want to add another entry to ${ this.paramName(typeDef) } vector (currently: ${entries.length})?`,
-                type: 'confirm',
-                default: defaultValue ? entries.length < defaultValue.length : false
-            });
-            const defaultEntryValue = defaultValue && defaultValue[entries.length];
-            if (addAnother) {
-                entries.push(await this.promptForParam(subtype.type, subtype.name, defaultEntryValue));
-            }
-        } while (addAnother);
-        this.closeIndentGroup();
-
-        return new Vec(subtype.type as any, entries);
+    if (paramOptions?.jsonSchema) {
+      const { struct, schemaValidator } = paramOptions.jsonSchema
+      return await this.promptForJsonBytes(
+        struct,
+        typeDef.name,
+        paramOptions.value?.default as Bytes | undefined,
+        schemaValidator
+      )
     }
 
-    // Prompt for Enum
-    async promptForEnum(typeDef: TypeDef, defaultValue?: Enum): Promise<Enum> {
-        const enumType = typeDef.type;
-        const rawTypeDef = this.getRawTypeDef(enumType);
-        // We assume enum always has array on TypeDefs inside ".sub"
-        const enumSubtypes = rawTypeDef.sub as TypeDef[];
-
-        const enumSubtypeName = await this.simplePrompt({
-            message: `Choose value for ${this.paramName(typeDef)}:`,
-            type: 'list',
-            choices: enumSubtypes.map(subtype => ({
-                name: subtype.name,
-                value: subtype.name
-            })),
-            default: defaultValue?.type
-        });
-
-        const enumSubtype = enumSubtypes.find(st => st.name === enumSubtypeName)!;
-
-        if (enumSubtype.type !== 'Null') {
-            return createType(
-                enumType as any,
-                { [enumSubtype.name!]: await this.promptForParam(enumSubtype.type, enumSubtype.name, defaultValue?.value) }
-            );
-        }
-
-        return createType(enumType as any, enumSubtype.name);
+    if (rawTypeDef.info === TypeDefInfo.Option) {
+      return await this.promptForOption(typeDef, paramOptions)
+    } else if (rawTypeDef.info === TypeDefInfo.Tuple) {
+      return await this.promptForTuple(typeDef, paramOptions)
+    } else if (rawTypeDef.info === TypeDefInfo.Struct) {
+      return await this.promptForStruct(typeDef, paramOptions)
+    } else if (rawTypeDef.info === TypeDefInfo.Enum) {
+      return await this.promptForEnum(typeDef, paramOptions)
+    } else if (rawTypeDef.info === TypeDefInfo.Vec) {
+      return await this.promptForVec(typeDef, paramOptions)
+    } else {
+      return await this.promptForSimple(typeDef, paramOptions)
     }
-
-    // Prompt for param based on "paramType" string (ie. Option<MemeberId>)
-    // TODO: This may not yet work for all possible types
-    async promptForParam(paramType: string, forcedName?: string, defaultValue?: ApiMethodInputArg): Promise<ApiMethodInputArg> {
-        const typeDef = getTypeDef(paramType);
-        const rawTypeDef = this.getRawTypeDef(paramType);
-
-        if (forcedName) {
-            typeDef.name = forcedName;
-        }
-
-        if (rawTypeDef.info === TypeDefInfo.Option) {
-            return await this.promptForOption(typeDef, defaultValue as Option<Codec>);
-        }
-        else if (rawTypeDef.info === TypeDefInfo.Tuple) {
-            return await this.promptForTuple(typeDef, defaultValue as Tuple);
-        }
-        else if (rawTypeDef.info === TypeDefInfo.Struct) {
-            return await this.promptForStruct(typeDef, defaultValue as Struct);
-        }
-        else if (rawTypeDef.info === TypeDefInfo.Enum) {
-            return await this.promptForEnum(typeDef, defaultValue as Enum);
-        }
-        else if (rawTypeDef.info === TypeDefInfo.Vec) {
-            return await this.promptForVec(typeDef, defaultValue as Vec<Codec>);
-        }
-        else {
-            return await this.promptForSimple(typeDef, defaultValue);
-        }
+  }
+
+  async promptForJsonBytes(
+    JsonStruct: Constructor<Struct>,
+    argName?: string,
+    defaultValue?: Bytes,
+    schemaValidator?: ajv.ValidateFunction
+  ) {
+    const rawType = new JsonStruct().toRawType()
+    const typeDef = getTypeDef(rawType)
+
+    const defaultStruct =
+      defaultValue && new JsonStruct(JSON.parse(Buffer.from(defaultValue.toHex().replace('0x', ''), 'hex').toString()))
+
+    if (argName) {
+      typeDef.name = argName
     }
 
-    async promptForJsonBytes(
-        JsonStruct: Constructor<Struct>,
-        argName?: string,
-        defaultValue?: Bytes,
-        schemaValidator?: ajv.ValidateFunction
-    ) {
-        const rawType = (new JsonStruct()).toRawType();
-        const typeDef = getTypeDef(rawType);
-
-        const defaultStruct =
-            defaultValue &&
-            new JsonStruct(JSON.parse(Buffer.from(defaultValue.toHex().replace('0x', ''), 'hex').toString()));
-
-        if (argName) {
-            typeDef.name = argName;
+    let isValid = true,
+      jsonText: string
+    do {
+      const structVal = await this.promptForStruct(typeDef, createParamOptions(typeDef.name, defaultStruct))
+      jsonText = JSON.stringify(structVal.toJSON())
+      if (schemaValidator) {
+        isValid = Boolean(schemaValidator(JSON.parse(jsonText)))
+        if (!isValid) {
+          this.log('\n')
+          this.warn(
+            'Schema validation failed with:\n' +
+              schemaValidator.errors?.map((e) => chalk.red(`${chalk.bold(e.dataPath)}: ${e.message}`)).join('\n') +
+              '\nTry again...'
+          )
+          this.log('\n')
         }
-
-        let isValid: boolean = true, jsonText: string;
-        do {
-            const structVal = await this.promptForStruct(typeDef, defaultStruct);
-            jsonText = JSON.stringify(structVal.toJSON());
-            if (schemaValidator) {
-                isValid = Boolean(schemaValidator(JSON.parse(jsonText)));
-                if (!isValid) {
-                    this.log("\n");
-                    this.warn(
-                        "Schema validation failed with:\n"+
-                        schemaValidator.errors?.map(e => chalk.red(`${chalk.bold(e.dataPath)}: ${e.message}`)).join("\n")+
-                        "\nTry again..."
-                    )
-                    this.log("\n");
-                }
-            }
-        } while(!isValid);
-
-        return new Bytes('0x'+Buffer.from(jsonText, 'ascii').toString('hex'));
+      }
+    } while (!isValid)
+
+    return new Bytes('0x' + Buffer.from(jsonText, 'ascii').toString('hex'))
+  }
+
+  async promptForExtrinsicParams(
+    module: string,
+    method: string,
+    paramsOptions?: ApiParamsOptions
+  ): Promise<ApiMethodArg[]> {
+    const extrinsicMethod = this.getOriginalApi().tx[module][method]
+    const values: ApiMethodArg[] = []
+
+    this.openIndentGroup()
+    for (const arg of extrinsicMethod.meta.args.toArray()) {
+      const argName = arg.name.toString()
+      const argType = arg.type.toString()
+      let argOptions = paramsOptions && paramsOptions[argName]
+      if (!argOptions?.forcedName) {
+        argOptions = { ...argOptions, forcedName: argName }
+      }
+      values.push(await this.promptForParam(argType, argOptions))
     }
-
-    async promptForExtrinsicParams(
-        module: string,
-        method: string,
-        jsonArgs?: JSONArgsMapping,
-        defaultValues?: ApiMethodInputArg[]
-    ): Promise<ApiMethodInputArg[]> {
-        const extrinsicMethod = this.getOriginalApi().tx[module][method];
-        let values: ApiMethodInputArg[] = [];
-
-        this.openIndentGroup();
-        for (const [index, arg] of Object.entries(extrinsicMethod.meta.args.toArray())) {
-            const argName = arg.name.toString();
-            const argType = arg.type.toString();
-            const defaultValue = defaultValues && defaultValues[parseInt(index)];
-            if (jsonArgs && jsonArgs[argName]) {
-                const { struct, schemaValidator } = jsonArgs[argName];
-                values.push(await this.promptForJsonBytes(struct, argName, defaultValue as Bytes, schemaValidator));
-            }
-            else {
-                values.push(await this.promptForParam(argType, argName, defaultValue));
-            }
-        };
-        this.closeIndentGroup();
-
-        return values;
+    this.closeIndentGroup()
+
+    return values
+  }
+
+  sendExtrinsic(account: KeyringPair, module: string, method: string, params: Codec[]) {
+    return new Promise((resolve, reject) => {
+      const extrinsicMethod = this.getOriginalApi().tx[module][method]
+      let unsubscribe: () => void
+      extrinsicMethod(...params)
+        .signAndSend(account, {}, (result: SubmittableResultImpl) => {
+          // Implementation loosely based on /pioneer/packages/react-signer/src/Modal.tsx
+          if (!result || !result.status) {
+            return
+          }
+
+          if (result.status.isFinalized) {
+            unsubscribe()
+            result.events
+              .filter(({ event: { section } }): boolean => section === 'system')
+              .forEach(({ event: { method } }): void => {
+                if (method === 'ExtrinsicFailed') {
+                  reject(new ExtrinsicFailedError('Extrinsic execution error!'))
+                } else if (method === 'ExtrinsicSuccess') {
+                  resolve()
+                }
+              })
+          } else if (result.isError) {
+            reject(new ExtrinsicFailedError('Extrinsic execution error!'))
+          }
+        })
+        .then((unsubFunc) => (unsubscribe = unsubFunc))
+        .catch((e) =>
+          reject(new ExtrinsicFailedError(`Cannot send the extrinsic: ${e.message ? e.message : JSON.stringify(e)}`))
+        )
+    })
+  }
+
+  async sendAndFollowExtrinsic(
+    account: KeyringPair,
+    module: string,
+    method: string,
+    params: Codec[],
+    warnOnly = false // If specified - only warning will be displayed (instead of error beeing thrown)
+  ) {
+    try {
+      this.log(chalk.white(`\nSending ${module}.${method} extrinsic...`))
+      await this.sendExtrinsic(account, module, method, params)
+      this.log(chalk.green(`Extrinsic successful!`))
+    } catch (e) {
+      if (e instanceof ExtrinsicFailedError && warnOnly) {
+        this.warn(`${module}.${method} extrinsic failed! ${e.message}`)
+      } else if (e instanceof ExtrinsicFailedError) {
+        throw new CLIError(`${module}.${method} extrinsic failed! ${e.message}`, { exit: ExitCodes.ApiError })
+      } else {
+        throw e
+      }
     }
-
-    sendExtrinsic(account: KeyringPair, module: string, method: string, params: Codec[]) {
-        return new Promise((resolve, reject) => {
-            const extrinsicMethod = this.getOriginalApi().tx[module][method];
-            let unsubscribe: () => void;
-            extrinsicMethod(...params)
-                .signAndSend(account, {}, (result: SubmittableResultImpl) => {
-                    // Implementation loosely based on /pioneer/packages/react-signer/src/Modal.tsx
-                    if (!result || !result.status) {
-                        return;
-                    }
-
-                    if (result.status.isFinalized) {
-                      unsubscribe();
-                      result.events
-                        .filter(({ event: { section } }): boolean => section === 'system')
-                        .forEach(({ event: { method } }): void => {
-                          if (method === 'ExtrinsicFailed') {
-                            reject(new ExtrinsicFailedError('Extrinsic execution error!'));
-                          } else if (method === 'ExtrinsicSuccess') {
-                            resolve();
-                          }
-                        });
-                    } else if (result.isError) {
-                        reject(new ExtrinsicFailedError('Extrinsic execution error!'));
-                    }
-                })
-                .then(unsubFunc => unsubscribe = unsubFunc)
-                .catch(e => reject(new ExtrinsicFailedError(`Cannot send the extrinsic: ${e.message ? e.message : JSON.stringify(e)}`)));
-        });
+  }
+
+  async buildAndSendExtrinsic(
+    account: KeyringPair,
+    module: string,
+    method: string,
+    paramsOptions: ApiParamsOptions,
+    warnOnly = false // If specified - only warning will be displayed (instead of error beeing thrown)
+  ): Promise<ApiMethodArg[]> {
+    const params = await this.promptForExtrinsicParams(module, method, paramsOptions)
+    await this.sendAndFollowExtrinsic(account, module, method, params, warnOnly)
+
+    return params
+  }
+
+  extrinsicArgsFromDraft(module: string, method: string, draftFilePath: string): ApiMethodNamedArgs {
+    let draftJSONObj
+    const parsedArgs: ApiMethodNamedArgs = []
+    const extrinsicMethod = this.getOriginalApi().tx[module][method]
+    try {
+      // eslint-disable-next-line @typescript-eslint/no-var-requires
+      draftJSONObj = require(draftFilePath)
+    } catch (e) {
+      throw new CLIError(`Could not load draft from: ${draftFilePath}`, { exit: ExitCodes.InvalidFile })
     }
-
-    async sendAndFollowExtrinsic(
-        account: KeyringPair,
-        module: string,
-        method: string,
-        params: Codec[],
-        warnOnly: boolean = false // If specified - only warning will be displayed (instead of error beeing thrown)
-    ) {
-        try {
-            this.log(chalk.white(`\nSending ${ module }.${ method } extrinsic...`));
-            await this.sendExtrinsic(account, module, method, params);
-            this.log(chalk.green(`Extrinsic successful!`));
-        } catch (e) {
-            if (e instanceof ExtrinsicFailedError && warnOnly) {
-                this.warn(`${ module }.${ method } extrinsic failed! ${ e.message }`);
-            }
-            else if (e instanceof ExtrinsicFailedError) {
-                throw new CLIError(`${ module }.${ method } extrinsic failed! ${ e.message }`, { exit: ExitCodes.ApiError });
-            }
-            else {
-                throw e;
-            }
-        }
+    if (!draftJSONObj || !Array.isArray(draftJSONObj) || draftJSONObj.length !== extrinsicMethod.meta.args.length) {
+      throw new CLIError(`The draft file at ${draftFilePath} is invalid!`, { exit: ExitCodes.InvalidFile })
     }
-
-    async buildAndSendExtrinsic(
-        account: KeyringPair,
-        module: string,
-        method: string,
-        jsonArgs?: JSONArgsMapping, // Special JSON arguments (ie. human_readable_text of working group opening)
-        defaultValues?: ApiMethodInputArg[],
-        warnOnly: boolean = false // If specified - only warning will be displayed (instead of error beeing thrown)
-    ): Promise<ApiMethodInputArg[]> {
-        const params = await this.promptForExtrinsicParams(module, method, jsonArgs, defaultValues);
-        await this.sendAndFollowExtrinsic(account, module, method, params, warnOnly);
-
-        return params;
+    for (const [index, arg] of Object.entries(extrinsicMethod.meta.args.toArray())) {
+      const argName = arg.name.toString()
+      const argType = arg.type.toString()
+      try {
+        parsedArgs.push({ name: argName, value: createType(argType as any, draftJSONObj[parseInt(index)]) })
+      } catch (e) {
+        throw new CLIError(`Couldn't parse ${argName} value from draft at ${draftFilePath}!`, {
+          exit: ExitCodes.InvalidFile,
+        })
+      }
     }
 
-    extrinsicArgsFromDraft(module: string, method: string, draftFilePath: string): ApiMethodInputArg[] {
-        let draftJSONObj, parsedArgs: ApiMethodInputArg[] = [];
-        const extrinsicMethod = this.getOriginalApi().tx[module][method];
-        try {
-            draftJSONObj = require(draftFilePath);
-        } catch(e) {
-            throw new CLIError(`Could not load draft from: ${draftFilePath}`, { exit: ExitCodes.InvalidFile });
-        }
-        if (
-            !draftJSONObj
-            || !Array.isArray(draftJSONObj)
-            || draftJSONObj.length !== extrinsicMethod.meta.args.length
-        ) {
-            throw new CLIError(`The draft file at ${draftFilePath} is invalid!`, { exit: ExitCodes.InvalidFile });
-        }
-        for (const [index, arg] of Object.entries(extrinsicMethod.meta.args.toArray())) {
-            const argName = arg.name.toString();
-            const argType = arg.type.toString();
-            try {
-                parsedArgs.push(createType(argType as any, draftJSONObj[parseInt(index)]));
-            } catch (e) {
-                throw new CLIError(`Couldn't parse ${argName} value from draft at ${draftFilePath}!`, { exit: ExitCodes.InvalidFile });
-            }
-        }
-
-        return parsedArgs;
-    }
+    return parsedArgs
+  }
 }

+ 84 - 73
cli/src/base/DefaultCommandBase.ts

@@ -1,95 +1,106 @@
-import ExitCodes from '../ExitCodes';
-import Command from '@oclif/command';
-import inquirer, { DistinctQuestion } from 'inquirer';
-import chalk from 'chalk';
+import ExitCodes from '../ExitCodes'
+import Command from '@oclif/command'
+import inquirer, { DistinctQuestion } from 'inquirer'
+import chalk from 'chalk'
 
 /**
  * Abstract base class for pretty much all commands
  * (prevents console.log from hanging the process and unifies the default exit code)
  */
 export default abstract class DefaultCommandBase extends Command {
-    protected indentGroupsOpened = 0;
-    protected jsonPrettyIdent = '';
+  protected indentGroupsOpened = 0
+  protected jsonPrettyIdent = ''
 
-    openIndentGroup() {
-        console.group();
-        ++this.indentGroupsOpened;
-    }
-
-    closeIndentGroup() {
-        console.groupEnd();
-        --this.indentGroupsOpened;
-    }
+  openIndentGroup() {
+    console.group()
+    ++this.indentGroupsOpened
+  }
 
-    async simplePrompt(question: DistinctQuestion) {
-        const { result } = await inquirer.prompt([{
-            ...question,
-            name: 'result',
-            // prefix = 2 spaces for each group - 1 (because 1 is always added by default)
-            prefix: Array.from(new Array(this.indentGroupsOpened)).map(() => '  ').join('').slice(1)
-        }]);
+  closeIndentGroup() {
+    console.groupEnd()
+    --this.indentGroupsOpened
+  }
 
-        return result;
-    }
+  async simplePrompt(question: DistinctQuestion) {
+    const { result } = await inquirer.prompt([
+      {
+        ...question,
+        name: 'result',
+        // prefix = 2 spaces for each group - 1 (because 1 is always added by default)
+        prefix: Array.from(new Array(this.indentGroupsOpened))
+          .map(() => '  ')
+          .join('')
+          .slice(1),
+      },
+    ])
 
-    private jsonPrettyIndented(line:string) {
-        return `${this.jsonPrettyIdent}${ line }`;
-    }
+    return result
+  }
 
-    private jsonPrettyOpen(char: '{' | '[') {
-        this.jsonPrettyIdent += '    ';
-        return chalk.gray(char)+"\n";
-    }
+  private jsonPrettyIndented(line: string) {
+    return `${this.jsonPrettyIdent}${line}`
+  }
 
-    private jsonPrettyClose(char: '}' | ']') {
-        this.jsonPrettyIdent = this.jsonPrettyIdent.slice(0, -4);
-        return this.jsonPrettyIndented(chalk.gray(char));
-    }
+  private jsonPrettyOpen(char: '{' | '[') {
+    this.jsonPrettyIdent += '    '
+    return chalk.gray(char) + '\n'
+  }
 
-    private jsonPrettyKeyVal(key:string, val:any): string {
-        return this.jsonPrettyIndented(chalk.white(`${key}: ${this.jsonPrettyAny(val)}`));
-    }
+  private jsonPrettyClose(char: '}' | ']') {
+    this.jsonPrettyIdent = this.jsonPrettyIdent.slice(0, -4)
+    return this.jsonPrettyIndented(chalk.gray(char))
+  }
 
-    private jsonPrettyObj(obj: { [key: string]: any }): string {
-        return this.jsonPrettyOpen('{')
-            + Object.keys(obj).map(k => this.jsonPrettyKeyVal(k, obj[k])).join(',\n') + "\n"
-            + this.jsonPrettyClose('}');
-    }
+  private jsonPrettyKeyVal(key: string, val: any): string {
+    return this.jsonPrettyIndented(chalk.white(`${key}: ${this.jsonPrettyAny(val)}`))
+  }
 
-    private jsonPrettyArr(arr: any[]): string {
-        return this.jsonPrettyOpen('[')
-            + arr.map(v => this.jsonPrettyIndented(this.jsonPrettyAny(v))).join(',\n') + "\n"
-            + this.jsonPrettyClose(']');
-    }
+  private jsonPrettyObj(obj: { [key: string]: any }): string {
+    return (
+      this.jsonPrettyOpen('{') +
+      Object.keys(obj)
+        .map((k) => this.jsonPrettyKeyVal(k, obj[k]))
+        .join(',\n') +
+      '\n' +
+      this.jsonPrettyClose('}')
+    )
+  }
 
-    private jsonPrettyAny(val: any): string {
-        if (Array.isArray(val)) {
-            return this.jsonPrettyArr(val);
-        }
-        else if (typeof val === 'object' && val !== null) {
-            return this.jsonPrettyObj(val);
-        }
-        else if (typeof val === 'string') {
-            return chalk.green(`"${val}"`);
-        }
+  private jsonPrettyArr(arr: any[]): string {
+    return (
+      this.jsonPrettyOpen('[') +
+      arr.map((v) => this.jsonPrettyIndented(this.jsonPrettyAny(v))).join(',\n') +
+      '\n' +
+      this.jsonPrettyClose(']')
+    )
+  }
 
-        // Number, boolean etc.
-        return chalk.cyan(val);
+  private jsonPrettyAny(val: any): string {
+    if (Array.isArray(val)) {
+      return this.jsonPrettyArr(val)
+    } else if (typeof val === 'object' && val !== null) {
+      return this.jsonPrettyObj(val)
+    } else if (typeof val === 'string') {
+      return chalk.green(`"${val}"`)
     }
 
-    jsonPrettyPrint(json: string) {
-        try {
-            const parsed = JSON.parse(json);
-            console.log(this.jsonPrettyAny(parsed));
-        } catch(e) {
-            console.log(this.jsonPrettyAny(json));
-        }
-    }
+    // Number, boolean etc.
+    return chalk.cyan(val)
+  }
 
-    async finally(err: any) {
-        // called after run and catch regardless of whether or not the command errored
-        // We'll force exit here, in case there is no error, to prevent console.log from hanging the process
-        if (!err) this.exit(ExitCodes.OK);
-        super.finally(err);
+  jsonPrettyPrint(json: string) {
+    try {
+      const parsed = JSON.parse(json)
+      console.log(this.jsonPrettyAny(parsed))
+    } catch (e) {
+      console.log(this.jsonPrettyAny(json))
     }
+  }
+
+  async finally(err: any) {
+    // called after run and catch regardless of whether or not the command errored
+    // We'll force exit here, in case there is no error, to prevent console.log from hanging the process
+    if (!err) this.exit(ExitCodes.OK)
+    super.finally(err)
+  }
 }

+ 94 - 95
cli/src/base/StateAwareCommandBase.ts

@@ -1,32 +1,32 @@
-import fs from 'fs';
-import path from 'path';
-import ExitCodes from '../ExitCodes';
-import { CLIError } from '@oclif/errors';
-import { DEFAULT_API_URI } from '../Api';
-import lockFile from 'proper-lockfile';
-import DefaultCommandBase from './DefaultCommandBase';
-import os from 'os';
+import fs from 'fs'
+import path from 'path'
+import ExitCodes from '../ExitCodes'
+import { CLIError } from '@oclif/errors'
+import { DEFAULT_API_URI } from '../Api'
+import lockFile from 'proper-lockfile'
+import DefaultCommandBase from './DefaultCommandBase'
+import os from 'os'
 
 // Type for the state object (which is preserved as json in the state file)
 type StateObject = {
-    selectedAccountFilename: string,
-    apiUri: string
-};
+  selectedAccountFilename: string
+  apiUri: string
+}
 
 // State object default values
 const DEFAULT_STATE: StateObject = {
-    selectedAccountFilename: '',
-    apiUri: DEFAULT_API_URI
+  selectedAccountFilename: '',
+  apiUri: DEFAULT_API_URI,
 }
 
 // State file path (relative to getAppDataPath())
-const STATE_FILE = '/state.json';
+const STATE_FILE = '/state.json'
 
 // Possible data directory access errors
 enum DataDirErrorType {
-    Init = 0,
-    Read = 1,
-    Write = 2,
+  Init = 0,
+  Read = 1,
+  Write = 2,
 }
 
 /**
@@ -37,95 +37,94 @@ enum DataDirErrorType {
  * choosen by the user after executing account:choose command etc. (see "StateObject" type above).
  */
 export default abstract class StateAwareCommandBase extends DefaultCommandBase {
-    getAppDataPath(): string {
-        const systemAppDataPath =
-            process.env.APPDATA ||
-            (
-                process.platform === 'darwin'
-                    ? path.join(os.homedir(), '/Library/Application Support')
-                    : path.join(os.homedir(), '/.local/share')
-            );
-        const packageJson: { name?: string } = require('../../package.json');
-        if (!packageJson || !packageJson.name) {
-            throw new CLIError('Cannot get package name from package.json!');
-        }
-        return path.join(systemAppDataPath, packageJson.name);
+  getAppDataPath(): string {
+    const systemAppDataPath =
+      process.env.APPDATA ||
+      (process.platform === 'darwin'
+        ? path.join(os.homedir(), '/Library/Application Support')
+        : path.join(os.homedir(), '/.local/share'))
+    // eslint-disable-next-line @typescript-eslint/no-var-requires
+    const packageJson: { name?: string } = require('../../package.json')
+    if (!packageJson || !packageJson.name) {
+      throw new CLIError('Cannot get package name from package.json!')
     }
-
-    getStateFilePath(): string {
-        return path.join(this.getAppDataPath(), STATE_FILE);
+    return path.join(systemAppDataPath, packageJson.name)
+  }
+
+  getStateFilePath(): string {
+    return path.join(this.getAppDataPath(), STATE_FILE)
+  }
+
+  private createDataDirFsError(errorType: DataDirErrorType, specificPath = '') {
+    const actionStrs: { [x in DataDirErrorType]: string } = {
+      [DataDirErrorType.Init]: 'initialize',
+      [DataDirErrorType.Read]: 'read from',
+      [DataDirErrorType.Write]: 'write into',
     }
 
-    private createDataDirFsError(errorType: DataDirErrorType, specificPath: string = '') {
-        const actionStrs: { [x in DataDirErrorType]: string } = {
-            [DataDirErrorType.Init]: 'initialize',
-            [DataDirErrorType.Read]: 'read from',
-            [DataDirErrorType.Write]: 'write into'
-        };
+    const errorMsg =
+      `Unexpected error while trying to ${actionStrs[errorType]} the data directory.` +
+      `(${path.join(this.getAppDataPath(), specificPath)})! Permissions issue?`
 
-        const errorMsg =
-            `Unexpected error while trying to ${ actionStrs[errorType] } the data directory.`+
-            `(${ path.join(this.getAppDataPath(), specificPath) })! Permissions issue?`;
+    return new CLIError(errorMsg, { exit: ExitCodes.FsOperationFailed })
+  }
 
-        return new CLIError(errorMsg, { exit: ExitCodes.FsOperationFailed });
-    }
+  createDataReadError(specificPath = ''): CLIError {
+    return this.createDataDirFsError(DataDirErrorType.Read, specificPath)
+  }
 
-    createDataReadError(specificPath: string = ''): CLIError {
-        return this.createDataDirFsError(DataDirErrorType.Read, specificPath);
-    }
+  createDataWriteError(specificPath = ''): CLIError {
+    return this.createDataDirFsError(DataDirErrorType.Write, specificPath)
+  }
 
-    createDataWriteError(specificPath: string = ''): CLIError {
-        return this.createDataDirFsError(DataDirErrorType.Write, specificPath);
-    }
+  createDataDirInitError(specificPath = ''): CLIError {
+    return this.createDataDirFsError(DataDirErrorType.Init, specificPath)
+  }
 
-    createDataDirInitError(specificPath: string = ''): CLIError {
-        return this.createDataDirFsError(DataDirErrorType.Init, specificPath);
+  private initStateFs(): void {
+    if (!fs.existsSync(this.getAppDataPath())) {
+      fs.mkdirSync(this.getAppDataPath())
     }
-
-    private initStateFs(): void {
-        if (!fs.existsSync(this.getAppDataPath())) {
-            fs.mkdirSync(this.getAppDataPath());
-        }
-        if (!fs.existsSync(this.getStateFilePath())) {
-            fs.writeFileSync(this.getStateFilePath(), JSON.stringify(DEFAULT_STATE));
-        }
+    if (!fs.existsSync(this.getStateFilePath())) {
+      fs.writeFileSync(this.getStateFilePath(), JSON.stringify(DEFAULT_STATE))
     }
-
-    getPreservedState(): StateObject {
-        let preservedState: StateObject;
-        try {
-            preservedState = <StateObject> require(this.getStateFilePath());
-        } catch(e) {
-            throw this.createDataReadError();
-        }
-        // The state preserved in a file may be missing some required values ie.
-        // if the user previously used the older version of the software.
-        // That's why we combine it with default state before returing.
-        return { ...DEFAULT_STATE, ...preservedState };
+  }
+
+  getPreservedState(): StateObject {
+    let preservedState: StateObject
+    try {
+      preservedState = require(this.getStateFilePath()) as StateObject
+    } catch (e) {
+      throw this.createDataReadError()
     }
-
-    // Modifies preserved state. Uses file lock in order to avoid updating an older state.
-    // (which could potentialy change between read and write operation)
-    async setPreservedState(modifiedState: Partial<StateObject>): Promise<void> {
-        const stateFilePath = this.getStateFilePath();
-        const unlock = await lockFile.lock(stateFilePath);
-        let oldState: StateObject = this.getPreservedState();
-        let newState: StateObject = { ...oldState, ...modifiedState };
-        try {
-            fs.writeFileSync(stateFilePath, JSON.stringify(newState));
-        } catch(e) {
-            await unlock();
-            throw this.createDataWriteError();
-        }
-        await unlock();
+    // The state preserved in a file may be missing some required values ie.
+    // if the user previously used the older version of the software.
+    // That's why we combine it with default state before returing.
+    return { ...DEFAULT_STATE, ...preservedState }
+  }
+
+  // Modifies preserved state. Uses file lock in order to avoid updating an older state.
+  // (which could potentialy change between read and write operation)
+  async setPreservedState(modifiedState: Partial<StateObject>): Promise<void> {
+    const stateFilePath = this.getStateFilePath()
+    const unlock = await lockFile.lock(stateFilePath)
+    const oldState: StateObject = this.getPreservedState()
+    const newState: StateObject = { ...oldState, ...modifiedState }
+    try {
+      fs.writeFileSync(stateFilePath, JSON.stringify(newState))
+    } catch (e) {
+      await unlock()
+      throw this.createDataWriteError()
     }
-
-    async init() {
-        await super.init();
-        try {
-            await this.initStateFs();
-        } catch (e) {
-            throw this.createDataDirInitError();
-        }
+    await unlock()
+  }
+
+  async init() {
+    await super.init()
+    try {
+      await this.initStateFs()
+    } catch (e) {
+      throw this.createDataDirInitError()
     }
+  }
 }

+ 241 - 160
cli/src/base/WorkingGroupsCommandBase.ts

@@ -1,190 +1,271 @@
-import ExitCodes from '../ExitCodes';
-import AccountsCommandBase from './AccountsCommandBase';
-import { flags } from '@oclif/command';
-import { WorkingGroups, AvailableGroups, NamedKeyringPair, GroupMember, GroupOpening } from '../Types';
-import { apiModuleByGroup } from '../Api';
-import { CLIError } from '@oclif/errors';
-import inquirer from 'inquirer';
-import { ApiMethodInputArg } from './ApiCommandBase';
-import fs from 'fs';
-import path from 'path';
-import _ from 'lodash';
-import { ApplicationStageKeys } from '@joystream/types/hiring';
-
-const DEFAULT_GROUP = WorkingGroups.StorageProviders;
-const DRAFTS_FOLDER = 'opening-drafts';
+import ExitCodes from '../ExitCodes'
+import AccountsCommandBase from './AccountsCommandBase'
+import { flags } from '@oclif/command'
+import {
+  WorkingGroups,
+  AvailableGroups,
+  NamedKeyringPair,
+  GroupMember,
+  GroupOpening,
+  ApiMethodArg,
+  ApiMethodNamedArgs,
+  OpeningStatus,
+  GroupApplication,
+} from '../Types'
+import { apiModuleByGroup } from '../Api'
+import { CLIError } from '@oclif/errors'
+import fs from 'fs'
+import path from 'path'
+import _ from 'lodash'
+import { ApplicationStageKeys } from '@joystream/types/hiring'
+
+const DEFAULT_GROUP = WorkingGroups.StorageProviders
+const DRAFTS_FOLDER = 'opening-drafts'
 
 /**
  * Abstract base class for commands related to working groups
  */
 export default abstract class WorkingGroupsCommandBase extends AccountsCommandBase {
-    group: WorkingGroups = DEFAULT_GROUP;
-
-    static flags = {
-        group: flags.string({
-            char: 'g',
-            description:
-                "The working group context in which the command should be executed\n" +
-                `Available values are: ${AvailableGroups.join(', ')}.`,
-            required: true,
-            default: DEFAULT_GROUP
-        }),
-    };
-
-    // Use when lead access is required in given command
-    async getRequiredLead(): Promise<GroupMember> {
-        let selectedAccount: NamedKeyringPair = await this.getRequiredSelectedAccount();
-        let lead = await this.getApi().groupLead(this.group);
-
-        if (!lead || lead.roleAccount.toString() !== selectedAccount.address) {
-            this.error('Lead access required for this command!', { exit: ExitCodes.AccessDenied });
-        }
-
-        return lead;
+  group: WorkingGroups = DEFAULT_GROUP
+
+  static flags = {
+    group: flags.string({
+      char: 'g',
+      description:
+        'The working group context in which the command should be executed\n' +
+        `Available values are: ${AvailableGroups.join(', ')}.`,
+      required: true,
+      default: DEFAULT_GROUP,
+    }),
+  }
+
+  // Use when lead access is required in given command
+  async getRequiredLead(): Promise<GroupMember> {
+    const selectedAccount: NamedKeyringPair = await this.getRequiredSelectedAccount()
+    const lead = await this.getApi().groupLead(this.group)
+
+    if (!lead || lead.roleAccount.toString() !== selectedAccount.address) {
+      this.error('Lead access required for this command!', { exit: ExitCodes.AccessDenied })
     }
 
-    // Use when worker access is required in given command
-    async getRequiredWorker(): Promise<GroupMember> {
-        let selectedAccount: NamedKeyringPair = await this.getRequiredSelectedAccount();
-        let groupMembers = await this.getApi().groupMembers(this.group);
-        let groupMembersByAccount = groupMembers.filter(m => m.roleAccount.toString() === selectedAccount.address);
-
-        if (!groupMembersByAccount.length) {
-            this.error('Worker access required for this command!', { exit: ExitCodes.AccessDenied });
-        }
-        else if (groupMembersByAccount.length === 1) {
-            return groupMembersByAccount[0];
-        }
-        else {
-            return await this.promptForWorker(groupMembersByAccount);
-        }
+    return lead
+  }
+
+  // Use when worker access is required in given command
+  async getRequiredWorker(): Promise<GroupMember> {
+    const selectedAccount: NamedKeyringPair = await this.getRequiredSelectedAccount()
+    const groupMembers = await this.getApi().groupMembers(this.group)
+    const groupMembersByAccount = groupMembers.filter((m) => m.roleAccount.toString() === selectedAccount.address)
+
+    if (!groupMembersByAccount.length) {
+      this.error('Worker access required for this command!', { exit: ExitCodes.AccessDenied })
+    } else if (groupMembersByAccount.length === 1) {
+      return groupMembersByAccount[0]
+    } else {
+      return await this.promptForWorker(groupMembersByAccount)
     }
+  }
 
-    async promptForWorker(groupMembers: GroupMember[]): Promise<GroupMember> {
-        const { choosenWorkerIndex } = await inquirer.prompt([{
-            name: 'chosenWorkerIndex',
-            message: 'Choose the worker to execute the command as',
-            type: 'list',
-            choices: groupMembers.map((groupMember, index) => ({
-                name: `Worker ID ${ groupMember.workerId.toString() }`,
-                value: index
-            }))
-        }]);
-
-        return groupMembers[choosenWorkerIndex];
+  // Use when member controller access is required, but one of the associated roles is expected to be selected
+  async getRequiredWorkerByMemberController(): Promise<GroupMember> {
+    const selectedAccount: NamedKeyringPair = await this.getRequiredSelectedAccount()
+    const memberIds = await this.getApi().getMemberIdsByControllerAccount(selectedAccount.address)
+    const controlledWorkers = (await this.getApi().groupMembers(this.group)).filter((groupMember) =>
+      memberIds.some((memberId) => groupMember.memberId.eq(memberId))
+    )
+
+    if (!controlledWorkers.length) {
+      this.error(`Member controller account with some associated ${this.group} group roles needs to be selected!`, {
+        exit: ExitCodes.AccessDenied,
+      })
+    } else if (controlledWorkers.length === 1) {
+      return controlledWorkers[0]
+    } else {
+      return await this.promptForWorker(controlledWorkers)
     }
+  }
+
+  async promptForWorker(groupMembers: GroupMember[]): Promise<GroupMember> {
+    const chosenWorkerIndex = await this.simplePrompt({
+      message: 'Choose the intended worker context:',
+      type: 'list',
+      choices: groupMembers.map((groupMember, index) => ({
+        name: `Worker ID ${groupMember.workerId.toString()}`,
+        value: index,
+      })),
+    })
 
-    async promptForApplicationsToAccept(opening: GroupOpening): Promise<number[]> {
-        const acceptableApplications = opening.applications.filter(a => a.stage === ApplicationStageKeys.Active);
-        const acceptedApplications = await this.simplePrompt({
-            message: 'Select succesful applicants',
-            type: 'checkbox',
-            choices: acceptableApplications.map(a => ({
-                name: ` ${a.wgApplicationId}: ${a.member?.handle.toString()}`,
-                value: a.wgApplicationId,
-            }))
-        });
-
-        return acceptedApplications;
+    return groupMembers[chosenWorkerIndex]
+  }
+
+  async promptForApplicationsToAccept(opening: GroupOpening): Promise<number[]> {
+    const acceptableApplications = opening.applications.filter((a) => a.stage === ApplicationStageKeys.Active)
+    const acceptedApplications = await this.simplePrompt({
+      message: 'Select succesful applicants',
+      type: 'checkbox',
+      choices: acceptableApplications.map((a) => ({
+        name: ` ${a.wgApplicationId}: ${a.member?.handle.toString()}`,
+        value: a.wgApplicationId,
+      })),
+    })
+
+    return acceptedApplications
+  }
+
+  async promptForNewOpeningDraftName() {
+    let draftName = '',
+      fileExists = false,
+      overrideConfirmed = false
+
+    do {
+      draftName = await this.simplePrompt({
+        type: 'input',
+        message: 'Provide the draft name',
+        validate: (val) => (typeof val === 'string' && val.length >= 1) || 'Draft name is required!',
+      })
+
+      fileExists = fs.existsSync(this.getOpeningDraftPath(draftName))
+      if (fileExists) {
+        overrideConfirmed = await this.simplePrompt({
+          type: 'confirm',
+          message: 'Such draft already exists. Do you wish to override it?',
+          default: false,
+        })
+      }
+    } while (fileExists && !overrideConfirmed)
+
+    return draftName
+  }
+
+  async promptForOpeningDraft() {
+    let draftFiles: string[] = []
+    try {
+      draftFiles = fs.readdirSync(this.getOpeingDraftsPath())
+    } catch (e) {
+      throw this.createDataReadError(DRAFTS_FOLDER)
+    }
+    if (!draftFiles.length) {
+      throw new CLIError('No drafts available!', { exit: ExitCodes.FileNotFound })
     }
+    const draftNames = draftFiles.map((fileName) => _.startCase(fileName.replace('.json', '')))
+    const selectedDraftName = await this.simplePrompt({
+      message: 'Select a draft',
+      type: 'list',
+      choices: draftNames,
+    })
 
-    async promptForNewOpeningDraftName() {
-        let
-            draftName: string = '',
-            fileExists: boolean = false,
-            overrideConfirmed: boolean = false;
-
-        do {
-            draftName = await this.simplePrompt({
-                type: 'input',
-                message: 'Provide the draft name',
-                validate: val => (typeof val === 'string' && val.length >= 1) || 'Draft name is required!'
-            });
-
-            fileExists = fs.existsSync(this.getOpeningDraftPath(draftName));
-            if (fileExists) {
-                overrideConfirmed = await this.simplePrompt({
-                    type: 'confirm',
-                    message: 'Such draft already exists. Do you wish to override it?',
-                    default: false
-                });
-            }
-        } while(fileExists && !overrideConfirmed);
-
-        return draftName;
+    return selectedDraftName
+  }
+
+  async getOpeningForLeadAction(id: number, requiredStatus?: OpeningStatus): Promise<GroupOpening> {
+    const opening = await this.getApi().groupOpening(this.group, id)
+
+    if (!opening.type.isOfType('Worker')) {
+      this.error('A lead can only manage Worker openings!', { exit: ExitCodes.AccessDenied })
     }
 
-    async promptForOpeningDraft() {
-        let draftFiles: string[] = [];
-        try {
-            draftFiles = fs.readdirSync(this.getOpeingDraftsPath());
-        }
-        catch(e) {
-            throw this.createDataReadError(DRAFTS_FOLDER);
-        }
-        if (!draftFiles.length) {
-            throw new CLIError('No drafts available!', { exit: ExitCodes.FileNotFound });
-        }
-        const draftNames = draftFiles.map(fileName => _.startCase(fileName.replace('.json', '')));
-        const selectedDraftName = await this.simplePrompt({
-            message: 'Select a draft',
-            type: 'list',
-            choices: draftNames
-        });
-
-        return selectedDraftName;
+    if (requiredStatus && opening.stage.status !== requiredStatus) {
+      this.error(
+        `The opening needs to be in "${_.startCase(requiredStatus)}" stage! ` +
+          `This one is: "${_.startCase(opening.stage.status)}"`,
+        { exit: ExitCodes.InvalidInput }
+      )
     }
 
-    loadOpeningDraftParams(draftName: string) {
-        const draftFilePath = this.getOpeningDraftPath(draftName);
-        const params = this.extrinsicArgsFromDraft(
-            apiModuleByGroup[this.group],
-            'addOpening',
-            draftFilePath
-        );
+    return opening
+  }
+
+  // An alias for better code readibility in case we don't need the actual return value
+  validateOpeningForLeadAction = this.getOpeningForLeadAction
+
+  async getApplicationForLeadAction(id: number, requiredStatus?: ApplicationStageKeys): Promise<GroupApplication> {
+    const application = await this.getApi().groupApplication(this.group, id)
+    const opening = await this.getApi().groupOpening(this.group, application.wgOpeningId)
 
-        return params;
+    if (!opening.type.isOfType('Worker')) {
+      this.error('A lead can only manage Worker opening applications!', { exit: ExitCodes.AccessDenied })
     }
 
-    getOpeingDraftsPath() {
-        return path.join(this.getAppDataPath(), DRAFTS_FOLDER);
+    if (requiredStatus && application.stage !== requiredStatus) {
+      this.error(
+        `The application needs to have "${_.startCase(requiredStatus)}" status! ` +
+          `This one has: "${_.startCase(application.stage)}"`,
+        { exit: ExitCodes.InvalidInput }
+      )
+    }
+
+    return application
+  }
+
+  async getWorkerForLeadAction(id: number, requireStakeProfile = false) {
+    const groupMember = await this.getApi().groupMember(this.group, id)
+    const groupLead = await this.getApi().groupLead(this.group)
+
+    if (groupLead?.workerId.eq(groupMember.workerId)) {
+      this.error('A lead cannot manage his own role this way!', { exit: ExitCodes.AccessDenied })
     }
 
-    getOpeningDraftPath(draftName: string) {
-        return path.join(this.getOpeingDraftsPath(), _.snakeCase(draftName)+'.json');
+    if (requireStakeProfile && !groupMember.stake) {
+      this.error('This worker has no associated role stake profile!', { exit: ExitCodes.InvalidInput })
     }
 
-    saveOpeningDraft(draftName: string, params: ApiMethodInputArg[]) {
-        const paramsJson = JSON.stringify(
-            params.map(p => p.toJSON()),
-            null,
-            2
-        );
-
-        try {
-            fs.writeFileSync(this.getOpeningDraftPath(draftName), paramsJson);
-        } catch(e) {
-            throw this.createDataWriteError(DRAFTS_FOLDER);
-        }
+    return groupMember
+  }
+
+  // Helper for better TS handling.
+  // We could also use some magic with conditional types instead, but those don't seem be very well supported yet.
+  async getWorkerWithStakeForLeadAction(id: number) {
+    return (await this.getWorkerForLeadAction(id, true)) as GroupMember & Required<Pick<GroupMember, 'stake'>>
+  }
+
+  loadOpeningDraftParams(draftName: string): ApiMethodNamedArgs {
+    const draftFilePath = this.getOpeningDraftPath(draftName)
+    const params = this.extrinsicArgsFromDraft(apiModuleByGroup[this.group], 'addOpening', draftFilePath)
+
+    return params
+  }
+
+  getOpeingDraftsPath() {
+    return path.join(this.getAppDataPath(), DRAFTS_FOLDER)
+  }
+
+  getOpeningDraftPath(draftName: string) {
+    return path.join(this.getOpeingDraftsPath(), _.snakeCase(draftName) + '.json')
+  }
+
+  saveOpeningDraft(draftName: string, params: ApiMethodArg[]) {
+    const paramsJson = JSON.stringify(
+      params.map((p) => p.toJSON()),
+      null,
+      2
+    )
+
+    try {
+      fs.writeFileSync(this.getOpeningDraftPath(draftName), paramsJson)
+    } catch (e) {
+      throw this.createDataWriteError(DRAFTS_FOLDER)
     }
+  }
 
-    private initOpeningDraftsDir(): void {
-        if (!fs.existsSync(this.getOpeingDraftsPath())) {
-            fs.mkdirSync(this.getOpeingDraftsPath());
-        }
+  private initOpeningDraftsDir(): void {
+    if (!fs.existsSync(this.getOpeingDraftsPath())) {
+      fs.mkdirSync(this.getOpeingDraftsPath())
     }
+  }
 
-    async init() {
-        await super.init();
-        try {
-            this.initOpeningDraftsDir();
-        } catch (e) {
-            throw this.createDataDirInitError();
-        }
-        const { flags } = this.parse(this.constructor as typeof WorkingGroupsCommandBase);
-        if (!AvailableGroups.includes(flags.group as any)) {
-            throw new CLIError(`Invalid group! Available values are: ${AvailableGroups.join(', ')}`, { exit: ExitCodes.InvalidInput });
-        }
-        this.group = flags.group as WorkingGroups;
+  async init() {
+    await super.init()
+    try {
+      this.initOpeningDraftsDir()
+    } catch (e) {
+      throw this.createDataDirInitError()
+    }
+    const { flags } = this.parse(this.constructor as typeof WorkingGroupsCommandBase)
+    if (!AvailableGroups.includes(flags.group as any)) {
+      throw new CLIError(`Invalid group! Available values are: ${AvailableGroups.join(', ')}`, {
+        exit: ExitCodes.InvalidInput,
+      })
     }
+    this.group = flags.group as WorkingGroups
+  }
 }

+ 25 - 25
cli/src/commands/account/choose.ts

@@ -1,33 +1,33 @@
-import AccountsCommandBase from '../../base/AccountsCommandBase';
-import chalk from 'chalk';
-import ExitCodes from '../../ExitCodes';
-import { NamedKeyringPair } from '../../Types';
-import { flags } from '@oclif/command';
+import AccountsCommandBase from '../../base/AccountsCommandBase'
+import chalk from 'chalk'
+import ExitCodes from '../../ExitCodes'
+import { NamedKeyringPair } from '../../Types'
+import { flags } from '@oclif/command'
 
 export default class AccountChoose extends AccountsCommandBase {
-    static description = 'Choose default account to use in the CLI';
-    static flags = {
-        showSpecial: flags.boolean({
-            description: 'Whether to show special (DEV chain) accounts',
-            required: false
-        }),
-    };
+  static description = 'Choose default account to use in the CLI'
+  static flags = {
+    showSpecial: flags.boolean({
+      description: 'Whether to show special (DEV chain) accounts',
+      required: false,
+    }),
+  }
 
-    async run() {
-        const { showSpecial } = this.parse(AccountChoose).flags;
-        const accounts: NamedKeyringPair[] = this.fetchAccounts(showSpecial);
-        const selectedAccount: NamedKeyringPair | null = this.getSelectedAccount();
+  async run() {
+    const { showSpecial } = this.parse(AccountChoose).flags
+    const accounts: NamedKeyringPair[] = this.fetchAccounts(showSpecial)
+    const selectedAccount: NamedKeyringPair | null = this.getSelectedAccount()
 
-        this.log(chalk.white(`Found ${ accounts.length } existing accounts...\n`));
+    this.log(chalk.white(`Found ${accounts.length} existing accounts...\n`))
 
-        if (accounts.length === 0) {
-            this.warn('No account to choose from. Add accont using account:import or account:create.');
-            this.exit(ExitCodes.NoAccountFound);
-        }
+    if (accounts.length === 0) {
+      this.warn('No account to choose from. Add accont using account:import or account:create.')
+      this.exit(ExitCodes.NoAccountFound)
+    }
 
-        const choosenAccount: NamedKeyringPair = await this.promptForAccount(accounts, selectedAccount);
+    const choosenAccount: NamedKeyringPair = await this.promptForAccount(accounts, selectedAccount)
 
-        await this.setSelectedAccount(choosenAccount);
-        this.log(chalk.greenBright("\nAccount switched!"));
-    }
+    await this.setSelectedAccount(choosenAccount)
+    this.log(chalk.greenBright('\nAccount switched!'))
   }
+}

+ 35 - 35
cli/src/commands/account/create.ts

@@ -1,47 +1,47 @@
-import chalk from 'chalk';
-import ExitCodes from '../../ExitCodes';
-import AccountsCommandBase from '../../base/AccountsCommandBase';
-import { Keyring } from '@polkadot/api';
+import chalk from 'chalk'
+import ExitCodes from '../../ExitCodes'
+import AccountsCommandBase from '../../base/AccountsCommandBase'
+import { Keyring } from '@polkadot/api'
 import { mnemonicGenerate } from '@polkadot/util-crypto'
-import { NamedKeyringPair } from '../../Types';
+import { NamedKeyringPair } from '../../Types'
 
 type AccountCreateArgs = {
-    name: string
-};
+  name: string
+}
 
 export default class AccountCreate extends AccountsCommandBase {
-    static description = 'Create new account';
-
-    static args = [
-        {
-            name: 'name',
-            required: true,
-            description: 'Account name'
-        },
-    ];
-
-    validatePass(password: string, password2: string): void {
-        if (password !== password2) this.error('Passwords are not the same!', { exit: ExitCodes.InvalidInput });
-        if (!password) this.error('You didn\'t provide a password', { exit: ExitCodes.InvalidInput });
-    }
+  static description = 'Create new account'
+
+  static args = [
+    {
+      name: 'name',
+      required: true,
+      description: 'Account name',
+    },
+  ]
+
+  validatePass(password: string, password2: string): void {
+    if (password !== password2) this.error('Passwords are not the same!', { exit: ExitCodes.InvalidInput })
+    if (!password) this.error("You didn't provide a password", { exit: ExitCodes.InvalidInput })
+  }
 
-    async run() {
-        const args: AccountCreateArgs = <AccountCreateArgs> this.parse(AccountCreate).args;
-        const keyring: Keyring = new Keyring();
-        const mnemonic: string = mnemonicGenerate();
+  async run() {
+    const args: AccountCreateArgs = this.parse(AccountCreate).args as AccountCreateArgs
+    const keyring: Keyring = new Keyring()
+    const mnemonic: string = mnemonicGenerate()
 
-        keyring.addFromMnemonic(mnemonic, { name: args.name, whenCreated: Date.now() });
-        const keys: NamedKeyringPair = <NamedKeyringPair> keyring.pairs[0]; // We assigned the name above
+    keyring.addFromMnemonic(mnemonic, { name: args.name, whenCreated: Date.now() })
+    const keys: NamedKeyringPair = keyring.pairs[0] as NamedKeyringPair // We assigned the name above
 
-        const password = await this.promptForPassword('Set your account\'s password');
-        const password2 = await this.promptForPassword('Confirm your password');
+    const password = await this.promptForPassword("Set your account's password")
+    const password2 = await this.promptForPassword('Confirm your password')
 
-        this.validatePass(password, password2);
+    this.validatePass(password, password2)
 
-        this.saveAccount(keys, password);
+    this.saveAccount(keys, password)
 
-        this.log(chalk.greenBright(`\nAccount succesfully created!`));
-        this.log(chalk.white(`${chalk.bold('Name:    ') }${ args.name }`));
-        this.log(chalk.white(`${chalk.bold('Address: ') }${ keys.address }`));
-    }
+    this.log(chalk.greenBright(`\nAccount succesfully created!`))
+    this.log(chalk.white(`${chalk.bold('Name:    ')}${args.name}`))
+    this.log(chalk.white(`${chalk.bold('Address: ')}${keys.address}`))
   }
+}

+ 34 - 34
cli/src/commands/account/current.ts

@@ -1,41 +1,41 @@
-import AccountsCommandBase from '../../base/AccountsCommandBase';
-import { AccountSummary, NameValueObj, NamedKeyringPair } from '../../Types';
-import { DerivedBalances } from '@polkadot/api-derive/types';
-import { displayHeader, displayNameValueTable } from '../../helpers/display';
-import { formatBalance } from '@polkadot/util';
-import moment from 'moment';
+import AccountsCommandBase from '../../base/AccountsCommandBase'
+import { AccountSummary, NameValueObj, NamedKeyringPair } from '../../Types'
+import { DerivedBalances } from '@polkadot/api-derive/types'
+import { displayHeader, displayNameValueTable } from '../../helpers/display'
+import { formatBalance } from '@polkadot/util'
+import moment from 'moment'
 
 export default class AccountCurrent extends AccountsCommandBase {
-    static description = 'Display information about currently choosen default account';
-    static aliases = ['account:info', 'account:default'];
+  static description = 'Display information about currently choosen default account'
+  static aliases = ['account:info', 'account:default']
 
-    async run() {
-        const currentAccount: NamedKeyringPair = await this.getRequiredSelectedAccount(false);
-        const summary: AccountSummary = await this.getApi().getAccountSummary(currentAccount.address);
+  async run() {
+    const currentAccount: NamedKeyringPair = await this.getRequiredSelectedAccount(false)
+    const summary: AccountSummary = await this.getApi().getAccountSummary(currentAccount.address)
 
-        displayHeader('Account information');
-        const creationDate: string = currentAccount.meta.whenCreated ?
-            moment(currentAccount.meta.whenCreated).format('YYYY-MM-DD HH:mm:ss')
-            : '?';
-        const accountRows: NameValueObj[] = [
-            { name: 'Account name:', value: currentAccount.meta.name },
-            { name: 'Address:', value: currentAccount.address },
-            { name: 'Created:', value: creationDate }
-        ];
-        displayNameValueTable(accountRows);
+    displayHeader('Account information')
+    const creationDate: string = currentAccount.meta.whenCreated
+      ? moment(currentAccount.meta.whenCreated).format('YYYY-MM-DD HH:mm:ss')
+      : '?'
+    const accountRows: NameValueObj[] = [
+      { name: 'Account name:', value: currentAccount.meta.name },
+      { name: 'Address:', value: currentAccount.address },
+      { name: 'Created:', value: creationDate },
+    ]
+    displayNameValueTable(accountRows)
 
-        displayHeader('Balances');
-        const balances: DerivedBalances = summary.balances;
-        let balancesRows: NameValueObj[] = [
-            { name: 'Total balance:', value: formatBalance(balances.votingBalance) },
-            { name: 'Transferable balance:', value: formatBalance(balances.availableBalance) }
-        ];
-        if (balances.lockedBalance.gtn(0)) {
-            balancesRows.push({ name: 'Locked balance:', value: formatBalance(balances.lockedBalance) });
-        }
-        if (balances.reservedBalance.gtn(0)) {
-            balancesRows.push({ name: 'Reserved balance:', value: formatBalance(balances.reservedBalance) });
-        }
-        displayNameValueTable(balancesRows);
+    displayHeader('Balances')
+    const balances: DerivedBalances = summary.balances
+    const balancesRows: NameValueObj[] = [
+      { name: 'Total balance:', value: formatBalance(balances.votingBalance) },
+      { name: 'Transferable balance:', value: formatBalance(balances.availableBalance) },
+    ]
+    if (balances.lockedBalance.gtn(0)) {
+      balancesRows.push({ name: 'Locked balance:', value: formatBalance(balances.lockedBalance) })
     }
+    if (balances.reservedBalance.gtn(0)) {
+      balancesRows.push({ name: 'Reserved balance:', value: formatBalance(balances.reservedBalance) })
+    }
+    displayNameValueTable(balancesRows)
   }
+}

+ 62 - 61
cli/src/commands/account/export.ts

@@ -1,73 +1,74 @@
-import fs from 'fs';
-import chalk from 'chalk';
-import path from 'path';
-import ExitCodes from '../../ExitCodes';
-import AccountsCommandBase from '../../base/AccountsCommandBase';
-import { flags } from '@oclif/command';
-import { NamedKeyringPair } from '../../Types';
+import fs from 'fs'
+import chalk from 'chalk'
+import path from 'path'
+import ExitCodes from '../../ExitCodes'
+import AccountsCommandBase from '../../base/AccountsCommandBase'
+import { flags } from '@oclif/command'
+import { NamedKeyringPair } from '../../Types'
 
-type AccountExportFlags = { all: boolean };
-type AccountExportArgs = { path: string };
+type AccountExportFlags = { all: boolean }
+type AccountExportArgs = { path: string }
 
 export default class AccountExport extends AccountsCommandBase {
-    static description = 'Export account(s) to given location';
-    static MULTI_EXPORT_FOLDER_NAME = 'exported_accounts';
+  static description = 'Export account(s) to given location'
+  static MULTI_EXPORT_FOLDER_NAME = 'exported_accounts'
 
-    static args = [
-        {
-            name: 'path',
-            required: true,
-            description: 'Path where the exported files should be placed'
-        }
-    ];
+  static args = [
+    {
+      name: 'path',
+      required: true,
+      description: 'Path where the exported files should be placed',
+    },
+  ]
 
-    static flags = {
-        all: flags.boolean({
-            char: 'a',
-            description: `If provided, exports all existing accounts into "${ AccountExport.MULTI_EXPORT_FOLDER_NAME }" folder inside given path`,
-        }),
-    };
-
-    exportAccount(account: NamedKeyringPair, destPath: string): string {
-        const sourceFilePath: string = this.getAccountFilePath(account);
-        const destFilePath: string = path.join(destPath, this.generateAccountFilename(account));
-        try {
-            fs.copyFileSync(sourceFilePath, destFilePath);
-        }
-        catch (e) {
-            this.error(
-                `Error while trying to copy into the export file: (${ destFilePath }). Permissions issue?`,
-                { exit: ExitCodes.FsOperationFailed }
-            );
-        }
+  static flags = {
+    all: flags.boolean({
+      char: 'a',
+      description: `If provided, exports all existing accounts into "${AccountExport.MULTI_EXPORT_FOLDER_NAME}" folder inside given path`,
+    }),
+  }
 
-        return destFilePath;
+  exportAccount(account: NamedKeyringPair, destPath: string): string {
+    const sourceFilePath: string = this.getAccountFilePath(account)
+    const destFilePath: string = path.join(destPath, this.generateAccountFilename(account))
+    try {
+      fs.copyFileSync(sourceFilePath, destFilePath)
+    } catch (e) {
+      this.error(`Error while trying to copy into the export file: (${destFilePath}). Permissions issue?`, {
+        exit: ExitCodes.FsOperationFailed,
+      })
     }
 
-    async run() {
-        const args: AccountExportArgs = <AccountExportArgs> this.parse(AccountExport).args;
-        const flags: AccountExportFlags = <AccountExportFlags> this.parse(AccountExport).flags;
-        const accounts: NamedKeyringPair[] = this.fetchAccounts();
+    return destFilePath
+  }
 
-        if (!accounts.length) {
-            this.error('No accounts found!', { exit: ExitCodes.NoAccountFound });
-        }
+  async run() {
+    const args: AccountExportArgs = this.parse(AccountExport).args as AccountExportArgs
+    const flags: AccountExportFlags = this.parse(AccountExport).flags as AccountExportFlags
+    const accounts: NamedKeyringPair[] = this.fetchAccounts()
+
+    if (!accounts.length) {
+      this.error('No accounts found!', { exit: ExitCodes.NoAccountFound })
+    }
 
-        if (flags.all) {
-            const destPath: string = path.join(args.path, AccountExport.MULTI_EXPORT_FOLDER_NAME);
-            try {
-                if (!fs.existsSync(destPath)) fs.mkdirSync(destPath);
-            } catch(e) {
-                this.error(`Failed to create the export folder (${ destPath })`, { exit: ExitCodes.FsOperationFailed });
-            }
-            for (let account of accounts) this.exportAccount(account, destPath);
-            this.log(chalk.greenBright(`All accounts succesfully exported succesfully to: ${ chalk.white(destPath) }!`));
-        }
-        else {
-            const destPath: string = args.path;
-            const choosenAccount: NamedKeyringPair = await this.promptForAccount(accounts, null, 'Select an account to export');
-            const exportedFilePath: string = this.exportAccount(choosenAccount, destPath);
-            this.log(chalk.greenBright(`Account succesfully exported to: ${ chalk.white(exportedFilePath) }`));
-        }
+    if (flags.all) {
+      const destPath: string = path.join(args.path, AccountExport.MULTI_EXPORT_FOLDER_NAME)
+      try {
+        if (!fs.existsSync(destPath)) fs.mkdirSync(destPath)
+      } catch (e) {
+        this.error(`Failed to create the export folder (${destPath})`, { exit: ExitCodes.FsOperationFailed })
+      }
+      for (const account of accounts) this.exportAccount(account, destPath)
+      this.log(chalk.greenBright(`All accounts succesfully exported succesfully to: ${chalk.white(destPath)}!`))
+    } else {
+      const destPath: string = args.path
+      const choosenAccount: NamedKeyringPair = await this.promptForAccount(
+        accounts,
+        null,
+        'Select an account to export'
+      )
+      const exportedFilePath: string = this.exportAccount(choosenAccount, destPath)
+      this.log(chalk.greenBright(`Account succesfully exported to: ${chalk.white(exportedFilePath)}`))
     }
   }
+}

+ 23 - 21
cli/src/commands/account/forget.ts

@@ -1,29 +1,31 @@
-import fs from 'fs';
-import chalk from 'chalk';
-import ExitCodes from '../../ExitCodes';
-import AccountsCommandBase from '../../base/AccountsCommandBase';
-import { NamedKeyringPair } from '../../Types';
+import fs from 'fs'
+import chalk from 'chalk'
+import ExitCodes from '../../ExitCodes'
+import AccountsCommandBase from '../../base/AccountsCommandBase'
+import { NamedKeyringPair } from '../../Types'
 
 export default class AccountForget extends AccountsCommandBase {
-    static description = 'Forget (remove) account from the list of available accounts';
+  static description = 'Forget (remove) account from the list of available accounts'
 
-    async run() {
-        const accounts: NamedKeyringPair[] = this.fetchAccounts();
+  async run() {
+    const accounts: NamedKeyringPair[] = this.fetchAccounts()
 
-        if (!accounts.length) {
-            this.error('No accounts found!', { exit: ExitCodes.NoAccountFound });
-        }
-
-        const choosenAccount: NamedKeyringPair = await this.promptForAccount(accounts, null, 'Select an account to forget');
-        await this.requireConfirmation('Are you sure you want this account to be forgotten?');
+    if (!accounts.length) {
+      this.error('No accounts found!', { exit: ExitCodes.NoAccountFound })
+    }
 
-        const accountFilePath: string = this.getAccountFilePath(choosenAccount);
-        try {
-            fs.unlinkSync(accountFilePath);
-        } catch (e) {
-            this.error(`Could not remove account file (${ accountFilePath }). Permissions issue?`, { exit: ExitCodes.FsOperationFailed });
-        }
+    const choosenAccount: NamedKeyringPair = await this.promptForAccount(accounts, null, 'Select an account to forget')
+    await this.requireConfirmation('Are you sure you want this account to be forgotten?')
 
-        this.log(chalk.greenBright(`\nAccount has been forgotten!`))
+    const accountFilePath: string = this.getAccountFilePath(choosenAccount)
+    try {
+      fs.unlinkSync(accountFilePath)
+    } catch (e) {
+      this.error(`Could not remove account file (${accountFilePath}). Permissions issue?`, {
+        exit: ExitCodes.FsOperationFailed,
+      })
     }
+
+    this.log(chalk.greenBright(`\nAccount has been forgotten!`))
   }
+}

+ 34 - 36
cli/src/commands/account/import.ts

@@ -1,46 +1,44 @@
-import fs from 'fs';
-import chalk from 'chalk';
-import path from 'path';
-import ExitCodes from '../../ExitCodes';
-import AccountsCommandBase from '../../base/AccountsCommandBase';
-import { NamedKeyringPair } from '../../Types';
+import fs from 'fs'
+import chalk from 'chalk'
+import path from 'path'
+import ExitCodes from '../../ExitCodes'
+import AccountsCommandBase from '../../base/AccountsCommandBase'
+import { NamedKeyringPair } from '../../Types'
 
 type AccountImportArgs = {
-    backupFilePath: string
-};
+  backupFilePath: string
+}
 
 export default class AccountImport extends AccountsCommandBase {
-    static description = 'Import account using JSON backup file';
+  static description = 'Import account using JSON backup file'
 
-    static args = [
-        {
-            name: 'backupFilePath',
-            required: true,
-            description: 'Path to account backup JSON file'
-        },
-    ];
+  static args = [
+    {
+      name: 'backupFilePath',
+      required: true,
+      description: 'Path to account backup JSON file',
+    },
+  ]
 
-    async run() {
-        const args: AccountImportArgs = <AccountImportArgs> this.parse(AccountImport).args;
-        const backupAcc: NamedKeyringPair = this.fetchAccountFromJsonFile(args.backupFilePath);
-        const accountName: string = backupAcc.meta.name;
-        const accountAddress: string = backupAcc.address;
+  async run() {
+    const args: AccountImportArgs = this.parse(AccountImport).args as AccountImportArgs
+    const backupAcc: NamedKeyringPair = this.fetchAccountFromJsonFile(args.backupFilePath)
+    const accountName: string = backupAcc.meta.name
+    const accountAddress: string = backupAcc.address
 
-        const sourcePath: string = args.backupFilePath;
-        const destPath: string = path.join(this.getAccountsDirPath(), this.generateAccountFilename(backupAcc));
+    const sourcePath: string = args.backupFilePath
+    const destPath: string = path.join(this.getAccountsDirPath(), this.generateAccountFilename(backupAcc))
 
-        try {
-            fs.copyFileSync(sourcePath, destPath);
-        }
-        catch (e) {
-            this.error(
-                'Unexpected error while trying to copy input file! Permissions issue?',
-                { exit: ExitCodes.FsOperationFailed }
-            );
-        }
-
-        this.log(chalk.bold.greenBright(`ACCOUNT IMPORTED SUCCESFULLY!`));
-        this.log(chalk.bold.white(`NAME:    `), accountName);
-        this.log(chalk.bold.white(`ADDRESS: `), accountAddress);
+    try {
+      fs.copyFileSync(sourcePath, destPath)
+    } catch (e) {
+      this.error('Unexpected error while trying to copy input file! Permissions issue?', {
+        exit: ExitCodes.FsOperationFailed,
+      })
     }
+
+    this.log(chalk.bold.greenBright(`ACCOUNT IMPORTED SUCCESFULLY!`))
+    this.log(chalk.bold.white(`NAME:    `), accountName)
+    this.log(chalk.bold.white(`ADDRESS: `), accountAddress)
   }
+}

+ 53 - 54
cli/src/commands/account/transferTokens.ts

@@ -1,68 +1,67 @@
-import BN from 'bn.js';
-import AccountsCommandBase from '../../base/AccountsCommandBase';
-import chalk from 'chalk';
-import ExitCodes from '../../ExitCodes';
-import { formatBalance } from '@polkadot/util';
-import { Hash } from '@polkadot/types/interfaces';
-import { NamedKeyringPair } from '../../Types';
-import { checkBalance, validateAddress } from '../../helpers/validation';
-import { DerivedBalances } from '@polkadot/api-derive/types';
+import BN from 'bn.js'
+import AccountsCommandBase from '../../base/AccountsCommandBase'
+import chalk from 'chalk'
+import ExitCodes from '../../ExitCodes'
+import { formatBalance } from '@polkadot/util'
+import { Hash } from '@polkadot/types/interfaces'
+import { NamedKeyringPair } from '../../Types'
+import { checkBalance, validateAddress } from '../../helpers/validation'
+import { DerivedBalances } from '@polkadot/api-derive/types'
 
 type AccountTransferArgs = {
-    recipient: string,
-    amount: string
-};
+  recipient: string
+  amount: string
+}
 
 export default class AccountTransferTokens extends AccountsCommandBase {
-    static description = 'Transfer tokens from currently choosen account';
+  static description = 'Transfer tokens from currently choosen account'
 
-    static args = [
-        {
-            name: 'recipient',
-            required: true,
-            description: 'Address of the transfer recipient'
-        },
-        {
-            name: 'amount',
-            required: true,
-            description: 'Amount of tokens to transfer'
-        },
-    ];
+  static args = [
+    {
+      name: 'recipient',
+      required: true,
+      description: 'Address of the transfer recipient',
+    },
+    {
+      name: 'amount',
+      required: true,
+      description: 'Amount of tokens to transfer',
+    },
+  ]
 
-    async run() {
-        const args: AccountTransferArgs = <AccountTransferArgs> this.parse(AccountTransferTokens).args;
-        const selectedAccount: NamedKeyringPair = await this.getRequiredSelectedAccount();
-        const amountBN: BN = new BN(args.amount);
+  async run() {
+    const args: AccountTransferArgs = this.parse(AccountTransferTokens).args as AccountTransferArgs
+    const selectedAccount: NamedKeyringPair = await this.getRequiredSelectedAccount()
+    const amountBN: BN = new BN(args.amount)
 
-        // Initial validation
-        validateAddress(args.recipient, 'Invalid recipient address');
-        const accBalances: DerivedBalances = (await this.getApi().getAccountsBalancesInfo([ selectedAccount.address ]))[0];
-        checkBalance(accBalances, amountBN);
+    // Initial validation
+    validateAddress(args.recipient, 'Invalid recipient address')
+    const accBalances: DerivedBalances = (await this.getApi().getAccountsBalancesInfo([selectedAccount.address]))[0]
+    checkBalance(accBalances, amountBN)
 
-        await this.requestAccountDecoding(selectedAccount);
+    await this.requestAccountDecoding(selectedAccount)
 
-        this.log(chalk.white('Estimating fee...'));
-        let estimatedFee: BN;
-        try {
-            estimatedFee = await this.getApi().estimateFee(selectedAccount, args.recipient, amountBN);
-        }
-        catch (e) {
-            this.error('Could not estimate the fee.', { exit: ExitCodes.UnexpectedException });
-        }
-        const totalAmount: BN = amountBN.add(estimatedFee);
-        this.log(chalk.white('Estimated fee:', formatBalance(estimatedFee)));
-        this.log(chalk.white('Total transfer amount:', formatBalance(totalAmount)));
+    this.log(chalk.white('Estimating fee...'))
+    let estimatedFee: BN
+    try {
+      estimatedFee = await this.getApi().estimateFee(selectedAccount, args.recipient, amountBN)
+    } catch (e) {
+      this.error('Could not estimate the fee.', { exit: ExitCodes.UnexpectedException })
+    }
+    const totalAmount: BN = amountBN.add(estimatedFee)
+    this.log(chalk.white('Estimated fee:', formatBalance(estimatedFee)))
+    this.log(chalk.white('Total transfer amount:', formatBalance(totalAmount)))
 
-        checkBalance(accBalances, totalAmount);
+    checkBalance(accBalances, totalAmount)
 
-        await this.requireConfirmation('Do you confirm the transfer?');
+    await this.requireConfirmation('Do you confirm the transfer?')
 
-        try {
-            const txHash: Hash = await this.getApi().transfer(selectedAccount, args.recipient, amountBN);
-            this.log(chalk.greenBright('Transaction succesfully sent!'));
-            this.log(chalk.white('Hash:', txHash.toString()));
-        } catch (e) {
-            this.error('Could not send the transaction.', { exit: ExitCodes.UnexpectedException });
-        }
+    try {
+      const txHash: Hash = await this.getApi().transfer(selectedAccount, args.recipient, amountBN)
+      this.log(chalk.greenBright('Transaction succesfully sent!'))
+      this.log(chalk.white('Hash:', txHash.toString()))
+    } catch (e) {
+      this.error('Could not send the transaction.', { exit: ExitCodes.UnexpectedException })
     }
   }
+}

+ 7 - 8
cli/src/commands/api/getUri.ts

@@ -1,12 +1,11 @@
-import StateAwareCommandBase from '../../base/StateAwareCommandBase';
-import chalk from 'chalk';
-
+import StateAwareCommandBase from '../../base/StateAwareCommandBase'
+import chalk from 'chalk'
 
 export default class ApiGetUri extends StateAwareCommandBase {
-    static description = 'Get current api WS provider uri';
+  static description = 'Get current api WS provider uri'
 
-    async run() {
-        const currentUri:string = this.getPreservedState().apiUri;
-        this.log(chalk.green(currentUri));
-    }
+  async run() {
+    const currentUri: string = this.getPreservedState().apiUri
+    this.log(chalk.green(currentUri))
   }
+}

+ 210 - 203
cli/src/commands/api/inspect.ts

@@ -1,227 +1,234 @@
-import { flags } from '@oclif/command';
-import { CLIError } from '@oclif/errors';
-import { displayNameValueTable } from '../../helpers/display';
-import { ApiPromise } from '@polkadot/api';
-import { Option } from '@polkadot/types';
-import { Codec } from '@polkadot/types/types';
-import { ConstantCodec } from '@polkadot/api-metadata/consts/types';
-import ExitCodes from '../../ExitCodes';
-import chalk from 'chalk';
-import { NameValueObj } from '../../Types';
-import ApiCommandBase, { ApiMethodInputArg } from '../../base/ApiCommandBase';
+import { flags } from '@oclif/command'
+import { CLIError } from '@oclif/errors'
+import { displayNameValueTable } from '../../helpers/display'
+import { ApiPromise } from '@polkadot/api'
+import { Option } from '@polkadot/types'
+import { Codec } from '@polkadot/types/types'
+import { ConstantCodec } from '@polkadot/api-metadata/consts/types'
+import ExitCodes from '../../ExitCodes'
+import chalk from 'chalk'
+import { NameValueObj, ApiMethodArg } from '../../Types'
+import ApiCommandBase from '../../base/ApiCommandBase'
 
 // Command flags type
 type ApiInspectFlags = {
-    type: string,
-    module: string,
-    method: string,
-    exec: boolean,
-    callArgs: string
-};
+  type: string
+  module: string
+  method: string
+  exec: boolean
+  callArgs: string
+}
 
 // Currently "inspectable" api types
-const TYPES_AVAILABLE = [
-    'query',
-    'consts',
-] as const;
+const TYPES_AVAILABLE = ['query', 'consts'] as const
 
 // String literals type based on TYPES_AVAILABLE const.
 // It works as if we specified: type ApiType = 'query' | 'consts'...;
-type ApiType = typeof TYPES_AVAILABLE[number];
+type ApiType = typeof TYPES_AVAILABLE[number]
 
 export default class ApiInspect extends ApiCommandBase {
-    static description =
-        'Lists available node API modules/methods and/or their description(s), '+
-        'or calls one of the API methods (depending on provided arguments and flags)';
-
-    static examples = [
-        '$ api:inspect',
-        '$ api:inspect -t=query',
-        '$ api:inspect -t=query -M=members',
-        '$ api:inspect -t=query -M=members -m=memberProfile',
-        '$ api:inspect -t=query -M=members -m=memberProfile -e',
-        '$ api:inspect -t=query -M=members -m=memberProfile -e -a=1',
-    ];
-
-    static flags = {
-        type: flags.string({
-            char: 't',
-            description:
-                'Specifies the type/category of the inspected request (ie. "query", "consts" etc.).\n'+
-                'If no "--module" flag is provided then all available modules in that type will be listed.\n'+
-                'If this flag is not provided then all available types will be listed.',
-        }),
-        module: flags.string({
-            char: 'M',
-            description:
-                'Specifies the api module, ie. "system", "staking" etc.\n'+
-                'If no "--method" flag is provided then all methods in that module will be listed along with the descriptions.',
-            dependsOn: ['type'],
-        }),
-        method: flags.string({
-            char: 'm',
-            description: 'Specifies the api method to call/describe.',
-            dependsOn: ['module'],
-        }),
-        exec: flags.boolean({
-            char: 'e',
-            description: 'Provide this flag if you want to execute the actual call, instead of displaying the method description (which is default)',
-            dependsOn: ['method'],
-        }),
-        callArgs: flags.string({
-            char: 'a',
-            description:
-                'Specifies the arguments to use when calling a method. Multiple arguments can be separated with a comma, ie. "-a=arg1,arg2".\n'+
-                'You can omit this flag even if the method requires some aguments.\n'+
-                'In that case you will be promted to provide value for each required argument.\n' +
-                'Ommiting this flag is recommended when input parameters are of more complex types (and it\'s hard to specify them as just simple comma-separated strings)',
-            dependsOn: ['exec'],
-        })
-    };
-
-    getMethodMeta(apiType: ApiType, apiModule: string, apiMethod: string) {
-        if (apiType === 'query') {
-            return this.getOriginalApi().query[apiModule][apiMethod].creator.meta;
-        }
-        else {
-            // Currently the only other optoin is api.consts
-            const method:ConstantCodec = <ConstantCodec> this.getOriginalApi().consts[apiModule][apiMethod];
-            return method.meta;
-        }
+  static description =
+    'Lists available node API modules/methods and/or their description(s), ' +
+    'or calls one of the API methods (depending on provided arguments and flags)'
+
+  static examples = [
+    '$ api:inspect',
+    '$ api:inspect -t=query',
+    '$ api:inspect -t=query -M=members',
+    '$ api:inspect -t=query -M=members -m=memberProfile',
+    '$ api:inspect -t=query -M=members -m=memberProfile -e',
+    '$ api:inspect -t=query -M=members -m=memberProfile -e -a=1',
+  ]
+
+  static flags = {
+    type: flags.string({
+      char: 't',
+      description:
+        'Specifies the type/category of the inspected request (ie. "query", "consts" etc.).\n' +
+        'If no "--module" flag is provided then all available modules in that type will be listed.\n' +
+        'If this flag is not provided then all available types will be listed.',
+    }),
+    module: flags.string({
+      char: 'M',
+      description:
+        'Specifies the api module, ie. "system", "staking" etc.\n' +
+        'If no "--method" flag is provided then all methods in that module will be listed along with the descriptions.',
+      dependsOn: ['type'],
+    }),
+    method: flags.string({
+      char: 'm',
+      description: 'Specifies the api method to call/describe.',
+      dependsOn: ['module'],
+    }),
+    exec: flags.boolean({
+      char: 'e',
+      description:
+        'Provide this flag if you want to execute the actual call, instead of displaying the method description (which is default)',
+      dependsOn: ['method'],
+    }),
+    callArgs: flags.string({
+      char: 'a',
+      description:
+        'Specifies the arguments to use when calling a method. Multiple arguments can be separated with a comma, ie. "-a=arg1,arg2".\n' +
+        'You can omit this flag even if the method requires some aguments.\n' +
+        'In that case you will be promted to provide value for each required argument.\n' +
+        "Ommiting this flag is recommended when input parameters are of more complex types (and it's hard to specify them as just simple comma-separated strings)",
+      dependsOn: ['exec'],
+    }),
+  }
+
+  getMethodMeta(apiType: ApiType, apiModule: string, apiMethod: string) {
+    if (apiType === 'query') {
+      return this.getOriginalApi().query[apiModule][apiMethod].creator.meta
+    } else {
+      // Currently the only other optoin is api.consts
+      const method: ConstantCodec = this.getOriginalApi().consts[apiModule][apiMethod] as ConstantCodec
+      return method.meta
     }
-
-    getMethodDescription(apiType: ApiType, apiModule: string, apiMethod: string): string {
-        let description:string = this.getMethodMeta(apiType, apiModule, apiMethod).documentation.join(' ');
-        return description || 'No description available.';
+  }
+
+  getMethodDescription(apiType: ApiType, apiModule: string, apiMethod: string): string {
+    const description: string = this.getMethodMeta(apiType, apiModule, apiMethod).documentation.join(' ')
+    return description || 'No description available.'
+  }
+
+  getQueryMethodParamsTypes(apiModule: string, apiMethod: string): string[] {
+    const method = this.getOriginalApi().query[apiModule][apiMethod]
+    const { type } = method.creator.meta
+    if (type.isDoubleMap) {
+      return [type.asDoubleMap.key1.toString(), type.asDoubleMap.key2.toString()]
     }
-
-    getQueryMethodParamsTypes(apiModule: string, apiMethod: string): string[] {
-        const method = this.getOriginalApi().query[apiModule][apiMethod];
-        const { type } = method.creator.meta;
-        if (type.isDoubleMap) {
-            return [ type.asDoubleMap.key1.toString(), type.asDoubleMap.key2.toString() ];
+    if (type.isMap) {
+      return type.asMap.linked.isTrue ? [`Option<${type.asMap.key.toString()}>`] : [type.asMap.key.toString()]
+    }
+    return []
+  }
+
+  getMethodReturnType(apiType: ApiType, apiModule: string, apiMethod: string): string {
+    if (apiType === 'query') {
+      const method = this.getOriginalApi().query[apiModule][apiMethod]
+      const {
+        meta: { type, modifier },
+      } = method.creator
+      if (type.isDoubleMap) {
+        return type.asDoubleMap.value.toString()
+      }
+      if (modifier.isOptional) {
+        return `Option<${type.toString()}>`
+      }
+    }
+    // Fallback for "query" and default for "consts"
+    return this.getMethodMeta(apiType, apiModule, apiMethod).type.toString()
+  }
+
+  // Validate the flags - throws an error if flags.type, flags.module or flags.method is invalid / does not exist in the api.
+  // Returns type, module and method which validity we can be sure about (notice they may still be "undefined" if weren't provided).
+  validateFlags(
+    api: ApiPromise,
+    flags: ApiInspectFlags
+  ): { apiType: ApiType | undefined; apiModule: string | undefined; apiMethod: string | undefined } {
+    let apiType: ApiType | undefined = undefined
+    const { module: apiModule, method: apiMethod } = flags
+
+    if (flags.type !== undefined) {
+      const availableTypes: readonly string[] = TYPES_AVAILABLE
+      if (!availableTypes.includes(flags.type)) {
+        throw new CLIError('Such type is not available', { exit: ExitCodes.InvalidInput })
+      }
+      apiType = flags.type as ApiType
+      if (apiModule !== undefined) {
+        if (!api[apiType][apiModule]) {
+          throw new CLIError('Such module was not found', { exit: ExitCodes.InvalidInput })
         }
-        if (type.isMap) {
-            return type.asMap.linked.isTrue ? [ `Option<${type.asMap.key.toString()}>` ] : [ type.asMap.key.toString() ];
+        if (apiMethod !== undefined && !api[apiType][apiModule][apiMethod]) {
+          throw new CLIError('Such method was not found', { exit: ExitCodes.InvalidInput })
         }
-        return [];
+      }
     }
 
-    getMethodReturnType(apiType: ApiType, apiModule: string, apiMethod: string): string {
-        if (apiType === 'query') {
-            const method = this.getOriginalApi().query[apiModule][apiMethod];
-            const { meta: { type, modifier } } = method.creator;
-            if (type.isDoubleMap) {
-                return type.asDoubleMap.value.toString();
-            }
-            if (modifier.isOptional) {
-                return `Option<${type.toString()}>`;
-            }
-        }
-        // Fallback for "query" and default for "consts"
-        return this.getMethodMeta(apiType, apiModule, apiMethod).type.toString();
+    return { apiType, apiModule, apiMethod }
+  }
+
+  // Request values for params using array of param types (strings)
+  async requestParamsValues(paramTypes: string[]): Promise<ApiMethodArg[]> {
+    const result: ApiMethodArg[] = []
+    for (const [key, paramType] of Object.entries(paramTypes)) {
+      this.log(chalk.bold.white(`Parameter no. ${parseInt(key) + 1} (${paramType}):`))
+      const paramValue = await this.promptForParam(paramType)
+      if (paramValue instanceof Option && paramValue.isSome) {
+        result.push(paramValue.unwrap())
+      } else if (!(paramValue instanceof Option)) {
+        result.push(paramValue)
+      }
+      // In case of empty option we MUST NOT add anything to the array (otherwise it causes some error)
     }
 
-    // Validate the flags - throws an error if flags.type, flags.module or flags.method is invalid / does not exist in the api.
-    // Returns type, module and method which validity we can be sure about (notice they may still be "undefined" if weren't provided).
-    validateFlags(api: ApiPromise, flags: ApiInspectFlags): { apiType: ApiType | undefined, apiModule: string | undefined, apiMethod: string | undefined } {
-        let apiType: ApiType | undefined = undefined;
-        const { module: apiModule, method: apiMethod } = flags;
-
-        if (flags.type !== undefined) {
-            const availableTypes: readonly string[] = TYPES_AVAILABLE;
-            if (!availableTypes.includes(flags.type)) {
-                throw new CLIError('Such type is not available', { exit: ExitCodes.InvalidInput });
-            }
-            apiType = <ApiType> flags.type;
-            if (apiModule !== undefined) {
-                if (!api[apiType][apiModule]) {
-                    throw new CLIError('Such module was not found', { exit: ExitCodes.InvalidInput });
-                }
-                if (apiMethod !== undefined && !api[apiType][apiModule][apiMethod]) {
-                    throw new CLIError('Such method was not found', { exit: ExitCodes.InvalidInput });
-                }
-            }
+    return result
+  }
+
+  async run() {
+    const api: ApiPromise = this.getOriginalApi()
+    const flags: ApiInspectFlags = this.parse(ApiInspect).flags as ApiInspectFlags
+    const availableTypes: readonly string[] = TYPES_AVAILABLE
+    const { apiType, apiModule, apiMethod } = this.validateFlags(api, flags)
+
+    // Executing a call
+    if (apiType && apiModule && apiMethod && flags.exec) {
+      let result: Codec
+
+      if (apiType === 'query') {
+        // Api query - call with (or without) arguments
+        let args: (string | ApiMethodArg)[] = flags.callArgs ? flags.callArgs.split(',') : []
+        const paramsTypes: string[] = this.getQueryMethodParamsTypes(apiModule, apiMethod)
+        if (args.length < paramsTypes.length) {
+          this.warn('Some parameters are missing! Please, provide the missing parameters:')
+          const missingParamsValues = await this.requestParamsValues(paramsTypes.slice(args.length))
+          args = args.concat(missingParamsValues)
         }
+        result = await api.query[apiModule][apiMethod](...args)
+      } else {
+        // Api consts - just assign the value
+        result = api.consts[apiModule][apiMethod]
+      }
 
-        return { apiType, apiModule, apiMethod };
+      this.log(chalk.green(result.toString()))
     }
-
-    // Request values for params using array of param types (strings)
-    async requestParamsValues(paramTypes: string[]): Promise<ApiMethodInputArg[]> {
-        let result: ApiMethodInputArg[] = [];
-        for (let [key, paramType] of Object.entries(paramTypes)) {
-            this.log(chalk.bold.white(`Parameter no. ${ parseInt(key)+1 } (${ paramType }):`));
-            let paramValue = await this.promptForParam(paramType);
-            if (paramValue instanceof Option && paramValue.isSome) {
-                result.push(paramValue.unwrap());
-            }
-            else if (!(paramValue instanceof Option)) {
-                result.push(paramValue);
-            }
-            // In case of empty option we MUST NOT add anything to the array (otherwise it causes some error)
-        }
-
-        return result;
+    // Describing a method
+    else if (apiType && apiModule && apiMethod) {
+      this.log(chalk.bold.white(`${apiType}.${apiModule}.${apiMethod}`))
+      const description: string = this.getMethodDescription(apiType, apiModule, apiMethod)
+      this.log(`\n${description}\n`)
+      const typesRows: NameValueObj[] = []
+      if (apiType === 'query') {
+        typesRows.push({
+          name: 'Params:',
+          value: this.getQueryMethodParamsTypes(apiModule, apiMethod).join(', ') || '-',
+        })
+      }
+      typesRows.push({ name: 'Returns:', value: this.getMethodReturnType(apiType, apiModule, apiMethod) })
+      displayNameValueTable(typesRows)
     }
-
-    async run() {
-        const api: ApiPromise = this.getOriginalApi();
-        const flags: ApiInspectFlags = <ApiInspectFlags> this.parse(ApiInspect).flags;
-        const availableTypes: readonly string[] = TYPES_AVAILABLE;
-        const { apiType, apiModule, apiMethod } = this.validateFlags(api, flags);
-
-        // Executing a call
-        if (apiType && apiModule && apiMethod && flags.exec) {
-            let result: Codec;
-
-            if (apiType === 'query') {
-                // Api query - call with (or without) arguments
-                let args: (string | ApiMethodInputArg)[] = flags.callArgs ? flags.callArgs.split(',') : [];
-                const paramsTypes: string[] = this.getQueryMethodParamsTypes(apiModule, apiMethod);
-                if (args.length < paramsTypes.length) {
-                    this.warn('Some parameters are missing! Please, provide the missing parameters:');
-                    let missingParamsValues = await this.requestParamsValues(paramsTypes.slice(args.length));
-                    args = args.concat(missingParamsValues);
-                }
-                result = await api.query[apiModule][apiMethod](...args);
-            }
-            else {
-                // Api consts - just assign the value
-                result = api.consts[apiModule][apiMethod];
-            }
-
-            this.log(chalk.green(result.toString()));
-        }
-        // Describing a method
-        else if (apiType && apiModule && apiMethod) {
-            this.log(chalk.bold.white(`${ apiType }.${ apiModule }.${ apiMethod }`));
-            const description: string = this.getMethodDescription(apiType, apiModule, apiMethod);
-            this.log(`\n${ description }\n`);
-            let typesRows: NameValueObj[] = [];
-            if (apiType === 'query') {
-                typesRows.push({ name: 'Params:', value: this.getQueryMethodParamsTypes(apiModule, apiMethod).join(', ') || '-' });
-            }
-            typesRows.push({ name: 'Returns:', value: this.getMethodReturnType(apiType, apiModule, apiMethod) });
-            displayNameValueTable(typesRows);
-        }
-        // Displaying all available methods
-        else if (apiType && apiModule) {
-            const module = api[apiType][apiModule];
-            const rows: NameValueObj[] = Object.keys(module).map((key: string) => {
-                return { name: key, value: this.getMethodDescription(apiType, apiModule, key) };
-            });
-            displayNameValueTable(rows);
-        }
-        // Displaying all available modules
-        else if (apiType) {
-            this.log(chalk.bold.white('Available modules:'));
-            this.log(Object.keys(api[apiType]).map(key => chalk.white(key)).join('\n'));
-        }
-        // Displaying all available types
-        else {
-            this.log(chalk.bold.white('Available types:'));
-            this.log(availableTypes.map(type => chalk.white(type)).join('\n'));
-        }
+    // Displaying all available methods
+    else if (apiType && apiModule) {
+      const module = api[apiType][apiModule]
+      const rows: NameValueObj[] = Object.keys(module).map((key: string) => {
+        return { name: key, value: this.getMethodDescription(apiType, apiModule, key) }
+      })
+      displayNameValueTable(rows)
+    }
+    // Displaying all available modules
+    else if (apiType) {
+      this.log(chalk.bold.white('Available modules:'))
+      this.log(
+        Object.keys(api[apiType])
+          .map((key) => chalk.white(key))
+          .join('\n')
+      )
+    }
+    // Displaying all available types
+    else {
+      this.log(chalk.bold.white('Available types:'))
+      this.log(availableTypes.map((type) => chalk.white(type)).join('\n'))
     }
+  }
 }

+ 22 - 22
cli/src/commands/api/setUri.ts

@@ -1,28 +1,28 @@
-import StateAwareCommandBase from '../../base/StateAwareCommandBase';
-import chalk from 'chalk';
-import { WsProvider } from '@polkadot/api';
-import ExitCodes from '../../ExitCodes';
+import StateAwareCommandBase from '../../base/StateAwareCommandBase'
+import chalk from 'chalk'
+import { WsProvider } from '@polkadot/api'
+import ExitCodes from '../../ExitCodes'
 
-type ApiSetUriArgs = { uri: string };
+type ApiSetUriArgs = { uri: string }
 
 export default class ApiSetUri extends StateAwareCommandBase {
-    static description = 'Set api WS provider uri';
-    static args = [
-        {
-            name: 'uri',
-            required: true,
-            description: 'Uri of the node api WS provider'
-        }
-    ];
+  static description = 'Set api WS provider uri'
+  static args = [
+    {
+      name: 'uri',
+      required: true,
+      description: 'Uri of the node api WS provider',
+    },
+  ]
 
-    async run() {
-        const args: ApiSetUriArgs = <ApiSetUriArgs> this.parse(ApiSetUri).args;
-        try {
-            new WsProvider(args.uri);
-        } catch(e) {
-            this.error('The WS provider uri seems to be incorrect', { exit: ExitCodes.InvalidInput });
-        }
-        await this.setPreservedState({ apiUri: args.uri });
-        this.log(chalk.greenBright('Api uri successfuly changed! New uri: ') + chalk.white(args.uri))
+  async run() {
+    const args: ApiSetUriArgs = this.parse(ApiSetUri).args as ApiSetUriArgs
+    try {
+      new WsProvider(args.uri)
+    } catch (e) {
+      this.error('The WS provider uri seems to be incorrect', { exit: ExitCodes.InvalidInput })
     }
+    await this.setPreservedState({ apiUri: args.uri })
+    this.log(chalk.greenBright('Api uri successfuly changed! New uri: ') + chalk.white(args.uri))
   }
+}

+ 48 - 49
cli/src/commands/council/info.ts

@@ -1,57 +1,56 @@
-import { ElectionStage } from '@joystream/types/council';
-import { formatNumber, formatBalance } from '@polkadot/util';
-import { BlockNumber } from '@polkadot/types/interfaces';
-import { CouncilInfoObj, NameValueObj } from '../../Types';
-import { displayHeader, displayNameValueTable } from '../../helpers/display';
-import ApiCommandBase from '../../base/ApiCommandBase';
+import { ElectionStage } from '@joystream/types/council'
+import { formatNumber, formatBalance } from '@polkadot/util'
+import { BlockNumber } from '@polkadot/types/interfaces'
+import { CouncilInfoObj, NameValueObj } from '../../Types'
+import { displayHeader, displayNameValueTable } from '../../helpers/display'
+import ApiCommandBase from '../../base/ApiCommandBase'
 
 export default class CouncilInfo extends ApiCommandBase {
-    static description = 'Get current council and council elections information';
+  static description = 'Get current council and council elections information'
 
-    displayInfo(infoObj: CouncilInfoObj) {
-        const { activeCouncil = [], round, stage } = infoObj;
+  displayInfo(infoObj: CouncilInfoObj) {
+    const { activeCouncil = [], round, stage } = infoObj
 
-        displayHeader('Council');
-        const councilRows: NameValueObj[] = [
-            { name: 'Elected:', value: activeCouncil.length ? 'YES' : 'NO' },
-            { name: 'Members:', value: activeCouncil.length.toString() },
-            { name: 'Term ends at block:', value: `#${formatNumber(infoObj.termEndsAt) }` },
-        ];
-        displayNameValueTable(councilRows);
+    displayHeader('Council')
+    const councilRows: NameValueObj[] = [
+      { name: 'Elected:', value: activeCouncil.length ? 'YES' : 'NO' },
+      { name: 'Members:', value: activeCouncil.length.toString() },
+      { name: 'Term ends at block:', value: `#${formatNumber(infoObj.termEndsAt)}` },
+    ]
+    displayNameValueTable(councilRows)
 
-
-        displayHeader('Election');
-        let electionTableRows: NameValueObj[] = [
-            { name: 'Running:', value: stage && stage.isSome ? 'YES' : 'NO' },
-            { name: 'Election round:', value: formatNumber(round) }
-        ];
-        if (stage && stage.isSome) {
-            const stageValue = <ElectionStage> stage.value;
-            const stageName: string = stageValue.type;
-            const stageEndsAt = <BlockNumber> stageValue.value;
-            electionTableRows.push({ name: 'Stage:', value: stageName });
-            electionTableRows.push({ name: 'Stage ends at block:', value: `#${stageEndsAt}` });
-        }
-        displayNameValueTable(electionTableRows);
-
-        displayHeader('Configuration');
-        const isAutoStart = (infoObj.autoStart || false).valueOf();
-        const configTableRows: NameValueObj[] = [
-            { name: 'Auto-start elections:', value: isAutoStart ? 'YES' : 'NO' },
-            { name: 'New term duration:', value: formatNumber(infoObj.newTermDuration) },
-            { name: 'Candidacy limit:', value: formatNumber(infoObj.candidacyLimit) },
-            { name: 'Council size:', value: formatNumber(infoObj.councilSize) },
-            { name: 'Min. council stake:', value: formatBalance(infoObj.minCouncilStake) },
-            { name: 'Min. voting stake:', value: formatBalance(infoObj.minVotingStake) },
-            { name: 'Announcing period:', value: `${ formatNumber(infoObj.announcingPeriod) } blocks` },
-            { name: 'Voting period:', value: `${ formatNumber(infoObj.votingPeriod) } blocks` },
-            { name: 'Revealing period:', value: `${ formatNumber(infoObj.revealingPeriod) } blocks` }
-        ];
-        displayNameValueTable(configTableRows);
+    displayHeader('Election')
+    const electionTableRows: NameValueObj[] = [
+      { name: 'Running:', value: stage && stage.isSome ? 'YES' : 'NO' },
+      { name: 'Election round:', value: formatNumber(round) },
+    ]
+    if (stage && stage.isSome) {
+      const stageValue = stage.value as ElectionStage
+      const stageName: string = stageValue.type
+      const stageEndsAt = stageValue.value as BlockNumber
+      electionTableRows.push({ name: 'Stage:', value: stageName })
+      electionTableRows.push({ name: 'Stage ends at block:', value: `#${stageEndsAt}` })
     }
+    displayNameValueTable(electionTableRows)
 
-    async run() {
-        const infoObj = await this.getApi().getCouncilInfo();
-        this.displayInfo(infoObj);
-    }
+    displayHeader('Configuration')
+    const isAutoStart = (infoObj.autoStart || false).valueOf()
+    const configTableRows: NameValueObj[] = [
+      { name: 'Auto-start elections:', value: isAutoStart ? 'YES' : 'NO' },
+      { name: 'New term duration:', value: formatNumber(infoObj.newTermDuration) },
+      { name: 'Candidacy limit:', value: formatNumber(infoObj.candidacyLimit) },
+      { name: 'Council size:', value: formatNumber(infoObj.councilSize) },
+      { name: 'Min. council stake:', value: formatBalance(infoObj.minCouncilStake) },
+      { name: 'Min. voting stake:', value: formatBalance(infoObj.minVotingStake) },
+      { name: 'Announcing period:', value: `${formatNumber(infoObj.announcingPeriod)} blocks` },
+      { name: 'Voting period:', value: `${formatNumber(infoObj.votingPeriod)} blocks` },
+      { name: 'Revealing period:', value: `${formatNumber(infoObj.revealingPeriod)} blocks` },
+    ]
+    displayNameValueTable(configTableRows)
+  }
+
+  async run() {
+    const infoObj = await this.getApi().getCouncilInfo()
+    this.displayInfo(infoObj)
   }
+}

+ 31 - 32
cli/src/commands/working-groups/application.ts

@@ -1,40 +1,39 @@
-import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase';
-import { displayCollapsedRow, displayHeader } from '../../helpers/display';
-import _ from 'lodash';
-import chalk from 'chalk';
+import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase'
+import { displayCollapsedRow, displayHeader } from '../../helpers/display'
+import chalk from 'chalk'
 
 export default class WorkingGroupsApplication extends WorkingGroupsCommandBase {
-    static description = 'Shows an overview of given application by Working Group Application ID';
-    static args = [
-        {
-            name: 'wgApplicationId',
-            required: true,
-            description: 'Working Group Application ID'
-        },
-    ]
-    static flags = {
-        ...WorkingGroupsCommandBase.flags,
-    };
+  static description = 'Shows an overview of given application by Working Group Application ID'
+  static args = [
+    {
+      name: 'wgApplicationId',
+      required: true,
+      description: 'Working Group Application ID',
+    },
+  ]
+  static flags = {
+    ...WorkingGroupsCommandBase.flags,
+  }
 
-    async run() {
-        const { args } = this.parse(WorkingGroupsApplication);
+  async run() {
+    const { args } = this.parse(WorkingGroupsApplication)
 
-        const application = await this.getApi().groupApplication(this.group, parseInt(args.wgApplicationId));
+    const application = await this.getApi().groupApplication(this.group, parseInt(args.wgApplicationId))
 
-        displayHeader('Human readable text');
-        this.jsonPrettyPrint(application.humanReadableText);
+    displayHeader('Human readable text')
+    this.jsonPrettyPrint(application.humanReadableText)
 
-        displayHeader(`Details`);
-        const applicationRow = {
-            'WG application ID': application.wgApplicationId,
-            'Application ID': application.applicationId,
-            'Member handle': application.member?.handle.toString() || chalk.red('NONE'),
-            'Role account': application.roleAccout.toString(),
-            'Stage': application.stage,
-            'Application stake': application.stakes.application,
-            'Role stake': application.stakes.role,
-            'Total stake': Object.values(application.stakes).reduce((a, b) => a + b)
-        };
-        displayCollapsedRow(applicationRow);
+    displayHeader(`Details`)
+    const applicationRow = {
+      'WG application ID': application.wgApplicationId,
+      'Application ID': application.applicationId,
+      'Member handle': application.member?.handle.toString() || chalk.red('NONE'),
+      'Role account': application.roleAccout.toString(),
+      Stage: application.stage,
+      'Application stake': application.stakes.application,
+      'Role stake': application.stakes.role,
+      'Total stake': Object.values(application.stakes).reduce((a, b) => a + b),
     }
+    displayCollapsedRow(applicationRow)
+  }
 }

+ 77 - 84
cli/src/commands/working-groups/createOpening.ts

@@ -1,96 +1,89 @@
-import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase';
-import { HRTStruct } from '../../Types';
-import chalk from 'chalk';
-import { flags } from '@oclif/command';
-import { ApiMethodInputArg } from '../../base/ApiCommandBase';
-import { schemaValidator } from '@joystream/types/hiring';
-import { apiModuleByGroup } from '../../Api';
+import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase'
+import { ApiMethodArg, ApiMethodNamedArgs } from '../../Types'
+import chalk from 'chalk'
+import { flags } from '@oclif/command'
+import { apiModuleByGroup } from '../../Api'
+import WorkerOpeningOptions from '../../promptOptions/addWorkerOpening'
+import { setDefaults } from '../../helpers/promptOptions'
 
 export default class WorkingGroupsCreateOpening extends WorkingGroupsCommandBase {
-    static description = 'Create working group opening (requires lead access)';
-    static flags = {
-        ...WorkingGroupsCommandBase.flags,
-        useDraft: flags.boolean({
-            char: 'd',
-            description:
-                "Whether to create the opening from existing draft.\n"+
-                "If provided without --draftName - the list of choices will be displayed."
-        }),
-        draftName: flags.string({
-            char: 'n',
-            description:
-                'Name of the draft to create the opening from.',
-            dependsOn: ['useDraft']
-        }),
-        createDraftOnly: flags.boolean({
-            char: 'c',
-            description:
-                'If provided - the extrinsic will not be executed. Use this flag if you only want to create a draft.'
-        }),
-        skipPrompts: flags.boolean({
-            char: 's',
-            description:
-                "Whether to skip all prompts when adding from draft (will use all default values)",
-            dependsOn: ['useDraft'],
-            exclusive: ['createDraftOnly']
-        })
-    };
-
-    async run() {
-        const account = await this.getRequiredSelectedAccount();
-        // lead-only gate
-        await this.getRequiredLead();
-
-        const { flags } = this.parse(WorkingGroupsCreateOpening);
+  static description = 'Create working group opening (requires lead access)'
+  static flags = {
+    ...WorkingGroupsCommandBase.flags,
+    useDraft: flags.boolean({
+      char: 'd',
+      description:
+        'Whether to create the opening from existing draft.\n' +
+        'If provided without --draftName - the list of choices will be displayed.',
+    }),
+    draftName: flags.string({
+      char: 'n',
+      description: 'Name of the draft to create the opening from.',
+      dependsOn: ['useDraft'],
+    }),
+    createDraftOnly: flags.boolean({
+      char: 'c',
+      description:
+        'If provided - the extrinsic will not be executed. Use this flag if you only want to create a draft.',
+    }),
+    skipPrompts: flags.boolean({
+      char: 's',
+      description: 'Whether to skip all prompts when adding from draft (will use all default values)',
+      dependsOn: ['useDraft'],
+      exclusive: ['createDraftOnly'],
+    }),
+  }
 
-        let defaultValues: ApiMethodInputArg[] | undefined = undefined;
-        if (flags.useDraft) {
-            const draftName = flags.draftName || await this.promptForOpeningDraft();
-            defaultValues =  await this.loadOpeningDraftParams(draftName);
-        }
+  async run() {
+    const account = await this.getRequiredSelectedAccount()
+    // lead-only gate
+    await this.getRequiredLead()
 
-        if (!flags.skipPrompts) {
-            const module = apiModuleByGroup[this.group];
-            const method = 'addOpening';
-            const jsonArgsMapping = { 'human_readable_text': { struct: HRTStruct, schemaValidator } };
+    const { flags } = this.parse(WorkingGroupsCreateOpening)
 
-            let saveDraft = false, params: ApiMethodInputArg[];
-            if (flags.createDraftOnly) {
-                params = await this.promptForExtrinsicParams(module, method, jsonArgsMapping, defaultValues);
-                saveDraft = true;
-            }
-            else {
-                await this.requestAccountDecoding(account); // Prompt for password
+    const promptOptions = new WorkerOpeningOptions()
+    let defaultValues: ApiMethodNamedArgs | undefined
+    if (flags.useDraft) {
+      const draftName = flags.draftName || (await this.promptForOpeningDraft())
+      defaultValues = await this.loadOpeningDraftParams(draftName)
+      setDefaults(promptOptions, defaultValues)
+    }
 
-                params = await this.buildAndSendExtrinsic(
-                    account,
-                    module,
-                    method,
-                    jsonArgsMapping,
-                    defaultValues,
-                    true
-                );
+    if (!flags.skipPrompts) {
+      const module = apiModuleByGroup[this.group]
+      const method = 'addOpening'
 
-                this.log(chalk.green('Opening succesfully created!'));
+      let saveDraft = false,
+        params: ApiMethodArg[]
+      if (flags.createDraftOnly) {
+        params = await this.promptForExtrinsicParams(module, method, promptOptions)
+        saveDraft = true
+      } else {
+        await this.requestAccountDecoding(account) // Prompt for password
+        params = await this.buildAndSendExtrinsic(account, module, method, promptOptions, true)
 
-                saveDraft = await this.simplePrompt({
-                    message: 'Do you wish to save this opening as draft?',
-                    type: 'confirm'
-                });
-            }
+        saveDraft = await this.simplePrompt({
+          message: 'Do you wish to save this opening as draft?',
+          type: 'confirm',
+        })
+      }
 
-            if (saveDraft) {
-                const draftName = await this.promptForNewOpeningDraftName();
-                this.saveOpeningDraft(draftName, params);
+      if (saveDraft) {
+        const draftName = await this.promptForNewOpeningDraftName()
+        this.saveOpeningDraft(draftName, params)
 
-                this.log(chalk.green(`Opening draft ${ chalk.white(draftName) } succesfully saved!`));
-            }
-        }
-        else {
-            await this.requestAccountDecoding(account); // Prompt for password
-            this.log(chalk.white('Sending the extrinsic...'));
-            await this.sendExtrinsic(account, apiModuleByGroup[this.group], 'addOpening', defaultValues!);
-            this.log(chalk.green('Opening succesfully created!'));
-        }
+        this.log(chalk.green(`Opening draft ${chalk.white(draftName)} succesfully saved!`))
+      }
+    } else {
+      await this.requestAccountDecoding(account) // Prompt for password
+      this.log(chalk.white('Sending the extrinsic...'))
+      await this.sendExtrinsic(
+        account,
+        apiModuleByGroup[this.group],
+        'addOpening',
+        defaultValues!.map((v) => v.value)
+      )
+      this.log(chalk.green('Opening succesfully created!'))
     }
+  }
 }

+ 56 - 0
cli/src/commands/working-groups/decreaseWorkerStake.ts

@@ -0,0 +1,56 @@
+import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase'
+import { apiModuleByGroup } from '../../Api'
+import { WorkerId } from '@joystream/types/working-group'
+import { Balance } from '@polkadot/types/interfaces'
+import { formatBalance } from '@polkadot/util'
+import { minMaxInt } from '../../validators/common'
+import chalk from 'chalk'
+import { createParamOptions } from '../../helpers/promptOptions'
+
+export default class WorkingGroupsDecreaseWorkerStake extends WorkingGroupsCommandBase {
+  static description =
+    'Decreases given worker stake by an amount that will be returned to the worker role account. ' +
+    'Requires lead access.'
+  static args = [
+    {
+      name: 'workerId',
+      required: true,
+      description: 'Worker ID',
+    },
+  ]
+  static flags = {
+    ...WorkingGroupsCommandBase.flags,
+  }
+
+  async run() {
+    const { args } = this.parse(WorkingGroupsDecreaseWorkerStake)
+
+    const account = await this.getRequiredSelectedAccount()
+    // Lead-only gate
+    await this.getRequiredLead()
+
+    const workerId = parseInt(args.workerId)
+    const groupMember = await this.getWorkerWithStakeForLeadAction(workerId)
+
+    this.log(chalk.white('Current worker stake: ', formatBalance(groupMember.stake)))
+    const balanceValidator = minMaxInt(1, groupMember.stake.toNumber())
+    const balance = (await this.promptForParam(
+      'Balance',
+      createParamOptions('amount', undefined, balanceValidator)
+    )) as Balance
+
+    await this.requestAccountDecoding(account)
+
+    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'decreaseStake', [
+      new WorkerId(workerId),
+      balance,
+    ])
+
+    this.log(
+      chalk.green(
+        `${chalk.white(formatBalance(balance))} from worker ${chalk.white(workerId)} stake ` +
+          `has been returned to worker's role account (${chalk.white(groupMember.roleAccount.toString())})!`
+      )
+    )
+  }
+}

+ 56 - 0
cli/src/commands/working-groups/evictWorker.ts

@@ -0,0 +1,56 @@
+import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase'
+import { apiModuleByGroup } from '../../Api'
+import { WorkerId } from '@joystream/types/working-group'
+import { bool } from '@polkadot/types/primitive'
+import { formatBalance } from '@polkadot/util'
+import chalk from 'chalk'
+import { createParamOptions } from '../../helpers/promptOptions'
+
+export default class WorkingGroupsEvictWorker extends WorkingGroupsCommandBase {
+  static description = 'Evicts given worker. Requires lead access.'
+  static args = [
+    {
+      name: 'workerId',
+      required: true,
+      description: 'Worker ID',
+    },
+  ]
+  static flags = {
+    ...WorkingGroupsCommandBase.flags,
+  }
+
+  async run() {
+    const { args } = this.parse(WorkingGroupsEvictWorker)
+
+    const account = await this.getRequiredSelectedAccount()
+    // Lead-only gate
+    await this.getRequiredLead()
+
+    const workerId = parseInt(args.workerId)
+    // This will also make sure the worker is valid
+    const groupMember = await this.getWorkerForLeadAction(workerId)
+
+    // TODO: Terminate worker text limits? (minMaxStr)
+    const rationale = await this.promptForParam('Bytes', createParamOptions('rationale'))
+    const shouldSlash = groupMember.stake
+      ? await this.simplePrompt({
+          message: `Should the worker stake (${formatBalance(groupMember.stake)}) be slashed?`,
+          type: 'confirm',
+          default: false,
+        })
+      : false
+
+    await this.requestAccountDecoding(account)
+
+    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'terminateRole', [
+      new WorkerId(workerId),
+      rationale,
+      new bool(shouldSlash),
+    ])
+
+    this.log(chalk.green(`Worker ${chalk.white(workerId)} has been evicted!`))
+    if (shouldSlash) {
+      this.log(chalk.green(`Worker stake totalling ${chalk.white(formatBalance(groupMember.stake))} has been slashed!`))
+    }
+  }
+}

+ 49 - 55
cli/src/commands/working-groups/fillOpening.ts

@@ -1,58 +1,52 @@
-import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase';
-import _ from 'lodash';
-import { OpeningStatus } from '../../Types';
-import ExitCodes from '../../ExitCodes';
-import { apiModuleByGroup } from '../../Api';
-import { OpeningId } from '@joystream/types/hiring';
-import { ApplicationIdSet, RewardPolicy } from '@joystream/types/working-group';
-import chalk from 'chalk';
+import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase'
+import { OpeningStatus } from '../../Types'
+import { apiModuleByGroup } from '../../Api'
+import { OpeningId } from '@joystream/types/hiring'
+import { ApplicationIdSet, RewardPolicy } from '@joystream/types/working-group'
+import chalk from 'chalk'
+import { createParamOptions } from '../../helpers/promptOptions'
 
 export default class WorkingGroupsFillOpening extends WorkingGroupsCommandBase {
-    static description = 'Allows filling working group opening that\'s currently in review. Requires lead access.';
-    static args = [
-        {
-            name: 'wgOpeningId',
-            required: true,
-            description: 'Working Group Opening ID'
-        },
-    ]
-    static flags = {
-        ...WorkingGroupsCommandBase.flags,
-    };
-
-    async run() {
-        const { args } = this.parse(WorkingGroupsFillOpening);
-
-        const account = await this.getRequiredSelectedAccount();
-        // Lead-only gate
-        await this.getRequiredLead();
-
-        const opening = await this.getApi().groupOpening(this.group, parseInt(args.wgOpeningId));
-
-        if (opening.stage.status !== OpeningStatus.InReview) {
-            this.error('This opening is not in the Review stage!', { exit: ExitCodes.InvalidInput });
-        }
-
-        const applicationIds = await this.promptForApplicationsToAccept(opening);
-        const rewardPolicyOpt = await this.promptForParam(`Option<${RewardPolicy.name}>`, 'RewardPolicy');
-
-        await this.requestAccountDecoding(account);
-
-        await this.sendAndFollowExtrinsic(
-            account,
-            apiModuleByGroup[this.group],
-            'fillOpening',
-            [
-                new OpeningId(opening.wgOpeningId),
-                new ApplicationIdSet(applicationIds),
-                rewardPolicyOpt
-            ]
-        );
-
-        this.log(chalk.green(`Opening ${chalk.white(opening.wgOpeningId)} succesfully filled!`));
-        this.log(
-            chalk.green('Accepted working group application IDs: ') +
-            chalk.white(applicationIds.length ? applicationIds.join(chalk.green(', ')) : 'NONE')
-        );
-    }
+  static description = "Allows filling working group opening that's currently in review. Requires lead access."
+  static args = [
+    {
+      name: 'wgOpeningId',
+      required: true,
+      description: 'Working Group Opening ID',
+    },
+  ]
+  static flags = {
+    ...WorkingGroupsCommandBase.flags,
+  }
+
+  async run() {
+    const { args } = this.parse(WorkingGroupsFillOpening)
+
+    const account = await this.getRequiredSelectedAccount()
+    // Lead-only gate
+    await this.getRequiredLead()
+
+    const openingId = parseInt(args.wgOpeningId)
+    const opening = await this.getOpeningForLeadAction(openingId, OpeningStatus.InReview)
+
+    const applicationIds = await this.promptForApplicationsToAccept(opening)
+    const rewardPolicyOpt = await this.promptForParam(
+      `Option<${RewardPolicy.name}>`,
+      createParamOptions('RewardPolicy')
+    )
+
+    await this.requestAccountDecoding(account)
+
+    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'fillOpening', [
+      new OpeningId(openingId),
+      new ApplicationIdSet(applicationIds),
+      rewardPolicyOpt,
+    ])
+
+    this.log(chalk.green(`Opening ${chalk.white(openingId)} succesfully filled!`))
+    this.log(
+      chalk.green('Accepted working group application IDs: ') +
+        chalk.white(applicationIds.length ? applicationIds.join(chalk.green(', ')) : 'NONE')
+    )
+  }
 }

+ 46 - 0
cli/src/commands/working-groups/increaseStake.ts

@@ -0,0 +1,46 @@
+import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase'
+import { apiModuleByGroup } from '../../Api'
+import { Balance } from '@polkadot/types/interfaces'
+import { formatBalance } from '@polkadot/util'
+import { positiveInt } from '../../validators/common'
+import chalk from 'chalk'
+import ExitCodes from '../../ExitCodes'
+import { createParamOptions } from '../../helpers/promptOptions'
+
+export default class WorkingGroupsIncreaseStake extends WorkingGroupsCommandBase {
+  static description = 'Increases current role (lead/worker) stake. Requires active role account to be selected.'
+  static flags = {
+    ...WorkingGroupsCommandBase.flags,
+  }
+
+  async run() {
+    const account = await this.getRequiredSelectedAccount()
+    // Worker-only gate
+    const worker = await this.getRequiredWorker()
+
+    if (!worker.stake) {
+      this.error('Cannot increase stake. No associated role stake profile found!', { exit: ExitCodes.InvalidInput })
+    }
+
+    this.log(chalk.white('Current stake: ', formatBalance(worker.stake)))
+    const balance = (await this.promptForParam(
+      'Balance',
+      createParamOptions('amount', undefined, positiveInt())
+    )) as Balance
+
+    await this.requestAccountDecoding(account)
+
+    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'increaseStake', [
+      worker.workerId,
+      balance,
+    ])
+
+    this.log(
+      chalk.green(
+        `Worker ${chalk.white(worker.workerId.toNumber())} stake has been increased by ${chalk.white(
+          formatBalance(balance)
+        )}`
+      )
+    )
+  }
+}

+ 28 - 0
cli/src/commands/working-groups/leaveRole.ts

@@ -0,0 +1,28 @@
+import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase'
+import { apiModuleByGroup } from '../../Api'
+import { minMaxStr } from '../../validators/common'
+import chalk from 'chalk'
+import { createParamOptions } from '../../helpers/promptOptions'
+
+export default class WorkingGroupsLeaveRole extends WorkingGroupsCommandBase {
+  static description = 'Leave the worker or lead role associated with currently selected account.'
+  static flags = {
+    ...WorkingGroupsCommandBase.flags,
+  }
+
+  async run() {
+    const account = await this.getRequiredSelectedAccount()
+    // Worker-only gate
+    const worker = await this.getRequiredWorker()
+
+    const constraint = await this.getApi().workerExitRationaleConstraint(this.group)
+    const rationaleValidator = minMaxStr(constraint.min.toNumber(), constraint.max.toNumber())
+    const rationale = await this.promptForParam('Bytes', createParamOptions('rationale', undefined, rationaleValidator))
+
+    await this.requestAccountDecoding(account)
+
+    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'leaveRole', [worker.workerId, rationale])
+
+    this.log(chalk.green(`Succesfully left the role! (worker id: ${chalk.white(worker.workerId.toNumber())})`))
+  }
+}

+ 67 - 65
cli/src/commands/working-groups/opening.ts

@@ -1,78 +1,80 @@
-import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase';
-import { displayTable, displayCollapsedRow, displayHeader } from '../../helpers/display';
-import _ from 'lodash';
-import { OpeningStatus, GroupOpeningStage, GroupOpeningStakes } from '../../Types';
-import { StakingAmountLimitModeKeys, StakingPolicy } from '@joystream/types/hiring';
-import { formatBalance } from '@polkadot/util';
-import chalk from 'chalk';
+import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase'
+import { displayTable, displayCollapsedRow, displayHeader } from '../../helpers/display'
+import _ from 'lodash'
+import { OpeningStatus, GroupOpeningStage, GroupOpeningStakes } from '../../Types'
+import { StakingAmountLimitModeKeys, StakingPolicy } from '@joystream/types/hiring'
+import { formatBalance } from '@polkadot/util'
+import chalk from 'chalk'
 
 export default class WorkingGroupsOpening extends WorkingGroupsCommandBase {
-    static description = 'Shows an overview of given working group opening by Working Group Opening ID';
-    static args = [
-        {
-            name: 'wgOpeningId',
-            required: true,
-            description: 'Working Group Opening ID'
-        },
-    ]
-    static flags = {
-        ...WorkingGroupsCommandBase.flags,
-    };
+  static description = 'Shows an overview of given working group opening by Working Group Opening ID'
+  static args = [
+    {
+      name: 'wgOpeningId',
+      required: true,
+      description: 'Working Group Opening ID',
+    },
+  ]
+  static flags = {
+    ...WorkingGroupsCommandBase.flags,
+  }
 
-    stageColumns(stage: GroupOpeningStage) {
-        const { status, date, block } = stage;
-        const statusTimeHeader = status === OpeningStatus.WaitingToBegin ? 'Starts at' : 'Last status change';
-        return {
-            'Stage': _.startCase(status),
-            [statusTimeHeader]: (date && block)
-                ? `~ ${date.toLocaleTimeString()} ${ date.toLocaleDateString()} (#${block})`
-                : (block && `#${block}` || '?')
-        };
+  stageColumns(stage: GroupOpeningStage) {
+    const { status, date, block } = stage
+    const statusTimeHeader = status === OpeningStatus.WaitingToBegin ? 'Starts at' : 'Last status change'
+    return {
+      Stage: _.startCase(status),
+      [statusTimeHeader]:
+        date && block
+          ? `~ ${date.toLocaleTimeString()} ${date.toLocaleDateString()} (#${block})`
+          : (block && `#${block}`) || '?',
     }
+  }
 
-    formatStake(stake: StakingPolicy | undefined) {
-        if (!stake) return 'NONE';
-        const { amount, amount_mode } = stake;
-        return amount_mode.type === StakingAmountLimitModeKeys.AtLeast
-            ? `>= ${ formatBalance(amount) }`
-            : `== ${ formatBalance(amount) }`;
-    }
+  formatStake(stake: StakingPolicy | undefined) {
+    if (!stake) return 'NONE'
+    const { amount, amount_mode: amountMode } = stake
+    return amountMode.type === StakingAmountLimitModeKeys.AtLeast
+      ? `>= ${formatBalance(amount)}`
+      : `== ${formatBalance(amount)}`
+  }
 
-    stakeColumns(stakes: GroupOpeningStakes) {
-        const { role, application } = stakes;
-        return {
-            'Application stake': this.formatStake(application),
-            'Role stake': this.formatStake(role),
-        }
+  stakeColumns(stakes: GroupOpeningStakes) {
+    const { role, application } = stakes
+    return {
+      'Application stake': this.formatStake(application),
+      'Role stake': this.formatStake(role),
     }
+  }
 
-    async run() {
-        const { args } = this.parse(WorkingGroupsOpening);
-
-        const opening = await this.getApi().groupOpening(this.group, parseInt(args.wgOpeningId));
+  async run() {
+    const { args } = this.parse(WorkingGroupsOpening)
 
-        displayHeader('Human readable text');
-        this.jsonPrettyPrint(opening.opening.human_readable_text.toString());
+    const opening = await this.getApi().groupOpening(this.group, parseInt(args.wgOpeningId))
 
-        displayHeader('Opening details');
-        const openingRow = {
-            'WG Opening ID': opening.wgOpeningId,
-            'Opening ID': opening.openingId,
-            ...this.stageColumns(opening.stage),
-            ...this.stakeColumns(opening.stakes)
-        };
-        displayCollapsedRow(openingRow);
+    displayHeader('Human readable text')
+    this.jsonPrettyPrint(opening.opening.human_readable_text.toString())
 
-        displayHeader(`Applications (${opening.applications.length})`);
-        const applicationsRows = opening.applications.map(a => ({
-            'WG appl. ID': a.wgApplicationId,
-            'Appl. ID': a.applicationId,
-            'Member': a.member?.handle.toString() || chalk.red('NONE'),
-            'Stage': a.stage,
-            'Appl. stake': a.stakes.application,
-            'Role stake': a.stakes.role,
-            'Total stake': Object.values(a.stakes).reduce((a, b) => a + b)
-        }));
-        displayTable(applicationsRows, 5);
+    displayHeader('Opening details')
+    const openingRow = {
+      'WG Opening ID': opening.wgOpeningId,
+      'Opening ID': opening.openingId,
+      Type: opening.type.type,
+      ...this.stageColumns(opening.stage),
+      ...this.stakeColumns(opening.stakes),
     }
+    displayCollapsedRow(openingRow)
+
+    displayHeader(`Applications (${opening.applications.length})`)
+    const applicationsRows = opening.applications.map((a) => ({
+      'WG appl. ID': a.wgApplicationId,
+      'Appl. ID': a.applicationId,
+      Member: a.member?.handle.toString() || chalk.red('NONE'),
+      Stage: a.stage,
+      'Appl. stake': a.stakes.application,
+      'Role stake': a.stakes.role,
+      'Total stake': Object.values(a.stakes).reduce((a, b) => a + b),
+    }))
+    displayTable(applicationsRows, 5)
   }
+}

+ 18 - 17
cli/src/commands/working-groups/openings.ts

@@ -1,22 +1,23 @@
-import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase';
-import { displayTable } from '../../helpers/display';
-import _ from 'lodash';
+import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase'
+import { displayTable } from '../../helpers/display'
+import _ from 'lodash'
 
 export default class WorkingGroupsOpenings extends WorkingGroupsCommandBase {
-    static description = 'Shows an overview of given working group openings';
-    static flags = {
-        ...WorkingGroupsCommandBase.flags,
-    };
+  static description = 'Shows an overview of given working group openings'
+  static flags = {
+    ...WorkingGroupsCommandBase.flags,
+  }
 
-    async run() {
-        const openings = await this.getApi().openingsByGroup(this.group);
+  async run() {
+    const openings = await this.getApi().openingsByGroup(this.group)
 
-        const openingsRows = openings.map(o => ({
-            'WG Opening ID': o.wgOpeningId,
-            'Opening ID': o.openingId,
-            'Stage': `${_.startCase(o.stage.status)}${o.stage.block ? ` (#${o.stage.block})` : ''}`,
-            'Applications': o.applications.length
-        }));
-        displayTable(openingsRows, 5);
-    }
+    const openingsRows = openings.map((o) => ({
+      'WG Opening ID': o.wgOpeningId,
+      'Opening ID': o.openingId,
+      Type: o.type.type,
+      Stage: `${_.startCase(o.stage.status)}${o.stage.block ? ` (#${o.stage.block})` : ''}`,
+      Applications: o.applications.length,
+    }))
+    displayTable(openingsRows, 5)
+  }
 }

+ 34 - 32
cli/src/commands/working-groups/overview.ts

@@ -1,38 +1,40 @@
-import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase';
-import { displayHeader, displayNameValueTable, displayTable } from '../../helpers/display';
-import { formatBalance } from '@polkadot/util';
-import chalk from 'chalk';
+import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase'
+import { displayHeader, displayNameValueTable, displayTable, shortAddress } from '../../helpers/display'
+import { formatBalance } from '@polkadot/util'
 
-export default class WorkingGroupsOverview extends WorkingGroupsCommandBase {
-    static description = 'Shows an overview of given working group (current lead and workers)';
-    static flags = {
-        ...WorkingGroupsCommandBase.flags,
-    };
+import chalk from 'chalk'
 
-    async run() {
-        const lead = await this.getApi().groupLead(this.group);
-        const members = await this.getApi().groupMembers(this.group);
+export default class WorkingGroupsOverview extends WorkingGroupsCommandBase {
+  static description = 'Shows an overview of given working group (current lead and workers)'
+  static flags = {
+    ...WorkingGroupsCommandBase.flags,
+  }
 
-        displayHeader('Group lead');
-        if (lead) {
-            displayNameValueTable([
-                { name: 'Member id:', value: lead.memberId.toString() },
-                { name: 'Member handle:', value: lead.profile.handle.toString() },
-                { name: 'Role account:', value: lead.roleAccount.toString() },
-            ]);
-        }
-        else {
-            this.log(chalk.yellow('No lead assigned!'));
-        }
+  async run() {
+    const lead = await this.getApi().groupLead(this.group)
+    const members = await this.getApi().groupMembers(this.group)
 
-        displayHeader('Members');
-        const membersRows = members.map(m => ({
-            'Worker id': m.workerId.toString(),
-            'Member id': m.memberId.toString(),
-            'Member handle': m.profile.handle.toString(),
-            'Stake': formatBalance(m.stake),
-            'Earned': formatBalance(m.earned)
-        }));
-        displayTable(membersRows, 5);
+    displayHeader('Group lead')
+    if (lead) {
+      displayNameValueTable([
+        { name: 'Member id:', value: lead.memberId.toString() },
+        { name: 'Member handle:', value: lead.profile.handle.toString() },
+        { name: 'Role account:', value: lead.roleAccount.toString() },
+      ])
+    } else {
+      this.log(chalk.yellow('No lead assigned!'))
     }
+
+    displayHeader('Members')
+    const membersRows = members.map((m) => ({
+      '': lead?.workerId.eq(m.workerId) ? '\u{2B50}' : '', // A nice star for the lead
+      'Worker id': m.workerId.toString(),
+      'Member id': m.memberId.toString(),
+      'Member handle': m.profile.handle.toString(),
+      Stake: formatBalance(m.stake),
+      Earned: formatBalance(m.reward?.totalRecieved),
+      'Role account': shortAddress(m.roleAccount),
+    }))
+    displayTable(membersRows, 5)
   }
+}

+ 55 - 0
cli/src/commands/working-groups/slashWorker.ts

@@ -0,0 +1,55 @@
+import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase'
+import { apiModuleByGroup } from '../../Api'
+import { WorkerId } from '@joystream/types/working-group'
+import { Balance } from '@polkadot/types/interfaces'
+import { formatBalance } from '@polkadot/util'
+import { minMaxInt } from '../../validators/common'
+import chalk from 'chalk'
+import { createParamOptions } from '../../helpers/promptOptions'
+
+export default class WorkingGroupsSlashWorker extends WorkingGroupsCommandBase {
+  static description = 'Slashes given worker stake. Requires lead access.'
+  static args = [
+    {
+      name: 'workerId',
+      required: true,
+      description: 'Worker ID',
+    },
+  ]
+  static flags = {
+    ...WorkingGroupsCommandBase.flags,
+  }
+
+  async run() {
+    const { args } = this.parse(WorkingGroupsSlashWorker)
+
+    const account = await this.getRequiredSelectedAccount()
+    // Lead-only gate
+    await this.getRequiredLead()
+
+    const workerId = parseInt(args.workerId)
+    const groupMember = await this.getWorkerWithStakeForLeadAction(workerId)
+
+    this.log(chalk.white('Current worker stake: ', formatBalance(groupMember.stake)))
+    const balanceValidator = minMaxInt(1, groupMember.stake.toNumber())
+    const balance = (await this.promptForParam(
+      'Balance',
+      createParamOptions('amount', undefined, balanceValidator)
+    )) as Balance
+
+    await this.requestAccountDecoding(account)
+
+    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'slashStake', [
+      new WorkerId(workerId),
+      balance,
+    ])
+
+    this.log(
+      chalk.green(
+        `${chalk.white(formatBalance(balance))} from worker ${chalk.white(
+          workerId
+        )} stake has been succesfully slashed!`
+      )
+    )
+  }
+}

+ 37 - 43
cli/src/commands/working-groups/startAcceptingApplications.ts

@@ -1,46 +1,40 @@
-import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase';
-import _ from 'lodash';
-import { OpeningStatus } from '../../Types';
-import ExitCodes from '../../ExitCodes';
-import { apiModuleByGroup } from '../../Api';
-import { OpeningId } from '@joystream/types/hiring';
-import chalk from 'chalk';
+import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase'
+import { OpeningStatus } from '../../Types'
+import { apiModuleByGroup } from '../../Api'
+import { OpeningId } from '@joystream/types/hiring'
+import chalk from 'chalk'
 
 export default class WorkingGroupsStartAcceptingApplications extends WorkingGroupsCommandBase {
-    static description = 'Changes the status of pending opening to "Accepting applications". Requires lead access.';
-    static args = [
-        {
-            name: 'wgOpeningId',
-            required: true,
-            description: 'Working Group Opening ID'
-        },
-    ]
-    static flags = {
-        ...WorkingGroupsCommandBase.flags,
-    };
-
-    async run() {
-        const { args } = this.parse(WorkingGroupsStartAcceptingApplications);
-
-        const account = await this.getRequiredSelectedAccount();
-        // Lead-only gate
-        await this.getRequiredLead();
-
-        const opening = await this.getApi().groupOpening(this.group, parseInt(args.wgOpeningId));
-
-        if (opening.stage.status !== OpeningStatus.WaitingToBegin) {
-            this.error('This opening is not in "Waiting To Begin" stage!', { exit: ExitCodes.InvalidInput });
-        }
-
-        await this.requestAccountDecoding(account);
-
-        await this.sendAndFollowExtrinsic(
-            account,
-            apiModuleByGroup[this.group],
-            'acceptApplications',
-            [ new OpeningId(opening.wgOpeningId) ]
-        );
-
-        this.log(chalk.green(`Opening ${chalk.white(opening.wgOpeningId)} status changed to: ${ chalk.white('Accepting Applications') }`));
-    }
+  static description = 'Changes the status of pending opening to "Accepting applications". Requires lead access.'
+  static args = [
+    {
+      name: 'wgOpeningId',
+      required: true,
+      description: 'Working Group Opening ID',
+    },
+  ]
+  static flags = {
+    ...WorkingGroupsCommandBase.flags,
+  }
+
+  async run() {
+    const { args } = this.parse(WorkingGroupsStartAcceptingApplications)
+
+    const account = await this.getRequiredSelectedAccount()
+    // Lead-only gate
+    await this.getRequiredLead()
+
+    const openingId = parseInt(args.wgOpeningId)
+    await this.validateOpeningForLeadAction(openingId, OpeningStatus.WaitingToBegin)
+
+    await this.requestAccountDecoding(account)
+
+    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'acceptApplications', [
+      new OpeningId(openingId),
+    ])
+
+    this.log(
+      chalk.green(`Opening ${chalk.white(openingId)} status changed to: ${chalk.white('Accepting Applications')}`)
+    )
+  }
 }

+ 35 - 43
cli/src/commands/working-groups/startReviewPeriod.ts

@@ -1,46 +1,38 @@
-import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase';
-import _ from 'lodash';
-import { OpeningStatus } from '../../Types';
-import ExitCodes from '../../ExitCodes';
-import { apiModuleByGroup } from '../../Api';
-import { OpeningId } from '@joystream/types/hiring';
-import chalk from 'chalk';
+import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase'
+import { OpeningStatus } from '../../Types'
+import { apiModuleByGroup } from '../../Api'
+import { OpeningId } from '@joystream/types/hiring'
+import chalk from 'chalk'
 
 export default class WorkingGroupsStartReviewPeriod extends WorkingGroupsCommandBase {
-    static description = 'Changes the status of active opening to "In review". Requires lead access.';
-    static args = [
-        {
-            name: 'wgOpeningId',
-            required: true,
-            description: 'Working Group Opening ID'
-        },
-    ]
-    static flags = {
-        ...WorkingGroupsCommandBase.flags,
-    };
-
-    async run() {
-        const { args } = this.parse(WorkingGroupsStartReviewPeriod);
-
-        const account = await this.getRequiredSelectedAccount();
-        // Lead-only gate
-        await this.getRequiredLead();
-
-        const opening = await this.getApi().groupOpening(this.group, parseInt(args.wgOpeningId));
-
-        if (opening.stage.status !== OpeningStatus.AcceptingApplications) {
-            this.error('This opening is not in "Accepting Applications" stage!', { exit: ExitCodes.InvalidInput });
-        }
-
-        await this.requestAccountDecoding(account);
-
-        await this.sendAndFollowExtrinsic(
-            account,
-            apiModuleByGroup[this.group],
-            'beginApplicantReview',
-            [ new OpeningId(opening.wgOpeningId) ]
-        );
-
-        this.log(chalk.green(`Opening ${chalk.white(opening.wgOpeningId)} status changed to: ${ chalk.white('In Review') }`));
-    }
+  static description = 'Changes the status of active opening to "In review". Requires lead access.'
+  static args = [
+    {
+      name: 'wgOpeningId',
+      required: true,
+      description: 'Working Group Opening ID',
+    },
+  ]
+  static flags = {
+    ...WorkingGroupsCommandBase.flags,
+  }
+
+  async run() {
+    const { args } = this.parse(WorkingGroupsStartReviewPeriod)
+
+    const account = await this.getRequiredSelectedAccount()
+    // Lead-only gate
+    await this.getRequiredLead()
+
+    const openingId = parseInt(args.wgOpeningId)
+    await this.validateOpeningForLeadAction(openingId, OpeningStatus.AcceptingApplications)
+
+    await this.requestAccountDecoding(account)
+
+    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'beginApplicantReview', [
+      new OpeningId(openingId),
+    ])
+
+    this.log(chalk.green(`Opening ${chalk.white(openingId)} status changed to: ${chalk.white('In Review')}`))
+  }
 }

+ 35 - 42
cli/src/commands/working-groups/terminateApplication.ts

@@ -1,45 +1,38 @@
-import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase';
-import _ from 'lodash';
-import ExitCodes from '../../ExitCodes';
-import { apiModuleByGroup } from '../../Api';
-import { ApplicationStageKeys, ApplicationId } from '@joystream/types/hiring';
-import chalk from 'chalk';
+import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase'
+import { apiModuleByGroup } from '../../Api'
+import { ApplicationStageKeys, ApplicationId } from '@joystream/types/hiring'
+import chalk from 'chalk'
 
 export default class WorkingGroupsTerminateApplication extends WorkingGroupsCommandBase {
-    static description = 'Terminates given working group application. Requires lead access.';
-    static args = [
-        {
-            name: 'wgApplicationId',
-            required: true,
-            description: 'Working Group Application ID'
-        },
-    ]
-    static flags = {
-        ...WorkingGroupsCommandBase.flags,
-    };
-
-    async run() {
-        const { args } = this.parse(WorkingGroupsTerminateApplication);
-
-        const account = await this.getRequiredSelectedAccount();
-        // Lead-only gate
-        await this.getRequiredLead();
-
-        const application = await this.getApi().groupApplication(this.group, parseInt(args.wgApplicationId));
-
-        if (application.stage !== ApplicationStageKeys.Active) {
-            this.error('This application is not active!', { exit: ExitCodes.InvalidInput });
-        }
-
-        await this.requestAccountDecoding(account);
-
-        await this.sendAndFollowExtrinsic(
-            account,
-            apiModuleByGroup[this.group],
-            'terminateApplication',
-            [new ApplicationId(application.wgApplicationId)]
-        );
-
-        this.log(chalk.green(`Application ${chalk.white(application.wgApplicationId)} has been succesfully terminated!`));
-    }
+  static description = 'Terminates given working group application. Requires lead access.'
+  static args = [
+    {
+      name: 'wgApplicationId',
+      required: true,
+      description: 'Working Group Application ID',
+    },
+  ]
+  static flags = {
+    ...WorkingGroupsCommandBase.flags,
+  }
+
+  async run() {
+    const { args } = this.parse(WorkingGroupsTerminateApplication)
+
+    const account = await this.getRequiredSelectedAccount()
+    // Lead-only gate
+    await this.getRequiredLead()
+
+    const applicationId = parseInt(args.wgApplicationId)
+    // We don't really need the application itself here, so this one is just for validation purposes
+    await this.getApplicationForLeadAction(applicationId, ApplicationStageKeys.Active)
+
+    await this.requestAccountDecoding(account)
+
+    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'terminateApplication', [
+      new ApplicationId(applicationId),
+    ])
+
+    this.log(chalk.green(`Application ${chalk.white(applicationId)} has been succesfully terminated!`))
+  }
 }

+ 48 - 0
cli/src/commands/working-groups/updateRewardAccount.ts

@@ -0,0 +1,48 @@
+import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase'
+import { apiModuleByGroup } from '../../Api'
+import { validateAddress } from '../../helpers/validation'
+import { GenericAccountId } from '@polkadot/types'
+import chalk from 'chalk'
+import ExitCodes from '../../ExitCodes'
+
+export default class WorkingGroupsUpdateRewardAccount extends WorkingGroupsCommandBase {
+  static description = 'Updates the worker/lead reward account (requires current role account to be selected)'
+  static args = [
+    {
+      name: 'accountAddress',
+      required: false,
+      description: 'New reward account address (if omitted, one of the existing CLI accounts can be selected)',
+    },
+  ]
+  static flags = {
+    ...WorkingGroupsCommandBase.flags,
+  }
+
+  async run() {
+    const { args } = this.parse(WorkingGroupsUpdateRewardAccount)
+
+    const account = await this.getRequiredSelectedAccount()
+    // Worker-only gate
+    const worker = await this.getRequiredWorker()
+
+    if (!worker.reward) {
+      this.error('There is no reward relationship associated with this role!', { exit: ExitCodes.InvalidInput })
+    }
+
+    let newRewardAccount: string = args.accountAddress
+    if (!newRewardAccount) {
+      const accounts = await this.fetchAccounts()
+      newRewardAccount = (await this.promptForAccount(accounts, undefined, 'Choose the new reward account')).address
+    }
+    validateAddress(newRewardAccount)
+
+    await this.requestAccountDecoding(account)
+
+    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'updateRewardAccount', [
+      worker.workerId,
+      new GenericAccountId(newRewardAccount),
+    ])
+
+    this.log(chalk.green(`Succesfully updated the reward account to: ${chalk.white(newRewardAccount)})`))
+  }
+}

+ 58 - 0
cli/src/commands/working-groups/updateRoleAccount.ts

@@ -0,0 +1,58 @@
+import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase'
+import { apiModuleByGroup } from '../../Api'
+import { validateAddress } from '../../helpers/validation'
+import { GenericAccountId } from '@polkadot/types'
+import chalk from 'chalk'
+
+export default class WorkingGroupsUpdateRoleAccount extends WorkingGroupsCommandBase {
+  static description = 'Updates the worker/lead role account. Requires member controller account to be selected'
+  static args = [
+    {
+      name: 'accountAddress',
+      required: false,
+      description: 'New role account address (if omitted, one of the existing CLI accounts can be selected)',
+    },
+  ]
+  static flags = {
+    ...WorkingGroupsCommandBase.flags,
+  }
+
+  async run() {
+    const { args } = this.parse(WorkingGroupsUpdateRoleAccount)
+
+    const account = await this.getRequiredSelectedAccount()
+    const worker = await this.getRequiredWorkerByMemberController()
+
+    const cliAccounts = await this.fetchAccounts()
+    let newRoleAccount: string = args.accountAddress
+    if (!newRoleAccount) {
+      newRoleAccount = (await this.promptForAccount(cliAccounts, undefined, 'Choose the new role account')).address
+    }
+    validateAddress(newRoleAccount)
+
+    await this.requestAccountDecoding(account)
+
+    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'updateRoleAccount', [
+      worker.workerId,
+      new GenericAccountId(newRoleAccount),
+    ])
+
+    this.log(chalk.green(`Succesfully updated the role account to: ${chalk.white(newRoleAccount)})`))
+
+    const matchingAccount = cliAccounts.find((account) => account.address === newRoleAccount)
+    if (matchingAccount) {
+      const switchAccount = await this.simplePrompt({
+        type: 'confirm',
+        message: 'Do you want to switch the currenly selected CLI account to the new role account?',
+        default: false,
+      })
+      if (switchAccount) {
+        await this.setSelectedAccount(matchingAccount)
+        this.log(
+          chalk.green('Account switched to: ') +
+            chalk.white(`${matchingAccount.meta.name} (${matchingAccount.address})`)
+        )
+      }
+    }
+  }
+}

+ 67 - 0
cli/src/commands/working-groups/updateWorkerReward.ts

@@ -0,0 +1,67 @@
+import WorkingGroupsCommandBase from '../../base/WorkingGroupsCommandBase'
+import { apiModuleByGroup } from '../../Api'
+import { WorkerId } from '@joystream/types/working-group'
+import { formatBalance } from '@polkadot/util'
+import chalk from 'chalk'
+import { Reward } from '../../Types'
+import { positiveInt } from '../../validators/common'
+import { createParamOptions } from '../../helpers/promptOptions'
+import ExitCodes from '../../ExitCodes'
+
+export default class WorkingGroupsUpdateWorkerReward extends WorkingGroupsCommandBase {
+  static description = "Change given worker's reward (amount only). Requires lead access."
+  static args = [
+    {
+      name: 'workerId',
+      required: true,
+      description: 'Worker ID',
+    },
+  ]
+  static flags = {
+    ...WorkingGroupsCommandBase.flags,
+  }
+
+  formatReward(reward?: Reward) {
+    return reward
+      ? formatBalance(reward.value) +
+          (reward.interval && ` / ${reward.interval} block(s)`) +
+          (reward.nextPaymentBlock && ` (next payment: #${reward.nextPaymentBlock})`)
+      : 'NONE'
+  }
+
+  async run() {
+    const { args } = this.parse(WorkingGroupsUpdateWorkerReward)
+
+    const account = await this.getRequiredSelectedAccount()
+    // Lead-only gate
+    await this.getRequiredLead()
+
+    const workerId = parseInt(args.workerId)
+    // This will also make sure the worker is valid
+    const groupMember = await this.getWorkerForLeadAction(workerId)
+
+    const { reward } = groupMember
+
+    if (!reward) {
+      this.error('There is no reward relationship associated with this worker!', { exit: ExitCodes.InvalidInput })
+    }
+
+    console.log(chalk.white(`Current worker reward: ${this.formatReward(reward)}`))
+
+    const newRewardValue = await this.promptForParam(
+      'BalanceOfMint',
+      createParamOptions('new_amount', undefined, positiveInt())
+    )
+
+    await this.requestAccountDecoding(account)
+
+    await this.sendAndFollowExtrinsic(account, apiModuleByGroup[this.group], 'updateRewardAmount', [
+      new WorkerId(workerId),
+      newRewardValue,
+    ])
+
+    const updatedGroupMember = await this.getApi().groupMember(this.group, workerId)
+    this.log(chalk.green(`Worker ${chalk.white(workerId)} reward has been updated!`))
+    this.log(chalk.green(`New worker reward: ${chalk.white(this.formatReward(updatedGroupMember.reward))}`))
+  }
+}

+ 52 - 47
cli/src/helpers/display.ts

@@ -1,67 +1,72 @@
-import { cli, Table } from 'cli-ux';
-import chalk from 'chalk';
-import { NameValueObj } from '../Types';
+import { cli, Table } from 'cli-ux'
+import chalk from 'chalk'
+import { NameValueObj } from '../Types'
+import { AccountId } from '@polkadot/types/interfaces'
 
-export function displayHeader(caption: string, placeholderSign: string = '_', size: number = 50) {
-    let singsPerSide: number = Math.floor((size - (caption.length + 2)) / 2);
-    let finalStr: string = '';
-    for (let i = 0; i < singsPerSide; ++i) finalStr += placeholderSign;
-    finalStr += ` ${ caption} `;
-    while (finalStr.length < size) finalStr += placeholderSign;
+export function displayHeader(caption: string, placeholderSign = '_', size = 50) {
+  const singsPerSide: number = Math.floor((size - (caption.length + 2)) / 2)
+  let finalStr = ''
+  for (let i = 0; i < singsPerSide; ++i) finalStr += placeholderSign
+  finalStr += ` ${caption} `
+  while (finalStr.length < size) finalStr += placeholderSign
 
-    process.stdout.write("\n" + chalk.bold.blueBright(finalStr) + "\n\n");
+  process.stdout.write('\n' + chalk.bold.blueBright(finalStr) + '\n\n')
 }
 
 export function displayNameValueTable(rows: NameValueObj[]) {
-    cli.table(
-        rows,
-        {
-            name: { minWidth: 30, get: row => chalk.bold.white(row.name) },
-            value: { get: row => chalk.white(row.value) }
-        },
-        { 'no-header': true }
-    );
+  cli.table(
+    rows,
+    {
+      name: { minWidth: 30, get: (row) => chalk.bold.white(row.name) },
+      value: { get: (row) => chalk.white(row.value) },
+    },
+    { 'no-header': true }
+  )
 }
 
 export function displayCollapsedRow(row: { [k: string]: string | number }) {
-    const collapsedRow: NameValueObj[] = Object.keys(row).map(name => ({
-        name,
-        value: typeof row[name] === 'string' ? row[name] as string : row[name].toString()
-    }));
+  const collapsedRow: NameValueObj[] = Object.keys(row).map((name) => ({
+    name,
+    value: typeof row[name] === 'string' ? (row[name] as string) : row[name].toString(),
+  }))
 
-    displayNameValueTable(collapsedRow);
+  displayNameValueTable(collapsedRow)
 }
 
 export function displayCollapsedTable(rows: { [k: string]: string | number }[]) {
-    for (const row of rows) displayCollapsedRow(row);
+  for (const row of rows) displayCollapsedRow(row)
 }
 
 export function displayTable(rows: { [k: string]: string | number }[], cellHorizontalPadding = 0) {
-    if (!rows.length) {
-        return;
-    }
-    const maxLength = (columnName: string) => rows.reduce(
-        (maxLength, row) => {
-            const val = row[columnName];
-            const valLength = typeof val === 'string' ? val.length : val.toString().length;
-            return Math.max(maxLength, valLength);
-        },
-        columnName.length
-    )
-    const columnDef = (columnName: string) => ({
-        get: (row: typeof rows[number])  => chalk.white(`${row[columnName]}`),
-        minWidth: maxLength(columnName) + cellHorizontalPadding
-    });
-    let columns: Table.table.Columns<{ [k: string]: string }> = {};
-    Object.keys(rows[0]).forEach(columnName => columns[columnName] = columnDef(columnName))
-    cli.table(rows, columns);
+  if (!rows.length) {
+    return
+  }
+  const maxLength = (columnName: string) =>
+    rows.reduce((maxLength, row) => {
+      const val = row[columnName]
+      const valLength = typeof val === 'string' ? val.length : val.toString().length
+      return Math.max(maxLength, valLength)
+    }, columnName.length)
+  const columnDef = (columnName: string) => ({
+    get: (row: typeof rows[number]) => chalk.white(`${row[columnName]}`),
+    minWidth: maxLength(columnName) + cellHorizontalPadding,
+  })
+  const columns: Table.table.Columns<{ [k: string]: string }> = {}
+  Object.keys(rows[0]).forEach((columnName) => (columns[columnName] = columnDef(columnName)))
+  cli.table(rows, columns)
 }
 
 export function toFixedLength(text: string, length: number, spacesOnLeft = false): string {
-    if (text.length > length && length > 3) {
-        return text.slice(0, length-3) + '...';
-    }
-    while(text.length < length) { spacesOnLeft ? text = ' '+text : text += ' ' };
+  if (text.length > length && length > 3) {
+    return text.slice(0, length - 3) + '...'
+  }
+  while (text.length < length) {
+    spacesOnLeft ? (text = ' ' + text) : (text += ' ')
+  }
 
-    return text;
+  return text
+}
+
+export function shortAddress(address: AccountId | string): string {
+  return address.toString().substr(0, 6) + '...' + address.toString().substr(-6)
 }

+ 30 - 0
cli/src/helpers/promptOptions.ts

@@ -0,0 +1,30 @@
+import { ApiParamsOptions, ApiMethodNamedArgs, ApiParamOptions, ApiMethodArg } from '../Types'
+import { Validator } from 'inquirer'
+
+export function setDefaults(promptOptions: ApiParamsOptions, defaultValues: ApiMethodNamedArgs) {
+  for (const defaultValue of defaultValues) {
+    const { name: paramName, value: paramValue } = defaultValue
+    const paramOptions = promptOptions[paramName]
+    if (paramOptions && paramOptions.value) {
+      paramOptions.value.default = paramValue
+    } else if (paramOptions) {
+      promptOptions[paramName].value = { default: paramValue }
+    } else {
+      promptOptions[paramName] = { value: { default: paramValue } }
+    }
+  }
+}
+
+// Temporary(?) helper for easier creation of common ApiParamOptions
+export function createParamOptions(
+  forcedName?: string,
+  defaultValue?: ApiMethodArg | undefined,
+  validator?: Validator
+): ApiParamOptions {
+  const paramOptions: ApiParamOptions = { forcedName, validator }
+  if (defaultValue) {
+    paramOptions.value = { default: defaultValue }
+  }
+
+  return paramOptions
+}

+ 14 - 14
cli/src/helpers/validation.ts

@@ -1,19 +1,19 @@
-import BN from 'bn.js';
-import ExitCodes from '../ExitCodes';
-import { decodeAddress } from '@polkadot/util-crypto';
-import { DerivedBalances } from '@polkadot/api-derive/types';
-import { CLIError } from '@oclif/errors';
+import BN from 'bn.js'
+import ExitCodes from '../ExitCodes'
+import { decodeAddress } from '@polkadot/util-crypto'
+import { DerivedBalances } from '@polkadot/api-derive/types'
+import { CLIError } from '@oclif/errors'
 
-export function validateAddress(address: string, errorMessage: string = 'Invalid address'): void {
-    try {
-        decodeAddress(address);
-    } catch (e) {
-        throw new CLIError(errorMessage, { exit: ExitCodes.InvalidInput });
-    }
+export function validateAddress(address: string, errorMessage = 'Invalid address'): void {
+  try {
+    decodeAddress(address)
+  } catch (e) {
+    throw new CLIError(errorMessage, { exit: ExitCodes.InvalidInput })
+  }
 }
 
 export function checkBalance(accBalances: DerivedBalances, requiredBalance: BN): void {
-    if (requiredBalance.gt(accBalances.availableBalance)) {
-        throw new CLIError('Not enough balance available', { exit: ExitCodes.InvalidInput });
-    }
+  if (requiredBalance.gt(accBalances.availableBalance)) {
+    throw new CLIError('Not enough balance available', { exit: ExitCodes.InvalidInput })
+  }
 }

+ 1 - 1
cli/src/index.ts

@@ -1 +1 @@
-export {run} from '@oclif/command'
+export { run } from '@oclif/command'

+ 44 - 0
cli/src/promptOptions/addWorkerOpening.ts

@@ -0,0 +1,44 @@
+import { ApiParamsOptions, ApiParamOptions, HRTStruct } from '../Types'
+import {
+  OpeningType,
+  SlashingTerms,
+  UnslashableTerms,
+  OpeningType_Worker as OpeningTypeWorker,
+  WorkingGroupOpeningPolicyCommitment,
+} from '@joystream/types/working-group'
+import { Bytes } from '@polkadot/types'
+import { schemaValidator } from '@joystream/types/hiring'
+
+class OpeningPolicyCommitmentOptions implements ApiParamsOptions {
+  [paramName: string]: ApiParamOptions
+  public role_slashing_terms: ApiParamOptions<SlashingTerms> = {
+    value: {
+      default: SlashingTerms.create('Unslashable', new UnslashableTerms()),
+      locked: true,
+    },
+  }
+}
+
+class AddWrokerOpeningOptions implements ApiParamsOptions {
+  [paramName: string]: ApiParamOptions
+  // Lock value for opening_type
+  public opening_type: ApiParamOptions<OpeningType> = {
+    value: {
+      default: OpeningType.create('Worker', new OpeningTypeWorker()),
+      locked: true,
+    },
+  }
+  // Json schema for human_readable_text
+  public human_readable_text: ApiParamOptions<Bytes> = {
+    jsonSchema: {
+      schemaValidator,
+      struct: HRTStruct,
+    },
+  }
+  // Lock value for role_slashing_terms
+  public commitment: ApiParamOptions<WorkingGroupOpeningPolicyCommitment> = {
+    nestedOptions: new OpeningPolicyCommitmentOptions(),
+  }
+}
+
+export default AddWrokerOpeningOptions

+ 51 - 0
cli/src/validators/common.ts

@@ -0,0 +1,51 @@
+//
+// Validators for console input
+// (usable with inquirer package)
+//
+
+type Validator = (value: any) => boolean | string
+
+export const isInt = (message?: string) => (value: any) =>
+  (typeof value === 'number' && Math.floor(value) === value) ||
+  (typeof value === 'string' && parseInt(value).toString() === value)
+    ? true
+    : message || 'The value must be an integer!'
+
+export const gte = (min: number, message?: string) => (value: any) =>
+  parseFloat(value) >= min
+    ? true
+    : message?.replace('{min}', min.toString()) || `The value must be a number greater than or equal ${min}`
+
+export const lte = (max: number, message?: string) => (value: any) =>
+  parseFloat(value) <= max
+    ? true
+    : message?.replace('{max}', max.toString()) || `The value must be less than or equal ${max}`
+
+export const minLen = (min: number, message?: string) => (value: any) =>
+  typeof value === 'string' && value.length >= min
+    ? true
+    : message?.replace('{min}', min.toString()) || `The value should be at least ${min} character(s) long`
+
+export const maxLen = (max: number, message?: string) => (value: any) =>
+  typeof value === 'string' && value.length <= max
+    ? true
+    : message?.replace('{max}', max.toString()) || `The value cannot be more than ${max} character(s) long`
+
+export const combined = (validators: Validator[], message?: string) => (value: any) => {
+  for (const validator of validators) {
+    const result = validator(value)
+    if (result !== true) {
+      return message || result
+    }
+  }
+
+  return true
+}
+
+export const positiveInt = (message?: string) => combined([isInt(), gte(0)], message)
+
+export const minMaxInt = (min: number, max: number, message?: string) =>
+  combined([isInt(), gte(min), lte(max)], message?.replace('{min}', min.toString()).replace('{max}', max.toString()))
+
+export const minMaxStr = (min: number, max: number, message?: string) =>
+  combined([minLen(min), maxLen(max)], message?.replace('{min}', min.toString()).replace('{max}', max.toString()))

+ 7 - 7
cli/test/commands/council/info.test.ts

@@ -1,11 +1,11 @@
-import {expect, test} from '@oclif/test'
+import { expect, test } from '@oclif/test'
 
 describe('info', () => {
   test
-  .stdout()
-  .command(['council:info'])
-  .exit(0)
-  .it('displays "Council" string', ctx => {
-    expect(ctx.stdout).to.contain('Council')
-  })
+    .stdout()
+    .command(['council:info'])
+    .exit(0)
+    .it('displays "Council" string', (ctx) => {
+      expect(ctx.stdout).to.contain('Council')
+    })
 })

+ 1 - 3
cli/test/tsconfig.json

@@ -3,7 +3,5 @@
   "compilerOptions": {
     "noEmit": true
   },
-  "references": [
-    {"path": ".."}
-  ]
+  "references": [{ "path": ".." }]
 }

+ 2 - 1
cli/tsconfig.json

@@ -8,7 +8,8 @@
     "strict": true,
     "target": "es2017",
     "esModuleInterop": true,
-	"types" : [ "node" ]
+    "types" : [ "node" ],
+    "noUnusedLocals": true
   },
   "include": [
     "src/**/*"

+ 12 - 0
devops/ansible/build-and-run-tests-single-node-playbook.yml

@@ -0,0 +1,12 @@
+- name: install dependencies
+  import_playbook: install-dependencies-playbook.yml
+
+- hosts: 127.0.0.1
+  user: root
+  become: yes
+  become_method: sudo
+  tasks:
+    - name: build node
+      include: build-image-tasklist.yml
+- name: run tests
+  import_playbook: run-tests-single-node-playbook.yml

+ 4 - 0
devops/ansible/build-image-tasklist.yml

@@ -0,0 +1,4 @@
+- name: create testing node docker image
+  shell: ./scripts/build-joystream-testing-node-docker-image.sh
+  args:
+    chdir: ../../

+ 34 - 0
devops/ansible/docker-compose.yml

@@ -0,0 +1,34 @@
+version: "3"
+services:
+  node_alice:
+    image: joystream/node-testing
+    container_name: alice
+    entrypoint: ./node --chain=chainspec.json --alice --validator --ws-external --rpc-cors=all
+    ports:
+      - "30333:30333"
+      - "9933:9933"
+      - "9944:9944"
+    networks:
+      testing_net:
+        ipv4_address: 172.28.1.1
+
+  node_bob:
+    image: joystream/node-testing
+    container_name: bob
+    entrypoint: ./node --chain=chainspec.json --bob --ws-external --rpc-cors=all
+    ports:
+      - "30335:30333"
+      - "9935:9933"
+      - "9945:9944"
+    links:
+      - "node_alice:alice"
+    networks:
+      testing_net:
+        ipv4_address: 172.28.1.2
+
+networks:
+  testing_net:
+    ipam:
+      driver: default
+      config:
+        - subnet: 172.28.0.0/16

+ 2 - 0
devops/ansible/hosts

@@ -0,0 +1,2 @@
+[sites]
+127.0.0.1 ansible_connection=local

+ 15 - 0
devops/ansible/install-dependencies-playbook.yml

@@ -0,0 +1,15 @@
+- hosts: 127.0.0.1
+  user: root
+  become: yes
+  become_method: sudo
+  tasks:
+    - name: install pip
+      apt: name=python-pip state=present
+    - name: install docker
+      pip: name=docker
+    - name: Install yarn with npm
+      npm:
+        name: yarn
+        global: yes
+    - name: Install docker compose
+      pip: name=docker-compose

+ 24 - 0
devops/ansible/run-tests-single-node-playbook.yml

@@ -0,0 +1,24 @@
+- hosts: 127.0.0.1
+  user: root
+  become: yes
+  become_method: sudo
+  tasks:
+    - name: run network
+      block:
+        - name: run docker container
+          docker_container:
+            name: "joystream-node-testing"
+            image: "joystream/node-testing"
+            ports:
+              - "9944:9944"
+            entrypoint: ./node --chain=chainspec.json --alice --validator --ws-external --rpc-cors=all
+            state: started
+        - name: execute network tests
+          shell: yarn debug >> ../../.tmp/tests.log
+          args:
+            chdir: ../../tests/network-tests/
+      always:
+        - name: stop docker container
+          docker_container:
+            name: "joystream-node-testing"
+            state: absent

+ 24 - 0
devops/ansible/run-tests-two-nodes-playbook.yml

@@ -0,0 +1,24 @@
+- name: install dependencies
+  import_playbook: install-dependencies-playbook.yml
+
+- hosts: 127.0.0.1
+  user: root
+  become: yes
+  become_method: sudo
+
+  tasks:
+    - name: run network
+      block:
+        - name: run two nodes containerized network
+          docker_compose:
+            project_src: ./
+            state: present
+        - name: execute network tests
+          shell: yarn test >> ../../.tmp/tests.log
+          args:
+            chdir: ../../tests/network-tests/
+      always:
+        - name: stop containers
+          docker_compose:
+            project_src: ./
+            state: absent

+ 37 - 0
devops/dockerfiles/ansible-node/Dockerfile

@@ -0,0 +1,37 @@
+FROM joystream/rust-builder AS builder
+LABEL description="Compiles all workspace artifacts"
+WORKDIR /joystream
+COPY . /joystream
+
+# Build joystream-node and its dependencies - runtime
+RUN cargo build --release -p joystream-node
+RUN /joystream/scripts/create-test-chainspec.sh
+
+FROM debian:stretch
+LABEL description="Joystream node"
+WORKDIR /joystream
+COPY --from=builder /joystream/target/release/joystream-node /joystream/node
+COPY --from=builder /joystream/target/release/wbuild/joystream-node-runtime/joystream_node_runtime.compact.wasm /joystream/runtime.compact.wasm
+COPY --from=builder /joystream/.tmp/chainspec.json /joystream/chainspec.json
+
+# confirm it works
+RUN /joystream/node --version
+
+# https://manpages.debian.org/stretch/coreutils/b2sum.1.en.html
+# RUN apt-get install coreutils
+# print the blake2 256 hash of the wasm blob
+RUN b2sum -l 256 /joystream/runtime.compact.wasm
+# print the blake2 512 hash of the wasm blob
+RUN b2sum -l 512 /joystream/runtime.compact.wasm
+
+EXPOSE 30333 9933 9944
+
+# Use these volumes to persits chain state and keystore, eg.:
+# --base-path /data
+# optionally separate keystore (otherwise it will be stored in the base path)
+# --keystore-path /keystore
+# if base-path isn't specified, chain state is stored inside container in ~/.local/share/joystream-node/
+# which is not ideal
+VOLUME ["/data", "/keystore"]
+
+ENTRYPOINT ["/joystream/node"]

+ 766 - 0
devops/vstore/classes.json

@@ -0,0 +1,766 @@
+[
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "createClass",
+		"args": [
+			"0x4d65646961204f626a656374",
+			"0x436c61737320666f72207265736f6c76696e67206120636f6e74656e7420656e7469747920746f20616e2061637475616c206d656469612066696c65206f72206c696e6b2e",
+			{
+				"entity_permissions": { "update": [0, 1, 2], "maintainer_has_all_permissions": true },
+				"entities_can_be_created": true,
+				"add_schemas": [0],
+				"create_entities": [0, 1, 2],
+				"reference_constraint": { "NoConstraint": null },
+				"admins": [0],
+				"last_permissions_update": 0
+			}
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "createClass",
+		"args": [
+			"0x4c616e6775616765",
+			"0x436c61737320666f722073657474696e67206c616e67756167652e",
+			{
+				"entity_permissions": { "update": [0], "maintainer_has_all_permissions": true },
+				"entities_can_be_created": true,
+				"add_schemas": [0],
+				"create_entities": [0],
+				"reference_constraint": { "NoConstraint": null },
+				"admins": [0],
+				"last_permissions_update": 0
+			}
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "createClass",
+		"args": [
+			"0x436f6e74656e74204c6963656e7365",
+			"0x436c61737320666f722073706563696679696e6720746865206c6963656e736520756e64657220776869636820636f6e74656e74206973207075626c69736865642e",
+			{
+				"entity_permissions": { "update": [0], "maintainer_has_all_permissions": true },
+				"entities_can_be_created": true,
+				"add_schemas": [0],
+				"create_entities": [0],
+				"reference_constraint": { "NoConstraint": null },
+				"admins": [0],
+				"last_permissions_update": 0
+			}
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "createClass",
+		"args": [
+			"0x5075626c69636174696f6e20537461747573",
+			"0x436c61737320666f722073657474696e6720746865207075626c69636174696f6e20737461747573206f66206120636f6e74656e7420656e746974792e",
+			{
+				"entity_permissions": { "update": [0], "maintainer_has_all_permissions": true },
+				"entities_can_be_created": true,
+				"add_schemas": [0],
+				"create_entities": [0],
+				"reference_constraint": { "NoConstraint": null },
+				"admins": [0],
+				"last_permissions_update": 0
+			}
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "createClass",
+		"args": [
+			"0x4375726174696f6e20537461747573",
+			"0x436c61737320666f722063757261746f727320746f2073657420746865207075626c69636174696f6e20737461747573206f66206120636f6e74656e7420656e746974792e",
+			{
+				"entity_permissions": { "update": [0], "maintainer_has_all_permissions": true },
+				"entities_can_be_created": true,
+				"add_schemas": [0],
+				"create_entities": [0],
+				"reference_constraint": { "NoConstraint": null },
+				"admins": [0],
+				"last_permissions_update": 0
+			}
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "createClass",
+		"args": [
+			"0x466561747572656420436f6e74656e74",
+			"0x436c61737320666f722073657474696e6720666561747572656420636f6e74656e74206f6e2074686520706c6174666f726d2e",
+			{
+				"entity_permissions": { "update": [0], "maintainer_has_all_permissions": true },
+				"entities_can_be_created": true,
+				"add_schemas": [0],
+				"create_entities": [0],
+				"reference_constraint": { "NoConstraint": null },
+				"admins": [0],
+				"last_permissions_update": 0
+			}
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "createClass",
+		"args": [
+			"0x566964656f",
+			"0x436c61737320666f722067656e6572616c20766964656f73206e6f742061737369676e61626c6520746f2061206d6f726520737065636966696320766964656f20636f6e74656e7420747970652e",
+			{
+				"entity_permissions": { "update": [0, 1, 2], "maintainer_has_all_permissions": true },
+				"entities_can_be_created": true,
+				"add_schemas": [0],
+				"create_entities": [0, 1, 2],
+				"reference_constraint": { "NoConstraint": null },
+				"admins": [0],
+				"last_permissions_update": 0
+			}
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "createClass",
+		"args": [
+			"0x566964656f2043617465676f7279",
+			"0x436c61737320666f722073657474696e67207468652063617465676f727920666f7220766964656f7320696e2074686520566964656f20636c6173732e",
+			{
+				"entity_permissions": { "update": [0], "maintainer_has_all_permissions": true },
+				"entities_can_be_created": true,
+				"add_schemas": [0],
+				"create_entities": [0],
+				"reference_constraint": { "NoConstraint": null },
+				"admins": [0],
+				"last_permissions_update": 0
+			}
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "createClass",
+		"args": [
+			"0x4d7573696320416c62756d",
+			"0x416e20616c62756d206973206120636f6c6c656374696f6e206f6620747261636b73206f7220617564696f207265636f7264696e67732e20557375616c6c7920627920612073696e676c6520617274697374206f722067726f75702e",
+			{
+				"entity_permissions": { "update": [0, 1, 2], "maintainer_has_all_permissions": true },
+				"entities_can_be_created": true,
+				"add_schemas": [0],
+				"create_entities": [0, 1, 2],
+				"reference_constraint": { "NoConstraint": null },
+				"admins": [0],
+				"last_permissions_update": 0
+			}
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "createClass",
+		"args": [
+			"0x4d757369632047656e7265",
+			"0x436c61737320666f722073657474696e67207468652067656e726520666f72206d757369632e",
+			{
+				"entity_permissions": { "update": [0], "maintainer_has_all_permissions": true },
+				"entities_can_be_created": true,
+				"add_schemas": [0],
+				"create_entities": [0],
+				"reference_constraint": { "NoConstraint": null },
+				"admins": [0],
+				"last_permissions_update": 0
+			}
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "createClass",
+		"args": [
+			"0x4d75736963204d6f6f64",
+			"0x436c61737320666f722073657474696e6720746865206d6f6f647320666f72206d757369632e",
+			{
+				"entity_permissions": { "update": [0], "maintainer_has_all_permissions": true },
+				"entities_can_be_created": true,
+				"add_schemas": [0],
+				"create_entities": [0],
+				"reference_constraint": { "NoConstraint": null },
+				"admins": [0],
+				"last_permissions_update": 0
+			}
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "createClass",
+		"args": [
+			"0x4d75736963205468656d65",
+			"0x436c61737320666f722073657474696e6720746865207468656d657320666f72206d757369632e",
+			{
+				"entity_permissions": { "update": [0], "maintainer_has_all_permissions": true },
+				"entities_can_be_created": true,
+				"add_schemas": [0],
+				"create_entities": [0],
+				"reference_constraint": { "NoConstraint": null },
+				"admins": [0],
+				"last_permissions_update": 0
+			}
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "createClass",
+		"args": [
+			"0x4d7573696320547261636b",
+			"0x4120747261636b20697320616e20696e646976696475616c20736f6e67206f7220696e737472756d656e74616c207265636f7264696e672e",
+			{
+				"entity_permissions": { "update": [0, 1, 2], "maintainer_has_all_permissions": true },
+				"entities_can_be_created": true,
+				"add_schemas": [0],
+				"create_entities": [0, 1, 2],
+				"reference_constraint": { "NoConstraint": null },
+				"admins": [0],
+				"last_permissions_update": 0
+			}
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "addClassSchema",
+		"args": [
+			0,
+			1,
+			[],
+			[
+				{
+					"prop_type": { "Text": 48 },
+					"required": true,
+					"name": "Value",
+					"description": "ContentId of object in the data directory"
+				},
+				{
+					"prop_type": { "Uint64": null },
+					"required": false,
+					"name": "Channel Id",
+					"description": "Id of the channel this media object is published under."
+				}
+			]
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "addClassSchema",
+		"args": [
+			0,
+			2,
+			[],
+			[
+				{
+					"prop_type": { "Text": 2 },
+					"required": true,
+					"name": "Value",
+					"description": "Language code following the ISO 639-1 two letter standard."
+				}
+			]
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "addClassSchema",
+		"args": [
+			0,
+			3,
+			[],
+			[
+				{
+					"prop_type": { "Text": 255 },
+					"required": true,
+					"name": "Value",
+					"description": "The license of which the content is originally published under."
+				}
+			]
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "addClassSchema",
+		"args": [
+			0,
+			4,
+			[],
+			[
+				{
+					"prop_type": { "Text": 50 },
+					"required": true,
+					"name": "Value",
+					"description": "The publication status of the content in the content directory."
+				}
+			]
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "addClassSchema",
+		"args": [
+			0,
+			5,
+			[],
+			[
+				{
+					"prop_type": { "Text": 255 },
+					"required": true,
+					"name": "Value",
+					"description": "The curator publication status of the content in the content directory."
+				}
+			]
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "addClassSchema",
+		"args": [
+			0,
+			6,
+			[],
+			[
+				{
+					"prop_type": { "Internal": 7 },
+					"required": false,
+					"name": "Top Video",
+					"description": "The video that has the most prominent position(s) on the platform."
+				},
+				{
+					"prop_type": { "InternalVec": [12, 7] },
+					"required": false,
+					"name": "Featured Videos",
+					"description": "Videos featured in the Video tab."
+				},
+				{
+					"prop_type": { "InternalVec": [12, 9] },
+					"required": false,
+					"name": "Featured Albums",
+					"description": "Music albums featured in the Music tab."
+				}
+			]
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "addClassSchema",
+		"args": [
+			0,
+			7,
+			[],
+			[
+				{
+					"prop_type": { "Text": 255 },
+					"required": true,
+					"name": "Title",
+					"description": "The title of the video"
+				},
+				{
+					"prop_type": { "Text": 255 },
+					"required": true,
+					"name": "Thumbnail",
+					"description": "URL to video thumbnail: NOTE: Should be an https link to an image of ratio 16:9, ideally 1280 pixels wide by 720 pixels tall, with a minimum width of 640 pixels, in JPEG or PNG format."
+				},
+				{
+					"prop_type": { "Text": 4000 },
+					"required": true,
+					"name": "Description",
+					"description": "Information about the video."
+				},
+				{
+					"prop_type": { "Internal": 2 },
+					"required": true,
+					"name": "Language",
+					"description": "The main language used in the video."
+				},
+				{
+					"prop_type": { "Int64": null },
+					"required": true,
+					"name": "First Released",
+					"description": "When the video was first released"
+				},
+				{
+					"prop_type": { "Internal": 8 },
+					"required": false,
+					"name": "Category",
+					"description": "The category of the video."
+				},
+				{
+					"prop_type": { "TextVec": [5, 255] },
+					"required": false,
+					"name": "Links",
+					"description": "Links to the creators pages."
+				},
+				{
+					"prop_type": { "Internal": 1 },
+					"required": false,
+					"name": "Object",
+					"description": "The entityId of the object in the data directory."
+				},
+				{
+					"prop_type": { "Internal": 4 },
+					"required": true,
+					"name": "Publication Status",
+					"description": "The publication status of the video."
+				},
+				{
+					"prop_type": { "Internal": 5 },
+					"required": false,
+					"name": "Curation Status",
+					"description": "The publication status of the video set by the a content curator on the platform."
+				},
+				{
+					"prop_type": { "Bool": null },
+					"required": true,
+					"name": "Explicit",
+					"description": "Indicates whether the video contains explicit material."
+				},
+				{
+					"prop_type": { "Internal": 3 },
+					"required": true,
+					"name": "License",
+					"description": "The license of which the video is released under."
+				},
+				{
+					"prop_type": { "Text": 255 },
+					"required": false,
+					"name": "Attribution",
+					"description": "If the License requires attribution, add this here."
+				},
+				{
+					"prop_type": { "Uint64": null },
+					"required": false,
+					"name": "Channel Id",
+					"description": "Id of the channel this video is published under."
+				}
+			]
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "addClassSchema",
+		"args": [
+			0,
+			8,
+			[],
+			[
+				{
+					"prop_type": { "Text": 255 },
+					"required": true,
+					"name": "Value",
+					"description": "Categories for videos."
+				}
+			]
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "addClassSchema",
+		"args": [
+			0,
+			9,
+			[],
+			[
+				{
+					"prop_type": { "Text": 255 },
+					"required": true,
+					"name": "Title",
+					"description": "The title of the album"
+				},
+				{
+					"prop_type": { "Text": 255 },
+					"required": true,
+					"name": "Artist",
+					"description": "The artist, composer, band or group that published the album."
+				},
+				{
+					"prop_type": { "Text": 255 },
+					"required": true,
+					"name": "Thumbnail",
+					"description": "URL to album cover art thumbnail: NOTE: Should be an https link to a square image, between 1400x1400 and 3000x3000 pixels, in JPEG or PNG format."
+				},
+				{
+					"prop_type": { "Text": 4000 },
+					"required": true,
+					"name": "Description",
+					"description": "Information about the album and artist."
+				},
+				{
+					"prop_type": { "Int64": null },
+					"required": true,
+					"name": "First Released",
+					"description": "When the album was first released"
+				},
+				{
+					"prop_type": { "InternalVec": [3, 10] },
+					"required": false,
+					"name": "Genre",
+					"description": "The genre(s) of the album."
+				},
+				{
+					"prop_type": { "InternalVec": [3, 11] },
+					"required": false,
+					"name": "Mood",
+					"description": "The mood(s) of the album."
+				},
+				{
+					"prop_type": { "InternalVec": [3, 12] },
+					"required": false,
+					"name": "Theme",
+					"description": "The theme(s) of the album."
+				},
+				{
+					"prop_type": { "InternalVec": [100, 13] },
+					"required": false,
+					"name": "Tracks",
+					"description": "The tracks of the album."
+				},
+				{
+					"prop_type": { "Internal": 2 },
+					"required": false,
+					"name": "Language",
+					"description": "The language of the song lyrics in the album."
+				},
+				{
+					"prop_type": { "TextVec": [5, 255] },
+					"required": false,
+					"name": "Links",
+					"description": "Links to the artist or album site, or social media pages."
+				},
+				{
+					"prop_type": { "Text": 255 },
+					"required": false,
+					"name": "Lyrics",
+					"description": "Link to the album tracks lyrics."
+				},
+				{
+					"prop_type": { "Text": 255 },
+					"required": false,
+					"name": "Composer or songwriter",
+					"description": "The composer(s) and/or songwriter(s) of the album."
+				},
+				{
+					"prop_type": { "TextVec": [5, 255] },
+					"required": false,
+					"name": "Reviews",
+					"description": "Links to reviews of the album."
+				},
+				{
+					"prop_type": { "Internal": 4 },
+					"required": true,
+					"name": "Publication Status",
+					"description": "The publication status of the album."
+				},
+				{
+					"prop_type": { "Internal": 5 },
+					"required": false,
+					"name": "Curation Status",
+					"description": "The publication status of the album set by the a content curator on the platform."
+				},
+				{
+					"prop_type": { "Bool": null },
+					"required": true,
+					"name": "Explicit",
+					"description": "Indicates whether the album contains explicit material."
+				},
+				{
+					"prop_type": { "Internal": 3 },
+					"required": true,
+					"name": "License",
+					"description": "The license of which the album is released under."
+				},
+				{
+					"prop_type": { "Text": 255 },
+					"required": false,
+					"name": "Attribution",
+					"description": "If the License requires attribution, add this here."
+				},
+				{
+					"prop_type": { "Uint64": null },
+					"required": false,
+					"name": "Channel Id",
+					"description": "Id of the channel this album is published under."
+				}
+			]
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "addClassSchema",
+		"args": [
+			0,
+			10,
+			[],
+			[{ "prop_type": { "Text": 100 }, "required": true, "name": "Value", "description": "Genres for music." }]
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "addClassSchema",
+		"args": [
+			0,
+			11,
+			[],
+			[{ "prop_type": { "Text": 100 }, "required": true, "name": "Value", "description": "Moods for music." }]
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "addClassSchema",
+		"args": [
+			0,
+			12,
+			[],
+			[{ "prop_type": { "Text": 100 }, "required": true, "name": "Value", "description": "Themes for music." }]
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "addClassSchema",
+		"args": [
+			0,
+			13,
+			[],
+			[
+				{
+					"prop_type": { "Text": 255 },
+					"required": true,
+					"name": "Title",
+					"description": "The title of the track"
+				},
+				{
+					"prop_type": { "Text": 255 },
+					"required": true,
+					"name": "Artist",
+					"description": "The artist, composer, band or group that published the track."
+				},
+				{
+					"prop_type": { "Text": 255 },
+					"required": true,
+					"name": "Thumbnail",
+					"description": "URL to track cover art: NOTE: Should be an https link to a square image, between 1400x1400 and 3000x3000 pixels, in JPEG or PNG format."
+				},
+				{
+					"prop_type": { "Text": 255 },
+					"required": false,
+					"name": "Description",
+					"description": "Information about the track."
+				},
+				{
+					"prop_type": { "Internal": 2 },
+					"required": false,
+					"name": "Language",
+					"description": "The language of the lyrics in the track."
+				},
+				{
+					"prop_type": { "Int64": null },
+					"required": true,
+					"name": "First Released",
+					"description": "When the track was first released"
+				},
+				{
+					"prop_type": { "Internal": 10 },
+					"required": false,
+					"name": "Genre",
+					"description": "The genre of the track."
+				},
+				{
+					"prop_type": { "Internal": 11 },
+					"required": false,
+					"name": "Mood",
+					"description": "The mood of the track."
+				},
+				{
+					"prop_type": { "Internal": 12 },
+					"required": false,
+					"name": "Theme",
+					"description": "The theme of the track."
+				},
+				{
+					"prop_type": { "TextVec": [5, 255] },
+					"required": false,
+					"name": "Links",
+					"description": "Links to the artist site or social media pages."
+				},
+				{
+					"prop_type": { "Text": 255 },
+					"required": false,
+					"name": "Composer or songwriter",
+					"description": "The composer(s) and/or songwriter(s) of the track."
+				},
+				{
+					"prop_type": { "Text": 255 },
+					"required": false,
+					"name": "Lyrics",
+					"description": "Link to the track lyrics."
+				},
+				{
+					"prop_type": { "Internal": 1 },
+					"required": false,
+					"name": "Object",
+					"description": "The entityId of the object in the data directory."
+				},
+				{
+					"prop_type": { "Internal": 4 },
+					"required": true,
+					"name": "Publication Status",
+					"description": "The publication status of the track."
+				},
+				{
+					"prop_type": { "Internal": 5 },
+					"required": false,
+					"name": "Curation Status",
+					"description": "The publication status of the track set by the a content curator on the platform."
+				},
+				{
+					"prop_type": { "Bool": null },
+					"required": true,
+					"name": "Explicit",
+					"description": "Indicates whether the track contains explicit material."
+				},
+				{
+					"prop_type": { "Internal": 3 },
+					"required": true,
+					"name": "License",
+					"description": "The license of which the track is released under."
+				},
+				{
+					"prop_type": { "Text": 255 },
+					"required": false,
+					"name": "Attribution",
+					"description": "If the License requires attribution, add this here."
+				},
+				{
+					"prop_type": { "Uint64": null },
+					"required": false,
+					"name": "Channel Id",
+					"description": "Id of the channel this track is published under."
+				}
+			]
+		]
+	}
+]

+ 12694 - 0
devops/vstore/entities.json

@@ -0,0 +1,12694 @@
+[
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "transaction",
+		"args": [
+			[
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 2 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 4 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 4 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 5 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 5 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 5 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 5 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 3 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 3 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 3 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 3 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 0 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "aa" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 1 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ab" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 2 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ae" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 3 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "af" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 4 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ak" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 5 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "am" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 6 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "an" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 7 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ar" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 8 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "as" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 9 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "av" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 10 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ay" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 11 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "az" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 12 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ba" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 13 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "be" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 14 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "bg" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 15 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "bh" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 16 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "bi" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 17 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "bm" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 18 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "bn" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 19 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "bo" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 20 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "br" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 21 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "bs" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 22 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ca" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 23 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ce" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 24 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ch" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 25 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "co" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 26 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "cr" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 27 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "cs" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 28 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "cu" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 29 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "cv" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 30 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "cy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 31 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "da" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 32 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "de" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 33 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "dv" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 34 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "dz" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 35 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ee" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 36 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "el" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 37 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "en" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 38 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "eo" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 39 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "es" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 40 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "et" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 41 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "eu" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 42 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "fa" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 43 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ff" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 44 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "fi" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 45 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "fj" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 46 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "fo" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 47 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "fr" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 48 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "fy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 49 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ga" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 50 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "gd" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 51 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "gl" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 52 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "gn" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 53 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "gu" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 54 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "gv" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 55 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ha" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 56 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "he" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 57 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "hi" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 58 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ho" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 59 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "hr" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 60 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ht" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 61 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "hu" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 62 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "hy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 63 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "hz" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 64 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ia" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 65 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "id" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 66 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ie" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 67 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ig" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 68 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ii" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 69 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ik" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 70 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "io" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 71 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "is" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 72 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "it" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 73 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "iu" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 74 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ja" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 75 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "jv" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 76 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ka" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 77 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "kg" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 78 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ki" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 79 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "kj" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 80 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "kk" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 81 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "kl" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 82 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "km" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 83 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "kn" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 84 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ko" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 85 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "kr" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 86 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ks" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 87 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ku" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 88 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "kv" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 89 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "kw" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 90 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ky" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 91 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "la" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 92 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "lb" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 93 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "lg" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 94 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "li" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 95 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ln" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 96 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "lo" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 97 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "lt" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 98 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "lu" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 99 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "lv" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 100 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "mg" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 101 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "mh" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 102 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "mi" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 103 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "mk" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 104 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ml" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 105 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "mn" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 106 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "mr" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 107 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ms" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 108 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "mt" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 109 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "my" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 110 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "na" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 111 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "nb" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 112 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "nd" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 113 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ne" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 114 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ng" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 115 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "nl" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 116 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "nn" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 117 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "no" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 118 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "nr" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 119 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "nv" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 120 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ny" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 121 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "oc" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 122 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "oj" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 123 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "om" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 124 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "or" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 125 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "os" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 126 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "pa" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 127 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "pi" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 128 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "pl" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 129 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ps" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 130 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "pt" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 131 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "qu" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 132 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "rm" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 133 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "rn" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 134 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ro" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 135 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ru" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 136 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "rw" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 137 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "sa" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 138 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "sc" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 139 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "sd" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 140 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "se" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 141 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "sg" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 142 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "si" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 143 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "sk" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 144 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "sl" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 145 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "sm" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 146 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "sn" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 147 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "so" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 148 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "sq" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 149 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "sr" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 150 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ss" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 151 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "st" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 152 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "su" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 153 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "sv" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 154 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "sw" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 155 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ta" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 156 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "te" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 157 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "tg" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 158 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "th" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 159 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ti" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 160 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "tk" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 161 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "tl" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 162 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "tn" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 163 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "to" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 164 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "tr" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 165 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ts" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 166 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "tt" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 167 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "tw" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 168 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ty" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 169 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ug" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 170 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "uk" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 171 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ur" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 172 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "uz" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 173 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "ve" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 174 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "vi" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 175 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "vo" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 176 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "wa" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 177 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "wo" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 178 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "xh" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 179 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "yi" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 180 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "yo" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 181 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "za" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 182 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "zh" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 183 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "zu" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 184 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Public" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 185 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Unlisted" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 186 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Edited" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 187 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Updated schema" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 188 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Under review" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 189 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Removed" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 190 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Public Domain" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 191 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Original content" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 192 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{
+									"in_class_index": 0,
+									"value": { "PropertyValue": { "Text": "Creative Commons (attribution required)" } }
+								}
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 193 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{
+									"in_class_index": 0,
+									"value": { "PropertyValue": { "Text": "Fair Use  (attribution required)" } }
+								}
+							]
+						}
+					}
+				}
+			]
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "transaction",
+		"args": [
+			[
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 8 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 8 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 8 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 8 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 8 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 8 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 8 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 8 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 8 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 8 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 8 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 8 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 8 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 8 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 8 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 0 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Film & Animation" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 1 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Autos & Vehicles" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 2 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Music" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 3 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Pets & Animals" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 4 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sports" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 5 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Travel & Events" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 6 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Gaming" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 7 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "People & Blogs" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 8 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Comedy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 9 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Entertainment" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 10 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "News & Politics" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 11 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Howto & Style" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 12 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Education" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 13 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{
+									"in_class_index": 0,
+									"value": { "PropertyValue": { "Text": "Science & Technology" } }
+								}
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 14 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{
+									"in_class_index": 0,
+									"value": { "PropertyValue": { "Text": "Nonprofits & Activism" } }
+								}
+							]
+						}
+					}
+				}
+			]
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "transaction",
+		"args": [
+			[
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 10 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 0 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Avant-Garde" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 1 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Blues" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 2 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Children's" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 3 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Classical" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 4 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Comedy/Spoken" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 5 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Country" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 6 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Easy Listening" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 7 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Electronic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 8 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Folk" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 9 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Holiday" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 10 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "International" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 11 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Jazz" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 12 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Latin" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 13 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "New Age" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 14 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Pop/Rock" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 15 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "R&B" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 16 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Rap" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 17 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Reggae" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 18 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Religious" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 19 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Stage & Screen" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 20 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Vocal" } } }
+							]
+						}
+					}
+				}
+			]
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "transaction",
+		"args": [
+			[
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 11 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 0 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Acerbic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 1 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Aggressive" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 2 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Agreeable" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 3 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Airy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 4 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Ambitious" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 5 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{
+									"in_class_index": 0,
+									"value": { "PropertyValue": { "Text": "Amiable/Good-Natured" } }
+								}
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 6 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Angry" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 7 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Angst-Ridden" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 8 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{
+									"in_class_index": 0,
+									"value": { "PropertyValue": { "Text": "Anguished/Distraught" } }
+								}
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 9 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Angular" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 10 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Animated" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 11 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Apocalyptic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 12 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Arid" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 13 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Athletic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 14 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Atmospheric" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 15 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Austere" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 16 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Autumnal" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 17 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Belligerent" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 18 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Benevolent" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 19 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Bitter" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 20 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Bittersweet" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 21 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Bleak" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 22 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Boisterous" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 23 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Bombastic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 24 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Brash" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 25 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Brassy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 26 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Bravado" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 27 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Bright" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 28 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Brittle" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 29 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Brooding" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 30 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Calm/Peaceful" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 31 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Campy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 32 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Capricious" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 33 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Carefree" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 34 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Cartoonish" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 35 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Cathartic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 36 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Celebratory" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 37 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Cerebral" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 38 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Cheerful" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 39 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Child-like" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 40 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Circular" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 41 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Clinical" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 42 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Cold" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 43 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Comic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 44 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Complex" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 45 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Concise" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 46 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Confident" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 47 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Confrontational" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 48 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Cosmopolitan" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 49 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Crunchy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 50 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Cynical/Sarcastic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 51 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Dark" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 52 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Declamatory" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 53 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Defiant" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 54 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Delicate" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 55 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Demonic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 56 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Desperate" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 57 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Detached" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 58 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Devotional" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 59 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Difficult" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 60 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Dignified/Noble" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 61 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Dramatic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 62 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Dreamy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 63 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Driving" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 64 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Druggy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 65 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Earnest" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 66 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Earthy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 67 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Ebullient" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 68 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Eccentric" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 69 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Ecstatic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 70 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Eerie" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 71 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Effervescent" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 72 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Elaborate" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 73 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Elegant" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 74 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Elegiac" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 75 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Energetic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 76 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Enigmatic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 77 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Epic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 78 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Erotic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 79 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Ethereal" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 80 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Euphoric" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 81 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Exciting" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 82 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Exotic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 83 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Explosive" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 84 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Extroverted" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 85 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Exuberant" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 86 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{
+									"in_class_index": 0,
+									"value": { "PropertyValue": { "Text": "Fantastic/Fantasy-like" } }
+								}
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 87 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Feral" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 88 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Feverish" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 89 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Fierce" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 90 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Fiery" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 91 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Flashy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 92 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Flowing" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 93 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Fractured" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 94 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Freewheeling" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 95 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Fun" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 96 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Funereal" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 97 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Gentle" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 98 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Giddy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 99 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Gleeful" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 100 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Gloomy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 101 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Graceful" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 102 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Greasy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 103 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Grim" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 104 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Gritty" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 105 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Gutsy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 106 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Happy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 107 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Harsh" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 108 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Hedonistic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 109 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Heroic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 110 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Hostile" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 111 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Humorous" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 112 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Hungry" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 113 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Hymn-like" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 114 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Hyper" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 115 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Hypnotic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 116 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Improvisatory" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 117 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Indulgent" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 118 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Innocent" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 119 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Insular" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 120 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Intense" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 121 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Intimate" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 122 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Introspective" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 123 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Ironic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 124 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Irreverent" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 125 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Jovial" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 126 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Joyous" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 127 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Kinetic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 128 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Knotty" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 129 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Laid-Back/Mellow" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 130 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Languid" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 131 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Lazy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 132 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Light" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 133 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Literate" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 134 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Lively" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 135 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Lonely" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 136 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Lush" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 137 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Lyrical" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 138 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Macabre" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 139 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Magical" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 140 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Majestic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 141 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Malevolent" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 142 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Manic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 143 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Marching" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 144 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Martial" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 145 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Meandering" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 146 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Mechanical" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 147 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Meditative" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 148 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Melancholy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 149 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Menacing" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 150 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Messy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 151 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Mighty" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 152 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Monastic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 153 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Monumental" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 154 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Motoric" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 155 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Mysterious" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 156 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Mystical" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 157 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Naive" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 158 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Narcotic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 159 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Narrative" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 160 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Negative" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 161 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Nervous/Jittery" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 162 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Nihilistic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 163 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Nocturnal" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 164 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Nostalgic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 165 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Ominous" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 166 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Optimistic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 167 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Opulent" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 168 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Organic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 169 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Ornate" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 170 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Outraged" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 171 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Outrageous" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 172 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Paranoid" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 173 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Passionate" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 174 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Pastoral" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 175 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Patriotic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 176 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Perky" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 177 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Philosophical" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 178 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Plain" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 179 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Plaintive" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 180 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Playful" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 181 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Poignant" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 182 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Positive" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 183 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Powerful" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 184 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Precious" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 185 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Provocative" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 186 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Pulsing" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 187 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Pure" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 188 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Quirky" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 189 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Rambunctious" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 190 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Ramshackle" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 191 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Raucous" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 192 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{
+									"in_class_index": 0,
+									"value": { "PropertyValue": { "Text": "Reassuring/Consoling" } }
+								}
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 193 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Rebellious" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 194 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Reckless" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 195 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Refined" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 196 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Reflective" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 197 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Regretful" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 198 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Relaxed" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 199 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Reserved" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 200 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Resolute" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 201 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Restrained" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 202 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Reverent" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 203 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Rhapsodic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 204 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Rollicking" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 205 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Romantic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 206 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Rousing" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 207 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Rowdy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 208 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Rustic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 209 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sacred" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 210 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sad" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 211 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sarcastic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 212 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sardonic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 213 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Satirical" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 214 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Savage" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 215 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Scary" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 216 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Scattered" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 217 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Searching" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 218 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Self-Conscious" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 219 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sensual" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 220 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sentimental" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 221 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Serious" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 222 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Severe" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 223 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sexual" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 224 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sexy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 225 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Shimmering" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 226 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Silly" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 227 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sleazy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 228 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Slick" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 229 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Smooth" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 230 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Snide" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 231 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Soft/Quiet" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 232 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Somber" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 233 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Soothing" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 234 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sophisticated" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 235 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Spacey" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 236 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sparkling" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 237 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sparse" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 238 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Spicy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 239 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Spiritual" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 240 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Spontaneous" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 241 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Spooky" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 242 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sprawling" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 243 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sprightly" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 244 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Springlike" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 245 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Stately" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 246 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Street-Smart" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 247 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Striding" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 248 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Strong" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 249 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Stylish" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 250 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Suffocating" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 251 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sugary" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 252 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Summery" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 253 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Suspenseful" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 254 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Swaggering" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 255 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sweet" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 256 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Swinging" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 257 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Technical" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 258 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Tender" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 259 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Tense/Anxious" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 260 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Theatrical" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 261 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Thoughtful" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 262 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Threatening" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 263 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Thrilling" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 264 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Thuggish" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 265 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Tragic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 266 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{
+									"in_class_index": 0,
+									"value": { "PropertyValue": { "Text": "Transparent/Translucent" } }
+								}
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 267 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Trashy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 268 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Trippy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 269 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Triumphant" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 270 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Tuneful" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 271 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Turbulent" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 272 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Uncompromising" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 273 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Understated" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 274 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Unsettling" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 275 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Uplifting" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 276 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Urgent" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 277 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Virile" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 278 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Visceral" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 279 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Volatile" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 280 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Vulgar" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 281 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Warm" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 282 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Weary" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 283 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Whimsical" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 284 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Wintry" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 285 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Wistful" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 286 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Witty" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 287 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Wry" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 288 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Yearning" } } }
+							]
+						}
+					}
+				}
+			]
+		]
+	},
+
+	{
+		"sectionName": "versionedStorePermissions",
+		"methodName": "transaction",
+		"args": [
+			[
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": { "CreateEntity": { "class_id": 12 } }
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 0 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Adventure" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 1 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Affection/Fondness" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 2 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Affirmation" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 3 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Anger/Hostility" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 4 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Animals" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 5 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Anniversary" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 6 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Argument" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 7 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "At the Beach" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 8 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "At the Office" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 9 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Autumn" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 10 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Award Winners" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 11 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Awareness" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 12 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Background Music" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 13 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Biographical" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 14 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Birth" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 15 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Birthday" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 16 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Breakup" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 17 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Cars" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 18 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Celebration" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 19 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Celebrities" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 20 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Children" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 21 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Christmas" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 22 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Christmas Party" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 23 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "City Life" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 24 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Classy Gatherings" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 25 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Club" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 26 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Comfort" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 27 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Conflict" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 28 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Cool & Cocky" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 29 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Country Life" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 30 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Crime" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 31 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "D-I-V-O-R-C-E" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 32 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Dance Party" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 33 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Day Driving" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 34 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Daydreaming" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 35 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Death" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 36 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Despair" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 37 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Destiny" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 38 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Dinner Ambiance" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 39 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Disappointment" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 40 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Dreaming" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 41 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Drinking" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 42 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Drugs" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 43 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Early Morning" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 44 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Easter" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 45 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Empowering" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 46 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Everyday Life" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 47 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Exercise/Workout" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 48 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Family" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 49 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Family Gatherings" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 50 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Fantasy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 51 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Fear" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 52 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Feeling Blue" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 53 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Flying" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 54 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Food/Eating" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 55 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Forgiveness" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 56 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Fourth of July" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 57 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Freedom" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 58 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Friendship" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 59 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Funeral" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 60 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Girls Night Out" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 61 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Good Times" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 62 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Goodbyes" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 63 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Graduation" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 64 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Guys Night Out" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 65 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Halloween" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 66 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Hanging Out" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 67 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Happiness" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 68 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Healing/Comfort" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 69 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Heartache" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 70 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Heartbreak" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 71 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "High School" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 72 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Historical Events" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 73 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Holidays" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 74 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Home" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 75 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Homecoming" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 76 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Hope" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 77 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Housework" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 78 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Illness" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 79 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "In Love" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 80 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Introspection" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 81 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Jealousy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 82 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Joy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 83 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Late Night" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 84 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Lifecycle" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 85 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Loneliness" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 86 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Long Walk" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 87 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Loss/Grief" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 88 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Lying" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 89 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Magic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 90 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Maverick" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 91 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Meditation" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 92 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Memorial" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 93 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Military" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 94 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Mischievous" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 95 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Monday Morning" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 96 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Money" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 97 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Moon" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 98 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Morning" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 99 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Motivation" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 100 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Music" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 101 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Myths & Legends" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 102 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Nature" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 103 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "New Love" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 104 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Night Driving" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 105 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Nighttime" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 106 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Open Road" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 107 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{
+									"in_class_index": 0,
+									"value": { "PropertyValue": { "Text": "Other Times & Places" } }
+								}
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 108 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Pain" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 109 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Parenthood" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 110 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Partying" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 111 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Passion" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 112 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Patriotism" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 113 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Peace" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 114 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Picnic" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 115 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Playful" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 116 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Poetry" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 117 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Politics/Society" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 118 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Pool Party" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 119 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Prom" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 120 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Promises" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 121 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Protest" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 122 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Rainy Day" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 123 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Reflection" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 124 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Regret" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 125 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Relationships" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 126 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Relaxation" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 127 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Religion" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 128 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Reminiscing" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 129 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Reunion" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 130 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Revolutionary" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 131 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Road Trip" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 132 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Romance" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 133 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Romantic Evening" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 134 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Scary Music" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 135 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "School" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 136 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Science" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 137 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "SciFi" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 138 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Seduction" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 139 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Separation" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 140 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sex" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 141 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Slow Dance" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 142 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Small Gathering" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 143 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Solitude" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 144 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sorrow" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 145 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sports" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 146 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Spring" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 147 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Starry Sky" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 148 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Starting Out" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 149 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Stay in Bed" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 150 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Storms" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 151 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Street Life" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 152 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Summer" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 153 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sun" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 154 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sunday Afternoon" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 155 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Sweet Dreams" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 156 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Teenagers" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 157 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Temptation" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 158 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "TGIF" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 159 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Thanksgiving" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 160 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "The Creative Side" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 161 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "The Great Outdoors" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 162 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Value" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 163 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Tragedy" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 164 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Travel" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 165 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Truth" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 166 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Vacation" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 167 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Victory" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 168 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Violence" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 169 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Visions" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 170 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "War" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 171 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Water" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 172 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Weather" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 173 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Wedding" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 174 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Winter" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 175 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Wisdom" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 176 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Word Play" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 177 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Work" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 178 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "World View" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 179 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Yearning" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 180 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Youth" } } }
+							]
+						}
+					}
+				},
+				{
+					"with_credential": 0,
+					"as_entity_maintainer": true,
+					"operation_type": {
+						"AddSchemaSupportToEntity": {
+							"entity_id": { "InternalEntityJustAdded": 181 },
+							"schema_id": 0,
+							"parametrized_property_values": [
+								{ "in_class_index": 0, "value": { "PropertyValue": { "Text": "Zeitgeist" } } }
+							]
+						}
+					}
+				}
+			]
+		]
+	}
+]

+ 1 - 1
node/src/chain_spec.rs

@@ -255,7 +255,7 @@ pub fn testnet_genesis(
                 min_voting_stake: 1 * DOLLARS,
             },
         }),
-        members: Some(MembersConfig {
+        membership: Some(MembersConfig {
             default_paid_membership_fee: 100u128,
             members: vec![],
         }),

+ 1 - 1
package.json

@@ -6,7 +6,7 @@
   "scripts": {
     "test": "yarn && yarn workspaces run test",
     "test-migration": "yarn && yarn workspaces run test-migration",
-    "postinstall": "yarn workspace @joystream/types build",
+    "postinstall": "yarn workspace @joystream/types build && yarn workspace storage-node run build",
     "cargo-checks": "devops/git-hooks/pre-commit && devops/git-hooks/pre-push",
     "cargo-build": "scripts/cargo-build.sh",
     "lint": "yarn workspaces run lint"

+ 53 - 3
pioneer/packages/joy-proposals/src/Proposal/Body.tsx

@@ -1,6 +1,7 @@
 import React from 'react';
 import { Card, Header, Button, Icon, Message } from 'semantic-ui-react';
 import { ProposalType } from '@polkadot/joy-utils/types/proposals';
+import { bytesToString } from '@polkadot/joy-utils/functions/misc';
 import { blake2AsHex } from '@polkadot/util-crypto';
 import styled from 'styled-components';
 import AddressMini from '@polkadot/react-components/AddressMiniJoy';
@@ -9,11 +10,16 @@ import { ProposalId } from '@joystream/types/proposals';
 import { MemberId, Profile } from '@joystream/types/members';
 import ProfilePreview from '@polkadot/joy-utils/MemberProfilePreview';
 import { useTransport, usePromise } from '@polkadot/joy-utils/react/hooks';
-
-import { Option } from '@polkadot/types/';
+import { Option, Bytes } from '@polkadot/types/';
 import { formatBalance } from '@polkadot/util';
 import { PromiseComponent } from '@polkadot/joy-utils/react/components';
 import ReactMarkdown from 'react-markdown';
+import { WorkingGroupOpeningPolicyCommitment } from '@joystream/types/working-group';
+import {
+  ActivateOpeningAt,
+  ActivateOpeningAtKeys
+} from '@joystream/types/hiring';
+import { WorkingGroup } from '@joystream/types/common';
 
 type BodyProps = {
   title: string;
@@ -65,6 +71,16 @@ function ProposedMember (props: { memberId?: MemberId | number | null }) {
   );
 }
 
+const ParsedHRT = styled.pre`
+  font-size: 14px;
+  font-weight: normal;
+  background: #eee;
+  border-radius: 0.5rem;
+  padding: 1rem;
+  margin: 0;
+  white-space: pre-wrap;
+`;
+
 // The methods for parsing params by Proposal type.
 // They take the params as array and return { LABEL: VALUE } object.
 const paramParsers: { [x in ProposalType]: (params: any[]) => { [key: string]: string | number | JSX.Element } } = {
@@ -116,7 +132,41 @@ const paramParsers: { [x in ProposalType]: (params: any[]) => { [key: string]: s
     // "Min. service period": params.min_service_period + " blocks",
     // "Startup grace period": params.startup_grace_period + " blocks",
     'Entry request fee': formatBalance(params.entry_request_fee)
-  })
+  }),
+  AddWorkingGroupLeaderOpening: ([{ activate_at, commitment, human_readable_text, working_group }]) => {
+    const workingGroup = new WorkingGroup(working_group);
+    const activateAt = new ActivateOpeningAt(activate_at);
+    const activateAtBlock = activateAt.type === ActivateOpeningAtKeys.ExactBlock ? activateAt.value : null;
+    const OPCommitment = new WorkingGroupOpeningPolicyCommitment(commitment);
+    const {
+      application_staking_policy: aSP,
+      role_staking_policy: rSP,
+      application_rationing_policy: rationingPolicy
+    } = OPCommitment;
+    let HRT = bytesToString(new Bytes(human_readable_text));
+    try { HRT = JSON.stringify(JSON.parse(HRT), undefined, 4); } catch (e) { /* Do nothing */ }
+    return {
+      'Working group': workingGroup.type,
+      'Activate at': `${activateAt.type}${activateAtBlock ? `(${activateAtBlock.toString()})` : ''}`,
+      'Application stake': aSP.isSome ? aSP.unwrap().amount_mode.type + `(${aSP.unwrap().amount})` : 'NONE',
+      'Role stake': rSP.isSome ? rSP.unwrap().amount_mode.type + `(${rSP.unwrap().amount})` : 'NONE',
+      'Max. applications': rationingPolicy.isSome ? rationingPolicy.unwrap().max_active_applicants.toNumber() : 'UNLIMITED',
+      'Terminate unstaking period (role stake)': OPCommitment.terminate_role_stake_unstaking_period.unwrapOr(0) + ' blocks',
+      'Exit unstaking period (role stake)': OPCommitment.exit_role_stake_unstaking_period.unwrapOr(0) + ' blocks',
+      // <required_to_prevent_sneaking>
+      'Terminate unstaking period (appl. stake)': OPCommitment.terminate_application_stake_unstaking_period.unwrapOr(0) + ' blocks',
+      'Exit unstaking period (appl. stake)': OPCommitment.exit_role_application_stake_unstaking_period.unwrapOr(0) + ' blocks',
+      'Appl. accepted unstaking period (appl. stake)': OPCommitment.fill_opening_successful_applicant_application_stake_unstaking_period.unwrapOr(0) + ' blocks',
+      'Appl. failed unstaking period (role stake)': OPCommitment.fill_opening_failed_applicant_role_stake_unstaking_period.unwrapOr(0) + ' blocks',
+      'Appl. failed unstaking period (appl. stake)': OPCommitment.fill_opening_failed_applicant_application_stake_unstaking_period.unwrapOr(0) + ' blocks',
+      'Crowded out unstaking period (role stake)': ((rSP.isSome && rSP.unwrap().crowded_out_unstaking_period_length.unwrapOr(0)) || 0) + ' blocks',
+      'Review period expierd unstaking period (role stake)': ((rSP.isSome && rSP.unwrap().review_period_expired_unstaking_period_length.unwrapOr(0)) || 0) + ' blocks',
+      'Crowded out unstaking period (appl. stake)': ((aSP.isSome && aSP.unwrap().crowded_out_unstaking_period_length.unwrapOr(0)) || 0) + ' blocks',
+      'Review period expierd unstaking period (appl. stake)': ((aSP.isSome && aSP.unwrap().review_period_expired_unstaking_period_length.unwrapOr(0)) || 0) + ' blocks',
+      // </required_to_prevent_sneaking>
+      'Human readable text': <ParsedHRT>{ HRT }</ParsedHRT>
+    };
+  }
 };
 
 const StyledProposalDescription = styled(Card.Description)`

+ 1 - 1
pioneer/packages/joy-proposals/src/Proposal/Votes.tsx

@@ -12,7 +12,7 @@ type VotesProps = {
 
 export default function Votes ({ votes }: VotesProps) {
   if (!votes.votes.length) {
-    return <Header as="h4">No votes has been submitted!</Header>;
+    return <Header as="h4">No votes have been submitted!</Header>;
   }
 
   return (

+ 357 - 0
pioneer/packages/joy-proposals/src/forms/AddWorkingGroupOpeningForm.tsx

@@ -0,0 +1,357 @@
+import React, { useEffect } from 'react';
+import { getFormErrorLabelsProps, FormErrorLabelsProps } from './errorHandling';
+import * as Yup from 'yup';
+import {
+  withProposalFormData,
+  ProposalFormExportProps,
+  ProposalFormContainerProps,
+  ProposalFormInnerProps,
+  genericFormDefaultOptions
+} from './GenericProposalForm';
+import {
+  GenericWorkingGroupProposalForm,
+  FormValues as WGFormValues,
+  defaultValues as wgFromDefaultValues
+} from './GenericWorkingGroupProposalForm';
+import { FormField, InputFormField, TextareaFormField } from './FormFields';
+import { withFormContainer } from './FormContainer';
+import './forms.css';
+import { ActivateOpeningAtKey, ActivateOpeningAtDef, StakingAmountLimitModeKeys, IApplicationRationingPolicy, IStakingPolicy } from '@joystream/types/hiring';
+import { GenericJoyStreamRoleSchema } from '@joystream/types/hiring/schemas/role.schema.typings';
+import { Dropdown, Grid, Message, Checkbox } from 'semantic-ui-react';
+import { formatBalance } from '@polkadot/util';
+import _ from 'lodash';
+import { IWorkingGroupOpeningPolicyCommitment } from '@joystream/types/working-group';
+import { IAddOpeningParameters } from '@joystream/types/proposals';
+import { WorkingGroupKeys } from '@joystream/types/common';
+import { BlockNumber } from '@polkadot/types/interfaces';
+import { withCalls } from '@polkadot/react-api';
+import { SimplifiedTypeInterface } from '@polkadot/joy-utils/types/common';
+import Validation from '../validationSchema';
+
+type FormValues = WGFormValues & {
+  activateAt: ActivateOpeningAtKey;
+  activateAtBlock: string;
+  maxReviewPeriodLength: string;
+  applicationsLimited: boolean;
+  maxApplications: string;
+  applicationStakeRequired: boolean;
+  applicationStakeMode: StakingAmountLimitModeKeys;
+  applicationStakeValue: string;
+  roleStakeRequired: boolean;
+  roleStakeMode: StakingAmountLimitModeKeys;
+  roleStakeValue: string;
+  terminateRoleUnstakingPeriod: string;
+  leaveRoleUnstakingPeriod: string;
+  humanReadableText: string;
+};
+
+const defaultValues: FormValues = {
+  ...wgFromDefaultValues,
+  activateAt: 'CurrentBlock',
+  activateAtBlock: '',
+  maxReviewPeriodLength: (14400 * 30).toString(), // 30 days
+  applicationsLimited: false,
+  maxApplications: '',
+  applicationStakeRequired: false,
+  applicationStakeMode: StakingAmountLimitModeKeys.Exact,
+  applicationStakeValue: '',
+  roleStakeRequired: false,
+  roleStakeMode: StakingAmountLimitModeKeys.Exact,
+  roleStakeValue: '',
+  terminateRoleUnstakingPeriod: (14400 * 7).toString(), // 7 days
+  leaveRoleUnstakingPeriod: (14400 * 7).toString(), // 7 days
+  humanReadableText: ''
+};
+
+const HRTDefault: (memberHandle: string, group: WorkingGroupKeys) => GenericJoyStreamRoleSchema =
+  (memberHandle, group) => ({
+    version: 1,
+    headline: `Looking for ${group} Working Group Leader!`,
+    job: {
+      title: `${group} Working Group Leader`,
+      description: `Become ${group} Working Group Leader! This is a great opportunity to support Joystream!`
+    },
+    application: {
+      sections: [
+        {
+          title: 'About you',
+          questions: [
+            {
+              title: 'Your name',
+              type: 'text'
+            },
+            {
+              title: 'What makes you a good fit for the job?',
+              type: 'text area'
+            }
+          ]
+        }
+      ]
+    },
+    reward: '100 JOY per block',
+    creator: {
+      membership: {
+        handle: memberHandle
+      }
+    }
+  });
+
+type FormAdditionalProps = {}; // Aditional props coming all the way from export component into the inner form.
+type ExportComponentProps = ProposalFormExportProps<FormAdditionalProps, FormValues>;
+type FormContainerProps = ProposalFormContainerProps<ExportComponentProps> & {
+  currentBlock?: BlockNumber;
+};
+type FormInnerProps = ProposalFormInnerProps<FormContainerProps, FormValues>;
+
+type StakeFieldsProps = Pick<FormInnerProps, 'values' | 'handleChange' | 'setFieldValue'> & {
+  errorLabelsProps: FormErrorLabelsProps<FormValues>;
+  stakeType: 'role' | 'application';
+};
+const StakeFields: React.FunctionComponent<StakeFieldsProps> = ({
+  values,
+  errorLabelsProps,
+  handleChange,
+  stakeType,
+  setFieldValue
+}) => {
+  return (
+  <>
+    <FormField label={`${_.startCase(stakeType)} stake` }>
+      <Checkbox
+        toggle
+        onChange={(e, data) => { setFieldValue(`${stakeType}StakeRequired`, data.checked); }}
+        label={ `Require ${stakeType} stake` }
+        checked={ stakeType === 'role' ? values.roleStakeRequired : values.applicationStakeRequired }/>
+    </FormField>
+    { (stakeType === 'role' ? values.roleStakeRequired : values.applicationStakeRequired) && (<>
+      <FormField label="Stake mode">
+        <Dropdown
+          onChange={handleChange}
+          name={ `${stakeType}StakeMode` }
+          selection
+          options={[StakingAmountLimitModeKeys.Exact, StakingAmountLimitModeKeys.AtLeast].map(mode => ({ text: mode, value: mode }))}
+          value={ stakeType === 'role' ? values.roleStakeMode : values.applicationStakeMode }
+        />
+      </FormField>
+      <InputFormField
+        label="Stake value"
+        unit={formatBalance.getDefaults().unit}
+        onChange={handleChange}
+        name={ `${stakeType}StakeValue` }
+        error={ stakeType === 'role' ? errorLabelsProps.roleStakeValue : errorLabelsProps.applicationStakeValue}
+        value={ stakeType === 'role' ? values.roleStakeValue : values.applicationStakeValue}
+        placeholder={'ie. 100'}
+      />
+    </>) }
+  </>
+  );
+};
+
+const valuesToAddOpeningParams = (values: FormValues): SimplifiedTypeInterface<IAddOpeningParameters> => {
+  const commitment: SimplifiedTypeInterface<IWorkingGroupOpeningPolicyCommitment> = {
+    max_review_period_length: parseInt(values.maxReviewPeriodLength)
+  };
+  if (parseInt(values.terminateRoleUnstakingPeriod) > 0) {
+    commitment.terminate_role_stake_unstaking_period = parseInt(values.terminateRoleUnstakingPeriod);
+  }
+  if (parseInt(values.leaveRoleUnstakingPeriod) > 0) {
+    commitment.exit_role_stake_unstaking_period = parseInt(values.leaveRoleUnstakingPeriod);
+  }
+  if (values.applicationsLimited) {
+    const rationingPolicy: SimplifiedTypeInterface<IApplicationRationingPolicy> = {
+      max_active_applicants: parseInt(values.maxApplications)
+    };
+    commitment.application_rationing_policy = rationingPolicy;
+  }
+  if (values.applicationStakeRequired) {
+    const applicationStakingPolicy: SimplifiedTypeInterface<IStakingPolicy> = {
+      amount: parseInt(values.applicationStakeValue),
+      amount_mode: values.applicationStakeMode
+    };
+    commitment.application_staking_policy = applicationStakingPolicy;
+  }
+  if (values.roleStakeRequired) {
+    const roleStakingPolicy: SimplifiedTypeInterface<IStakingPolicy> = {
+      amount: parseInt(values.roleStakeValue),
+      amount_mode: values.roleStakeMode
+    };
+    commitment.role_staking_policy = roleStakingPolicy;
+  }
+  return {
+    activate_at: { [values.activateAt]: values.activateAt === 'ExactBlock' ? parseInt(values.activateAtBlock) : null },
+    commitment: commitment,
+    human_readable_text: values.humanReadableText,
+    working_group: values.workingGroup
+  };
+};
+
+const AddWorkingGroupOpeningForm: React.FunctionComponent<FormInnerProps> = props => {
+  const { handleChange, errors, touched, values, setFieldValue, myMemberId, memberProfile } = props;
+  useEffect(() => {
+    if (memberProfile?.isSome && !touched.humanReadableText) {
+      setFieldValue(
+        'humanReadableText',
+        JSON.stringify(HRTDefault(memberProfile.unwrap().handle.toString(), values.workingGroup), undefined, 4)
+      );
+    }
+  }, [values.workingGroup, memberProfile]);
+  const errorLabelsProps = getFormErrorLabelsProps<FormValues>(errors, touched);
+
+  return (
+    <GenericWorkingGroupProposalForm
+      {...props}
+      txMethod="createAddWorkingGroupLeaderOpeningProposal"
+      proposalType="AddWorkingGroupLeaderOpening"
+      submitParams={[
+        myMemberId,
+        values.title,
+        values.rationale,
+        '{STAKE}',
+        valuesToAddOpeningParams(values)
+      ]}
+    >
+      <Grid columns="4" doubling stackable verticalAlign="bottom">
+        <Grid.Row>
+          <Grid.Column>
+            <FormField label="Activate opening at">
+              <Dropdown
+                onChange={handleChange}
+                name="activateAt"
+                selection
+                options={Object.keys(ActivateOpeningAtDef).map(wgKey => ({ text: wgKey, value: wgKey }))}
+                value={values.activateAt}
+              />
+            </FormField>
+          </Grid.Column>
+          <Grid.Column>
+            { values.activateAt === 'ExactBlock' && (
+              <InputFormField
+                onChange={handleChange}
+                name="activateAtBlock"
+                error={errorLabelsProps.activateAtBlock}
+                value={values.activateAtBlock}
+                placeholder={'Provide the block number'}
+              />
+            ) }
+          </Grid.Column>
+        </Grid.Row>
+      </Grid>
+      { values.activateAt === 'ExactBlock' && (
+        <Message info>
+          In case <b>ExactBlock</b> is specified, the opening will remain in <i>Waiting to Begin</i> stage (which means it will be visible,
+          but no applicants will be able to apply yet) until current block number will equal the specified number.
+        </Message>
+      ) }
+      <Grid columns="4" doubling stackable verticalAlign="bottom">
+        <Grid.Row>
+          <Grid.Column>
+            <InputFormField
+              label="Max. review period"
+              onChange={handleChange}
+              name="maxReviewPeriodLength"
+              error={errorLabelsProps.maxReviewPeriodLength}
+              value={values.maxReviewPeriodLength}
+              placeholder={'ie. 72000'}
+              unit="blocks"
+            />
+          </Grid.Column>
+        </Grid.Row>
+      </Grid>
+      <Grid columns="4" doubling stackable verticalAlign="bottom">
+        <Grid.Row>
+          <Grid.Column>
+            <FormField label="Applications limit">
+              <Checkbox
+                toggle
+                onChange={(e, data) => { setFieldValue('applicationsLimited', data.checked); }}
+                label="Limit applications"
+                checked={values.applicationsLimited}/>
+            </FormField>
+            { values.applicationsLimited && (
+              <InputFormField
+                onChange={handleChange}
+                name="maxApplications"
+                error={errorLabelsProps.maxApplications}
+                value={values.maxApplications}
+                placeholder={'Max. number of applications'}
+              />
+            ) }
+          </Grid.Column>
+        </Grid.Row>
+      </Grid>
+      <Grid columns="2" stackable style={{ marginBottom: 0 }}>
+        <Grid.Row>
+          <Grid.Column>
+            <StakeFields stakeType="application" {...{ errorLabelsProps, values, handleChange, setFieldValue }}/>
+          </Grid.Column>
+          <Grid.Column>
+            <StakeFields stakeType="role" {...{ errorLabelsProps, values, handleChange, setFieldValue }}/>
+          </Grid.Column>
+        </Grid.Row>
+      </Grid>
+      <Grid columns="2" stackable style={{ marginBottom: 0 }}>
+        <Grid.Row>
+          <Grid.Column>
+            <InputFormField
+              onChange={handleChange}
+              name="terminateRoleUnstakingPeriod"
+              error={errorLabelsProps.terminateRoleUnstakingPeriod}
+              value={values.terminateRoleUnstakingPeriod}
+              label={'Terminate role unstaking period'}
+              placeholder={'ie. 14400'}
+              unit="blocks"
+              help={
+                'In case leader role or application is terminated - this will be the unstaking period for the role stake (in blocks).'
+              }
+            />
+          </Grid.Column>
+          <Grid.Column>
+            <InputFormField
+              onChange={handleChange}
+              name="leaveRoleUnstakingPeriod"
+              error={errorLabelsProps.leaveRoleUnstakingPeriod}
+              value={values.leaveRoleUnstakingPeriod}
+              label={'Leave role unstaking period'}
+              placeholder={'ie. 14400'}
+              unit="blocks"
+              help={
+                'In case leader leaves/exits his role - this will be the unstaking period for his role stake (in blocks). ' +
+                'It also applies when user is withdrawing an active leader application.'
+              }
+            />
+          </Grid.Column>
+        </Grid.Row>
+      </Grid>
+      <TextareaFormField
+        label="Opening schema (human_readable_text)"
+        help="JSON schema that describes some characteristics of the opening presented in the UI (headers, content, application form etc.)"
+        onChange={handleChange}
+        name="humanReadableText"
+        placeholder="Paste the JSON schema here..."
+        error={errorLabelsProps.humanReadableText}
+        value={values.humanReadableText}
+        rows={20}
+      />
+    </GenericWorkingGroupProposalForm>
+  );
+};
+
+const FormContainer = withFormContainer<FormContainerProps, FormValues>({
+  mapPropsToValues: (props: FormContainerProps) => ({
+    ...defaultValues,
+    ...(props.initialData || {})
+  }),
+  validationSchema: (props: FormContainerProps) => Yup.object().shape({
+    ...genericFormDefaultOptions.validationSchema,
+    ...Validation.AddWorkingGroupLeaderOpening(props.currentBlock?.toNumber() || 0)
+  }),
+  handleSubmit: genericFormDefaultOptions.handleSubmit,
+  displayName: 'AddWorkingGroupOpeningForm'
+})(AddWorkingGroupOpeningForm);
+
+export default withCalls<ExportComponentProps>(
+  ['derive.chain.bestNumber', { propName: 'currentBlock' }]
+)(
+  withProposalFormData<FormContainerProps, ExportComponentProps>(FormContainer)
+);

+ 48 - 10
pioneer/packages/joy-proposals/src/forms/GenericProposalForm.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useEffect, useState, useRef } from 'react';
 import { FormikProps, WithFormikConfig } from 'formik';
 import { Form, Icon, Button, Message } from 'semantic-ui-react';
 import { getFormErrorLabelsProps } from './errorHandling';
@@ -81,25 +81,58 @@ export const genericFormDefaultOptions: GenericFormDefaultOptions = {
 export const GenericProposalForm: React.FunctionComponent<GenericFormInnerProps> = props => {
   const {
     handleChange,
+    handleSubmit,
     errors,
     isSubmitting,
+    isValidating,
+    isValid,
     touched,
-    handleSubmit,
+    submitForm,
     children,
     handleReset,
     values,
     txMethod,
     submitParams,
-    isValid,
     setSubmitting,
     history,
     balances_totalIssuance,
     proposalType
   } = props;
   const errorLabelsProps = getFormErrorLabelsProps<GenericFormValues>(errors, touched);
+  const [afterSubmit, setAfterSubmit] = useState(null as (() => () => void) | null);
+  const formContainerRef = useRef<HTMLDivElement>(null);
+
+  // After-submit effect
+  // With current version of Formik, there seems to be no other viable way to handle this (ie. for sendTx)
+  useEffect(() => {
+    if (!isValidating && afterSubmit) {
+      if (isValid) {
+        afterSubmit();
+      }
+      setAfterSubmit(null);
+      setSubmitting(false);
+    }
+  }, [isValidating, isValid, afterSubmit]);
+
+  // Focus first error field when isValidating changes to false (which happens after form is validated)
+  // (operates directly on DOM)
+  useEffect(() => {
+    if (!isValidating && formContainerRef.current !== null) {
+      const [errorField] = formContainerRef.current.getElementsByClassName('error field');
+      if (errorField) {
+        errorField.scrollIntoView({ behavior: 'smooth' });
+        const [errorInput] = errorField.querySelectorAll('input,textarea');
+        if (errorInput) {
+          (errorInput as (HTMLInputElement | HTMLTextAreaElement)).focus();
+        }
+      }
+    }
+  }, [isValidating]);
 
-  const onSubmit = (sendTx: () => void) => {
-    if (isValid) sendTx();
+  // Replaces standard submit handler (in order to work with TxButton)
+  const onTxButtonClick = (sendTx: () => void) => {
+    submitForm();
+    setAfterSubmit(() => sendTx);
   };
 
   const onTxFailed: TxFailedCallback = (txResult: SubmittableResult | null) => {
@@ -127,8 +160,13 @@ export const GenericProposalForm: React.FunctionComponent<GenericFormInnerProps>
     proposalsConsts[proposalType].stake;
 
   return (
-    <div className="Forms">
-      <Form className="proposal-form" onSubmit={handleSubmit}>
+    <div className="Forms" ref={formContainerRef}>
+      <Form
+        className="proposal-form"
+        onSubmit={txMethod
+          ? () => { /* Do nothing. Tx button uses custom submit handler - "onTxButtonClick" */ }
+          : handleSubmit
+        }>
         <InputFormField
           label="Title"
           help="The title of your proposal"
@@ -157,15 +195,15 @@ export const GenericProposalForm: React.FunctionComponent<GenericFormInnerProps>
         <div className="form-buttons">
           {txMethod ? (
             <TxButton
-              type="submit"
+              type="button" // Tx button uses custom submit handler - "onTxButtonClick"
               label="Submit proposal"
               icon="paper plane"
-              isDisabled={isSubmitting || !isValid}
+              isDisabled={isSubmitting}
               params={(submitParams || []).map(p => (p === '{STAKE}' ? requiredStake : p))}
               tx={`proposalsCodex.${txMethod}`}
-              onClick={onSubmit}
               txFailedCb={onTxFailed}
               txSuccessCb={onTxSuccess}
+              onClick={onTxButtonClick} // This replaces standard submit
             />
           ) : (
             <Button type="submit" color="blue" loading={isSubmitting}>

+ 84 - 0
pioneer/packages/joy-proposals/src/forms/GenericWorkingGroupProposalForm.tsx

@@ -0,0 +1,84 @@
+import React from 'react';
+import { getFormErrorLabelsProps } from './errorHandling';
+import {
+  GenericProposalForm,
+  GenericFormValues,
+  genericFormDefaultValues,
+  ProposalFormExportProps,
+  ProposalFormContainerProps,
+  ProposalFormInnerProps
+} from './GenericProposalForm';
+import { FormField } from './FormFields';
+import { ProposalType } from '@polkadot/joy-utils/types/proposals';
+import { WorkingGroupKeys, WorkingGroupDef } from '@joystream/types/common';
+import './forms.css';
+import { Dropdown, Message } from 'semantic-ui-react';
+import { usePromise, useTransport } from '@polkadot/joy-utils/react/hooks';
+import { PromiseComponent } from '@polkadot/joy-utils/react/components';
+import { ProfilePreviewFromStruct as MemberPreview } from '@polkadot/joy-utils/MemberProfilePreview';
+
+export type FormValues = GenericFormValues & {
+  workingGroup: WorkingGroupKeys;
+};
+
+export const defaultValues: FormValues = {
+  ...genericFormDefaultValues,
+  workingGroup: 'Storage'
+};
+
+// Aditional props coming all the way from export comonent into the inner form.
+type FormAdditionalProps = {
+  txMethod: string;
+  submitParams: any[];
+  proposalType: ProposalType;
+  showLead?: boolean;
+};
+
+// We don't exactly use "container" and "export" components here, but those types are useful for
+// generiting the right "FormInnerProps"
+type ExportComponentProps = ProposalFormExportProps<FormAdditionalProps, FormValues>;
+type FormContainerProps = ProposalFormContainerProps<ExportComponentProps>;
+export type FormInnerProps = ProposalFormInnerProps<FormContainerProps, FormValues>;
+
+export const GenericWorkingGroupProposalForm: React.FunctionComponent<FormInnerProps> = props => {
+  const { handleChange, errors, touched, values, showLead = true } = props;
+  const transport = useTransport();
+  const [lead, error, loading] = usePromise(
+    () => transport.workingGroups.currentLead(values.workingGroup),
+    null,
+    [values.workingGroup]
+  );
+  const leadRes = { lead, error, loading };
+
+  const errorLabelsProps = getFormErrorLabelsProps<FormValues>(errors, touched);
+  return (
+    <GenericProposalForm {...props}>
+      <FormField
+        error={errorLabelsProps.workingGroup}
+        label="Working group"
+      >
+        <Dropdown
+          name="workingGroup"
+          placeholder="Select the working group"
+          selection
+          options={Object.keys(WorkingGroupDef).map(wgKey => ({ text: wgKey + ' Wroking Group', value: wgKey }))}
+          value={values.workingGroup}
+          onChange={ handleChange }
+        />
+      </FormField>
+      { showLead && (
+        <PromiseComponent message={'Fetching current lead...'} {...leadRes}>
+          <Message info>
+            <Message.Content>
+              <Message.Header>Current {values.workingGroup} Working Group lead:</Message.Header>
+              <div style={{ padding: '0.5rem 0' }}>
+                { leadRes.lead ? <MemberPreview profile={leadRes.lead.profile} /> : 'NONE' }
+              </div>
+            </Message.Content>
+          </Message>
+        </PromiseComponent>
+      ) }
+      { props.children }
+    </GenericProposalForm>
+  );
+};

+ 1 - 1
pioneer/packages/joy-proposals/src/forms/errorHandling.ts

@@ -2,7 +2,7 @@ import { FormikErrors, FormikTouched } from 'formik';
 import { LabelProps } from 'semantic-ui-react';
 
 type FieldErrorLabelProps = LabelProps | null; // This is used for displaying semantic-ui errors
-type FormErrorLabelsProps<ValuesT> = { [T in keyof ValuesT]: FieldErrorLabelProps };
+export type FormErrorLabelsProps<ValuesT> = { [T in keyof ValuesT]: FieldErrorLabelProps };
 
 // Single form field error state.
 // Takes formik "errors" and "touched" objects and the field name as arguments.

+ 1 - 0
pioneer/packages/joy-proposals/src/forms/index.ts

@@ -9,3 +9,4 @@ export { default as RuntimeUpgradeForm } from './RuntimeUpgradeForm';
 export { default as SetContentWorkingGroupMintCapForm } from './SetContentWorkingGroupMintCapForm';
 export { default as SetCouncilMintCapForm } from './SetCouncilMintCapForm';
 export { default as SetMaxValidatorCountForm } from './SetMaxValidatorCountForm';
+export { default as AddWorkingGroupOpeningForm } from './AddWorkingGroupOpeningForm';

+ 3 - 1
pioneer/packages/joy-proposals/src/index.tsx

@@ -21,7 +21,8 @@ import {
   SetCouncilParamsForm,
   SetStorageRoleParamsForm,
   SetMaxValidatorCountForm,
-  RuntimeUpgradeForm
+  RuntimeUpgradeForm,
+  AddWorkingGroupOpeningForm
 } from './forms';
 
 interface Props extends AppProps, I18nProps {}
@@ -70,6 +71,7 @@ function App (props: Props): React.ReactElement<Props> {
           <Route exact path={`${basePath}/new/evict-storage-provider`} component={EvictStorageProviderForm} />
           <Route exact path={`${basePath}/new/set-validator-count`} component={SetMaxValidatorCountForm} />
           <Route exact path={`${basePath}/new/set-storage-role-parameters`} component={SetStorageRoleParamsForm} />
+          <Route exact path={`${basePath}/new/add-working-group-leader-opening`} component={AddWorkingGroupOpeningForm} />
           <Route exact path={`${basePath}/active`} component={NotDone} />
           <Route exact path={`${basePath}/finalized`} component={NotDone} />
           <Route exact path={`${basePath}/:id`} component={ProposalFromId} />

+ 105 - 1
pioneer/packages/joy-proposals/src/validationSchema.ts

@@ -1,4 +1,5 @@
 import * as Yup from 'yup';
+import { schemaValidator, ActivateOpeningAtKeys } from '@joystream/types/hiring';
 
 // TODO: If we really need this (currency unit) we can we make "Validation" a functiction that returns an object.
 // We could then "instantialize" it in "withFormContainer" where instead of passing
@@ -70,6 +71,23 @@ const STARTUP_GRACE_PERIOD_MAX = 28800;
 const ENTRY_REQUEST_FEE_MIN = 1;
 const ENTRY_REQUEST_FEE_MAX = 100000;
 
+// Add Working Group Leader Opening Parameters
+// TODO: Discuss the actual values
+const MIN_EXACT_BLOCK_MINUS_CURRENT = 14400 * 5; // ~5 days
+const MAX_EXACT_BLOCK_MINUS_CURRENT = 14400 * 60; // 2 months
+const MAX_REVIEW_PERIOD_LENGTH_MIN = 14400 * 5; // ~5 days
+const MAX_REVIEW_PERIOD_LENGTH_MAX = 14400 * 60; // 2 months
+const MAX_APPLICATIONS_MIN = 1;
+const MAX_APPLICATIONS_MAX = 1000;
+const APPLICATION_STAKE_VALUE_MIN = 1;
+const APPLICATION_STAKE_VALUE_MAX = 1000000;
+const ROLE_STAKE_VALUE_MIN = 1;
+const ROLE_STAKE_VALUE_MAX = 1000000;
+const TERMINATE_ROLE_UNSTAKING_MIN = 0;
+const TERMINATE_ROLE_UNSTAKING_MAX = 14 * 14400; // 14 days
+const LEAVE_ROLE_UNSTAKING_MIN = 0;
+const LEAVE_ROLE_UNSTAKING_MAX = 14 * 14400; // 14 days
+
 function errorMessage (name: string, min?: number | string, max?: number | string, unit?: string): string {
   return `${name} should be at least ${min} and no more than ${max}${unit ? ` ${unit}.` : '.'}`;
 }
@@ -139,8 +157,31 @@ type ValidationType = {
     startup_grace_period: Yup.NumberSchema<number>;
     entry_request_fee: Yup.NumberSchema<number>;
   };
+  AddWorkingGroupLeaderOpening: (currentBlock: number) => {
+    applicationsLimited: Yup.BooleanSchema<boolean>;
+    activateAt: Yup.StringSchema<string>;
+    activateAtBlock: Yup.NumberSchema<number>;
+    maxReviewPeriodLength: Yup.NumberSchema<number>;
+    maxApplications: Yup.NumberSchema<number>;
+    applicationStakeRequired: Yup.BooleanSchema<boolean>;
+    applicationStakeValue: Yup.NumberSchema<number>;
+    roleStakeRequired: Yup.BooleanSchema<boolean>;
+    roleStakeValue: Yup.NumberSchema<number>;
+    terminateRoleUnstakingPeriod: Yup.NumberSchema<number>;
+    leaveRoleUnstakingPeriod: Yup.NumberSchema<number>;
+    humanReadableText: Yup.StringSchema<string>;
+  };
 };
 
+// Helpers for common validation
+function minMaxInt (min: number, max: number, fieldName: string) {
+  return Yup.number()
+    .required(`${fieldName} is required!`)
+    .integer(`${fieldName} must be an integer!`)
+    .min(min, errorMessage(fieldName, min, max))
+    .max(max, errorMessage(fieldName, min, max));
+}
+
 const Validation: ValidationType = {
   All: {
     title: Yup.string()
@@ -346,7 +387,70 @@ const Validation: ValidationType = {
         STARTUP_GRACE_PERIOD_MAX,
         errorMessage('The entry request fee', ENTRY_REQUEST_FEE_MIN, ENTRY_REQUEST_FEE_MAX, CURRENCY_UNIT)
       )
-  }
+  },
+  AddWorkingGroupLeaderOpening: (currentBlock: number) => ({
+    activateAt: Yup.string().required(),
+    activateAtBlock: Yup.number()
+      .when('activateAt', {
+        is: ActivateOpeningAtKeys.ExactBlock,
+        then: minMaxInt(
+          MIN_EXACT_BLOCK_MINUS_CURRENT + currentBlock,
+          MAX_EXACT_BLOCK_MINUS_CURRENT + currentBlock,
+          'Exact block'
+        )
+      }),
+    maxReviewPeriodLength: minMaxInt(MAX_REVIEW_PERIOD_LENGTH_MIN, MAX_REVIEW_PERIOD_LENGTH_MAX, 'Max. review period length'),
+    applicationsLimited: Yup.boolean(),
+    maxApplications: Yup.number()
+      .when('applicationsLimited', {
+        is: true,
+        then: minMaxInt(MAX_APPLICATIONS_MIN, MAX_APPLICATIONS_MAX, 'Max. number of applications')
+      }),
+    applicationStakeRequired: Yup.boolean(),
+    applicationStakeValue: Yup.number()
+      .when('applicationStakeRequired', {
+        is: true,
+        then: minMaxInt(APPLICATION_STAKE_VALUE_MIN, APPLICATION_STAKE_VALUE_MAX, 'Application stake value')
+      }),
+    roleStakeRequired: Yup.boolean(),
+    roleStakeValue: Yup.number()
+      .when('roleStakeRequired', {
+        is: true,
+        then: minMaxInt(ROLE_STAKE_VALUE_MIN, ROLE_STAKE_VALUE_MAX, 'Role stake value')
+      }),
+    terminateRoleUnstakingPeriod: minMaxInt(
+      TERMINATE_ROLE_UNSTAKING_MIN,
+      TERMINATE_ROLE_UNSTAKING_MAX,
+      'Terminate role unstaking period'
+    ),
+    leaveRoleUnstakingPeriod: minMaxInt(
+      LEAVE_ROLE_UNSTAKING_MIN,
+      LEAVE_ROLE_UNSTAKING_MAX,
+      'Leave role unstaking period'
+    ),
+    humanReadableText: Yup.string()
+      .required()
+      .test(
+        'schemaIsValid',
+        'Schema validation failed!',
+        function (val) {
+          let schemaObj: any;
+          try {
+            schemaObj = JSON.parse(val);
+          } catch (e) {
+            return this.createError({ message: 'Schema validation failed: Invalid JSON' });
+          }
+          const isValid = schemaValidator(schemaObj);
+          const errors = schemaValidator.errors || [];
+          if (!isValid) {
+            return this.createError({
+              message: 'Schema validation failed: ' + errors.map(e => `${e.message}${e.dataPath && ` (${e.dataPath})`}`).join(', ')
+            });
+          }
+          return true;
+        }
+      )
+  })
 };
 
 export default Validation;

+ 1 - 1
pioneer/packages/joy-roles/src/index.tsx

@@ -60,7 +60,7 @@ export const App: React.FC<Props> = (props: Props) => {
   const oppsCtrl = new OpportunitiesController(transport, props.myMemberId);
   const [applyCtrl] = useState(new ApplyController(transport));
   const myRolesCtrl = new MyRolesController(transport, props.myAddress);
-  const [adminCtrl] = useState(new AdminController(transport, api));
+  const [adminCtrl] = useState(new AdminController(transport, api, queueExtrinsic));
 
   useEffect(() => {
     return () => {

+ 72 - 113
pioneer/packages/joy-roles/src/tabs/Admin.controller.tsx

@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useState, useRef } from 'react';
 import { Link } from 'react-router-dom';
 import { formatBalance } from '@polkadot/util';
 
@@ -7,8 +7,9 @@ import { GenericAccountId, Option, Text, Vec, u32, u128 } from '@polkadot/types'
 import { Balance } from '@polkadot/types/interfaces';
 
 import { SingleLinkedMapEntry, Controller, View } from '@polkadot/joy-utils/index';
-import { MyAccountProvider, useMyAccount } from '@polkadot/joy-utils/MyAccountContext';
+import { useMyAccount } from '@polkadot/joy-utils/MyAccountContext';
 import { SubmittableExtrinsic } from '@polkadot/api/promise/types';
+import { QueueTxExtrinsicAdd } from '@polkadot/react-components/Status/types';
 
 import {
   Accordion,
@@ -367,53 +368,35 @@ const newEmptyState = (): State => {
 };
 
 export class AdminController extends Controller<State, ITransport> {
-  api: ApiPromise
-  constructor (transport: ITransport, api: ApiPromise, initialState: State = newEmptyState()) {
+  api: ApiPromise;
+  queueExtrinsic: QueueTxExtrinsicAdd;
+
+  constructor (transport: ITransport, api: ApiPromise, queueExtrinsic: QueueTxExtrinsicAdd, initialState: State = newEmptyState()) {
     super(transport, initialState);
     this.api = api;
+    this.queueExtrinsic = queueExtrinsic;
     this.state.currentDescriptor = stockOpenings[0];
     this.updateState();
   }
 
-  newOpening (creatorAddress: string, desc: openingDescriptor) {
+  onTxSuccess = () => { this.updateState(); }
+
+  newOpening (accountId: string, desc: openingDescriptor) {
     const tx = this.api.tx.contentWorkingGroup.addCuratorOpening(
       desc.start,
       desc.policy,
       desc.text
     ) as unknown as SubmittableExtrinsic;
 
-    // FIXME: That's a bad way to send extrinsic in Pioneer (without "queueExtrinsic" etc.)
-    // and probably the reason why it always appears as succesful
-    tx.signAndSend(creatorAddress, ({ events = [], status }) => {
-      if (status.isFinalized) {
-        this.updateState();
-        console.log('Successful transfer with hash ' + status.asFinalized.toHex());
-      } else {
-        console.log('Status of transfer: ' + status.type);
-      }
-
-      events.forEach(({ phase, event: { data, method, section } }) => {
-        console.log(phase.toString() + ' : ' + section + '.' + method + ' ' + data.toString());
-      });
-    });
+    // FIXME: Normally we would keep it open in case of errror, but due to bad design
+    // the values in the form are reset at this point anyway, so there is no point
+    this.closeModal();
+    this.queueExtrinsic({ extrinsic: tx, txSuccessCb: this.onTxSuccess, accountId });
   }
 
-  startAcceptingApplications (creatorAddress: string, id = 0) {
+  startAcceptingApplications (accountId: string, id = 0) {
     const tx = this.api.tx.contentWorkingGroup.acceptCuratorApplications(id);
-    // FIXME: That's a bad way to send extrinsic in Pioneer (without "queueExtrinsic" etc.)
-    // and probably the reason why it always appears as succesful
-    tx.signAndSend(creatorAddress, ({ events = [], status }) => {
-      if (status.isFinalized) {
-        this.updateState();
-        console.log('Successful transfer with hash ' + status.asFinalized.toHex());
-      } else {
-        console.log('Status of transfer: ' + status.type);
-      }
-
-      events.forEach(({ phase, event: { data, method, section } }) => {
-        console.log(phase.toString() + ' : ' + section + '.' + method + ' ' + data.toString());
-      });
-    });
+    this.queueExtrinsic({ extrinsic: tx, txSuccessCb: this.onTxSuccess, accountId });
   }
 
   async applyAsACurator (creatorAddress: string, openingId: number) {
@@ -430,60 +413,21 @@ export class AdminController extends Controller<State, ITransport> {
       new Option(u128, 400),
       new Text('This is my application')
     ) as unknown as SubmittableExtrinsic;
-    // FIXME: That's a bad way to send extrinsic in Pioneer (without "queueExtrinsic" etc.)
-    // and probably the reason why it always appears as succesful
-    tx.signAndSend(creatorAddress, ({ events = [], status }) => {
-      if (status.isFinalized) {
-        this.updateState();
-        console.log('Successful transfer with hash ' + status.asFinalized.toHex());
-      } else {
-        console.log('Status of transfer: ' + status.type);
-      }
-
-      events.forEach(({ phase, event: { data, method, section } }) => {
-        console.log(phase.toString() + ' : ' + section + '.' + method + ' ' + data.toString());
-      });
-    });
+    this.queueExtrinsic({ extrinsic: tx, txSuccessCb: this.onTxSuccess, accountId: creatorAddress });
   }
 
-  beginApplicantReview (creatorAddress: string, openingId: number) {
+  beginApplicantReview (accountId: string, openingId: number) {
     const tx = this.api.tx.contentWorkingGroup.beginCuratorApplicantReview(openingId);
-    // FIXME: That's a bad way to send extrinsic in Pioneer (without "queueExtrinsic" etc.)
-    // and probably the reason why it always appears as succesful
-    tx.signAndSend(creatorAddress, ({ events = [], status }) => {
-      if (status.isFinalized) {
-        this.updateState();
-        console.log('Successful transfer with hash ' + status.asFinalized.toHex());
-      } else {
-        console.log('Status of transfer: ' + status.type);
-      }
-
-      events.forEach(({ phase, event: { data, method, section } }) => {
-        console.log(phase.toString() + ' : ' + section + '.' + method + ' ' + data.toString());
-      });
-    });
+    this.queueExtrinsic({ extrinsic: tx, txSuccessCb: this.onTxSuccess, accountId });
   }
 
-  acceptCuratorApplications (creatorAddress: string, openingId: number, applications: Array<number>) {
+  acceptCuratorApplications (accountId: string, openingId: number, applications: Array<number>) {
     const tx = this.api.tx.contentWorkingGroup.fillCuratorOpening(
       openingId,
       applications,
       null
     ) as unknown as SubmittableExtrinsic;
-    // FIXME: That's a bad way to send extrinsic in Pioneer (without "queueExtrinsic" etc.)
-    // and probably the reason why it always appears as succesful
-    tx.signAndSend(creatorAddress, ({ events = [], status }) => {
-      if (status.isFinalized) {
-        this.updateState();
-        console.log('Successful transfer with hash ' + status.asFinalized.toHex());
-      } else {
-        console.log('Status of transfer: ' + status.type);
-      }
-
-      events.forEach(({ phase, event: { data, method, section } }) => {
-        console.log(phase.toString() + ' : ' + section + '.' + method + ' ' + data.toString());
-      });
-    });
+    this.queueExtrinsic({ extrinsic: tx, txSuccessCb: this.onTxSuccess, accountId });
   }
 
   protected async profile (id: MemberId): Promise<Option<Profile>> {
@@ -595,44 +539,59 @@ export class AdminController extends Controller<State, ITransport> {
   }
 }
 
+type AdminContainerProps = {
+  state: State;
+  controller: AdminController;
+}
+const AdminContainer = ({ state, controller }: AdminContainerProps) => {
+  const address = useMyAccount().state.address;
+  const containerRef = useRef<HTMLDivElement>(null);
+  return (
+    <div ref={containerRef}>
+      <Container className="admin">
+        <Card fluid color='orange'>
+          <Card.Content>
+            <Dropdown text='Create new opening...'>
+              <Dropdown.Menu>
+                {
+                  stockOpenings.map((value, key) => {
+                    return (
+                      <Dropdown.Item
+                        key={value.title}
+                        text={value.title}
+                        onClick={() => controller.showNewOpeningModal(value)}
+                      />
+                    );
+                  })
+                }
+              </Dropdown.Menu>
+            </Dropdown>
+            <Modal
+              open={state.modalOpen}
+              onClose={() => controller.closeModal()}
+              mountNode={containerRef.current} // Prevent conflicts with tx-modal (after form values reset issue is fixed, see FIXME: above)
+            >
+              <Modal.Content image>
+                <Modal.Description>
+                  <NewOpening desc={state.currentDescriptor} fn={(desc) => address && controller.newOpening(address, desc)} />
+                </Modal.Description>
+              </Modal.Content>
+            </Modal>
+          </Card.Content>
+        </Card>
+        {
+          [...state.openings.keys()].map(key => <OpeningView key={key} opening={state.openings.get(key) as opening} controller={controller} />)
+        }
+        <br />
+      </Container>
+    </div>
+  );
+};
+
 export const AdminView = View<AdminController, State>(
   (state, controller) => {
-    const address = useMyAccount().state.address as string;
     return (
-      <MyAccountProvider>
-        <Container className="admin">
-          <Card fluid color='orange'>
-            <Card.Content>
-              <Dropdown text='Create new opening...'>
-                <Dropdown.Menu>
-                  {
-                    stockOpenings.map((value, key) => {
-                      return (
-                        <Dropdown.Item
-                          key={value.title}
-                          text={value.title}
-                          onClick={() => controller.showNewOpeningModal(value)}
-                        />
-                      );
-                    })
-                  }
-                </Dropdown.Menu>
-              </Dropdown>
-              <Modal open={state.modalOpen} onClose={() => controller.closeModal()}>
-                <Modal.Content image>
-                  <Modal.Description>
-                    <NewOpening desc={state.currentDescriptor} fn={(desc) => controller.newOpening(address, desc)} />
-                  </Modal.Description>
-                </Modal.Content>
-              </Modal>
-            </Card.Content>
-          </Card>
-          {
-            [...state.openings.keys()].map(key => <OpeningView key={key} opening={state.openings.get(key) as opening} controller={controller} />)
-          }
-          <br />
-        </Container>
-      </MyAccountProvider>
+      <AdminContainer state={state} controller={controller} />
     );
   }
 );

+ 4 - 0
pioneer/packages/joy-roles/src/tabs/MyRoles.tsx

@@ -129,6 +129,7 @@ export const CurrentRoles = Loadable<CurrentRolesProps>(
             <Table.Header>
               <Table.Row>
                 <Table.HeaderCell>Role</Table.HeaderCell>
+                <Table.HeaderCell>Worker / Curator ID</Table.HeaderCell>
                 <Table.HeaderCell>Earned</Table.HeaderCell>
                 <Table.HeaderCell>Stake</Table.HeaderCell>
                 <Table.HeaderCell></Table.HeaderCell>
@@ -140,6 +141,9 @@ export const CurrentRoles = Loadable<CurrentRolesProps>(
                   <Table.Cell>
                     <RoleName name={role.name} url={role.url} />
                   </Table.Cell>
+                  <Table.Cell>
+                    {role.workerId.toString()}
+                  </Table.Cell>
                   <Table.Cell>
                     {formatBalance(role.reward)}
                   </Table.Cell>

+ 24 - 10
pioneer/packages/joy-utils/src/MemberProfilePreview.tsx

@@ -2,14 +2,17 @@ import React from 'react';
 import { Image } from 'semantic-ui-react';
 import { IdentityIcon } from '@polkadot/react-components';
 import { Link } from 'react-router-dom';
+import { Text } from '@polkadot/types';
+import { AccountId } from '@polkadot/types/interfaces';
+import { MemberId, Profile } from '@joystream/types/members';
 import styled from 'styled-components';
 
 type ProfileItemProps = {
-  avatar_uri: string;
-  root_account: string;
-  handle: string;
+  avatar_uri: string | Text;
+  root_account: string | AccountId;
+  handle: string | Text;
   link?: boolean;
-  id?: number;
+  id?: number | MemberId;
 };
 
 const StyledProfilePreview = styled.div`
@@ -41,21 +44,32 @@ const DetailsID = styled.div`
 export default function ProfilePreview ({ id, avatar_uri, root_account, handle, link = false }: ProfileItemProps) {
   const Preview = (
     <StyledProfilePreview>
-      {avatar_uri ? (
-        <Image src={avatar_uri} avatar floated="left" />
+      {avatar_uri.toString() ? (
+        <Image src={avatar_uri.toString()} avatar floated="left" />
       ) : (
-        <IdentityIcon className="image" value={root_account} size={40} />
+        <IdentityIcon className="image" value={root_account.toString()} size={40} />
       )}
       <Details>
-        <DetailsHandle>{handle}</DetailsHandle>
-        { id !== undefined && <DetailsID>ID: {id}</DetailsID> }
+        <DetailsHandle>{handle.toString()}</DetailsHandle>
+        { id !== undefined && <DetailsID>ID: {id.toString()}</DetailsID> }
       </Details>
     </StyledProfilePreview>
   );
 
   if (link) {
-    return <Link to={ `/members/${handle}` }>{ Preview }</Link>;
+    return <Link to={ `/members/${handle.toString()}` }>{ Preview }</Link>;
   }
 
   return Preview;
 }
+
+type ProfilePreviewFromStructProps = {
+  profile: Profile;
+  link?: boolean;
+  id?: number | MemberId;
+};
+
+export function ProfilePreviewFromStruct ({ profile, link, id }: ProfilePreviewFromStructProps) {
+  const { avatar_uri, root_account, handle } = profile;
+  return <ProfilePreview {...{ avatar_uri, root_account, handle, link, id }} />;
+}

+ 2 - 2
pioneer/packages/joy-utils/src/MyAccount.tsx

@@ -29,7 +29,7 @@ export type MyAccountProps = MyAddressProps & {
   memberIdsByControllerAccountId?: Vec<MemberId>;
   myMemberIdChecked?: boolean;
   iAmMember?: boolean;
-  memberProfile?: Option<any>;
+  memberProfile?: Option<Profile>;
 
   // Content Working Group
   curatorEntries?: any; // entire linked_map: CuratorId => Curator
@@ -134,7 +134,7 @@ function withMyRoles<P extends MyAccountProps> (Component: React.ComponentType<P
     const myCuratorIds: Array<CuratorId> = [];
 
     if (iAmMember && memberProfile && memberProfile.isSome) {
-      const profile = memberProfile.unwrap() as Profile;
+      const profile = memberProfile.unwrap();
       profile.roles.forEach(role => {
         if (role.isContentLead) {
           myContentLeadId = role.actor_id;

+ 57 - 1
pioneer/packages/joy-utils/src/consts/proposals.ts

@@ -1,6 +1,6 @@
 import { ProposalType, ProposalMeta } from '../types/proposals';
 
-const metadata: { [k in ProposalType]: ProposalMeta } = {
+export const metadata: { [k in ProposalType]: ProposalMeta } = {
   EvictStorageProvider: {
     description: 'Evicting Storage Provider Proposal',
     category: 'Storage',
@@ -81,7 +81,63 @@ const metadata: { [k in ProposalType]: ProposalMeta } = {
     approvalThreshold: 100,
     slashingQuorum: 60,
     slashingThreshold: 80
+  },
+  AddWorkingGroupLeaderOpening: {
+    description: 'Add Working Group Leader Opening Proposal',
+    category: 'Other',
+    stake: 100000,
+    approvalQuorum: 60,
+    approvalThreshold: 80,
+    slashingQuorum: 60,
+    slashingThreshold: 80
   }
 };
 
+type ProposalsApiMethodNames = {
+  votingPeriod: string;
+  gracePeriod: string;
+}
+export const apiMethods: { [k in ProposalType]: ProposalsApiMethodNames } = {
+  EvictStorageProvider: {
+    votingPeriod: 'evictStorageProviderProposalVotingPeriod',
+    gracePeriod: 'evictStorageProviderProposalPeriod'
+  },
+  Text: {
+    votingPeriod: 'textProposalVotingPeriod',
+    gracePeriod: 'textProposalGracePeriod'
+  },
+  SetStorageRoleParameters: {
+    votingPeriod: 'setStorageRoleParametersProposalVotingPeriod',
+    gracePeriod: 'setStorageRoleParametersProposalGracePeriod'
+  },
+  SetValidatorCount: {
+    votingPeriod: 'setValidatorCountProposalVotingPeriod',
+    gracePeriod: 'setValidatorCountProposalGracePeriod'
+  },
+  SetLead: {
+    votingPeriod: 'setLeadProposalVotingPeriod',
+    gracePeriod: 'setLeadProposalGracePeriod'
+  },
+  SetContentWorkingGroupMintCapacity: {
+    votingPeriod: 'setContentWorkingGroupMintCapacityProposalVotingPeriod',
+    gracePeriod: 'setContentWorkingGroupMintCapacityProposalGracePeriod'
+  },
+  Spending: {
+    votingPeriod: 'spendingProposalVotingPeriod',
+    gracePeriod: 'spendingProposalGracePeriod'
+  },
+  SetElectionParameters: {
+    votingPeriod: 'setElectionParametersProposalVotingPeriod',
+    gracePeriod: 'setElectionParametersProposalGracePeriod'
+  },
+  RuntimeUpgrade: {
+    votingPeriod: 'runtimeUpgradeProposalVotingPeriod',
+    gracePeriod: 'runtimeUpgradeProposalGracePeriod'
+  },
+  AddWorkingGroupLeaderOpening: {
+    votingPeriod: 'addWorkingGroupOpeningProposalVotingPeriod',
+    gracePeriod: 'addWorkingGroupOpeningProposalGracePeriod'
+  }
+} as const;
+
 export default metadata;

+ 4 - 0
pioneer/packages/joy-utils/src/consts/workingGroups.ts

@@ -0,0 +1,4 @@
+import { WorkingGroupKeys } from '@joystream/types/common';
+export const apiModuleByGroup: { [k in WorkingGroupKeys]: string } = {
+  Storage: 'storageWorkingGroup'
+};

+ 4 - 2
pioneer/packages/joy-utils/src/react/hooks/usePromise.tsx

@@ -1,6 +1,8 @@
 import { useState, useEffect, useCallback } from 'react';
 
-export default function usePromise<T> (promise: () => Promise<T>, defaultValue: T): [T, any, boolean, () => Promise<void|null>] {
+export type UsePromiseReturnValues<T> = [T, any, boolean, () => Promise<void|null>];
+
+export default function usePromise<T> (promise: () => Promise<T>, defaultValue: T, dependsOn: any[] = []): UsePromiseReturnValues<T> {
   const [state, setState] = useState<{
     value: T;
     error: any;
@@ -19,7 +21,7 @@ export default function usePromise<T> (promise: () => Promise<T>, defaultValue:
     return () => {
       isSubscribed = false;
     };
-  }, []);
+  }, dependsOn);
 
   const { value, error, isPending } = state;
   return [value, error, isPending, execute];

+ 3 - 0
pioneer/packages/joy-utils/src/transport/index.ts

@@ -6,6 +6,7 @@ import MembersTransport from './members';
 import CouncilTransport from './council';
 import StorageProvidersTransport from './storageProviders';
 import ValidatorsTransport from './validators';
+import WorkingGroupsTransport from './workingGroups';
 
 export default class Transport {
   protected api: ApiPromise;
@@ -17,6 +18,7 @@ export default class Transport {
   public contentWorkingGroup: ContentWorkingGroupTransport;
   public storageProviders: StorageProvidersTransport;
   public validators: ValidatorsTransport;
+  public workingGroups: WorkingGroupsTransport;
 
   constructor (api: ApiPromise) {
     this.api = api;
@@ -27,5 +29,6 @@ export default class Transport {
     this.council = new CouncilTransport(api, this.members, this.chain);
     this.contentWorkingGroup = new ContentWorkingGroupTransport(api, this.members);
     this.proposals = new ProposalsTransport(api, this.members, this.chain, this.council);
+    this.workingGroups = new WorkingGroupsTransport(api, this.members);
   }
 }

+ 5 - 0
pioneer/packages/joy-utils/src/transport/members.ts

@@ -7,6 +7,11 @@ export default class MembersTransport extends BaseTransport {
     return this.members.memberProfile(id) as Promise<Option<Profile>>;
   }
 
+  // Throws if profile not found
+  async expectedMemberProfile (id: MemberId | number): Promise<Profile> {
+    return (await this.memberProfile(id)).unwrap();
+  }
+
   async membersCreated (): Promise<number> {
     return (await this.members.membersCreated() as MemberId).toNumber();
   }

+ 10 - 28
pioneer/packages/joy-utils/src/transport/proposals.ts

@@ -18,9 +18,9 @@ import { MemberId } from '@joystream/types/members';
 import { u32, u64 } from '@polkadot/types/';
 import { BalanceOf } from '@polkadot/types/interfaces';
 
-import { includeKeys, bytesToString } from '../functions/misc';
+import { bytesToString } from '../functions/misc';
 import _ from 'lodash';
-import proposalsConsts from '../consts/proposals';
+import { metadata as proposalsConsts, apiMethods as proposalsApiMethods } from '../consts/proposals';
 import { FIRST_MEMBER_ID } from '../consts/members';
 
 import { ApiPromise } from '@polkadot/api';
@@ -153,33 +153,15 @@ export default class ProposalsTransport extends BaseTransport {
     };
   }
 
-  async fetchProposalMethodsFromCodex (includeKey: string) {
-    const methods = includeKeys(this.proposalsCodex, includeKey);
-    // methods = [proposalTypeVotingPeriod...]
-    return methods.reduce(async (prevProm, method) => {
-      const obj = await prevProm;
-      const period = (await this.proposalsCodex[method]()) as u32;
-      // setValidatorCountProposalVotingPeriod to SetValidatorCount
-      const key = _.words(_.startCase(method))
-        .slice(0, -3)
-        .map((w, i) => (i === 0 ? w.slice(0, 1).toUpperCase() + w.slice(1) : w))
-        .join('') as ProposalType;
-
-      return { ...obj, [`${key}`]: period.toNumber() };
-    }, Promise.resolve({}) as Promise<{ [k in ProposalType]: number }>);
-  }
-
-  async proposalTypesGracePeriod (): Promise<{ [k in ProposalType]: number }> {
-    return this.fetchProposalMethodsFromCodex('GracePeriod');
-  }
-
-  async proposalTypesVotingPeriod (): Promise<{ [k in ProposalType]: number }> {
-    return this.fetchProposalMethodsFromCodex('VotingPeriod');
-  }
-
   async parametersFromProposalType (type: ProposalType) {
-    const votingPeriod = (await this.proposalTypesVotingPeriod())[type];
-    const gracePeriod = (await this.proposalTypesGracePeriod())[type];
+    const { votingPeriod: votingPeriodMethod, gracePeriod: gracePeriodMethod } = proposalsApiMethods[type];
+    // TODO: Remove the fallback after outdated proposals are removed
+    const votingPeriod = this.proposalsCodex[votingPeriodMethod]
+      ? ((await this.proposalsCodex[votingPeriodMethod]()) as u32).toNumber()
+      : 0;
+    const gracePeriod = this.proposalsCodex[gracePeriodMethod]
+      ? ((await this.proposalsCodex[gracePeriodMethod]()) as u32).toNumber()
+      : 0;
     // Currently it's same for all types, but this will change soon
     const cancellationFee = this.cancellationFee();
     return {

+ 47 - 0
pioneer/packages/joy-utils/src/transport/workingGroups.ts

@@ -0,0 +1,47 @@
+import { Option } from '@polkadot/types/';
+import BaseTransport from './base';
+import { ApiPromise } from '@polkadot/api';
+import MembersTransport from './members';
+import { SingleLinkedMapEntry } from '../index';
+import { Worker, WorkerId } from '@joystream/types/working-group';
+import { apiModuleByGroup } from '../consts/workingGroups';
+import { WorkingGroupKeys } from '@joystream/types/common';
+import { LeadWithProfile } from '../types/workingGroups';
+
+export default class WorkingGroupsTransport extends BaseTransport {
+  private membersT: MembersTransport;
+
+  constructor (api: ApiPromise, membersTransport: MembersTransport) {
+    super(api);
+    this.membersT = membersTransport;
+  }
+
+  protected queryByGroup (group: WorkingGroupKeys) {
+    const module = apiModuleByGroup[group];
+    return this.api.query[module];
+  }
+
+  public async currentLead (group: WorkingGroupKeys): Promise <LeadWithProfile | null> {
+    const optLeadId = (await this.queryByGroup(group).currentLead()) as Option<WorkerId>;
+
+    if (!optLeadId.isSome) {
+      return null;
+    }
+
+    const leadWorkerId = optLeadId.unwrap();
+    const leadWorkerLink = new SingleLinkedMapEntry(
+      Worker,
+      await this.queryByGroup(group).workerById(leadWorkerId)
+    );
+    const leadWorker = leadWorkerLink.value;
+
+    if (!leadWorker.is_active) {
+      return null;
+    }
+
+    return {
+      worker: leadWorker,
+      profile: await this.membersT.expectedMemberProfile(leadWorker.member_id)
+    };
+  }
+}

+ 1 - 0
pioneer/packages/joy-utils/src/types/common.ts

@@ -0,0 +1 @@
+export type SimplifiedTypeInterface<I> = Partial<{ [k in keyof I]: any }>;

+ 2 - 1
pioneer/packages/joy-utils/src/types/proposals.ts

@@ -12,7 +12,8 @@ export const ProposalTypes = [
   'SetContentWorkingGroupMintCapacity',
   'EvictStorageProvider',
   'SetValidatorCount',
-  'SetStorageRoleParameters'
+  'SetStorageRoleParameters',
+  'AddWorkingGroupLeaderOpening'
 ] as const;
 
 export type ProposalType = typeof ProposalTypes[number];

+ 7 - 0
pioneer/packages/joy-utils/src/types/workingGroups.ts

@@ -0,0 +1,7 @@
+import { Worker } from '@joystream/types/working-group';
+import { Profile } from '@joystream/types/members';
+
+export type LeadWithProfile = {
+  worker: Worker;
+  profile: Profile;
+};

+ 1 - 1
runtime-modules/content-working-group/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = 'substrate-content-working-group-module'
-version = '1.0.1'
+version = '1.1.0'
 authors = ['Joystream contributors']
 edition = '2018'
 

+ 42 - 238
runtime-modules/content-working-group/src/lib.rs

@@ -19,23 +19,15 @@ pub mod genesis;
 #[cfg(feature = "std")]
 use serde::{Deserialize, Serialize};
 
-use codec::{Decode, Encode}; // Codec
-                             //use rstd::collections::btree_map::BTreeMap;
-use membership::{members, role_types};
+use codec::{Decode, Encode};
 use rstd::borrow::ToOwned;
 use rstd::collections::btree_map::BTreeMap;
 use rstd::collections::btree_set::BTreeSet;
 use rstd::convert::From;
 use rstd::prelude::*;
-use sr_primitives::traits::{One, Zero}; // Member, SimpleArithmetic, MaybeSerialize
+use sr_primitives::traits::{One, Zero};
 use srml_support::traits::{Currency, ExistenceRequirement, WithdrawReasons};
-use srml_support::{
-    decl_event,
-    decl_module,
-    decl_storage,
-    dispatch, // , StorageMap, , Parameter
-    ensure,
-};
+use srml_support::{decl_event, decl_module, decl_storage, dispatch, ensure};
 use system::{self, ensure_root, ensure_signed};
 
 use common::constraints::InputValidationLengthConstraint;
@@ -48,7 +40,7 @@ pub trait Trait:
     + stake::Trait
     + hiring::Trait
     + versioned_store_permissions::Trait
-    + members::Trait
+    + membership::Trait
 {
     // + Sized
 
@@ -56,19 +48,19 @@ pub trait Trait:
     type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
 }
 
-/// Type constraint for identifer used for actors in members module in this runtime.
-pub type ActorIdInMembersModule<T> = <T as members::Trait>::ActorId;
+/// Type constraint for identifer used for actors.
+pub type ActorId<T> = <T as membership::Trait>::ActorId;
 
 /// Type for identifier for channels.
 /// The ChannelId must be capable of behaving like an actor id for membership module,
 /// since publishers are identified by their channel id.
-pub type ChannelId<T> = ActorIdInMembersModule<T>;
+pub type ChannelId<T> = ActorId<T>;
 
 /// Type identifier for lead role, which must be same as membership actor identifeir
-pub type LeadId<T> = ActorIdInMembersModule<T>;
+pub type LeadId<T> = ActorId<T>;
 
 /// Type identifier for curator role, which must be same as membership actor identifeir
-pub type CuratorId<T> = ActorIdInMembersModule<T>;
+pub type CuratorId<T> = ActorId<T>;
 
 /// Type for the identifer for an opening for a curator.
 pub type CuratorOpeningId<T> = <T as hiring::Trait>::OpeningId;
@@ -232,9 +224,8 @@ pub static MSG_ADD_CURATOR_OPENING_ZERO_MAX_APPLICANT_COUNT: &str =
 
 // Errors for `apply_on_curator_opening`
 pub static MSG_APPLY_ON_CURATOR_OPENING_UNSIGNED_ORIGIN: &str = "Unsigned origin";
-pub static MSG_APPLY_ON_CURATOR_OPENING_MEMBER_ID_INVALID: &str = "Member id is invalid";
-pub static MSG_APPLY_ON_CURATOR_OPENING_SIGNER_NOT_CONTROLLER_ACCOUNT: &str =
-    "Signer does not match controller account";
+pub static MSG_MEMBER_ID_INVALID: &str = "Member id is invalid";
+pub static MSG_SIGNER_NOT_CONTROLLER_ACCOUNT: &str = "Signer does not match controller account";
 pub static MSG_ORIGIN_IS_NIETHER_MEMBER_CONTROLLER_OR_ROOT: &str =
     "Origin must be controller or root account of member";
 pub static MSG_MEMBER_HAS_ACTIVE_APPLICATION_ON_OPENING: &str =
@@ -276,7 +267,10 @@ impl<BlockNumber> Default for LeadRoleState<BlockNumber> {
 /// hence information about this is missing. Recurring rewards is included, somewhat arbitrarily!
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]
-pub struct Lead<AccountId, RewardRelationshipId, BlockNumber> {
+pub struct Lead<AccountId, RewardRelationshipId, BlockNumber, MemberId> {
+    /// Leader member id,
+    pub member_id: MemberId,
+
     /// Account used to authenticate in this role,
     pub role_account: AccountId,
 
@@ -438,9 +432,6 @@ pub struct Curator<
     /// How the curator was inducted into the working group.
     pub induction: CuratorInduction<LeadId, CuratorApplicationId, BlockNumber>,
 
-    /// Whether this curator can unilaterally alter the curation status of a channel.
-    //pub can_update_channel_curation_status: bool,
-
     /// Permissions module principal id
     pub principal_id: PrincipalId,
 }
@@ -774,27 +765,6 @@ impl<LeadId: Default, CuratorId> Default for WorkingGroupUnstaker<LeadId, Curato
     }
 }
 
-/*
-/// ...
-#[derive(Encode, Decode, Debug, Eq, PartialEq, Clone, PartialOrd, Ord)]
-pub struct OpeningHire<CuratorApplicationId, CuratorId, PrincipalId> {
-    curator_application_id: CuratorApplicationId,
-    curator_id: CuratorId,
-    principal_id: PrincipalId
-}
-*/
-
-/*
-pub enum ChannelActor<T: Trait> {
-
-    ///
-    WorkingGroupActor(WorkingGroupActor<T>),
-
-    ///
-    Owner
-}
-*/
-
 // ======================================================================== //
 // Move section below, this out in its own file later                       //
 // ======================================================================== //
@@ -936,13 +906,13 @@ impl rstd::convert::From<WrappedError<hiring::DeactivateApplicationError>> for &
     }
 }
 
-impl rstd::convert::From<WrappedError<members::ControllerAccountForMemberCheckFailed>> for &str {
-    fn from(wrapper: WrappedError<members::ControllerAccountForMemberCheckFailed>) -> Self {
+impl rstd::convert::From<WrappedError<membership::ControllerAccountForMemberCheckFailed>> for &str {
+    fn from(wrapper: WrappedError<membership::ControllerAccountForMemberCheckFailed>) -> Self {
         match wrapper.error {
-            members::ControllerAccountForMemberCheckFailed::NotMember => {
+            membership::ControllerAccountForMemberCheckFailed::NotMember => {
                 MSG_CREATE_CHANNEL_IS_NOT_MEMBER
             }
-            members::ControllerAccountForMemberCheckFailed::NotControllerAccount => {
+            membership::ControllerAccountForMemberCheckFailed::NotControllerAccount => {
                 MSG_CREATE_CHANNEL_NOT_CONTROLLER_ACCOUNT
             }
         }
@@ -974,17 +944,15 @@ impl rstd::convert::From<WrappedError<hiring::AddApplicationError>> for &str {
     }
 }
 
-impl rstd::convert::From<WrappedError<members::MemberControllerAccountDidNotSign>> for &str {
-    fn from(wrapper: WrappedError<members::MemberControllerAccountDidNotSign>) -> Self {
+impl rstd::convert::From<WrappedError<membership::MemberControllerAccountDidNotSign>> for &str {
+    fn from(wrapper: WrappedError<membership::MemberControllerAccountDidNotSign>) -> Self {
         match wrapper.error {
-            members::MemberControllerAccountDidNotSign::UnsignedOrigin => {
+            membership::MemberControllerAccountDidNotSign::UnsignedOrigin => {
                 MSG_APPLY_ON_CURATOR_OPENING_UNSIGNED_ORIGIN
             }
-            members::MemberControllerAccountDidNotSign::MemberIdInvalid => {
-                MSG_APPLY_ON_CURATOR_OPENING_MEMBER_ID_INVALID
-            }
-            members::MemberControllerAccountDidNotSign::SignerControllerAccountMismatch => {
-                MSG_APPLY_ON_CURATOR_OPENING_SIGNER_NOT_CONTROLLER_ACCOUNT
+            membership::MemberControllerAccountDidNotSign::MemberIdInvalid => MSG_MEMBER_ID_INVALID,
+            membership::MemberControllerAccountDidNotSign::SignerControllerAccountMismatch => {
+                MSG_SIGNER_NOT_CONTROLLER_ACCOUNT
             }
         }
     }
@@ -1012,7 +980,7 @@ decl_storage! {
         pub CurrentLeadId get(current_lead_id) : Option<LeadId<T>>;
 
         /// Maps identifier to corresponding lead.
-        pub LeadById get(lead_by_id): linked_map LeadId<T> => Lead<T::AccountId, T::RewardRelationshipId, T::BlockNumber>;
+        pub LeadById get(lead_by_id): linked_map LeadId<T> => Lead<T::AccountId, T::RewardRelationshipId, T::BlockNumber, T::MemberId>;
 
         /// Next identifier for new current lead.
         pub NextLeadId get(next_lead_id): LeadId<T>;
@@ -1145,13 +1113,12 @@ decl_module! {
             banner: OptionalText,
             publication_status: ChannelPublicationStatus
         ) {
-
-            // Ensure that it is signed
-            let signer_account = ensure_signed(origin)?;
-
-            // Ensure that owner member can authenticate with signer account
+            // Ensure that owner member is signed and can authenticate with signer account
             ensure_on_wrapped_error!(
-                members::Module::<T>::ensure_is_controller_account_for_member(&owner, &signer_account)
+                membership::Module::<T>::ensure_member_controller_account_signed(
+                    origin,
+                    &owner
+                )
             )?;
 
             // Ensure it is currently possible to create channels (ChannelCreationEnabled).
@@ -1160,9 +1127,6 @@ decl_module! {
                 MSG_CHANNEL_CREATION_DISABLED
             );
 
-            // Ensure prospective owner member is currently allowed to become channel owner
-            let (member_in_role, next_channel_id) = Self::ensure_can_register_channel_owner_role_on_member(&owner, None)?;
-
             // Ensure channel handle is acceptable length
             Self::ensure_channel_handle_is_valid(&handle)?;
 
@@ -1183,6 +1147,7 @@ decl_module! {
             //
 
             // Make and add new principal
+            let next_channel_id = NextChannelId::<T>::get();
             let principal_id = Self::add_new_principal(&Principal::ChannelOwner(next_channel_id));
 
             // Construct channel
@@ -1213,14 +1178,8 @@ decl_module! {
 
             // CREDENTIAL STUFF //
 
-            // Dial out to membership module and inform about new role as channe owner.
-            let registered_role = <members::Module<T>>::register_role_on_member(owner, &member_in_role).is_ok();
-
-            assert!(registered_role);
-
             // Trigger event
             Self::deposit_event(RawEvent::ChannelCreated(next_channel_id));
-
         }
 
         /// An owner transfers channel ownership to a new owner.
@@ -1233,13 +1192,6 @@ decl_module! {
             // Ensure channel owner has signed
             let channel = Self::ensure_channel_owner_signed(origin, &channel_id)?;
 
-            // Ensure prospective new owner can actually become a channel owner (with a new channel id)
-            // We do not pass the existing channel id because it is already owned and the call would
-            // return with Err, since the membership system doesn't allow the same ActorInRole to be assigned
-            // to more than one member, and we don't use the returned actor_in_role because its not
-            // for the channel being transferred.
-            Self::ensure_can_register_channel_owner_role_on_member(&new_owner, None)?;
-
             //
             // == MUTATION SAFE ==
             //
@@ -1254,26 +1206,6 @@ decl_module! {
             // Overwrite entry in ChannelById
             ChannelById::<T>::insert(channel_id, new_channel);
 
-            let role = role_types::ActorInRole::new(
-                role_types::Role::ChannelOwner,
-                channel_id
-            );
-
-            // Remove
-            let unregistered_role = <members::Module<T>>::unregister_role(
-                role
-            ).is_ok();
-
-            assert!(unregistered_role);
-
-            // Dial out to membership module and inform about new role as channel owner.
-            let registered_role = <members::Module<T>>::register_role_on_member(
-                new_owner,
-                &role
-            ).is_ok();
-
-            assert!(registered_role);
-
             // Trigger event
             Self::deposit_event(RawEvent::ChannelOwnershipTransferred(channel_id));
         }
@@ -1520,18 +1452,6 @@ decl_module! {
                                             .map(|(successful_curator_application, _, _)| successful_curator_application.application_id)
                                             .collect::<BTreeSet<_>>();
 
-            // Ensure all applications are from members that _still_ can step into the given role
-            let num_successful_applications_that_can_register_as_curator = successful_iter
-                                                                        .clone()
-                                                                        .map(|(successful_curator_application, _, _)| successful_curator_application.member_id)
-                                                                        .filter_map(|successful_member_id| Self::ensure_can_register_curator_role_on_member(&successful_member_id).ok() )
-                                                                        .count();
-
-            ensure!(
-                num_successful_applications_that_can_register_as_curator == num_provided_successful_curator_application_ids,
-                MSG_MEMBER_NO_LONGER_REGISTRABLE_AS_CURATOR
-            );
-
             // NB: Combined ensure check and mutation in hiring module
             ensure_on_wrapped_error!(
                 hiring::Module::<T>::fill_opening(
@@ -1566,10 +1486,10 @@ decl_module! {
                     let recipient = <recurringrewards::Module<T>>::add_recipient();
 
                     // member must exist, since it was checked that it can enter the role
-                    let member_profile = <members::Module<T>>::member_profile(successful_curator_application.member_id).unwrap();
+                    let membership = <membership::Module<T>>::membership(successful_curator_application.member_id);
 
                     // rewards are deposited in the member's root account
-                    let reward_destination_account = member_profile.root_account;
+                    let reward_destination_account = membership.root_account;
 
                     // values have been checked so this should not fail!
                     let relationship_id = <recurringrewards::Module<T>>::add_reward_relationship(
@@ -1624,14 +1544,6 @@ decl_module! {
                 // Store curator
                 CuratorById::<T>::insert(new_curator_id, curator);
 
-                // Register role on member
-                let registered_role = members::Module::<T>::register_role_on_member(
-                    successful_curator_application.member_id,
-                    &role_types::ActorInRole::new(role_types::Role::Curator, new_curator_id)
-                ).is_ok();
-
-                assert!(registered_role);
-
                 // Update next curator id
                 NextCuratorId::<T>::mutate(|id| *id += <CuratorId<T> as One>::one());
 
@@ -1643,20 +1555,6 @@ decl_module! {
 
         }
 
-        /*
-        /// ...
-        pub fn update_curator_reward(_origin) {
-
-        }
-        */
-
-        /*
-        /// ...
-        pub fn slash_curator(_origin) {
-
-        }
-        */
-
         pub fn withdraw_curator_application(
             origin,
             curator_application_id: CuratorApplicationId<T>
@@ -1740,17 +1638,14 @@ decl_module! {
             // and cannot specify another arbitrary account as the source account.
             // Ensure the source_account is either the controller or root account of member with given id
             ensure!(
-                members::Module::<T>::ensure_member_controller_account(&source_account, &member_id).is_ok() ||
-                members::Module::<T>::ensure_member_root_account(&source_account, &member_id).is_ok(),
+                membership::Module::<T>::ensure_member_controller_account(&source_account, &member_id).is_ok() ||
+                membership::Module::<T>::ensure_member_root_account(&source_account, &member_id).is_ok(),
                 MSG_ORIGIN_IS_NIETHER_MEMBER_CONTROLLER_OR_ROOT
             );
 
             // Ensure curator opening exists
             let (curator_opening, _opening) = Self::ensure_curator_opening_exists(&curator_opening_id)?;
 
-            // Ensure new owner can actually become a curator
-            let (_member_as_curator, _new_curator_id) = Self::ensure_can_register_curator_role_on_member(&member_id)?;
-
             // Ensure that there is sufficient balance to cover stake proposed
             Self::ensure_can_make_stake_imbalance(
                 vec![&opt_role_stake_balance, &opt_application_stake_balance],
@@ -1822,16 +1717,9 @@ decl_module! {
         ) {
             // Ensure that origin is signed by member with given id.
             ensure_on_wrapped_error!(
-                members::Module::<T>::ensure_member_controller_account_signed(origin, &member_id)
+                membership::Module::<T>::ensure_member_controller_account_signed(origin, &member_id)
             )?;
 
-            // Ensure that member is this curator
-            let actor_in_role = role_types::ActorInRole::new(role_types::Role::Curator, curator_id);
-
-            ensure!(
-                members::MembershipIdByActorInRole::<T>::get(actor_in_role) == member_id,
-                MSG_CURATOR_NOT_CONTROLLED_BY_MEMBER
-            );
 
             //
             // == MUTATION SAFE ==
@@ -2071,18 +1959,13 @@ impl<T: Trait> Module<T> {
 
         let new_lead_id = <NextLeadId<T>>::get();
 
-        let new_lead_role =
-            role_types::ActorInRole::new(role_types::Role::CuratorLead, new_lead_id);
-
         //
         // == MUTATION SAFE ==
         //
 
-        // Register in role - will fail if member cannot become lead
-        members::Module::<T>::register_role_on_member(member, &new_lead_role)?;
-
         // Construct lead
         let new_lead = Lead {
+            member_id: member,
             role_account,
             reward_relationship: None,
             inducted: <system::Module<T>>::block_number(),
@@ -2093,7 +1976,7 @@ impl<T: Trait> Module<T> {
         <LeadById<T>>::insert(new_lead_id, new_lead);
 
         // Update current lead
-        <CurrentLeadId<T>>::put(new_lead_id); // Some(new_lead_id)
+        <CurrentLeadId<T>>::put(new_lead_id);
 
         // Update next lead counter
         <NextLeadId<T>>::mutate(|id| *id += <LeadId<T> as One>::one());
@@ -2113,14 +1996,6 @@ impl<T: Trait> Module<T> {
         // == MUTATION SAFE ==
         //
 
-        // Unregister from role in membership model
-        let current_lead_role = role_types::ActorInRole {
-            role: role_types::Role::CuratorLead,
-            actor_id: lead_id,
-        };
-
-        <members::Module<T>>::unregister_role(current_lead_role)?;
-
         // Update lead stage as exited
         let current_block = <system::Module<T>>::block_number();
 
@@ -2134,7 +2009,7 @@ impl<T: Trait> Module<T> {
         <LeadById<T>>::insert(lead_id, new_lead);
 
         // Update current lead
-        <CurrentLeadId<T>>::take(); // None
+        <CurrentLeadId<T>>::take();
 
         // Trigger event
         Self::deposit_event(RawEvent::LeadUnset(lead_id));
@@ -2163,54 +2038,7 @@ impl<T: Trait> Module<T> {
         Ok(())
     }
 
-    fn ensure_can_register_role_on_member(
-        member_id: &T::MemberId,
-        role: role_types::Role,
-        actor_id: &ActorIdInMembersModule<T>,
-    ) -> Result<members::ActorInRole<ActorIdInMembersModule<T>>, &'static str> {
-        let new_actor_in_role = role_types::ActorInRole::new(role, *actor_id);
-
-        <members::Module<T>>::can_register_role_on_member(member_id, &new_actor_in_role)
-            .map(|_| new_actor_in_role)
-    }
-
-    fn ensure_can_register_curator_role_on_member(
-        member_id: &T::MemberId,
-    ) -> Result<
-        (
-            members::ActorInRole<ActorIdInMembersModule<T>>,
-            CuratorId<T>,
-        ),
-        &'static str,
-    > {
-        let next_id = <NextCuratorId<T>>::get();
-
-        Self::ensure_can_register_role_on_member(member_id, role_types::Role::Curator, &next_id)
-            .map(|curator_in_role| (curator_in_role, next_id))
-    }
-
-    fn ensure_can_register_channel_owner_role_on_member(
-        member_id: &T::MemberId,
-        opt_channel_id: Option<ChannelId<T>>,
-    ) -> Result<
-        (
-            members::ActorInRole<ActorIdInMembersModule<T>>,
-            ChannelId<T>,
-        ),
-        &'static str,
-    > {
-        let next_channel_id = opt_channel_id.unwrap_or_else(NextChannelId::<T>::get);
-
-        Self::ensure_can_register_role_on_member(
-            member_id,
-            role_types::Role::ChannelOwner,
-            &next_channel_id,
-        )
-        .map(|member_in_role| (member_in_role, next_channel_id))
-    }
-
     // TODO: convert InputConstraint ensurer routines into macroes
-
     fn ensure_channel_handle_is_valid(handle: &[u8]) -> dispatch::Result {
         ChannelHandleConstraint::get().ensure_valid(
             handle.len(),
@@ -2315,7 +2143,7 @@ impl<T: Trait> Module<T> {
     pub fn ensure_lead_is_set() -> Result<
         (
             LeadId<T>,
-            Lead<T::AccountId, T::RewardRelationshipId, T::BlockNumber>,
+            Lead<T::AccountId, T::RewardRelationshipId, T::BlockNumber, T::MemberId>,
         ),
         &'static str,
     > {
@@ -2344,7 +2172,7 @@ impl<T: Trait> Module<T> {
     ) -> Result<
         (
             LeadId<T>,
-            Lead<T::AccountId, T::RewardRelationshipId, T::BlockNumber>,
+            Lead<T::AccountId, T::RewardRelationshipId, T::BlockNumber, T::MemberId>,
         ),
         &'static str,
     > {
@@ -2359,25 +2187,7 @@ impl<T: Trait> Module<T> {
 
         Ok((lead_id, lead))
     }
-    /*
-        fn ensure_activate_opening_at_valid(activate_at: &hiring::ActivateOpeningAt<T::BlockNumber>) -> Result<T::BlockNumber, &'static str>{
 
-            let current_block = <system::Module<T>>::block_number();
-
-            let starting_block =
-                match activate_at {
-                    hiring::ActivateOpeningAt::CurrentBlock => current_block,
-                    hiring::ActivateOpeningAt::ExactBlock(block_number) => block_number.clone()
-            };
-
-            ensure!(
-                starting_block >= current_block,
-                MSG_OPENING_CANNOT_ACTIVATE_IN_THE_PAST
-            );
-
-            Ok(starting_block)
-        }
-    */
     fn ensure_curator_opening_exists(
         curator_opening_id: &CuratorOpeningId<T>,
     ) -> Result<
@@ -2542,8 +2352,6 @@ impl<T: Trait> Module<T> {
         ),
         &'static str,
     > {
-        //Result<(hiring::Application<<T as hiring::Trait>::OpeningId, T::BlockNumber, <T as stake::Trait>::StakeId>, CuratorOpening<T::OpeningId, T::BlockNumber, BalanceOf<T>, CuratorApplicationId<T>> ,hiring::Opening<BalanceOf<T>, T::BlockNumber, <T as hiring::Trait>::ApplicationId>), &'static str> {
-
         ensure!(
             CuratorApplicationById::<T>::exists(curator_application_id),
             MSG_CURATOR_APPLICATION_DOES_NOT_EXIST
@@ -2551,12 +2359,8 @@ impl<T: Trait> Module<T> {
 
         let curator_application = CuratorApplicationById::<T>::get(curator_application_id);
 
-        //let application = hiring::ApplicationById::<T>::get(curator_application.application_id);
-
         let curator_opening = CuratorOpeningById::<T>::get(curator_application.curator_opening_id);
 
-        //let opening = hiring::OpeningById::<T>::get(curator_opening.opening_id);
-
         Ok((
             curator_application,
             *curator_application_id,

+ 6 - 30
runtime-modules/content-working-group/src/mock.rs

@@ -1,6 +1,6 @@
 #![cfg(test)]
 
-pub use crate::*; // {self, Module, Trait, GenesisConfig};
+pub use crate::*;
 pub use srml_support::traits::Currency;
 pub use system;
 
@@ -16,7 +16,7 @@ use srml_support::{impl_outer_event, impl_outer_origin, parameter_types};
 
 pub use common::currency::GovernanceCurrency;
 pub use hiring;
-pub use membership::members;
+pub use membership;
 pub use minting;
 pub use recurringrewards;
 pub use stake;
@@ -36,7 +36,6 @@ parameter_types! {
     pub const CreationFee: u32 = 0;
     pub const TransactionBaseFee: u32 = 1;
     pub const TransactionByteFee: u32 = 0;
-    pub const InitialMembersBalance: u64 = 2000;
     pub const StakePoolId: [u8; 8] = *b"joystake";
 }
 
@@ -55,7 +54,7 @@ mod lib {
 impl_outer_event! {
     pub enum TestEvent for Test {
         versioned_store<T>,
-        members<T>,
+        membership<T>,
         balances<T>,
         lib<T>,
     }
@@ -124,16 +123,6 @@ impl balances::Trait for Test {
     type CreationFee = CreationFee;
 }
 
-/*
-pub trait PrincipalIdChecker<T: Trait> {
-    fn account_can_act_as_principal(account: &T::AccountId, group: T::PrincipalId) -> bool;
-}
-
-pub trait CreateClassPermissionsChecker<T: Trait> {
-    fn account_can_create_class_permissions(account: &T::AccountId) -> bool;
-}
-*/
-
 impl GovernanceCurrency for Test {
     type Currency = Balances;
 }
@@ -183,13 +172,12 @@ impl versioned_store_permissions::Trait for Test {
 }
 
 type TestMemberId = u64;
-impl members::Trait for Test {
+impl membership::Trait for Test {
     type Event = TestEvent;
     type MemberId = TestMemberId;
     type PaidTermId = u64;
     type SubscriptionId = u64;
     type ActorId = u64;
-    type InitialMembersBalance = InitialMembersBalance;
 }
 
 impl Trait for Test {
@@ -198,7 +186,7 @@ impl Trait for Test {
 
 pub struct TestExternalitiesBuilder<T: Trait> {
     system_config: Option<system::GenesisConfig>,
-    membership_config: Option<members::GenesisConfig<T>>,
+    membership_config: Option<membership::GenesisConfig<T>>,
     content_wg_config: Option<GenesisConfig<T>>,
 }
 
@@ -213,17 +201,6 @@ impl<T: Trait> Default for TestExternalitiesBuilder<T> {
 }
 
 impl<T: Trait> TestExternalitiesBuilder<T> {
-    /*
-    pub fn set_system_config(mut self, system_config: system::GenesisConfig) -> Self {
-        self.system_config = Some(system_config);
-        self
-    }
-    pub fn set_membership_config(mut self, membership_config: members::GenesisConfig<T>) -> Self {
-        self.membership_config = Some(membership_config);
-        self
-    }
-    */
-
     pub fn with_content_wg_config(mut self, conteng_wg_config: GenesisConfig<T>) -> Self {
         self.content_wg_config = Some(conteng_wg_config);
         self
@@ -239,12 +216,11 @@ impl<T: Trait> TestExternalitiesBuilder<T> {
 
         // Add membership
         self.membership_config
-            .unwrap_or(members::GenesisConfig::default())
+            .unwrap_or(membership::GenesisConfig::default())
             .assimilate_storage(&mut t)
             .unwrap();
 
         // Add content wg
-
         if self.content_wg_config.is_none() {
             genesis::GenesisConfigBuilder::<Test>::default()
                 .build()

+ 21 - 23
runtime-modules/content-working-group/src/tests.rs

@@ -2,7 +2,6 @@
 
 use super::genesis;
 use super::mock::{self, *};
-//use crate::membership;
 use hiring;
 use rstd::collections::btree_map::BTreeMap;
 use rstd::collections::btree_set::BTreeSet;
@@ -47,9 +46,9 @@ fn create_channel_is_not_a_member() {
 
             // Change to invalid member id, i.e. != channel_creator_member_id
             fixture.channel_creator_member_id = fixture.channel_creator_member_id
-                + <<Test as members::Trait>::MemberId as One>::one();
+                + <<Test as membership::Trait>::MemberId as One>::one();
 
-            fixture.call_and_assert_error(MSG_CREATE_CHANNEL_IS_NOT_MEMBER);
+            fixture.call_and_assert_error(MSG_MEMBER_ID_INVALID);
         });
 }
 
@@ -85,7 +84,7 @@ fn create_channel_with_bad_member_role_account() {
                 Some(0),
             );
 
-            fixture.call_and_assert_error(MSG_CREATE_CHANNEL_NOT_CONTROLLER_ACCOUNT);
+            fixture.call_and_assert_error(MSG_SIGNER_NOT_CONTROLLER_ACCOUNT);
         });
 }
 
@@ -862,7 +861,7 @@ fn multiple_applications_by_same_member_to_opening_fails() {
 
 struct UpdateCuratorRoleAccountFixture {
     pub origin: Origin,
-    pub member_id: <Test as members::Trait>::MemberId,
+    pub member_id: <Test as membership::Trait>::MemberId,
     pub curator_id: CuratorId<Test>,
     pub new_role_account: <Test as system::Trait>::AccountId,
 }
@@ -1152,7 +1151,7 @@ fn terminate_curator_role_success() {
 
 struct SetLeadFixture {
     pub origin: Origin,
-    pub member_id: <Test as members::Trait>::MemberId,
+    pub member_id: <Test as membership::Trait>::MemberId,
     pub new_role_account: <Test as system::Trait>::AccountId,
 }
 
@@ -1184,6 +1183,7 @@ impl SetLeadFixture {
         let new_lead = LeadById::<Test>::get(new_lead_id);
 
         let expected_new_lead = Lead {
+            member_id: self.member_id,
             role_account: self.new_role_account,
             reward_relationship: None,
             inducted: 1, // make dynamic later
@@ -1500,7 +1500,7 @@ fn add_members_and_apply_on_opening(
 
 #[derive(Clone)]
 struct NewMemberAppliedResult {
-    pub member_id: <Test as members::Trait>::MemberId,
+    pub member_id: <Test as membership::Trait>::MemberId,
     pub curator_application_id: crate::CuratorApplicationId<Test>,
 }
 
@@ -1899,7 +1899,7 @@ impl SetupLeadAndHireCuratorResult {
         }
     }
 
-    pub fn curator_member_id(&self) -> <Test as members::Trait>::MemberId {
+    pub fn curator_member_id(&self) -> <Test as membership::Trait>::MemberId {
         self.setup_and_fill_opening_result
             .setup_opening_in_review
             .added_members_application_result[0]
@@ -1924,7 +1924,7 @@ fn setup_lead_and_hire_curator() -> SetupLeadAndHireCuratorResult {
 }
 
 struct CreateChannelFixture {
-    pub channel_creator_member_id: <Test as members::Trait>::MemberId,
+    pub channel_creator_member_id: <Test as membership::Trait>::MemberId,
     pub controller_account: <Test as system::Trait>::AccountId,
     pub channel_creator_role_account: <Test as system::Trait>::AccountId,
     pub channel_handle: Vec<u8>,
@@ -1938,13 +1938,13 @@ struct CreateChannelFixture {
 
 impl CreateChannelFixture {
     pub fn make_valid_unpulished_video_channel_for(
-        channel_creator_member_id: <Test as members::Trait>::MemberId,
+        channel_creator_member_id: <Test as membership::Trait>::MemberId,
         override_controller_account: Option<<Test as system::Trait>::AccountId>,
     ) -> Self {
         let controller_account = if let Some(account) = override_controller_account {
             account
         } else {
-            members::Module::<Test>::ensure_profile(channel_creator_member_id)
+            membership::Module::<Test>::ensure_membership(channel_creator_member_id)
                 .unwrap()
                 .controller_account
         };
@@ -2062,7 +2062,7 @@ impl CreateChannelFixture {
 }
 
 struct NewMemberAsLead {
-    pub member_id: <Test as members::Trait>::MemberId,
+    pub member_id: <Test as membership::Trait>::MemberId,
     pub lead_id: LeadId<Test>,
 }
 
@@ -2079,7 +2079,7 @@ pub fn set_channel_creation_enabled(enabled: bool) {
         .unwrap()
 }
 
-pub fn add_channel_creator_member() -> <Test as members::Trait>::MemberId {
+pub fn add_channel_creator_member() -> <Test as membership::Trait>::MemberId {
     let channel_creator_member_id = add_member(
         CHANNEL_CREATOR_ROOT_AND_CONTROLLER_ACCOUNT,
         to_vec(CHANNEL_CREATOR_HANDLE),
@@ -2091,18 +2091,16 @@ pub fn add_channel_creator_member() -> <Test as members::Trait>::MemberId {
 pub fn add_member(
     root_and_controller_account: <Test as system::Trait>::AccountId,
     handle: Vec<u8>,
-) -> <Test as members::Trait>::MemberId {
-    let next_member_id = members::MembersCreated::<Test>::get();
+) -> <Test as membership::Trait>::MemberId {
+    let next_member_id = membership::NextMemberId::<Test>::get();
 
     assert_eq!(
-        members::Module::<Test>::buy_membership(
+        membership::Module::<Test>::buy_membership(
             Origin::signed(root_and_controller_account),
             0,
-            members::UserInfo {
-                handle: Some(handle),
-                avatar_uri: None,
-                about: None,
-            }
+            Some(handle),
+            None,
+            None
         )
         .unwrap(),
         ()
@@ -2112,11 +2110,11 @@ pub fn add_member(
 }
 
 pub fn set_lead(
-    member_id: <Test as members::Trait>::MemberId,
+    member_id: <Test as membership::Trait>::MemberId,
     new_role_account: <Test as system::Trait>::AccountId,
 ) -> LeadId<Test> {
     // Get controller account
-    //let lead_member_controller_account = members::Module::<Test>::ensure_profile(member_id).unwrap().controller_account;
+    //let lead_member_controller_account = membership::Module::<Test>::ensure_membership(member_id).unwrap().controller_account;
 
     let expected_lead_id = NextLeadId::<Test>::get();
 

+ 2 - 4
runtime-modules/governance/src/election.rs

@@ -48,9 +48,7 @@ use super::council;
 use crate::election_params::ElectionParameters;
 pub use common::currency::{BalanceOf, GovernanceCurrency};
 
-pub trait Trait:
-    system::Trait + council::Trait + GovernanceCurrency + membership::members::Trait
-{
+pub trait Trait: system::Trait + council::Trait + GovernanceCurrency + membership::Trait {
     type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
 
     type CouncilElected: CouncilElected<Seats<Self::AccountId, BalanceOf<Self>>, Self::BlockNumber>;
@@ -213,7 +211,7 @@ impl<T: Trait> Module<T> {
 
     fn can_participate(sender: &T::AccountId) -> bool {
         !<T as GovernanceCurrency>::Currency::free_balance(sender).is_zero()
-            && <membership::members::Module<T>>::is_member_account(sender)
+            && <membership::Module<T>>::is_member_account(sender)
     }
 
     // PUBLIC IMMUTABLES

+ 2 - 4
runtime-modules/governance/src/mock.rs

@@ -62,13 +62,12 @@ impl election::Trait for Test {
 
     type CouncilElected = (Council,);
 }
-impl membership::members::Trait for Test {
+impl membership::Trait for Test {
     type Event = ();
     type MemberId = u32;
     type SubscriptionId = u32;
     type PaidTermId = u32;
     type ActorId = u32;
-    type InitialMembersBalance = InitialMembersBalance;
 }
 impl minting::Trait for Test {
     type Currency = Balances;
@@ -85,7 +84,6 @@ parameter_types! {
     pub const CreationFee: u32 = 0;
     pub const TransactionBaseFee: u32 = 1;
     pub const TransactionByteFee: u32 = 0;
-    pub const InitialMembersBalance: u32 = 0;
 }
 
 impl balances::Trait for Test {
@@ -118,7 +116,7 @@ pub fn initial_test_ext() -> runtime_io::TestExternalities {
         .build_storage::<Test>()
         .unwrap();
 
-    membership::members::GenesisConfig::<Test> {
+    membership::GenesisConfig::<Test> {
         default_paid_membership_fee: 0,
         members: vec![
             (1, "member1".into(), "".into(), "".into()),

+ 1 - 1
runtime-modules/membership/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = 'substrate-membership-module'
-version = '1.0.1'
+version = '1.1.0'
 authors = ['Joystream contributors']
 edition = '2018'
 

+ 3 - 4
runtime-modules/membership/src/genesis.rs

@@ -2,9 +2,8 @@
 
 use common::currency::BalanceOf;
 use rstd::prelude::*;
-//pub use super::members::{GenesisConfig, Trait};
 
-use super::members::{self, Trait};
+use crate::{GenesisConfig, Trait};
 
 /// Builder fo membership module genesis configuration.
 pub struct GenesisConfigBuilder<T: Trait> {
@@ -34,8 +33,8 @@ impl<T: Trait> GenesisConfigBuilder<T> {
         self
     }
 
-    pub fn build(&self) -> members::GenesisConfig<T> {
-        members::GenesisConfig::<T> {
+    pub fn build(&self) -> GenesisConfig<T> {
+        GenesisConfig::<T> {
             default_paid_membership_fee: self.default_paid_membership_fee,
             members: self
                 .members

+ 647 - 3
runtime-modules/membership/src/lib.rs

@@ -1,9 +1,653 @@
 // Ensure we're `no_std` when compiling for Wasm.
 #![cfg_attr(not(feature = "std"), no_std)]
+// Clippy linter requirement.
+// Disable it because of the substrate lib design
+// Example:  pub PaidMembershipTermsById get(paid_membership_terms_by_id) build(|config: &GenesisConfig<T>| {}
+#![allow(clippy::redundant_closure_call)]
 
 pub mod genesis;
-pub mod members;
-pub mod role_types;
-
 pub(crate) mod mock;
 mod tests;
+
+use codec::{Codec, Decode, Encode};
+use rstd::borrow::ToOwned;
+use rstd::prelude::*;
+use sr_primitives::traits::{MaybeSerialize, Member, One, SimpleArithmetic};
+use srml_support::traits::Currency;
+use srml_support::{decl_event, decl_module, decl_storage, dispatch, ensure, Parameter};
+use system::{self, ensure_root, ensure_signed};
+
+use common::currency::{BalanceOf, GovernanceCurrency};
+
+pub trait Trait: system::Trait + GovernanceCurrency + timestamp::Trait {
+    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
+
+    type MemberId: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + Default
+        + Copy
+        + MaybeSerialize
+        + PartialEq;
+
+    type PaidTermId: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + Default
+        + Copy
+        + MaybeSerialize
+        + PartialEq;
+
+    type SubscriptionId: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + Default
+        + Copy
+        + MaybeSerialize
+        + PartialEq;
+
+    /// Describes the common type for the working group members (workers).
+    type ActorId: Parameter
+        + Member
+        + SimpleArithmetic
+        + Codec
+        + Default
+        + Copy
+        + MaybeSerialize
+        + PartialEq
+        + Ord;
+}
+
+const FIRST_PAID_TERMS_ID: u32 = 1;
+
+// Default paid membership terms
+pub const DEFAULT_PAID_TERM_ID: u32 = 0;
+
+// Default user info constraints
+const DEFAULT_MIN_HANDLE_LENGTH: u32 = 5;
+const DEFAULT_MAX_HANDLE_LENGTH: u32 = 40;
+const DEFAULT_MAX_AVATAR_URI_LENGTH: u32 = 1024;
+const DEFAULT_MAX_ABOUT_TEXT_LENGTH: u32 = 2048;
+
+/// Public membership object alias.
+pub type Membership<T> = MembershipObject<
+    <T as system::Trait>::BlockNumber,
+    <T as timestamp::Trait>::Moment,
+    <T as Trait>::PaidTermId,
+    <T as Trait>::SubscriptionId,
+    <T as system::Trait>::AccountId,
+>;
+
+#[derive(Encode, Decode, Default)]
+/// Stored information about a registered user
+pub struct MembershipObject<BlockNumber, Moment, PaidTermId, SubscriptionId, AccountId> {
+    /// The unique handle chosen by member
+    pub handle: Vec<u8>,
+
+    /// A Url to member's Avatar image
+    pub avatar_uri: Vec<u8>,
+
+    /// Short text chosen by member to share information about themselves
+    pub about: Vec<u8>,
+
+    /// Block number when member was registered
+    pub registered_at_block: BlockNumber,
+
+    /// Timestamp when member was registered
+    pub registered_at_time: Moment,
+
+    /// How the member was registered
+    pub entry: EntryMethod<PaidTermId, AccountId>,
+
+    /// Whether the member is suspended or not.
+    pub suspended: bool,
+
+    /// The type of subscription the member has purchased if any.
+    pub subscription: Option<SubscriptionId>,
+
+    /// Member's root account id. Only the root account is permitted to set a new root account
+    /// and update the controller account. Other modules may only allow certain actions if
+    /// signed with root account. It is intended to be an account that can remain offline and
+    /// potentially hold a member's funds, and be a source for staking roles.
+    pub root_account: AccountId,
+
+    /// Member's controller account id. This account is intended to be used by
+    /// a member to act under their identity in other modules. It will usually be used more
+    /// online and will have less funds in its balance.
+    pub controller_account: AccountId,
+}
+
+// Contains valid or default user details
+struct ValidatedUserInfo {
+    handle: Vec<u8>,
+    avatar_uri: Vec<u8>,
+    about: Vec<u8>,
+}
+
+#[derive(Encode, Decode, Debug, PartialEq)]
+pub enum EntryMethod<PaidTermId, AccountId> {
+    Paid(PaidTermId),
+    Screening(AccountId),
+    Genesis,
+}
+
+/// Must be default constructible because it indirectly is a value in a storage map.
+/// ***SHOULD NEVER ACTUALLY GET CALLED, IS REQUIRED TO DUE BAD STORAGE MODEL IN SUBSTRATE***
+impl<PaidTermId, AccountId> Default for EntryMethod<PaidTermId, AccountId> {
+    fn default() -> Self {
+        Self::Genesis
+    }
+}
+
+#[derive(Encode, Decode, Eq, PartialEq, Default)]
+pub struct PaidMembershipTerms<Balance> {
+    /// Quantity of native tokens which must be provably burned
+    pub fee: Balance,
+    /// String of capped length describing human readable conditions which are being agreed upon
+    pub text: Vec<u8>,
+}
+
+decl_storage! {
+    trait Store for Module<T: Trait> as Membership {
+        /// MemberId to assign to next member that is added to the registry, and is also the
+        /// total number of members created. MemberIds start at Zero.
+        pub NextMemberId get(members_created) : T::MemberId;
+
+        /// Mapping of member's id to their membership profile
+        pub MembershipById get(membership) : map T::MemberId => Membership<T>;
+
+        /// Mapping of a root account id to vector of member ids it controls.
+        pub(crate) MemberIdsByRootAccountId : map T::AccountId => Vec<T::MemberId>;
+
+        /// Mapping of a controller account id to vector of member ids it controls
+        pub(crate) MemberIdsByControllerAccountId : map T::AccountId => Vec<T::MemberId>;
+
+        /// Registered unique handles and their mapping to their owner
+        pub MemberIdByHandle get(handles) : map Vec<u8> => T::MemberId;
+
+        /// Next paid membership terms id
+        pub NextPaidMembershipTermsId get(next_paid_membership_terms_id) : T::PaidTermId = T::PaidTermId::from(FIRST_PAID_TERMS_ID);
+
+        /// Paid membership terms record
+        // Remember to add _genesis_phantom_data: std::marker::PhantomData{} to membership
+        // genesis config if not providing config() or extra_genesis
+        pub PaidMembershipTermsById get(paid_membership_terms_by_id) build(|config: &GenesisConfig<T>| {
+            // This method only gets called when initializing storage, and is
+            // compiled as native code. (Will be called when building `raw` chainspec)
+            // So it can't be relied upon to initialize storage for runtimes updates.
+            // Initialization for updated runtime is done in run_migration()
+            let terms = PaidMembershipTerms {
+                fee:  config.default_paid_membership_fee,
+                text: Vec::default(),
+            };
+            vec![(T::PaidTermId::from(DEFAULT_PAID_TERM_ID), terms)]
+        }) : map T::PaidTermId => PaidMembershipTerms<BalanceOf<T>>;
+
+        /// Active Paid membership terms
+        pub ActivePaidMembershipTerms get(active_paid_membership_terms) : Vec<T::PaidTermId> = vec![T::PaidTermId::from(DEFAULT_PAID_TERM_ID)];
+
+        /// Is the platform is accepting new members or not
+        pub NewMembershipsAllowed get(new_memberships_allowed) : bool = true;
+
+        pub ScreeningAuthority get(screening_authority) : T::AccountId;
+
+        // User Input Validation parameters - do these really need to be state variables
+        // I don't see a need to adjust these in future?
+        pub MinHandleLength get(min_handle_length) : u32 = DEFAULT_MIN_HANDLE_LENGTH;
+        pub MaxHandleLength get(max_handle_length) : u32 = DEFAULT_MAX_HANDLE_LENGTH;
+        pub MaxAvatarUriLength get(max_avatar_uri_length) : u32 = DEFAULT_MAX_AVATAR_URI_LENGTH;
+        pub MaxAboutTextLength get(max_about_text_length) : u32 = DEFAULT_MAX_ABOUT_TEXT_LENGTH;
+
+    }
+    add_extra_genesis {
+        config(default_paid_membership_fee): BalanceOf<T>;
+        config(members) : Vec<(T::AccountId, String, String, String)>;
+        build(|config: &GenesisConfig<T>| {
+            for (who, handle, avatar_uri, about) in &config.members {
+                let user_info = ValidatedUserInfo {
+                    handle: handle.clone().into_bytes(),
+                    avatar_uri: avatar_uri.clone().into_bytes(),
+                    about: about.clone().into_bytes()
+                };
+
+                <Module<T>>::insert_member(&who, &user_info, EntryMethod::Genesis);
+            }
+        });
+    }
+}
+
+decl_event! {
+    pub enum Event<T> where
+      <T as system::Trait>::AccountId,
+      <T as Trait>::MemberId,
+    {
+        MemberRegistered(MemberId, AccountId),
+        MemberUpdatedAboutText(MemberId),
+        MemberUpdatedAvatar(MemberId),
+        MemberUpdatedHandle(MemberId),
+        MemberSetRootAccount(MemberId, AccountId),
+        MemberSetControllerAccount(MemberId, AccountId),
+    }
+}
+
+decl_module! {
+    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
+        fn deposit_event() = default;
+
+        /// Non-members can buy membership
+        pub fn buy_membership(
+            origin,
+            paid_terms_id: T::PaidTermId,
+            handle: Option<Vec<u8>>,
+            avatar_uri: Option<Vec<u8>>,
+            about: Option<Vec<u8>>
+        ) {
+            let who = ensure_signed(origin)?;
+
+            // make sure we are accepting new memberships
+            ensure!(Self::new_memberships_allowed(), "new members not allowed");
+
+            // ensure paid_terms_id is active
+            let terms = Self::ensure_active_terms_id(paid_terms_id)?;
+
+            // ensure enough free balance to cover terms fees
+            ensure!(T::Currency::can_slash(&who, terms.fee), "not enough balance to buy membership");
+
+            let user_info = Self::check_user_registration_info(handle, avatar_uri, about)?;
+
+            // ensure handle is not already registered
+            Self::ensure_unique_handle(&user_info.handle)?;
+
+            let _ = T::Currency::slash(&who, terms.fee);
+            let member_id = Self::insert_member(&who, &user_info, EntryMethod::Paid(paid_terms_id));
+
+            Self::deposit_event(RawEvent::MemberRegistered(member_id, who));
+        }
+
+        /// Change member's about text
+        pub fn change_member_about_text(origin, member_id: T::MemberId, text: Vec<u8>) {
+            let sender = ensure_signed(origin)?;
+
+            let membership = Self::ensure_membership(member_id)?;
+
+            ensure!(membership.controller_account == sender, "only controller account can update member about text");
+
+            Self::_change_member_about_text(member_id, &text)?;
+        }
+
+        /// Change member's avatar
+        pub fn change_member_avatar(origin, member_id: T::MemberId, uri: Vec<u8>) {
+            let sender = ensure_signed(origin)?;
+
+            let membership = Self::ensure_membership(member_id)?;
+
+            ensure!(membership.controller_account == sender, "only controller account can update member avatar");
+
+            Self::_change_member_avatar(member_id, &uri)?;
+        }
+
+        /// Change member's handle. Will ensure new handle is unique and old one will be available
+        /// for other members to use.
+        pub fn change_member_handle(origin, member_id: T::MemberId, handle: Vec<u8>) {
+            let sender = ensure_signed(origin)?;
+
+            let membership = Self::ensure_membership(member_id)?;
+
+            ensure!(membership.controller_account == sender, "only controller account can update member handle");
+
+            Self::_change_member_handle(member_id, handle)?;
+        }
+
+        /// Update member's all or some of handle, avatar and about text.
+        pub fn update_membership(
+            origin,
+            member_id: T::MemberId,
+            handle: Option<Vec<u8>>,
+            avatar_uri: Option<Vec<u8>>,
+            about: Option<Vec<u8>>
+        ) {
+            let sender = ensure_signed(origin)?;
+
+            let membership = Self::ensure_membership(member_id)?;
+
+            ensure!(membership.controller_account == sender, "only controller account can update member info");
+
+            if let Some(uri) = avatar_uri {
+                Self::_change_member_avatar(member_id, &uri)?;
+            }
+            if let Some(about) = about {
+                Self::_change_member_about_text(member_id, &about)?;
+            }
+            if let Some(handle) = handle {
+                Self::_change_member_handle(member_id, handle)?;
+            }
+        }
+
+        pub fn set_controller_account(origin, member_id: T::MemberId, new_controller_account: T::AccountId) {
+            let sender = ensure_signed(origin)?;
+
+            let mut membership = Self::ensure_membership(member_id)?;
+
+            ensure!(membership.root_account == sender, "only root account can set new controller account");
+
+            // only update if new_controller_account is different than current one
+            if membership.controller_account != new_controller_account {
+                <MemberIdsByControllerAccountId<T>>::mutate(&membership.controller_account, |ids| {
+                    ids.retain(|id| *id != member_id);
+                });
+
+                <MemberIdsByControllerAccountId<T>>::mutate(&new_controller_account, |ids| {
+                    ids.push(member_id);
+                });
+
+                membership.controller_account = new_controller_account.clone();
+                <MembershipById<T>>::insert(member_id, membership);
+                Self::deposit_event(RawEvent::MemberSetControllerAccount(member_id, new_controller_account));
+            }
+        }
+
+        pub fn set_root_account(origin, member_id: T::MemberId, new_root_account: T::AccountId) {
+            let sender = ensure_signed(origin)?;
+
+            let mut membership = Self::ensure_membership(member_id)?;
+
+            ensure!(membership.root_account == sender, "only root account can set new root account");
+
+            // only update if new root account is different than current one
+            if membership.root_account != new_root_account {
+                <MemberIdsByRootAccountId<T>>::mutate(&membership.root_account, |ids| {
+                    ids.retain(|id| *id != member_id);
+                });
+
+                <MemberIdsByRootAccountId<T>>::mutate(&new_root_account, |ids| {
+                    ids.push(member_id);
+                });
+
+                membership.root_account = new_root_account.clone();
+                Self::deposit_event(RawEvent::MemberSetRootAccount(member_id, new_root_account));
+            }
+        }
+
+        pub fn add_screened_member(
+            origin,
+            new_member_account: T::AccountId,
+            handle: Option<Vec<u8>>,
+            avatar_uri: Option<Vec<u8>>,
+            about: Option<Vec<u8>>
+        ) {
+            // ensure sender is screening authority
+            let sender = ensure_signed(origin)?;
+
+            if <ScreeningAuthority<T>>::exists() {
+                ensure!(sender == Self::screening_authority(), "not screener");
+            } else {
+                // no screening authority defined. Cannot accept this request
+                return Err("no screening authority defined");
+            }
+
+            // make sure we are accepting new memberships
+            ensure!(Self::new_memberships_allowed(), "new members not allowed");
+
+            let user_info = Self::check_user_registration_info(handle, avatar_uri, about)?;
+
+            // ensure handle is not already registered
+            Self::ensure_unique_handle(&user_info.handle)?;
+
+            let member_id = Self::insert_member(&new_member_account, &user_info, EntryMethod::Screening(sender));
+
+            Self::deposit_event(RawEvent::MemberRegistered(member_id, new_member_account));
+        }
+
+        pub fn set_screening_authority(origin, authority: T::AccountId) {
+            ensure_root(origin)?;
+            <ScreeningAuthority<T>>::put(authority);
+        }
+    }
+}
+
+/// Reason why a given member id does not have a given account as the controller account.
+pub enum ControllerAccountForMemberCheckFailed {
+    NotMember,
+    NotControllerAccount,
+}
+
+pub enum MemberControllerAccountDidNotSign {
+    UnsignedOrigin,
+    MemberIdInvalid,
+    SignerControllerAccountMismatch,
+}
+
+pub enum MemberControllerAccountMismatch {
+    MemberIdInvalid,
+    SignerControllerAccountMismatch,
+}
+pub enum MemberRootAccountMismatch {
+    MemberIdInvalid,
+    SignerRootAccountMismatch,
+}
+
+impl<T: Trait> Module<T> {
+    /// Provided that the member_id exists return its membership. Returns error otherwise.
+    pub fn ensure_membership(id: T::MemberId) -> Result<Membership<T>, &'static str> {
+        if <MembershipById<T>>::exists(&id) {
+            Ok(Self::membership(&id))
+        } else {
+            Err("member profile not found")
+        }
+    }
+
+    /// Ensure that given member has given account as the controller account
+    pub fn ensure_is_controller_account_for_member(
+        member_id: &T::MemberId,
+        account: &T::AccountId,
+    ) -> Result<Membership<T>, ControllerAccountForMemberCheckFailed> {
+        if MembershipById::<T>::exists(member_id) {
+            let membership = MembershipById::<T>::get(member_id);
+
+            if membership.controller_account == *account {
+                Ok(membership)
+            } else {
+                Err(ControllerAccountForMemberCheckFailed::NotControllerAccount)
+            }
+        } else {
+            Err(ControllerAccountForMemberCheckFailed::NotMember)
+        }
+    }
+
+    /// Returns true if account is either a member's root or controller account
+    pub fn is_member_account(who: &T::AccountId) -> bool {
+        <MemberIdsByRootAccountId<T>>::exists(who)
+            || <MemberIdsByControllerAccountId<T>>::exists(who)
+    }
+
+    fn ensure_active_terms_id(
+        terms_id: T::PaidTermId,
+    ) -> Result<PaidMembershipTerms<BalanceOf<T>>, &'static str> {
+        let active_terms = Self::active_paid_membership_terms();
+        ensure!(
+            active_terms.iter().any(|&id| id == terms_id),
+            "paid terms id not active"
+        );
+
+        if <PaidMembershipTermsById<T>>::exists(terms_id) {
+            Ok(Self::paid_membership_terms_by_id(terms_id))
+        } else {
+            Err("paid membership term id does not exist")
+        }
+    }
+
+    #[allow(clippy::ptr_arg)] // cannot change to the "&[u8]" suggested by clippy
+    fn ensure_unique_handle(handle: &Vec<u8>) -> dispatch::Result {
+        ensure!(
+            !<MemberIdByHandle<T>>::exists(handle),
+            "handle already registered"
+        );
+        Ok(())
+    }
+
+    fn validate_handle(handle: &[u8]) -> dispatch::Result {
+        ensure!(
+            handle.len() >= Self::min_handle_length() as usize,
+            "handle too short"
+        );
+        ensure!(
+            handle.len() <= Self::max_handle_length() as usize,
+            "handle too long"
+        );
+        Ok(())
+    }
+
+    fn validate_text(text: &[u8]) -> Vec<u8> {
+        let mut text = text.to_owned();
+        text.truncate(Self::max_about_text_length() as usize);
+        text
+    }
+
+    fn validate_avatar(uri: &[u8]) -> dispatch::Result {
+        ensure!(
+            uri.len() <= Self::max_avatar_uri_length() as usize,
+            "avatar uri too long"
+        );
+        Ok(())
+    }
+
+    /// Basic user input validation
+    fn check_user_registration_info(
+        handle: Option<Vec<u8>>,
+        avatar_uri: Option<Vec<u8>>,
+        about: Option<Vec<u8>>,
+    ) -> Result<ValidatedUserInfo, &'static str> {
+        // Handle is required during registration
+        let handle = handle.ok_or("handle must be provided during registration")?;
+        Self::validate_handle(&handle)?;
+
+        let about = Self::validate_text(&about.unwrap_or_default());
+        let avatar_uri = avatar_uri.unwrap_or_default();
+        Self::validate_avatar(&avatar_uri)?;
+
+        Ok(ValidatedUserInfo {
+            handle,
+            avatar_uri,
+            about,
+        })
+    }
+
+    fn insert_member(
+        who: &T::AccountId,
+        user_info: &ValidatedUserInfo,
+        entry_method: EntryMethod<T::PaidTermId, T::AccountId>,
+    ) -> T::MemberId {
+        let new_member_id = Self::members_created();
+
+        let membership: Membership<T> = MembershipObject {
+            handle: user_info.handle.clone(),
+            avatar_uri: user_info.avatar_uri.clone(),
+            about: user_info.about.clone(),
+            registered_at_block: <system::Module<T>>::block_number(),
+            registered_at_time: <timestamp::Module<T>>::now(),
+            entry: entry_method,
+            suspended: false,
+            subscription: None,
+            root_account: who.clone(),
+            controller_account: who.clone(),
+        };
+
+        <MemberIdsByRootAccountId<T>>::mutate(who, |ids| {
+            ids.push(new_member_id);
+        });
+        <MemberIdsByControllerAccountId<T>>::mutate(who, |ids| {
+            ids.push(new_member_id);
+        });
+
+        <MembershipById<T>>::insert(new_member_id, membership);
+        <MemberIdByHandle<T>>::insert(user_info.handle.clone(), new_member_id);
+        <NextMemberId<T>>::put(new_member_id + One::one());
+
+        new_member_id
+    }
+
+    fn _change_member_about_text(id: T::MemberId, text: &[u8]) -> dispatch::Result {
+        let mut membership = Self::ensure_membership(id)?;
+        let text = Self::validate_text(text);
+        membership.about = text;
+        Self::deposit_event(RawEvent::MemberUpdatedAboutText(id));
+        <MembershipById<T>>::insert(id, membership);
+        Ok(())
+    }
+
+    fn _change_member_avatar(id: T::MemberId, uri: &[u8]) -> dispatch::Result {
+        let mut membership = Self::ensure_membership(id)?;
+        Self::validate_avatar(uri)?;
+        membership.avatar_uri = uri.to_owned();
+        Self::deposit_event(RawEvent::MemberUpdatedAvatar(id));
+        <MembershipById<T>>::insert(id, membership);
+        Ok(())
+    }
+
+    fn _change_member_handle(id: T::MemberId, handle: Vec<u8>) -> dispatch::Result {
+        let mut membership = Self::ensure_membership(id)?;
+        Self::validate_handle(&handle)?;
+        Self::ensure_unique_handle(&handle)?;
+        <MemberIdByHandle<T>>::remove(&membership.handle);
+        <MemberIdByHandle<T>>::insert(handle.clone(), id);
+        membership.handle = handle;
+        Self::deposit_event(RawEvent::MemberUpdatedHandle(id));
+        <MembershipById<T>>::insert(id, membership);
+        Ok(())
+    }
+
+    pub fn ensure_member_controller_account_signed(
+        origin: T::Origin,
+        member_id: &T::MemberId,
+    ) -> Result<T::AccountId, MemberControllerAccountDidNotSign> {
+        // Ensure transaction is signed.
+        let signer_account =
+            ensure_signed(origin).map_err(|_| MemberControllerAccountDidNotSign::UnsignedOrigin)?;
+
+        // Ensure member exists
+        let membership = Self::ensure_membership(*member_id)
+            .map_err(|_| MemberControllerAccountDidNotSign::MemberIdInvalid)?;
+
+        ensure!(
+            membership.controller_account == signer_account,
+            MemberControllerAccountDidNotSign::SignerControllerAccountMismatch
+        );
+
+        Ok(signer_account)
+    }
+
+    pub fn ensure_member_controller_account(
+        signer_account: &T::AccountId,
+        member_id: &T::MemberId,
+    ) -> Result<(), MemberControllerAccountMismatch> {
+        // Ensure member exists
+        let membership = Self::ensure_membership(*member_id)
+            .map_err(|_| MemberControllerAccountMismatch::MemberIdInvalid)?;
+
+        ensure!(
+            membership.controller_account == *signer_account,
+            MemberControllerAccountMismatch::SignerControllerAccountMismatch
+        );
+
+        Ok(())
+    }
+
+    pub fn ensure_member_root_account(
+        signer_account: &T::AccountId,
+        member_id: &T::MemberId,
+    ) -> Result<(), MemberRootAccountMismatch> {
+        // Ensure member exists
+        let membership = Self::ensure_membership(*member_id)
+            .map_err(|_| MemberRootAccountMismatch::MemberIdInvalid)?;
+
+        ensure!(
+            membership.root_account == *signer_account,
+            MemberRootAccountMismatch::SignerRootAccountMismatch
+        );
+
+        Ok(())
+    }
+}

+ 0 - 725
runtime-modules/membership/src/members.rs

@@ -1,725 +0,0 @@
-// Clippy linter requirement
-#![allow(clippy::redundant_closure_call)] // disable it because of the substrate lib design
-                                          // example:  pub PaidMembershipTermsById get(paid_membership_terms_by_id) build(|config: &GenesisConfig<T>| {}
-
-use codec::{Codec, Decode, Encode};
-use common::currency::{BalanceOf, GovernanceCurrency};
-
-use rstd::borrow::ToOwned;
-use rstd::prelude::*;
-use sr_primitives::traits::{MaybeSerialize, Member, One, SimpleArithmetic};
-use srml_support::traits::{Currency, Get};
-use srml_support::{decl_event, decl_module, decl_storage, dispatch, ensure, Parameter};
-
-use system::{self, ensure_root, ensure_signed};
-
-pub use super::role_types::*;
-
-pub trait Trait: system::Trait + GovernanceCurrency + timestamp::Trait {
-    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
-
-    type MemberId: Parameter
-        + Member
-        + SimpleArithmetic
-        + Codec
-        + Default
-        + Copy
-        + MaybeSerialize
-        + PartialEq;
-
-    type PaidTermId: Parameter
-        + Member
-        + SimpleArithmetic
-        + Codec
-        + Default
-        + Copy
-        + MaybeSerialize
-        + PartialEq;
-
-    type SubscriptionId: Parameter
-        + Member
-        + SimpleArithmetic
-        + Codec
-        + Default
-        + Copy
-        + MaybeSerialize
-        + PartialEq;
-
-    type ActorId: Parameter
-        + Member
-        + SimpleArithmetic
-        + Codec
-        + Default
-        + Copy
-        + MaybeSerialize
-        + PartialEq
-        + Ord;
-
-    /// Initial balance of members created at genesis
-    type InitialMembersBalance: Get<BalanceOf<Self>>;
-}
-
-const FIRST_PAID_TERMS_ID: u32 = 1;
-
-// Default paid membership terms
-pub const DEFAULT_PAID_TERM_ID: u32 = 0;
-
-// Default user info constraints
-const DEFAULT_MIN_HANDLE_LENGTH: u32 = 5;
-const DEFAULT_MAX_HANDLE_LENGTH: u32 = 40;
-const DEFAULT_MAX_AVATAR_URI_LENGTH: u32 = 1024;
-const DEFAULT_MAX_ABOUT_TEXT_LENGTH: u32 = 2048;
-
-//#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
-#[derive(Encode, Decode)]
-/// Stored information about a registered user
-pub struct Profile<T: Trait> {
-    /// The unique handle chosen by member
-    pub handle: Vec<u8>,
-
-    /// A Url to member's Avatar image
-    pub avatar_uri: Vec<u8>,
-
-    /// Short text chosen by member to share information about themselves
-    pub about: Vec<u8>,
-
-    /// Blocknumber when member was registered
-    pub registered_at_block: T::BlockNumber,
-
-    /// Timestamp when member was registered
-    pub registered_at_time: T::Moment,
-
-    /// How the member was registered
-    pub entry: EntryMethod<T>,
-
-    /// Wether the member is suspended or not.
-    pub suspended: bool,
-
-    /// The type of subsction the member has purchased if any.
-    pub subscription: Option<T::SubscriptionId>,
-
-    /// Member's root account id. Only the root account is permitted to set a new root account
-    /// and update the controller account. Other modules may only allow certain actions if
-    /// signed with root account. It is intended to be an account that can remain offline and
-    /// potentially hold a member's funds, and be a source for staking roles.
-    pub root_account: T::AccountId,
-
-    /// Member's controller account id. This account is intended to be used by
-    /// a member to act under their identity in other modules. It will usually be used more
-    /// online and will have less funds in its balance.
-    pub controller_account: T::AccountId,
-
-    /// The set of registered roles the member has enrolled in.
-    pub roles: ActorInRoleSet<T::ActorId>,
-}
-
-#[derive(Clone, Debug, Encode, Decode, PartialEq)]
-/// A "Partial" struct used to batch user configurable profile information when registering or updating info
-pub struct UserInfo {
-    pub handle: Option<Vec<u8>>,
-    pub avatar_uri: Option<Vec<u8>>,
-    pub about: Option<Vec<u8>>,
-}
-
-struct CheckedUserInfo {
-    handle: Vec<u8>,
-    avatar_uri: Vec<u8>,
-    about: Vec<u8>,
-}
-
-//#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
-#[derive(Encode, Decode, Debug, PartialEq)]
-pub enum EntryMethod<T: Trait> {
-    Paid(T::PaidTermId),
-    Screening(T::AccountId),
-    Genesis,
-}
-
-//#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
-#[derive(Encode, Decode, Eq, PartialEq)]
-pub struct PaidMembershipTerms<T: Trait> {
-    /// Quantity of native tokens which must be provably burned
-    pub fee: BalanceOf<T>,
-    /// String of capped length describing human readable conditions which are being agreed upon
-    pub text: Vec<u8>,
-}
-
-decl_storage! {
-    trait Store for Module<T: Trait> as Membership {
-        /// MemberId to assign to next member that is added to the registry, and is also the
-        /// total number of members created. MemberIds start at Zero.
-        pub MembersCreated get(members_created) : T::MemberId;
-
-        /// Mapping of member's id to their membership profile
-        pub MemberProfile get(member_profile) : map T::MemberId => Option<Profile<T>>;
-
-        /// Mapping of a root account id to vector of member ids it controls
-        pub MemberIdsByRootAccountId get(member_ids_by_root_account_id) : map T::AccountId => Vec<T::MemberId>;
-
-        /// Mapping of a controller account id to vector of member ids it controls
-        pub MemberIdsByControllerAccountId get(member_ids_by_controller_account_id) : map T::AccountId => Vec<T::MemberId>;
-
-        /// Registered unique handles and their mapping to their owner
-        pub Handles get(handles) : map Vec<u8> => T::MemberId;
-
-        /// Next paid membership terms id
-        pub NextPaidMembershipTermsId get(next_paid_membership_terms_id) : T::PaidTermId = T::PaidTermId::from(FIRST_PAID_TERMS_ID);
-
-        /// Paid membership terms record
-        // Remember to add _genesis_phantom_data: std::marker::PhantomData{} to membership
-        // genesis config if not providing config() or extra_genesis
-        pub PaidMembershipTermsById get(paid_membership_terms_by_id) build(|config: &GenesisConfig<T>| {
-            // This method only gets called when initializing storage, and is
-            // compiled as native code. (Will be called when building `raw` chainspec)
-            // So it can't be relied upon to initialize storage for runtimes updates.
-            // Initialization for updated runtime is done in run_migration()
-            let terms = PaidMembershipTerms {
-                fee:  config.default_paid_membership_fee,
-                text: Vec::default(),
-            };
-            vec![(T::PaidTermId::from(DEFAULT_PAID_TERM_ID), terms)]
-        }) : map T::PaidTermId => Option<PaidMembershipTerms<T>>;
-
-        /// Active Paid membership terms
-        pub ActivePaidMembershipTerms get(active_paid_membership_terms) : Vec<T::PaidTermId> = vec![T::PaidTermId::from(DEFAULT_PAID_TERM_ID)];
-
-        /// Is the platform is accepting new members or not
-        pub NewMembershipsAllowed get(new_memberships_allowed) : bool = true;
-
-        pub ScreeningAuthority get(screening_authority) : Option<T::AccountId>;
-
-        // User Input Validation parameters - do these really need to be state variables
-        // I don't see a need to adjust these in future?
-        pub MinHandleLength get(min_handle_length) : u32 = DEFAULT_MIN_HANDLE_LENGTH;
-        pub MaxHandleLength get(max_handle_length) : u32 = DEFAULT_MAX_HANDLE_LENGTH;
-        pub MaxAvatarUriLength get(max_avatar_uri_length) : u32 = DEFAULT_MAX_AVATAR_URI_LENGTH;
-        pub MaxAboutTextLength get(max_about_text_length) : u32 = DEFAULT_MAX_ABOUT_TEXT_LENGTH;
-
-        pub MembershipIdByActorInRole get(membership_id_by_actor_in_role): map ActorInRole<T::ActorId> => T::MemberId;
-    }
-    add_extra_genesis {
-        config(default_paid_membership_fee): BalanceOf<T>;
-        config(members) : Vec<(T::AccountId, String, String, String)>;
-        build(|config: &GenesisConfig<T>| {
-            for (who, handle, avatar_uri, about) in &config.members {
-                let user_info = CheckedUserInfo {
-                    handle: handle.clone().into_bytes(), avatar_uri: avatar_uri.clone().into_bytes(), about: about.clone().into_bytes()
-                };
-
-                <Module<T>>::insert_member(&who, &user_info, EntryMethod::Genesis);
-
-                // Give member starting balance
-                T::Currency::deposit_creating(&who, T::InitialMembersBalance::get());
-            }
-        });
-    }
-}
-
-decl_event! {
-    pub enum Event<T> where
-      <T as system::Trait>::AccountId,
-      <T as Trait>::MemberId,
-      <T as Trait>::ActorId, {
-        MemberRegistered(MemberId, AccountId),
-        MemberUpdatedAboutText(MemberId),
-        MemberUpdatedAvatar(MemberId),
-        MemberUpdatedHandle(MemberId),
-        MemberSetRootAccount(MemberId, AccountId),
-        MemberSetControllerAccount(MemberId, AccountId),
-        MemberRegisteredRole(MemberId, ActorInRole<ActorId>),
-        MemberUnregisteredRole(MemberId, ActorInRole<ActorId>),
-    }
-}
-
-decl_module! {
-    pub struct Module<T: Trait> for enum Call where origin: T::Origin {
-        fn deposit_event() = default;
-
-        /// Non-members can buy membership
-        pub fn buy_membership(origin, paid_terms_id: T::PaidTermId, user_info: UserInfo) {
-            let who = ensure_signed(origin)?;
-
-            // make sure we are accepting new memberships
-            ensure!(Self::new_memberships_allowed(), "new members not allowed");
-
-            // ensure paid_terms_id is active
-            let terms = Self::ensure_active_terms_id(paid_terms_id)?;
-
-            // ensure enough free balance to cover terms fees
-            ensure!(T::Currency::can_slash(&who, terms.fee), "not enough balance to buy membership");
-
-            let user_info = Self::check_user_registration_info(user_info)?;
-
-            // ensure handle is not already registered
-            Self::ensure_unique_handle(&user_info.handle)?;
-
-            let _ = T::Currency::slash(&who, terms.fee);
-            let member_id = Self::insert_member(&who, &user_info, EntryMethod::Paid(paid_terms_id));
-
-            Self::deposit_event(RawEvent::MemberRegistered(member_id, who));
-        }
-
-        /// Change member's about text
-        pub fn change_member_about_text(origin, member_id: T::MemberId, text: Vec<u8>) {
-            let sender = ensure_signed(origin)?;
-
-            let profile = Self::ensure_profile(member_id)?;
-
-            ensure!(profile.controller_account == sender, "only controller account can update member about text");
-
-            Self::_change_member_about_text(member_id, &text)?;
-        }
-
-        /// Change member's avatar
-        pub fn change_member_avatar(origin, member_id: T::MemberId, uri: Vec<u8>) {
-            let sender = ensure_signed(origin)?;
-
-            let profile = Self::ensure_profile(member_id)?;
-
-            ensure!(profile.controller_account == sender, "only controller account can update member avatar");
-
-            Self::_change_member_avatar(member_id, &uri)?;
-        }
-
-        /// Change member's handle. Will ensure new handle is unique and old one will be available
-        /// for other members to use.
-        pub fn change_member_handle(origin, member_id: T::MemberId, handle: Vec<u8>) {
-            let sender = ensure_signed(origin)?;
-
-            let profile = Self::ensure_profile(member_id)?;
-
-            ensure!(profile.controller_account == sender, "only controller account can update member handle");
-
-            Self::_change_member_handle(member_id, handle)?;
-        }
-
-        /// Update member's all or some of handle, avatar and about text.
-        pub fn update_profile(origin, member_id: T::MemberId, user_info: UserInfo) {
-            let sender = ensure_signed(origin)?;
-
-            let profile = Self::ensure_profile(member_id)?;
-
-            ensure!(profile.controller_account == sender, "only controller account can update member info");
-
-            if let Some(uri) = user_info.avatar_uri {
-                Self::_change_member_avatar(member_id, &uri)?;
-            }
-            if let Some(about) = user_info.about {
-                Self::_change_member_about_text(member_id, &about)?;
-            }
-            if let Some(handle) = user_info.handle {
-                Self::_change_member_handle(member_id, handle)?;
-            }
-        }
-
-        pub fn set_controller_account(origin, member_id: T::MemberId, new_controller_account: T::AccountId) {
-            let sender = ensure_signed(origin)?;
-
-            let mut profile = Self::ensure_profile(member_id)?;
-
-            ensure!(profile.root_account == sender, "only root account can set new controller account");
-
-            // only update if new_controller_account is different than current one
-            if profile.controller_account != new_controller_account {
-                <MemberIdsByControllerAccountId<T>>::mutate(&profile.controller_account, |ids| {
-                    ids.retain(|id| *id != member_id);
-                });
-
-                <MemberIdsByControllerAccountId<T>>::mutate(&new_controller_account, |ids| {
-                    ids.push(member_id);
-                });
-
-                profile.controller_account = new_controller_account.clone();
-                <MemberProfile<T>>::insert(member_id, profile);
-                Self::deposit_event(RawEvent::MemberSetControllerAccount(member_id, new_controller_account));
-            }
-        }
-
-        pub fn set_root_account(origin, member_id: T::MemberId, new_root_account: T::AccountId) {
-            let sender = ensure_signed(origin)?;
-
-            let mut profile = Self::ensure_profile(member_id)?;
-
-            ensure!(profile.root_account == sender, "only root account can set new root account");
-
-            // only update if new root account is different than current one
-            if profile.root_account != new_root_account {
-                <MemberIdsByRootAccountId<T>>::mutate(&profile.root_account, |ids| {
-                    ids.retain(|id| *id != member_id);
-                });
-
-                <MemberIdsByRootAccountId<T>>::mutate(&new_root_account, |ids| {
-                    ids.push(member_id);
-                });
-
-                profile.root_account = new_root_account.clone();
-                Self::deposit_event(RawEvent::MemberSetRootAccount(member_id, new_root_account));
-            }
-        }
-
-        pub fn add_screened_member(origin, new_member_account: T::AccountId, user_info: UserInfo) {
-            // ensure sender is screening authority
-            let sender = ensure_signed(origin)?;
-
-            if let Some(screening_authority) = Self::screening_authority() {
-                ensure!(sender == screening_authority, "not screener");
-            } else {
-                // no screening authority defined. Cannot accept this request
-                return Err("no screening authority defined");
-            }
-
-            // make sure we are accepting new memberships
-            ensure!(Self::new_memberships_allowed(), "new members not allowed");
-
-            let user_info = Self::check_user_registration_info(user_info)?;
-
-            // ensure handle is not already registered
-            Self::ensure_unique_handle(&user_info.handle)?;
-
-            let member_id = Self::insert_member(&new_member_account, &user_info, EntryMethod::Screening(sender));
-
-            Self::deposit_event(RawEvent::MemberRegistered(member_id, new_member_account));
-        }
-
-        pub fn set_screening_authority(origin, authority: T::AccountId) {
-            ensure_root(origin)?;
-            <ScreeningAuthority<T>>::put(authority);
-        }
-    }
-}
-
-/// Reason why a given member id does not have a given account as the controller account.
-pub enum ControllerAccountForMemberCheckFailed {
-    NotMember,
-    NotControllerAccount,
-}
-
-pub enum MemberControllerAccountDidNotSign {
-    UnsignedOrigin,
-    MemberIdInvalid,
-    SignerControllerAccountMismatch,
-}
-
-pub enum MemberControllerAccountMismatch {
-    MemberIdInvalid,
-    SignerControllerAccountMismatch,
-}
-pub enum MemberRootAccountMismatch {
-    MemberIdInvalid,
-    SignerRootAccountMismatch,
-}
-
-impl<T: Trait> Module<T> {
-    /// Provided that the memberid exists return its profile. Returns error otherwise.
-    pub fn ensure_profile(id: T::MemberId) -> Result<Profile<T>, &'static str> {
-        Self::member_profile(&id).ok_or("member profile not found")
-    }
-
-    /// Ensure that given member has given account as the controller account
-    pub fn ensure_is_controller_account_for_member(
-        member_id: &T::MemberId,
-        account: &T::AccountId,
-    ) -> Result<Profile<T>, ControllerAccountForMemberCheckFailed> {
-        if MemberProfile::<T>::exists(member_id) {
-            let profile = MemberProfile::<T>::get(member_id).unwrap();
-
-            if profile.controller_account == *account {
-                Ok(profile)
-            } else {
-                Err(ControllerAccountForMemberCheckFailed::NotControllerAccount)
-            }
-        } else {
-            Err(ControllerAccountForMemberCheckFailed::NotMember)
-        }
-    }
-
-    /// Returns true if account is either a member's root or controller account
-    pub fn is_member_account(who: &T::AccountId) -> bool {
-        <MemberIdsByRootAccountId<T>>::exists(who)
-            || <MemberIdsByControllerAccountId<T>>::exists(who)
-    }
-
-    fn ensure_active_terms_id(
-        terms_id: T::PaidTermId,
-    ) -> Result<PaidMembershipTerms<T>, &'static str> {
-        let active_terms = Self::active_paid_membership_terms();
-        ensure!(
-            active_terms.iter().any(|&id| id == terms_id),
-            "paid terms id not active"
-        );
-        let terms = Self::paid_membership_terms_by_id(terms_id)
-            .ok_or("paid membership term id does not exist")?;
-        Ok(terms)
-    }
-
-    #[allow(clippy::ptr_arg)] // cannot change to the "&[u8]" suggested by clippy
-    fn ensure_unique_handle(handle: &Vec<u8>) -> dispatch::Result {
-        ensure!(!<Handles<T>>::exists(handle), "handle already registered");
-        Ok(())
-    }
-
-    fn validate_handle(handle: &[u8]) -> dispatch::Result {
-        ensure!(
-            handle.len() >= Self::min_handle_length() as usize,
-            "handle too short"
-        );
-        ensure!(
-            handle.len() <= Self::max_handle_length() as usize,
-            "handle too long"
-        );
-        Ok(())
-    }
-
-    fn validate_text(text: &[u8]) -> Vec<u8> {
-        let mut text = text.to_owned();
-        text.truncate(Self::max_about_text_length() as usize);
-        text
-    }
-
-    fn validate_avatar(uri: &[u8]) -> dispatch::Result {
-        ensure!(
-            uri.len() <= Self::max_avatar_uri_length() as usize,
-            "avatar uri too long"
-        );
-        Ok(())
-    }
-
-    /// Basic user input validation
-    fn check_user_registration_info(user_info: UserInfo) -> Result<CheckedUserInfo, &'static str> {
-        // Handle is required during registration
-        let handle = user_info
-            .handle
-            .ok_or("handle must be provided during registration")?;
-        Self::validate_handle(&handle)?;
-
-        let about = Self::validate_text(&user_info.about.unwrap_or_default());
-        let avatar_uri = user_info.avatar_uri.unwrap_or_default();
-        Self::validate_avatar(&avatar_uri)?;
-
-        Ok(CheckedUserInfo {
-            handle,
-            avatar_uri,
-            about,
-        })
-    }
-
-    fn insert_member(
-        who: &T::AccountId,
-        user_info: &CheckedUserInfo,
-        entry_method: EntryMethod<T>,
-    ) -> T::MemberId {
-        let new_member_id = Self::members_created();
-
-        let profile: Profile<T> = Profile {
-            handle: user_info.handle.clone(),
-            avatar_uri: user_info.avatar_uri.clone(),
-            about: user_info.about.clone(),
-            registered_at_block: <system::Module<T>>::block_number(),
-            registered_at_time: <timestamp::Module<T>>::now(),
-            entry: entry_method,
-            suspended: false,
-            subscription: None,
-            root_account: who.clone(),
-            controller_account: who.clone(),
-            roles: ActorInRoleSet::new(),
-        };
-
-        <MemberIdsByRootAccountId<T>>::mutate(who, |ids| {
-            ids.push(new_member_id);
-        });
-        <MemberIdsByControllerAccountId<T>>::mutate(who, |ids| {
-            ids.push(new_member_id);
-        });
-
-        <MemberProfile<T>>::insert(new_member_id, profile);
-        <Handles<T>>::insert(user_info.handle.clone(), new_member_id);
-        <MembersCreated<T>>::put(new_member_id + One::one());
-
-        new_member_id
-    }
-
-    fn _change_member_about_text(id: T::MemberId, text: &[u8]) -> dispatch::Result {
-        let mut profile = Self::ensure_profile(id)?;
-        let text = Self::validate_text(text);
-        profile.about = text;
-        Self::deposit_event(RawEvent::MemberUpdatedAboutText(id));
-        <MemberProfile<T>>::insert(id, profile);
-        Ok(())
-    }
-
-    fn _change_member_avatar(id: T::MemberId, uri: &[u8]) -> dispatch::Result {
-        let mut profile = Self::ensure_profile(id)?;
-        Self::validate_avatar(uri)?;
-        profile.avatar_uri = uri.to_owned();
-        Self::deposit_event(RawEvent::MemberUpdatedAvatar(id));
-        <MemberProfile<T>>::insert(id, profile);
-        Ok(())
-    }
-
-    fn _change_member_handle(id: T::MemberId, handle: Vec<u8>) -> dispatch::Result {
-        let mut profile = Self::ensure_profile(id)?;
-        Self::validate_handle(&handle)?;
-        Self::ensure_unique_handle(&handle)?;
-        <Handles<T>>::remove(&profile.handle);
-        <Handles<T>>::insert(handle.clone(), id);
-        profile.handle = handle;
-        Self::deposit_event(RawEvent::MemberUpdatedHandle(id));
-        <MemberProfile<T>>::insert(id, profile);
-        Ok(())
-    }
-
-    /// Determines if the signing account is a controller account of a member that has the registered
-    /// actor_in_role.
-    pub fn key_can_sign_for_role(
-        signing_account: &T::AccountId,
-        actor_in_role: ActorInRole<T::ActorId>,
-    ) -> bool {
-        Self::member_ids_by_controller_account_id(signing_account)
-            .iter()
-            .any(|member_id| {
-                let profile = Self::member_profile(member_id).unwrap(); // must exist
-                profile.roles.has_registered_role(&actor_in_role)
-            })
-    }
-
-    /// Returns true if member identified by their member id occupies a Role at least once
-    pub fn member_is_in_role(member_id: T::MemberId, role: Role) -> bool {
-        Self::ensure_profile(member_id)
-            .ok()
-            .map_or(false, |profile| profile.roles.occupies_role(role))
-    }
-
-    pub fn ensure_member_controller_account_signed(
-        origin: T::Origin,
-        member_id: &T::MemberId,
-    ) -> Result<T::AccountId, MemberControllerAccountDidNotSign> {
-        // Ensure transaction is signed.
-        let signer_account =
-            ensure_signed(origin).map_err(|_| MemberControllerAccountDidNotSign::UnsignedOrigin)?;
-
-        // Ensure member exists
-        let profile = Self::ensure_profile(*member_id)
-            .map_err(|_| MemberControllerAccountDidNotSign::MemberIdInvalid)?;
-
-        ensure!(
-            profile.controller_account == signer_account,
-            MemberControllerAccountDidNotSign::SignerControllerAccountMismatch
-        );
-
-        Ok(signer_account)
-    }
-
-    pub fn ensure_member_controller_account(
-        signer_account: &T::AccountId,
-        member_id: &T::MemberId,
-    ) -> Result<(), MemberControllerAccountMismatch> {
-        // Ensure member exists
-        let profile = Self::ensure_profile(*member_id)
-            .map_err(|_| MemberControllerAccountMismatch::MemberIdInvalid)?;
-
-        ensure!(
-            profile.controller_account == *signer_account,
-            MemberControllerAccountMismatch::SignerControllerAccountMismatch
-        );
-
-        Ok(())
-    }
-
-    pub fn ensure_member_root_account(
-        signer_account: &T::AccountId,
-        member_id: &T::MemberId,
-    ) -> Result<(), MemberRootAccountMismatch> {
-        // Ensure member exists
-        let profile = Self::ensure_profile(*member_id)
-            .map_err(|_| MemberRootAccountMismatch::MemberIdInvalid)?;
-
-        ensure!(
-            profile.root_account == *signer_account,
-            MemberRootAccountMismatch::SignerRootAccountMismatch
-        );
-
-        Ok(())
-    }
-
-    // policy across all roles is:
-    // members can only occupy a role at most once at a time
-    // members can enter any role
-    // no limit on total number of roles a member can enter
-    // ** Note ** Role specific policies should be enforced by the client modules
-    // this method should handle higher level policies
-    pub fn can_register_role_on_member(
-        member_id: &T::MemberId,
-        actor_in_role: &ActorInRole<T::ActorId>,
-    ) -> Result<Profile<T>, &'static str> {
-        // Ensure member exists
-        let profile = Self::ensure_profile(*member_id)?;
-
-        // ensure is active member
-        ensure!(!profile.suspended, "SuspendedMemberCannotEnterRole");
-
-        // guard against duplicate ActorInRole
-        ensure!(
-            !<MembershipIdByActorInRole<T>>::exists(actor_in_role),
-            "ActorInRoleAlreadyExists"
-        );
-
-        /*
-        Disabling this temporarily for Rome, later we will drop all this
-        integration with Membership module anyway:
-        https://github.com/Joystream/substrate-runtime-joystream/issues/115
-        ensure!(
-            !profile.roles.occupies_role(actor_in_role.role),
-            "MemberAlreadyInRole"
-        );
-        */
-
-        // Other possible checks:
-        // How long the member has been registered
-        // Minimum balance
-        // EntryMethod
-
-        Ok(profile)
-    }
-
-    pub fn register_role_on_member(
-        member_id: T::MemberId,
-        actor_in_role: &ActorInRole<T::ActorId>,
-    ) -> Result<(), &'static str> {
-        // Policy check
-        let mut profile = Self::can_register_role_on_member(&member_id, actor_in_role)?;
-
-        assert!(profile.roles.register_role(actor_in_role));
-
-        <MemberProfile<T>>::insert(member_id, profile);
-        <MembershipIdByActorInRole<T>>::insert(actor_in_role, member_id);
-        Self::deposit_event(RawEvent::MemberRegisteredRole(member_id, *actor_in_role));
-        Ok(())
-    }
-
-    pub fn can_unregister_role(actor_in_role: ActorInRole<T::ActorId>) -> Result<(), &'static str> {
-        ensure!(
-            <MembershipIdByActorInRole<T>>::exists(&actor_in_role),
-            "ActorInRoleNotFound"
-        );
-
-        Ok(())
-    }
-
-    pub fn unregister_role(actor_in_role: ActorInRole<T::ActorId>) -> Result<(), &'static str> {
-        Self::can_unregister_role(actor_in_role)?;
-
-        let member_id = <MembershipIdByActorInRole<T>>::get(actor_in_role);
-
-        let mut profile = Self::ensure_profile(member_id)?; // .expect().. ?
-
-        assert!(profile.roles.unregister_role(&actor_in_role));
-
-        <MembershipIdByActorInRole<T>>::remove(actor_in_role);
-
-        <MemberProfile<T>>::insert(member_id, profile);
-
-        Self::deposit_event(RawEvent::MemberUnregisteredRole(member_id, actor_in_role));
-
-        Ok(())
-    }
-}

+ 6 - 8
runtime-modules/membership/src/mock.rs

@@ -1,6 +1,6 @@
 #![cfg(test)]
 
-pub use super::members::{self, Trait, DEFAULT_PAID_TERM_ID};
+pub use crate::{GenesisConfig, Trait, DEFAULT_PAID_TERM_ID};
 pub use common::currency::GovernanceCurrency;
 pub use srml_support::traits::Currency;
 pub use system;
@@ -60,7 +60,6 @@ parameter_types! {
     pub const CreationFee: u32 = 0;
     pub const TransactionBaseFee: u32 = 1;
     pub const TransactionByteFee: u32 = 0;
-    pub const InitialMembersBalance: u64 = 2000;
 }
 
 impl balances::Trait for Test {
@@ -84,18 +83,17 @@ impl GovernanceCurrency for Test {
     type Currency = balances::Module<Self>;
 }
 
-impl members::Trait for Test {
+impl Trait for Test {
     type Event = ();
     type MemberId = u32;
     type PaidTermId = u32;
     type SubscriptionId = u32;
     type ActorId = u32;
-    type InitialMembersBalance = InitialMembersBalance;
 }
 
 pub struct TestExternalitiesBuilder<T: Trait> {
     system_config: Option<system::GenesisConfig>,
-    membership_config: Option<members::GenesisConfig<T>>,
+    membership_config: Option<GenesisConfig<T>>,
 }
 
 impl<T: Trait> Default for TestExternalitiesBuilder<T> {
@@ -114,7 +112,7 @@ impl<T: Trait> TestExternalitiesBuilder<T> {
         self
     }
     */
-    pub fn set_membership_config(mut self, membership_config: members::GenesisConfig<T>) -> Self {
+    pub fn set_membership_config(mut self, membership_config: GenesisConfig<T>) -> Self {
         self.membership_config = Some(membership_config);
         self
     }
@@ -128,7 +126,7 @@ impl<T: Trait> TestExternalitiesBuilder<T> {
 
         // Add membership
         self.membership_config
-            .unwrap_or(members::GenesisConfig::default())
+            .unwrap_or(GenesisConfig::default())
             .assimilate_storage(&mut t)
             .unwrap();
 
@@ -137,4 +135,4 @@ impl<T: Trait> TestExternalitiesBuilder<T> {
 }
 
 pub type Balances = balances::Module<Test>;
-pub type Members = members::Module<Test>;
+pub type Members = crate::Module<Test>;

+ 0 - 64
runtime-modules/membership/src/role_types.rs

@@ -1,64 +0,0 @@
-#![allow(clippy::new_without_default)] // disable because Default for enums doesn't make sense
-
-use codec::{Decode, Encode};
-use rstd::collections::btree_set::BTreeSet;
-use rstd::prelude::*;
-
-#[cfg(feature = "std")]
-use serde::{Deserialize, Serialize};
-
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
-pub enum Role {
-    StorageProvider,
-    ChannelOwner,
-    CuratorLead,
-    Curator,
-}
-
-#[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
-pub struct ActorInRole<ActorId> {
-    pub role: Role,
-    pub actor_id: ActorId,
-}
-
-impl<ActorId> ActorInRole<ActorId> {
-    pub fn new(role: Role, actor_id: ActorId) -> Self {
-        ActorInRole { role, actor_id }
-    }
-}
-
-#[derive(Encode, Decode, Eq, PartialEq, Ord, PartialOrd)]
-pub struct ActorInRoleSet<ActorId: Ord + Copy>(BTreeSet<ActorInRole<ActorId>>);
-
-impl<ActorId: Ord + Copy> ActorInRoleSet<ActorId> {
-    pub fn new() -> Self {
-        Self(BTreeSet::<ActorInRole<ActorId>>::new())
-    }
-
-    fn role_instance_count(&self, role: Role) -> usize {
-        self.0.iter().fold(0, |count, actor_in_role| {
-            if actor_in_role.role == role {
-                count + 1
-            } else {
-                count
-            }
-        })
-    }
-
-    pub fn occupies_role(&self, role: Role) -> bool {
-        self.role_instance_count(role) > 0
-    }
-
-    pub fn register_role(&mut self, actor_in_role: &ActorInRole<ActorId>) -> bool {
-        self.0.insert(*actor_in_role)
-    }
-
-    pub fn unregister_role(&mut self, actor_in_role: &ActorInRole<ActorId>) -> bool {
-        self.0.remove(actor_in_role)
-    }
-
-    pub fn has_registered_role(&self, actor_in_role: &ActorInRole<ActorId>) -> bool {
-        self.0.contains(actor_in_role)
-    }
-}

+ 55 - 135
runtime-modules/membership/src/tests.rs

@@ -5,16 +5,11 @@ use super::mock::*;
 
 use srml_support::*;
 
-fn assert_ok_unwrap<T>(value: Option<T>, err: &'static str) -> T {
-    match value {
-        None => {
-            assert!(false, err);
-            // although this code path is not reached, we need to return correct type
-            // in match arm. Using panic! would remove this need, but assert! gives us better error in
-            // console output
-            value.unwrap()
-        }
-        Some(v) => v,
+fn get_membership_by_id(member_id: u32) -> crate::Membership<Test> {
+    if <crate::MembershipById<Test>>::exists(member_id) {
+        Members::membership(member_id)
+    } else {
+        panic!("member profile not created");
     }
 }
 
@@ -24,8 +19,15 @@ fn assert_dispatch_error_message(result: Result<(), &'static str>, expected_mess
     assert_eq!(message, expected_message);
 }
 
-fn get_alice_info() -> members::UserInfo {
-    members::UserInfo {
+#[derive(Clone, Debug, PartialEq)]
+pub struct TestUserInfo {
+    pub handle: Option<Vec<u8>>,
+    pub avatar_uri: Option<Vec<u8>>,
+    pub about: Option<Vec<u8>>,
+}
+
+fn get_alice_info() -> TestUserInfo {
+    TestUserInfo {
         handle: Some(String::from("alice").as_bytes().to_vec()),
         avatar_uri: Some(
             String::from("http://avatar-url.com/alice")
@@ -36,8 +38,8 @@ fn get_alice_info() -> members::UserInfo {
     }
 }
 
-fn get_bob_info() -> members::UserInfo {
-    members::UserInfo {
+fn get_bob_info() -> TestUserInfo {
+    TestUserInfo {
         handle: Some(String::from("bobby").as_bytes().to_vec()),
         avatar_uri: Some(
             String::from("http://avatar-url.com/bob")
@@ -51,10 +53,13 @@ fn get_bob_info() -> members::UserInfo {
 const ALICE_ACCOUNT_ID: u64 = 1;
 
 fn buy_default_membership_as_alice() -> dispatch::Result {
+    let info = get_alice_info();
     Members::buy_membership(
         Origin::signed(ALICE_ACCOUNT_ID),
         DEFAULT_PAID_TERM_ID,
-        get_alice_info(),
+        info.handle,
+        info.avatar_uri,
+        info.about,
     )
 }
 
@@ -76,18 +81,14 @@ fn initial_state() {
         )
         .build()
         .execute_with(|| {
-            let default_terms = assert_ok_unwrap(
-                Members::paid_membership_terms_by_id(DEFAULT_PAID_TERM_ID),
-                "default terms not initialized",
-            );
+            let default_terms =
+                if <crate::PaidMembershipTermsById<Test>>::exists(DEFAULT_PAID_TERM_ID) {
+                    Members::paid_membership_terms_by_id(DEFAULT_PAID_TERM_ID)
+                } else {
+                    panic!("default terms not initialized");
+                };
 
             assert_eq!(default_terms.fee, DEFAULT_FEE);
-
-            // initial balance
-            assert_eq!(
-                Balances::free_balance(initial_members[0]),
-                <Test as members::Trait>::InitialMembersBalance::get()
-            );
         });
 }
 
@@ -111,13 +112,10 @@ fn buy_membership() {
 
             assert_ok!(buy_default_membership_as_alice());
 
-            let member_ids = Members::member_ids_by_root_account_id(&ALICE_ACCOUNT_ID);
+            let member_ids = vec![0];
             assert_eq!(member_ids, vec![next_member_id]);
 
-            let profile = assert_ok_unwrap(
-                Members::member_profile(&next_member_id),
-                "member profile not created",
-            );
+            let profile = get_membership_by_id(next_member_id);
 
             assert_eq!(Some(profile.handle), get_alice_info().handle);
             assert_eq!(Some(profile.avatar_uri), get_alice_info().avatar_uri);
@@ -128,7 +126,7 @@ fn buy_membership() {
             // controller account initially set to primary account
             assert_eq!(profile.controller_account, ALICE_ACCOUNT_ID);
             assert_eq!(
-                Members::member_ids_by_controller_account_id(ALICE_ACCOUNT_ID),
+                <crate::MemberIdsByControllerAccountId<Test>>::get(ALICE_ACCOUNT_ID),
                 vec![next_member_id]
             );
         });
@@ -171,7 +169,7 @@ fn new_memberships_allowed_flag() {
             let initial_balance = DEFAULT_FEE + 1;
             set_alice_free_balance(initial_balance);
 
-            members::NewMembershipsAllowed::put(false);
+            crate::NewMembershipsAllowed::put(false);
 
             assert_dispatch_error_message(
                 buy_default_membership_as_alice(),
@@ -197,7 +195,7 @@ fn unique_handles() {
             set_alice_free_balance(initial_balance);
 
             // alice's handle already taken
-            <members::Handles<Test>>::insert(get_alice_info().handle.unwrap(), 1);
+            <crate::MemberIdByHandle<Test>>::insert(get_alice_info().handle.unwrap(), 1);
 
             // should not be allowed to buy membership with that handle
             assert_dispatch_error_message(
@@ -226,17 +224,16 @@ fn update_profile() {
             let next_member_id = Members::members_created();
 
             assert_ok!(buy_default_membership_as_alice());
-
-            assert_ok!(Members::update_profile(
+            let info = get_bob_info();
+            assert_ok!(Members::update_membership(
                 Origin::signed(ALICE_ACCOUNT_ID),
                 next_member_id,
-                get_bob_info()
+                info.handle,
+                info.avatar_uri,
+                info.about,
             ));
 
-            let profile = assert_ok_unwrap(
-                Members::member_profile(&next_member_id),
-                "member profile not created",
-            );
+            let profile = get_membership_by_id(next_member_id);
 
             assert_eq!(Some(profile.handle), get_bob_info().handle);
             assert_eq!(Some(profile.avatar_uri), get_bob_info().avatar_uri);
@@ -251,26 +248,26 @@ fn add_screened_member() {
         .build()
         .execute_with(|| {
             let screening_authority = 5;
-            <members::ScreeningAuthority<Test>>::put(&screening_authority);
+            <crate::ScreeningAuthority<Test>>::put(&screening_authority);
 
             let next_member_id = Members::members_created();
 
+            let info = get_alice_info();
             assert_ok!(Members::add_screened_member(
                 Origin::signed(screening_authority),
                 ALICE_ACCOUNT_ID,
-                get_alice_info()
+                info.handle,
+                info.avatar_uri,
+                info.about
             ));
 
-            let profile = assert_ok_unwrap(
-                Members::member_profile(&next_member_id),
-                "member profile not created",
-            );
+            let profile = get_membership_by_id(next_member_id);
 
             assert_eq!(Some(profile.handle), get_alice_info().handle);
             assert_eq!(Some(profile.avatar_uri), get_alice_info().avatar_uri);
             assert_eq!(Some(profile.about), get_alice_info().about);
             assert_eq!(
-                members::EntryMethod::Screening(screening_authority),
+                crate::EntryMethod::Screening(screening_authority),
                 profile.entry
             );
         });
@@ -289,7 +286,7 @@ fn set_controller_key() {
         )
         .build()
         .execute_with(|| {
-            let member_id = Members::member_ids_by_root_account_id(&ALICE_ACCOUNT_ID)[0];
+            let member_id = 0;
 
             assert_ok!(Members::set_controller_account(
                 Origin::signed(ALICE_ACCOUNT_ID),
@@ -297,17 +294,16 @@ fn set_controller_key() {
                 ALICE_CONTROLLER_ID
             ));
 
-            let profile = assert_ok_unwrap(
-                Members::member_profile(&member_id),
-                "member profile not created",
-            );
+            let profile = get_membership_by_id(member_id);
 
             assert_eq!(profile.controller_account, ALICE_CONTROLLER_ID);
             assert_eq!(
-                Members::member_ids_by_controller_account_id(&ALICE_CONTROLLER_ID),
+                <crate::MemberIdsByControllerAccountId<Test>>::get(&ALICE_CONTROLLER_ID),
                 vec![member_id]
             );
-            assert!(Members::member_ids_by_controller_account_id(&ALICE_ACCOUNT_ID).is_empty());
+            assert!(
+                <crate::MemberIdsByControllerAccountId<Test>>::get(&ALICE_ACCOUNT_ID).is_empty()
+            );
         });
 }
 
@@ -324,94 +320,18 @@ fn set_root_account() {
         )
         .build()
         .execute_with(|| {
-            let member_id_1 = Members::member_ids_by_root_account_id(&ALICE_ACCOUNT_ID)[0];
+            let member_id = 0;
 
             assert_ok!(Members::set_root_account(
                 Origin::signed(ALICE_ACCOUNT_ID),
-                member_id_1,
+                member_id,
                 ALICE_NEW_ROOT_ACCOUNT
             ));
 
-            let member_id_2 = Members::member_ids_by_root_account_id(&ALICE_NEW_ROOT_ACCOUNT)[0];
-
-            assert_eq!(member_id_1, member_id_2);
+            let membership = Members::membership(member_id);
 
-            assert!(Members::member_ids_by_root_account_id(&ALICE_ACCOUNT_ID).is_empty());
-        });
-}
+            assert_eq!(ALICE_ACCOUNT_ID, membership.root_account);
 
-#[test]
-fn registering_and_unregistering_roles_on_member() {
-    let initial_members = [1, 2];
-
-    TestExternalitiesBuilder::<Test>::default()
-        .set_membership_config(
-            genesis::GenesisConfigBuilder::default()
-                .members(initial_members.to_vec())
-                .build(),
-        )
-        .build()
-        .execute_with(|| {
-            const DUMMY_ACTOR_ID: u32 = 100;
-            let member_id_1 = Members::member_ids_by_root_account_id(&1)[0];
-            let member_id_2 = Members::member_ids_by_root_account_id(&2)[0];
-
-            // no initial roles for member
-            assert!(!Members::member_is_in_role(
-                member_id_1,
-                members::Role::ChannelOwner
-            ));
-
-            // REGISTERING
-
-            // successful registration
-            assert_ok!(Members::register_role_on_member(
-                member_id_1,
-                &members::ActorInRole::new(members::Role::ChannelOwner, DUMMY_ACTOR_ID)
-            ));
-            assert!(Members::member_is_in_role(
-                member_id_1,
-                members::Role::ChannelOwner
-            ));
-
-            /*
-            Disabling this temporarily for Rome, later we will drop all this
-            integration with Membership module anyway:
-            https://github.com/Joystream/substrate-runtime-joystream/issues/115
-            assert_dispatch_error_message(
-                Members::register_role_on_member(
-                    member_id_1,
-                    &members::ActorInRole::new(members::Role::ChannelOwner, DUMMY_ACTOR_ID + 1),
-                ),
-                "MemberAlreadyInRole",
-            );
-            */
-
-            // registering another member in same role and actorid combination should fail
-            assert_dispatch_error_message(
-                Members::register_role_on_member(
-                    member_id_2,
-                    &members::ActorInRole::new(members::Role::ChannelOwner, DUMMY_ACTOR_ID),
-                ),
-                "ActorInRoleAlreadyExists",
-            );
-
-            // UNREGISTERING
-
-            // trying to unregister non existant actor role should fail
-            assert_dispatch_error_message(
-                Members::unregister_role(members::ActorInRole::new(members::Role::Curator, 222)),
-                "ActorInRoleNotFound",
-            );
-
-            // successfully unregister role
-            assert_ok!(Members::unregister_role(members::ActorInRole::new(
-                members::Role::ChannelOwner,
-                DUMMY_ACTOR_ID
-            )));
-            assert!(!Members::member_is_in_role(
-                member_id_1,
-                members::Role::ChannelOwner
-            ));
+            assert!(<crate::MemberIdsByRootAccountId<Test>>::get(&ALICE_ACCOUNT_ID).is_empty());
         });
 }

+ 2 - 2
runtime-modules/proposals/codex/src/lib.rs

@@ -143,7 +143,7 @@ pub trait Trait:
     system::Trait
     + proposal_engine::Trait
     + proposal_discussion::Trait
-    + membership::members::Trait
+    + membership::Trait
     + governance::election::Trait
     + content_working_group::Trait
     + staking::Trait
@@ -186,7 +186,7 @@ pub type BalanceOfMint<T> =
 pub type NegativeImbalance<T> =
     <<T as stake::Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::NegativeImbalance;
 
-type MemberId<T> = <T as membership::members::Trait>::MemberId;
+type MemberId<T> = <T as membership::Trait>::MemberId;
 
 decl_error! {
     /// Codex module predefined errors

+ 1 - 2
runtime-modules/proposals/codex/src/tests/mock.rs

@@ -44,13 +44,12 @@ impl common::currency::GovernanceCurrency for Test {
     type Currency = balances::Module<Self>;
 }
 
-impl membership::members::Trait for Test {
+impl membership::Trait for Test {
     type Event = ();
     type MemberId = u64;
     type PaidTermId = u64;
     type SubscriptionId = u64;
     type ActorId = u64;
-    type InitialMembersBalance = ();
 }
 
 parameter_types! {

+ 3 - 3
runtime-modules/proposals/discussion/src/lib.rs

@@ -23,7 +23,7 @@
 //! use system::ensure_root;
 //! use substrate_proposals_discussion_module::{self as discussions};
 //!
-//! pub trait Trait: discussions::Trait + membership::members::Trait {}
+//! pub trait Trait: discussions::Trait + membership::Trait {}
 //!
 //! decl_module! {
 //!     pub struct Module<T: Trait> for enum Call where origin: T::Origin {
@@ -59,7 +59,7 @@ use types::{DiscussionPost, DiscussionThread, ThreadCounter};
 use common::origin::ActorOriginValidator;
 use srml_support::dispatch::DispatchResult;
 
-type MemberId<T> = <T as membership::members::Trait>::MemberId;
+type MemberId<T> = <T as membership::Trait>::MemberId;
 
 decl_event!(
     /// Proposals engine events
@@ -81,7 +81,7 @@ decl_event!(
 );
 
 /// 'Proposal discussion' substrate module Trait
-pub trait Trait: system::Trait + membership::members::Trait {
+pub trait Trait: system::Trait + membership::Trait {
     /// Discussion event type.
     type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
 

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels