Browse Source

https://github.com/Joystream/joystream/pull/1509#issuecomment-709213195 fixes

Leszek Wiesner 4 years ago
parent
commit
7e450dd71b

+ 1 - 1
cli/src/Api.ts

@@ -501,7 +501,7 @@ export default class Api {
   }
 
   async curatorGroupById(id: number): Promise<CuratorGroup | null> {
-    const exists = !!(await this._api.query.contentDirectory.curatorGroupById.size(id))
+    const exists = !!(await this._api.query.contentDirectory.curatorGroupById.size(id)).toNumber()
     return exists ? await this._api.query.contentDirectory.curatorGroupById<CuratorGroup>(id) : null
   }
 

+ 22 - 6
cli/src/base/ApiCommandBase.ts

@@ -2,7 +2,7 @@ import ExitCodes from '../ExitCodes'
 import { CLIError } from '@oclif/errors'
 import StateAwareCommandBase from './StateAwareCommandBase'
 import Api from '../Api'
-import { getTypeDef, Option, Tuple, Bytes } from '@polkadot/types'
+import { getTypeDef, Option, Tuple, Bytes, TypeRegistry } from '@polkadot/types'
 import { Registry, Codec, CodecArg, TypeDef, TypeDefInfo, Constructor } from '@polkadot/types/types'
 
 import { Vec, Struct, Enum } from '@polkadot/types/codec'
@@ -16,6 +16,7 @@ import { createParamOptions } from '../helpers/promptOptions'
 import { SubmittableExtrinsic } from '@polkadot/api/types'
 import { DistinctQuestion } from 'inquirer'
 import { BOOL_PROMPT_OPTIONS } from '../helpers/prompting'
+import { DispatchError } from '@polkadot/types/interfaces/system'
 
 class ExtrinsicFailedError extends Error {}
 
