Browse Source

Cleanup related to drafts and previous json-schema prompting approach

Leszek Wiesner 4 years ago
parent
commit
b1c61aef20
3 changed files with 4 additions and 347 deletions
  1. 2 200
      cli/src/Types.ts
  2. 2 56
      cli/src/base/ApiCommandBase.ts
  3. 0 91
      cli/src/base/WorkingGroupsCommandBase.ts

+ 2 - 200
cli/src/Types.ts

@@ -1,31 +1,14 @@
 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 { Option } from '@polkadot/types'
+import { Codec } from '@polkadot/types/types'
 import { BlockNumber, Balance, AccountId } from '@polkadot/types/interfaces'
 import { DeriveBalancesAll } from '@polkadot/api-derive/types'
 import { KeyringPair } from '@polkadot/keyring/types'
 import { WorkerId, OpeningType } from '@joystream/types/working-group'
 import { Membership, 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'
 import { Validator } from 'inquirer'
-import { JoyStructCustom } from '@joystream/types/common'
 
 // KeyringPair type extended with mandatory "meta.name"
 // It's used for accounts/keys management within CLI.
@@ -187,183 +170,6 @@ export type GroupOpening = {
   unstakingPeriods: UnstakingPeriods
 }
 
-// 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> {
-  toJSONObj: () => T
-}
-export class HRTJobSpecificsStruct
-  extends JoyStructCustom({
-    title: Text,
-    description: Text,
-  })
-  implements WithJSONable<JobSpecifics> {
-  get title(): string {
-    return this.getField('title').toString()
-  }
-
-  get description(): string {
-    return this.getField('description').toString()
-  }
-
-  toJSONObj(): JobSpecifics {
-    const { title, description } = this
-    return { title, description }
-  }
-}
-export class HRTEntryInMembershipModukeStruct
-  extends JoyStructCustom({
-    handle: Text,
-  })
-  implements WithJSONable<EntryInMembershipModuke> {
-  get handle(): string {
-    return this.getField('handle').toString()
-  }
-
-  toJSONObj(): EntryInMembershipModuke {
-    const { handle } = this
-    return { handle }
-  }
-}
-export class HRTCreatorDetailsStruct
-  extends JoyStructCustom({
-    membership: HRTEntryInMembershipModukeStruct,
-  })
-  implements WithJSONable<CreatorDetails> {
-  get membership(): EntryInMembershipModuke {
-    return this.getField('membership').toJSONObj()
-  }
-
-  toJSONObj(): CreatorDetails {
-    const { membership } = this
-    return { membership }
-  }
-}
-export class HRTHiringProcessStruct
-  extends JoyStructCustom({
-    details: Vec.with(Text),
-  })
-  implements WithJSONable<HiringProcess> {
-  get details(): AdditionalRolehiringProcessDetails {
-    return this.getField('details')
-      .toArray()
-      .map((v) => v.toString())
-  }
-
-  toJSONObj(): HiringProcess {
-    const { details } = this
-    return { details }
-  }
-}
-export class HRTQuestionFieldStruct
-  extends JoyStructCustom({
-    title: Text,
-    type: Text,
-  })
-  implements WithJSONable<QuestionField> {
-  get title(): string {
-    return this.getField('title').toString()
-  }
-
-  get type(): string {
-    return this.getField('type').toString()
-  }
-
-  toJSONObj(): QuestionField {
-    const { title, type } = this
-    return { title, type }
-  }
-}
-class HRTQuestionsFieldsVec extends Vec.with(HRTQuestionFieldStruct) implements WithJSONable<QuestionsFields> {
-  toJSONObj(): QuestionsFields {
-    return this.toArray().map((v) => v.toJSONObj())
-  }
-}
-export class HRTQuestionSectionStruct
-  extends JoyStructCustom({
-    title: Text,
-    questions: HRTQuestionsFieldsVec,
-  })
-  implements WithJSONable<QuestionSection> {
-  get title(): string {
-    return this.getField('title').toString()
-  }
-
-  get questions(): QuestionsFields {
-    return this.getField('questions').toJSONObj()
-  }
-
-  toJSONObj(): QuestionSection {
-    const { title, questions } = this
-    return { title, questions }
-  }
-}
-export class HRTQuestionSectionsVec extends Vec.with(HRTQuestionSectionStruct)
-  implements WithJSONable<QuestionSections> {
-  toJSONObj(): QuestionSections {
-    return this.toArray().map((v) => v.toJSONObj())
-  }
-}
-export class HRTApplicationDetailsStruct
-  extends JoyStructCustom({
-    sections: HRTQuestionSectionsVec,
-  })
-  implements WithJSONable<ApplicationDetails> {
-  get sections(): QuestionSections {
-    return this.getField('sections').toJSONObj()
-  }
-
-  toJSONObj(): ApplicationDetails {
-    const { sections } = this
-    return { sections }
-  }
-}
-export class HRTStruct
-  extends JoyStructCustom({
-    version: u32,
-    headline: Text,
-    job: HRTJobSpecificsStruct,
-    application: HRTApplicationDetailsStruct,
-    reward: Text,
-    creator: HRTCreatorDetailsStruct,
-    process: HRTHiringProcessStruct,
-  })
-  implements WithJSONable<GenericJoyStreamRoleSchema> {
-  get version(): number {
-    return this.getField('version').toNumber()
-  }
-
-  get headline(): string {
-    return this.getField('headline').toString()
-  }
-
-  get job(): JobSpecifics {
-    return this.getField('job').toJSONObj()
-  }
-
-  get application(): ApplicationDetails {
-    return this.getField('application').toJSONObj()
-  }
-
-  get reward(): string {
-    return this.getField('reward').toString()
-  }
-
-  get creator(): CreatorDetails {
-    return this.getField('creator').toJSONObj()
-  }
-
-  get process(): HiringProcess {
-    return this.getField('process').toJSONObj()
-  }
-
-  toJSONObj(): GenericJoyStreamRoleSchema {
-    const { version, headline, job, application, reward, creator, process } = this
-    return { version, headline, job, application, reward, creator, process }
-  }
-}
-
 // Api-related
 
 // Additional options that can be passed to ApiCommandBase.promptForParam in order to override