@@ -405,11 +406,26 @@ export default abstract class ApiCommandBase extends StateAwareCommandBase {
         if (result.status.isInBlock) {
           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') {
+            .filter(({ event }) => event.section === 'system')
+            .forEach(({ event }) => {
+              if (event.method === 'ExtrinsicFailed') {
+                const dispatchError = event.data[0] as DispatchError
+                let errorMsg = dispatchError.toString()
+                if (dispatchError.isModule) {
+                  try {
+                    // Need to assert that registry is of TypeRegistry type, since Registry intefrace
+                    // seems outdated and doesn't include DispatchErrorModule as possible argument for "findMetaError"
+                    const { name, documentation } = (this.getOriginalApi().registry as TypeRegistry).findMetaError(
+                      dispatchError.asModule
+                    )
+                    errorMsg = `${name} (${documentation})`
+                  } catch (e) {
+                    // This probably means we don't have this error in the metadata
+                    // In this case - continue (we'll just display dispatchError.toString())
+                  }
+                }
+                reject(new ExtrinsicFailedError(`Extrinsic execution error: ${errorMsg}`))
+              } else if (event.method === 'ExtrinsicSuccess') {
                 resolve()
               }
             })

+ 4 - 1
cli/src/base/ContentDirectoryCommandBase.ts

@@ -11,7 +11,7 @@ import { Codec } from '@polkadot/types/types'
 import _ from 'lodash'
 
 /**
- * Abstract base class for commands related to working groups
+ * Abstract base class for commands related to content directory
  */
 export default abstract class ContentDirectoryCommandBase extends AccountsCommandBase {
   // Use when lead access is required in given command
@@ -74,6 +74,9 @@ export default abstract class ContentDirectoryCommandBase extends AccountsComman
 
   async promptForCuratorGroups(message = 'Select Curator Groups'): Promise<number[]> {
     const choices = await this.curatorGroupChoices()
+    if (!choices.length) {
+      return []
+    }
     const selectedIds = await this.simplePrompt({ message, type: 'checkbox', choices })
 
     return selectedIds

+ 18 - 5
cli/src/commands/content-directory/addClassSchema.ts

@@ -33,14 +33,27 @@ export default class AddClassSchemaCommand extends ContentDirectoryCommandBase {
         ],
         [
           'existingProperties',
-          async () =>
-            this.simplePrompt({
+          async () => {
+            const choices = selectedClass!.properties.map((p, i) => ({ name: `${i}: ${p.name.toString()}`, value: i }))
+            if (!choices.length) {
+              return []
+            }
+            return await this.simplePrompt({
               type: 'checkbox',
               message: 'Choose existing properties to keep',
-              choices: selectedClass!.properties.map((p, i) => ({ name: `${i}: ${p.name.toString()}`, value: i })),
-            }),
+              choices,
+            })
+          },
+        ],
+        [
+          /^newProperties\[\d+\]\.property_type\.(Single|Vector\.vec_type)\.Reference/,
+          async () => this.promptForClassReference(),
+        ],
+        [/^newProperties\[\d+\]\.property_type\.(Single|Vector\.vec_type)\.Text/, { message: 'Provide TextMaxLength' }],
+        [
+          /^newProperties\[\d+\]\.property_type\.(Single|Vector\.vec_type)\.Hash/,
+          { message: 'Provide HashedTextMaxLength' },
         ],
-        [/^newProperties\[\d+\]\.property_type\.Single\.Reference/, async () => this.promptForClassReference()],
       ]
 
       const prompter = new JsonSchemaPrompter<AddClassSchema>(

+ 8 - 1
cli/src/commands/content-directory/createClass.ts

@@ -17,10 +17,17 @@ export default class CreateClassCommand extends ContentDirectoryCommandBase {
     await this.requireLead()
 
     const { input, output } = this.parse(CreateClassCommand).flags
+    const existingClassnames = (await this.getApi().availableClasses()).map(([, aClass]) => aClass.name.toString())
 
     let inputJson = await getInputJson<CreateClass>(input, CreateClassSchema as JSONSchema)
     if (!inputJson) {
-      const customPrompts: JsonSchemaCustomPrompts = [
+      const customPrompts: JsonSchemaCustomPrompts<CreateClass> = [
+        [
+          'name',
+          {
+            validate: (className) => existingClassnames.includes(className) && 'A class with this name already exists!',
+          },
+        ],
         ['class_permissions.maintainers', () => this.promptForCuratorGroups('Select class maintainers')],
       ]
 

+ 13 - 9
cli/src/commands/content-directory/curatorGroups.ts

@@ -8,14 +8,18 @@ export default class CuratorGroupsCommand extends ContentDirectoryCommandBase {
   async run() {
     const groups = await this.getApi().availableCuratorGroups()
 
-    displayTable(
-      groups.map(([id, group]) => ({
-        'ID': id.toString(),
-        'Status': group.active.valueOf() ? 'Active' : 'Inactive',
-        'Classes maintained': group.number_of_classes_maintained.toNumber(),
-        'Members': group.curators.toArray().length,
-      })),
-      5
-    )
+    if (groups.length) {
+      displayTable(
+        groups.map(([id, group]) => ({
+          'ID': id.toString(),
+          'Status': group.active.valueOf() ? 'Active' : 'Inactive',
+          'Classes maintained': group.number_of_classes_maintained.toNumber(),
+          'Members': group.curators.toArray().length,
+        })),
+        5
+      )
+    } else {
+      this.log('No Curator Groups available!')
+    }
   }
 }

+ 7 - 2
cli/src/commands/content-directory/removeCuratorGroup.ts

@@ -1,5 +1,6 @@
 import ContentDirectoryCommandBase from '../../base/ContentDirectoryCommandBase'
 import chalk from 'chalk'
+import ExitCodes from '../../ExitCodes'
 
 export default class AddCuratorGroupCommand extends ContentDirectoryCommandBase {
   static description = 'Remove existing Curator Group.'
@@ -18,8 +19,12 @@ export default class AddCuratorGroupCommand extends ContentDirectoryCommandBase
     let { id } = this.parse(AddCuratorGroupCommand).args
     if (id === undefined) {
       id = await this.promptForCuratorGroup('Select Curator Group to remove')
-    } else {
-      await this.getCuratorGroup(id)
+    }
+
+    const group = await this.getCuratorGroup(id)
+
+    if (group.number_of_classes_maintained.toNumber() > 0) {
+      this.error('Cannot remove a group which has some maintained classes!', { exit: ExitCodes.InvalidInput })
     }
 
     await this.requestAccountDecoding(account)

+ 2 - 1
cli/src/helpers/InputOutput.ts

@@ -20,7 +20,8 @@ export const IOFlags = {
   output: flags.string({
     char: 'o',
     required: false,
-    description: 'Path where the output JSON file should be placed (can be then reused as input)',
+    description:
+      'Path to the directory where the output JSON file should be placed (the output file can be then reused as input)',
   }),
 }
 

+ 3 - 2
content-directory-schemas/schemas/extrinsics/AddClassSchema.schema.json

@@ -50,11 +50,12 @@
     "PropertyName": {
       "type": "string",
       "minLength": 1,
-      "maxLength": 100
+      "maxLength": 49
     },
     "PropertyDescription": {
       "type": "string",
-      "minLength": 0,
+      "minLength": 1,
+      "maxLength": 500,
       "default": ""
     },
     "SinglePropertyType": {

+ 6 - 2
content-directory-schemas/schemas/extrinsics/CreateClass.schema.json

@@ -9,11 +9,15 @@
   "properties": {
     "name": {
       "type": "string",
-      "description": "Name of this class. Required property."
+      "description": "Name of this class. Required property.",
+      "minLength": 1,
+      "maxLength": 49
     },
     "description": {
       "type": "string",
-      "description": "Description of this class."
+      "description": "Description of this class.",
+      "minLength": 1,
+      "maxLength": 500
     },
     "class_permissions": {
       "type": "object",