@@ -374,10 +180,6 @@ export type ApiParamOptions<ParamType = Codec> = {
     default: ParamType
     locked?: boolean
   }
-  jsonSchema?: {
-    struct: Constructor<Struct>
-    schemaValidator: ajv.ValidateFunction
-  }
   validator?: Validator
   nestedOptions?: ApiParamsOptions // For more complex params, like structs
 }

+ 2 - 56
cli/src/base/ApiCommandBase.ts

@@ -2,15 +2,14 @@ import ExitCodes from '../ExitCodes'
 import { CLIError } from '@oclif/errors'
 import StateAwareCommandBase from './StateAwareCommandBase'
 import Api from '../Api'
-import { getTypeDef, Option, Tuple, Bytes, TypeRegistry } from '@polkadot/types'
-import { Registry, Codec, CodecArg, TypeDef, TypeDefInfo, Constructor } from '@polkadot/types/types'
+import { getTypeDef, Option, Tuple, TypeRegistry } from '@polkadot/types'
+import { Registry, Codec, CodecArg, TypeDef, TypeDefInfo } from '@polkadot/types/types'
 
 import { Vec, Struct, Enum } from '@polkadot/types/codec'
 import { ApiPromise, WsProvider } from '@polkadot/api'
 import { KeyringPair } from '@polkadot/keyring/types'
 import chalk from 'chalk'
 import { InterfaceTypes } from '@polkadot/types/types/registry'
-import ajv from 'ajv'
 import { ApiMethodArg, ApiMethodNamedArgs, ApiParamsOptions, ApiParamOptions } from '../Types'
 import { createParamOptions } from '../helpers/promptOptions'
 import { SubmittableExtrinsic } from '@polkadot/api/types'
@@ -308,16 +307,6 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
       return paramOptions.value.default
     }
 
-    if (paramOptions?.jsonSchema) {
-      const { struct, schemaValidator } = paramOptions.jsonSchema
-      return await this.promptForJsonBytes(
-        struct,
-        typeDef.name,
-        paramOptions.value?.default as Bytes | undefined,
-        schemaValidator
-      )
-    }
-
     if (rawTypeDef.info === TypeDefInfo.Option) {
       return await this.promptForOption(typeDef, paramOptions)
     } else if (rawTypeDef.info === TypeDefInfo.Tuple) {
@@ -338,49 +327,6 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
     return await this.promptForParam(type, options)
   }
 
-  async promptForJsonBytes(
-    jsonStruct: Constructor<Struct>,
-    argName?: string,
-    defaultValue?: Bytes,
-    schemaValidator?: ajv.ValidateFunction
-  ) {
-    const JsonStructObject = jsonStruct
-    const rawType = new JsonStructObject(this.getTypesRegistry()).toRawType()
-    const typeDef = getTypeDef(rawType)
-
-    const defaultStruct =
-      defaultValue &&
-      new JsonStructObject(
-        this.getTypesRegistry(),
-        JSON.parse(Buffer.from(defaultValue.toHex().replace('0x', ''), 'hex').toString())
-      )
-
-    if (argName) {
-      typeDef.name = argName
-    }
-
-    let isValid = true
-    let 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')
-        }
-      }
-    } while (!isValid)
-
-    return this.createType('Bytes', '0x' + Buffer.from(jsonText, 'ascii').toString('hex'))
-  }
-
   async promptForExtrinsicParams(
     module: string,
     method: string,

+ 0 - 91
cli/src/base/WorkingGroupsCommandBase.ts

@@ -7,21 +7,15 @@ import {
   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'
 import chalk from 'chalk'
 
 const DEFAULT_GROUP = WorkingGroups.StorageProviders
-const DRAFTS_FOLDER = 'opening-drafts'
 
 /**
  * Abstract base class for commands that need to use gates based on user's roles
@@ -124,51 +118,6 @@ export default abstract class WorkingGroupsCommandBase extends RolesCommandBase
     return acceptedApplications
   }
 
-  async promptForNewOpeningDraftName() {
-    let draftName = ''
-    let fileExists = false
-    let 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,
-    })
-
-    return selectedDraftName
-  }
-
   async getOpeningForLeadAction(id: number, requiredStatus?: OpeningStatus): Promise<GroupOpening> {
     const opening = await this.getApi().groupOpening(this.group, id)
 
@@ -230,48 +179,8 @@ export default abstract class WorkingGroupsCommandBase extends RolesCommandBase
     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())
-    }
-  }
-
   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(', ')}`, {