Ver Fonte

Update distribution bucket id representation

Leszek Wiesner há 3 anos atrás
pai
commit
5c52fd3256
25 ficheiros alterados com 176 adições e 160 exclusões
  1. 7 6
      distributor-node/scripts/init-bucket.sh
  2. 36 21
      distributor-node/scripts/test-commands.sh
  3. 2 2
      distributor-node/src/api-spec/operator.yml
  4. 2 2
      distributor-node/src/api-spec/public.yml
  5. 9 0
      distributor-node/src/command-base/default.ts
  6. 7 11
      distributor-node/src/commands/leader/cancel-invitation.ts
  7. 5 2
      distributor-node/src/commands/leader/create-bucket.ts
  8. 1 1
      distributor-node/src/commands/leader/delete-bucket-family.ts
  9. 5 13
      distributor-node/src/commands/leader/delete-bucket.ts
  10. 7 11
      distributor-node/src/commands/leader/invite-bucket-operator.ts
  11. 7 11
      distributor-node/src/commands/leader/remove-bucket-operator.ts
  12. 4 1
      distributor-node/src/commands/leader/set-bucket-family-metadata.ts
  13. 1 1
      distributor-node/src/commands/leader/set-buckets-per-bag-limit.ts
  14. 10 12
      distributor-node/src/commands/leader/update-bag.ts
  15. 4 11
      distributor-node/src/commands/leader/update-bucket-mode.ts
  16. 5 13
      distributor-node/src/commands/leader/update-bucket-status.ts
  17. 6 2
      distributor-node/src/commands/leader/update-dynamic-bag-policy.ts
  18. 12 6
      distributor-node/src/commands/node/set-buckets.ts
  19. 7 11
      distributor-node/src/commands/operator/accept-invitation.ts
  20. 7 11
      distributor-node/src/commands/operator/set-metadata.ts
  21. 6 3
      distributor-node/src/schemas/configSchema.ts
  22. 21 0
      distributor-node/src/services/parsers/BucketIdParserService.ts
  23. 3 3
      distributor-node/src/types/generated/ConfigJson.d.ts
  24. 1 1
      distributor-node/src/types/generated/OperatorApi.ts
  25. 1 5
      distributor-node/src/types/generated/PublicApi.ts

+ 7 - 6
distributor-node/scripts/init-bucket.sh

@@ -10,11 +10,12 @@ CLI=../bin/run
 ${CLI} dev:init
 ${CLI} leader:set-buckets-per-bag-limit -l 10
 FAMILY_ID=`${CLI} leader:create-bucket-family ${CONFIG}`
-BUCKET_ID=`${CLI} leader:create-bucket -f ${FAMILY_ID} -a yes`
-${CLI} leader:update-bag -b static:council -f ${FAMILY_ID} -a ${BUCKET_ID}
-${CLI} leader:update-bucket-mode -f ${FAMILY_ID} -B ${BUCKET_ID} --mode on
-${CLI} leader:invite-bucket-operator -f ${FAMILY_ID} -B ${BUCKET_ID} -w 0
-${CLI} operator:accept-invitation -f ${FAMILY_ID} -B ${BUCKET_ID} -w 0
-${CLI} operator:set-metadata -f ${FAMILY_ID} -B ${BUCKET_ID} -w 0 -e http://localhost:3334
+BUCKET_INDEX=`${CLI} leader:create-bucket -f ${FAMILY_ID} -a yes`
+BUCKET_ID="${FAMILY_ID}:${BUCKET_INDEX}"
+${CLI} leader:update-bag -b static:council -f ${FAMILY_ID} -a ${BUCKET_INDEX}
+${CLI} leader:update-bucket-mode -B ${BUCKET_ID} --mode on
+${CLI} leader:invite-bucket-operator -B ${BUCKET_ID} -w 0
+${CLI} operator:accept-invitation -B ${BUCKET_ID} -w 0
+${CLI} operator:set-metadata -B ${BUCKET_ID} -w 0 -e http://localhost:3334
 ${CLI} leader:update-dynamic-bag-policy -t Channel -p ${FAMILY_ID}:1
 ${CLI} leader:update-dynamic-bag-policy -t Member -p ${FAMILY_ID}:1

+ 36 - 21
distributor-node/scripts/test-commands.sh

@@ -9,33 +9,48 @@ CLI=../bin/run
 
 ${CLI} dev:init
 ${CLI} leader:set-buckets-per-bag-limit -l 10
+# Create family and buckets
 FAMILY_ID=`${CLI} leader:create-bucket-family`
-BUCKET_ID=`${CLI} leader:create-bucket -f ${FAMILY_ID} -a yes`
-${CLI} leader:update-bag -b static:council -f ${FAMILY_ID} -a ${BUCKET_ID}
-${CLI} leader:update-bag -b static:wg:storage -f ${FAMILY_ID} -a ${BUCKET_ID}
-${CLI} leader:update-bag -b static:wg:content -f ${FAMILY_ID} -a ${BUCKET_ID}
-${CLI} leader:update-bag -b static:wg:operationsAlpha -f ${FAMILY_ID} -a ${BUCKET_ID}
-${CLI} leader:update-bag -b static:wg:operationsBeta -f ${FAMILY_ID} -a ${BUCKET_ID}
-${CLI} leader:update-bag -b static:wg:operationsGamma -f ${FAMILY_ID} -a ${BUCKET_ID}
-${CLI} leader:update-bag -b static:wg:gateway -f ${FAMILY_ID} -a ${BUCKET_ID}
-${CLI} leader:update-bag -b static:wg:distribution -f ${FAMILY_ID} -a ${BUCKET_ID}
-${CLI} leader:update-bucket-status -f ${FAMILY_ID} -B ${BUCKET_ID}  --acceptingBags yes
-${CLI} leader:update-bucket-mode -f ${FAMILY_ID} -B ${BUCKET_ID} --mode on
+BUCKET_1_INDEX=`${CLI} leader:create-bucket -f ${FAMILY_ID} -a yes`
+BUCKET_2_INDEX=`${CLI} leader:create-bucket -f ${FAMILY_ID} -a yes`
+BUCKET_1_ID="${FAMILY_ID}:${BUCKET_1_INDEX}"
+BUCKET_2_ID="${FAMILY_ID}:${BUCKET_2_INDEX}"
+# Test adding 2 buckets to bag at once
+${CLI} leader:update-bag -b static:council -f ${FAMILY_ID} -a ${BUCKET_1_INDEX} ${BUCKET_2_INDEX}
+# Test removing 2 buckets from bag at once
+${CLI} leader:update-bag -b static:council -f ${FAMILY_ID} -r ${BUCKET_1_INDEX} ${BUCKET_2_INDEX}
+# Adding single bucket to all static bags
+${CLI} leader:update-bag -b static:wg:storage -f ${FAMILY_ID} -a ${BUCKET_1_INDEX}
+${CLI} leader:update-bag -b static:wg:content -f ${FAMILY_ID} -a ${BUCKET_1_INDEX}
+${CLI} leader:update-bag -b static:wg:operationsAlpha -f ${FAMILY_ID} -a ${BUCKET_1_INDEX}
+${CLI} leader:update-bag -b static:wg:operationsBeta -f ${FAMILY_ID} -a ${BUCKET_1_INDEX}
+${CLI} leader:update-bag -b static:wg:operationsGamma -f ${FAMILY_ID} -a ${BUCKET_1_INDEX}
+${CLI} leader:update-bag -b static:wg:gateway -f ${FAMILY_ID} -a ${BUCKET_1_INDEX}
+${CLI} leader:update-bag -b static:wg:distribution -f ${FAMILY_ID} -a ${BUCKET_1_INDEX}
+# Update bucket status / mode
+${CLI} leader:update-bucket-status -B ${BUCKET_1_ID}  --acceptingBags yes
+${CLI} leader:update-bucket-mode -B ${BUCKET_1_ID} --mode on
+${CLI} leader:update-bucket-status -B ${BUCKET_2_ID}  --acceptingBags no
+${CLI} leader:update-bucket-mode -B ${BUCKET_2_ID} --mode off
+# Update dynamic bag policies
 ${CLI} leader:update-dynamic-bag-policy -t Channel -p ${FAMILY_ID}:5
 ${CLI} leader:update-dynamic-bag-policy -t Member -p ${FAMILY_ID}:5
 ${CLI} leader:update-dynamic-bag-policy -t Member
-${CLI} leader:invite-bucket-operator -f ${FAMILY_ID} -B ${BUCKET_ID} -w 0
-${CLI} leader:cancel-invitation -f ${FAMILY_ID} -B ${BUCKET_ID} -w 0
-${CLI} leader:invite-bucket-operator -f ${FAMILY_ID} -B ${BUCKET_ID} -w 0
-${CLI} operator:accept-invitation -f ${FAMILY_ID} -B ${BUCKET_ID} -w 0
-${CLI} operator:set-metadata -f ${FAMILY_ID} -B ${BUCKET_ID} -w 0 -i ./data/operator-metadata.json
+# Bucket invitations + cancelling and accepting
+${CLI} leader:invite-bucket-operator -B ${BUCKET_1_ID} -w 0
+${CLI} leader:invite-bucket-operator -B ${BUCKET_2_ID} -w 0
+${CLI} operator:accept-invitation -B ${BUCKET_1_ID} -w 0
+${CLI} leader:cancel-invitation -B ${BUCKET_2_ID} -w 0
+# Setting metadata
+${CLI} operator:set-metadata -B ${BUCKET_1_ID} -w 0 -i ./data/operator-metadata.json
 ${CLI} leader:set-bucket-family-metadata -f ${FAMILY_ID} -i ./data/family-metadata.json
 
 # Deletion commands tested separately
 FAMILY_TO_DELETE_ID=`${CLI} leader:create-bucket-family`
-BUCKET_TO_DELETE_ID=`${CLI} leader:create-bucket -f ${FAMILY_TO_DELETE_ID} -a yes`
-${CLI} leader:invite-bucket-operator -f ${FAMILY_TO_DELETE_ID} -B ${BUCKET_TO_DELETE_ID} -w 0
-${CLI} operator:accept-invitation -f ${FAMILY_TO_DELETE_ID} -B ${BUCKET_TO_DELETE_ID} -w 0
-${CLI} leader:remove-bucket-operator -f ${FAMILY_TO_DELETE_ID} -B ${BUCKET_TO_DELETE_ID} -w 0
-${CLI} leader:delete-bucket -f ${FAMILY_TO_DELETE_ID} -B ${BUCKET_TO_DELETE_ID}
+BUCKET_TO_DELETE_INDEX=`${CLI} leader:create-bucket -f ${FAMILY_TO_DELETE_ID} -a yes`
+BUCKET_TO_DELETE_ID="${FAMILY_TO_DELETE_ID}:${BUCKET_TO_DELETE_INDEX}"
+${CLI} leader:invite-bucket-operator -B ${BUCKET_TO_DELETE_ID} -w 0
+${CLI} operator:accept-invitation -B ${BUCKET_TO_DELETE_ID} -w 0
+${CLI} leader:remove-bucket-operator -B ${BUCKET_TO_DELETE_ID} -w 0
+${CLI} leader:delete-bucket -B ${BUCKET_TO_DELETE_ID}
 ${CLI} leader:delete-bucket-family -f ${FAMILY_TO_DELETE_ID}

+ 2 - 2
distributor-node/src/api-spec/operator.yml

@@ -112,8 +112,8 @@ components:
           type: array
           minItems: 1
           items:
-            type: integer
-            minimum: 0
+            type: string
+            pattern: ^[0-9]+:[0-9]+$
 
 security:
   - OperatorAuth: []

+ 2 - 2
distributor-node/src/api-spec/public.yml

@@ -192,8 +192,8 @@ components:
             bucketIds:
               type: array
               items:
-                type: integer
-                minimum: 0
+                type: string
+                pattern: ^[0-9]+:[0-9]+$
         - type: object
           required:
             - 'allByWorkerId'

+ 9 - 0
distributor-node/src/command-base/default.ts

@@ -6,6 +6,7 @@ import { ConfigParserService } from '../services/parsers/ConfigParserService'
 import { LoggingService } from '../services/logging'
 import { Logger } from 'winston'
 import { BagIdParserService } from '../services/parsers/BagIdParserService'
+import { BucketIdParserService } from '../services/parsers/BucketIdParserService'
 
 export const flags = {
   ...oclifFlags,
@@ -21,6 +22,7 @@ export const flags = {
     },
   }),
   bagId: oclifFlags.build({
+    char: 'b',
     parse: (value: string) => {
       const parser = new BagIdParserService(value)
       return parser.parse()
@@ -37,6 +39,13 @@ export const flags = {
     - static:wg:storage
     - dynamic:member:4`,
   }),
+  bucketId: oclifFlags.build({
+    char: 'B',
+    parse: (value: string) => {
+      return BucketIdParserService.parseBucketId(value)
+    },
+    description: `Distribution bucket ID in {familyId}:{bucketIndex} format.`,
+  }),
 }
 export default abstract class DefaultCommandBase extends Command {
   protected appConfig!: ReadonlyConfig

+ 7 - 11
distributor-node/src/commands/leader/cancel-invitation.ts

@@ -6,14 +6,7 @@ export default class LeaderCancelInvitation extends AccountsCommandBase {
   Requires distribution working group leader permissions.`
 
   static flags = {
-    bucketId: flags.integer({
-      char: 'B',
-      description: 'Distribution bucket id',
-      required: true,
-    }),
-    familyId: flags.integer({
-      char: 'f',
-      description: 'Distribution bucket family id',
+    bucketId: flags.bucketId({
       required: true,
     }),
     workerId: flags.integer({
@@ -25,13 +18,16 @@ export default class LeaderCancelInvitation extends AccountsCommandBase {
   }
 
   async run(): Promise<void> {
-    const { bucketId, familyId, workerId } = this.parse(LeaderCancelInvitation).flags
+    const { bucketId, workerId } = this.parse(LeaderCancelInvitation).flags
     const leadKey = await this.getDistributorLeadKey()
 
-    this.log(`Canceling distribution bucket operator invitation (bucket: ${bucketId}, worker: ${workerId})...`)
+    this.log(`Canceling distribution bucket operator invitation...`, {
+      bucketId: bucketId.toHuman(),
+      workerId: workerId,
+    })
     await this.sendAndFollowTx(
       await this.getDecodedPair(leadKey),
-      this.api.tx.storage.cancelDistributionBucketOperatorInvite(familyId, bucketId, workerId)
+      this.api.tx.storage.cancelDistributionBucketOperatorInvite(bucketId, workerId)
     )
     this.log('Invitation succesfully canceled!')
   }

+ 5 - 2
distributor-node/src/commands/leader/create-bucket.ts

@@ -24,7 +24,10 @@ export default class LeaderCreateBucket extends AccountsCommandBase {
     const { familyId, acceptingBags } = this.parse(LeaderCreateBucket).flags
     const leadKey = await this.getDistributorLeadKey()
 
-    this.log('Creating new distribution bucket...')
+    this.log('Creating new distribution bucket...', {
+      familyId,
+      acceptingBags,
+    })
     const result = await this.sendAndFollowTx(
       await this.getDecodedPair(leadKey),
       this.api.tx.storage.createDistributionBucket(familyId, acceptingBags === 'yes')
@@ -33,6 +36,6 @@ export default class LeaderCreateBucket extends AccountsCommandBase {
 
     this.log('Bucket succesfully created!')
     const bucketId = event.data[2]
-    this.output(bucketId.toString())
+    this.output(bucketId.distribution_bucket_index.toString())
   }
 }

+ 1 - 1
distributor-node/src/commands/leader/delete-bucket-family.ts

@@ -18,7 +18,7 @@ export default class LeaderDeleteBucketFamily extends AccountsCommandBase {
     const { familyId } = this.parse(LeaderDeleteBucketFamily).flags
     const leadKey = await this.getDistributorLeadKey()
 
-    this.log(`Deleting distribution bucket family (${familyId})...`)
+    this.log(`Deleting distribution bucket family...`, { familyId })
     await this.sendAndFollowTx(
       await this.getDecodedPair(leadKey),
       this.api.tx.storage.deleteDistributionBucketFamily(familyId)

+ 5 - 13
distributor-node/src/commands/leader/delete-bucket.ts

@@ -1,32 +1,24 @@
-import { flags } from '@oclif/command'
 import AccountsCommandBase from '../../command-base/accounts'
-import DefaultCommandBase from '../../command-base/default'
+import DefaultCommandBase, { flags } from '../../command-base/default'
 
 export default class LeaderDeleteBucket extends AccountsCommandBase {
   static description = `Delete distribution bucket. The bucket must have no operators. Requires distribution working group leader permissions.`
 
   static flags = {
-    bucketId: flags.integer({
-      char: 'B',
-      description: 'Distribution bucket id',
-      required: true,
-    }),
-    familyId: flags.integer({
-      char: 'f',
-      description: 'Distribution bucket family id',
+    bucketId: flags.bucketId({
       required: true,
     }),
     ...DefaultCommandBase.flags,
   }
 
   async run(): Promise<void> {
-    const { bucketId, familyId } = this.parse(LeaderDeleteBucket).flags
+    const { bucketId } = this.parse(LeaderDeleteBucket).flags
     const leadKey = await this.getDistributorLeadKey()
 
-    this.log(`Deleting distribution bucket (${bucketId})...`)
+    this.log(`Deleting distribution bucket...`, { bucketId: bucketId.toHuman() })
     await this.sendAndFollowTx(
       await this.getDecodedPair(leadKey),
-      this.api.tx.storage.deleteDistributionBucket(familyId, bucketId)
+      this.api.tx.storage.deleteDistributionBucket(bucketId)
     )
     this.log('Bucket succesfully deleted!')
   }

+ 7 - 11
distributor-node/src/commands/leader/invite-bucket-operator.ts

@@ -7,14 +7,7 @@ export default class LeaderInviteBucketOperator extends AccountsCommandBase {
   Requires distribution working group leader permissions.`
 
   static flags = {
-    bucketId: flags.integer({
-      char: 'B',
-      description: 'Distribution bucket id',
-      required: true,
-    }),
-    familyId: flags.integer({
-      char: 'f',
-      description: 'Distribution bucket family id',
+    bucketId: flags.bucketId({
       required: true,
     }),
     workerId: flags.integer({
@@ -26,13 +19,16 @@ export default class LeaderInviteBucketOperator extends AccountsCommandBase {
   }
 
   async run(): Promise<void> {
-    const { bucketId, familyId, workerId } = this.parse(LeaderInviteBucketOperator).flags
+    const { bucketId, workerId } = this.parse(LeaderInviteBucketOperator).flags
     const leadKey = await this.getDistributorLeadKey()
 
-    this.log(`Inviting distribution bucket operator (bucket: ${bucketId}, worker: ${workerId})...`)
+    this.log(`Inviting distribution bucket operator...`, {
+      bucketId: bucketId.toHuman(),
+      workerId,
+    })
     await this.sendAndFollowTx(
       await this.getDecodedPair(leadKey),
-      this.api.tx.storage.inviteDistributionBucketOperator(familyId, bucketId, workerId)
+      this.api.tx.storage.inviteDistributionBucketOperator(bucketId, workerId)
     )
     this.log('Bucket operator succesfully invited!')
   }

+ 7 - 11
distributor-node/src/commands/leader/remove-bucket-operator.ts

@@ -6,14 +6,7 @@ export default class LeaderRemoveBucketOperator extends AccountsCommandBase {
   Requires distribution working group leader permissions.`
 
   static flags = {
-    bucketId: flags.integer({
-      char: 'B',
-      description: 'Distribution bucket id',
-      required: true,
-    }),
-    familyId: flags.integer({
-      char: 'f',
-      description: 'Distribution bucket family id',
+    bucketId: flags.bucketId({
       required: true,
     }),
     workerId: flags.integer({
@@ -25,13 +18,16 @@ export default class LeaderRemoveBucketOperator extends AccountsCommandBase {
   }
 
   async run(): Promise<void> {
-    const { bucketId, familyId, workerId } = this.parse(LeaderRemoveBucketOperator).flags
+    const { bucketId, workerId } = this.parse(LeaderRemoveBucketOperator).flags
     const leadKey = await this.getDistributorLeadKey()
 
-    this.log(`Removing distribution bucket operator (bucket: ${bucketId}, worker: ${workerId})...`)
+    this.log(`Removing distribution bucket operator...`, {
+      bucketId: bucketId.toHuman(),
+      workerId,
+    })
     await this.sendAndFollowTx(
       await this.getDecodedPair(leadKey),
-      this.api.tx.storage.removeDistributionBucketOperator(familyId, bucketId, workerId)
+      this.api.tx.storage.removeDistributionBucketOperator(bucketId, workerId)
     )
     this.log('Bucket operator succesfully removed!')
   }

+ 4 - 1
distributor-node/src/commands/leader/set-bucket-family-metadata.ts

@@ -73,7 +73,10 @@ export default class LeaderSetBucketFamilyMetadata extends AccountsCommandBase {
     )
     const metadata = this.parseAndValidateMetadata(metadataInput)
 
-    this.log(`Setting bucket family metadata (family: ${familyId})`, metadata)
+    this.log(`Setting bucket family metadata`, {
+      familyId,
+      metadata,
+    })
     await this.sendAndFollowTx(
       await this.getDecodedPair(leadKey),
       this.api.tx.storage.setDistributionBucketFamilyMetadata(

+ 1 - 1
distributor-node/src/commands/leader/set-buckets-per-bag-limit.ts

@@ -18,7 +18,7 @@ export default class LeaderSetBucketsPerBagLimit extends AccountsCommandBase {
     const { limit } = this.parse(LeaderSetBucketsPerBagLimit).flags
     const leadKey = await this.getDistributorLeadKey()
 
-    this.log(`Setting new buckets per bag limit (${limit})...`)
+    this.log(`Setting new buckets per bag limit...`, { limit })
     await this.sendAndFollowTx(
       await this.getDecodedPair(leadKey),
       this.api.tx.storage.updateDistributionBucketsPerBagLimit(limit)

+ 10 - 12
distributor-node/src/commands/leader/update-bag.ts

@@ -1,3 +1,5 @@
+import { createType } from '@joystream/types'
+import { DistributionBucketIndexSet } from '@joystream/types/storage'
 import AccountsCommandBase from '../../command-base/accounts'
 import DefaultCommandBase, { flags } from '../../command-base/default'
 
@@ -14,39 +16,35 @@ export default class LeaderUpdateBag extends AccountsCommandBase {
       description: 'ID of the distribution bucket family',
       required: true,
     }),
-    add: flags.integerArr({
+    add: flags.integer({
       char: 'a',
-      description: 'ID of a bucket to add to bag',
+      description: 'Index(es) (within the family) of bucket(s) to add to the bag',
       default: [],
       multiple: true,
     }),
-    remove: flags.integerArr({
+    remove: flags.integer({
       char: 'r',
-      description: 'ID of a bucket to remove from bag',
+      description: 'Index(es) (within the family) of bucket(s) to remove from the bag',
       default: [],
       multiple: true,
     }),
     ...DefaultCommandBase.flags,
   }
 
-  static examples = [`$ joystream-distributor leader:update-bag -b 1 -f 1 -a 1 -a 2 -a 3 -r 4 -r 5`]
+  static examples = [`$ joystream-distributor leader:update-bag -b 1 -f 1 -a 1 2 3 -r 4 5`]
 
   async run(): Promise<void> {
     const { bagId, familyId, add, remove } = this.parse(LeaderUpdateBag).flags
     const leadKey = await this.getDistributorLeadKey()
 
-    this.log(
-      `Updating distribution buckets for bag ${bagId} (adding: ${add.join(',' || 'NONE')}, removing: ${
-        remove.join(',') || 'NONE'
-      })...`
-    )
+    this.log(`Updating distribution buckets for bag...`, { bagId: bagId.toHuman(), familyId, add, remove })
     await this.sendAndFollowTx(
       await this.getDecodedPair(leadKey),
       this.api.tx.storage.updateDistributionBucketsForBag(
         bagId,
         familyId,
-        this.api.createType('DistributionBucketIdSet', add),
-        this.api.createType('DistributionBucketIdSet', remove)
+        createType<DistributionBucketIndexSet, 'DistributionBucketIndexSet'>('DistributionBucketIndexSet', add),
+        createType<DistributionBucketIndexSet, 'DistributionBucketIndexSet'>('DistributionBucketIndexSet', remove)
       )
     )
     this.log('Bag succesfully updated!')

+ 4 - 11
distributor-node/src/commands/leader/update-bucket-mode.ts

@@ -5,14 +5,7 @@ export default class LeaderUpdateBucketMode extends AccountsCommandBase {
   static description = `Update distribution bucket mode ("distributing" flag). Requires distribution working group leader permissions.`
 
   static flags = {
-    bucketId: flags.integer({
-      char: 'B',
-      description: 'Distribution bucket id',
-      required: true,
-    }),
-    familyId: flags.integer({
-      char: 'f',
-      description: 'Distribution bucket family id',
+    bucketId: flags.bucketId({
       required: true,
     }),
     mode: flags.enum<'on' | 'off'>({
@@ -25,13 +18,13 @@ export default class LeaderUpdateBucketMode extends AccountsCommandBase {
   }
 
   async run(): Promise<void> {
-    const { bucketId, familyId, mode } = this.parse(LeaderUpdateBucketMode).flags
+    const { bucketId, mode } = this.parse(LeaderUpdateBucketMode).flags
     const leadKey = await this.getDistributorLeadKey()
 
-    this.log(`Updating distribution bucket mode (${bucketId}, distributing: ${mode})...`)
+    this.log(`Updating distribution bucket mode...`, { bucketId: bucketId.toHuman(), mode })
     await this.sendAndFollowTx(
       await this.getDecodedPair(leadKey),
-      this.api.tx.storage.updateDistributionBucketMode(familyId, bucketId, mode === 'on')
+      this.api.tx.storage.updateDistributionBucketMode(bucketId, mode === 'on')
     )
     this.log('Bucket mode succesfully updated!')
   }

+ 5 - 13
distributor-node/src/commands/leader/update-bucket-status.ts

@@ -1,19 +1,11 @@
-import { flags } from '@oclif/command'
 import AccountsCommandBase from '../../command-base/accounts'
-import DefaultCommandBase from '../../command-base/default'
+import DefaultCommandBase, { flags } from '../../command-base/default'
 
 export default class LeaderUpdateBucketStatus extends AccountsCommandBase {
   static description = `Update distribution bucket status ("acceptingNewBags" flag). Requires distribution working group leader permissions.`
 
   static flags = {
-    bucketId: flags.integer({
-      char: 'B',
-      description: 'Distribution bucket id',
-      required: true,
-    }),
-    familyId: flags.integer({
-      char: 'f',
-      description: 'Distribution bucket family id',
+    bucketId: flags.bucketId({
       required: true,
     }),
     acceptingBags: flags.enum<'yes' | 'no'>({
@@ -26,13 +18,13 @@ export default class LeaderUpdateBucketStatus extends AccountsCommandBase {
   }
 
   async run(): Promise<void> {
-    const { bucketId, familyId, acceptingBags } = this.parse(LeaderUpdateBucketStatus).flags
+    const { bucketId, acceptingBags } = this.parse(LeaderUpdateBucketStatus).flags
     const leadKey = await this.getDistributorLeadKey()
 
-    this.log(`Updating distribution bucket status (${bucketId}, acceptingNewBags: ${acceptingBags})...`)
+    this.log(`Updating distribution bucket status...`, { bucketId: bucketId.toHuman(), acceptingBags })
     await this.sendAndFollowTx(
       await this.getDecodedPair(leadKey),
-      this.api.tx.storage.updateDistributionBucketStatus(familyId, bucketId, acceptingBags === 'yes')
+      this.api.tx.storage.updateDistributionBucketStatus(bucketId, acceptingBags === 'yes')
     )
     this.log('Bucket status succesfully updated!')
   }

+ 6 - 2
distributor-node/src/commands/leader/update-dynamic-bag-policy.ts

@@ -27,17 +27,21 @@ export default class LeaderUpdateDynamicBagPolicy extends AccountsCommandBase {
       char: 'p',
       description: 'Key-value pair of {familyId}:{numberOfBuckets}',
       multiple: true,
+      default: [],
     }),
     ...DefaultCommandBase.flags,
   }
 
-  static examples = [`$ joystream-distributor leader:update-dynamic-bag-policy -t Member -p 1:5 -p 2:10 -p 3:5`]
+  static examples = [`$ joystream-distributor leader:update-dynamic-bag-policy -t Member -p 1:5 2:10 3:5`]
 
   async run(): Promise<void> {
     const { type, policy } = this.parse(LeaderUpdateDynamicBagPolicy).flags
     const leadKey = await this.getDistributorLeadKey()
 
-    this.log(`Updating dynamic bag policy (${type})...`)
+    this.log(`Updating dynamic bag policy...`, {
+      type,
+      policy: policy.map(([familyId, numberOfBuckets]) => ({ familyId, numberOfBuckets })),
+    })
     await this.sendAndFollowTx(
       await this.getDecodedPair(leadKey),
       this.api.tx.storage.updateFamiliesInDynamicBagCreationPolicy(

+ 12 - 6
distributor-node/src/commands/node/set-buckets.ts

@@ -1,7 +1,8 @@
-import { flags } from '@oclif/command'
 import ExitCodes from '../../command-base/ExitCodes'
 import NodeCommandBase from '../../command-base/node'
+import { flags } from '../../command-base/default'
 import { SetBucketsOperation } from '../../types'
+import { BucketIdParserService } from '../../services/parsers/BucketIdParserService'
 
 export default class NodeSetBucketsCommand extends NodeCommandBase {
   static description = `Send an api request to change the set of buckets distributed by given distributor node.`
@@ -12,15 +13,20 @@ export default class NodeSetBucketsCommand extends NodeCommandBase {
       description: 'Distribute all buckets belonging to configured worker',
       exclusive: ['bucketIds'],
     }),
-    bucketIds: flags.integer({
-      char: 'B',
-      description: 'Set of bucket ids to distribute',
-      exclusive: ['all'],
+    bucketIds: flags.bucketId({
+      description:
+        'Set of bucket ids to distribute. Each bucket id should be in {familyId}:{bucketIndex} format. ' +
+        'Multiple ids can be provided, separated by space.',
       multiple: true,
     }),
     ...NodeCommandBase.flags,
   }
 
+  static examples = [
+    '$ joystream-distributor node:set-buckets --bucketIds 1:1 1:2 1:3 2:1 2:2',
+    '$ joystream-distributor node:set-buckets --all',
+  ]
+
   protected reqUrl(): string {
     return '/api/v1/set-buckets'
   }
@@ -35,7 +41,7 @@ export default class NodeSetBucketsCommand extends NodeCommandBase {
     return all
       ? {}
       : {
-          buckets: bucketIds,
+          buckets: bucketIds.map((b) => BucketIdParserService.formatBucketId(b)),
         }
   }
 }

+ 7 - 11
distributor-node/src/commands/operator/accept-invitation.ts

@@ -6,14 +6,7 @@ export default class OperatorAcceptInvitation extends AccountsCommandBase {
   Requires the invited distribution group worker role key.`
 
   static flags = {
-    bucketId: flags.integer({
-      char: 'B',
-      description: 'Distribution bucket id',
-      required: true,
-    }),
-    familyId: flags.integer({
-      char: 'f',
-      description: 'Distribution bucket family id',
+    bucketId: flags.bucketId({
       required: true,
     }),
     workerId: flags.integer({
@@ -25,13 +18,16 @@ export default class OperatorAcceptInvitation extends AccountsCommandBase {
   }
 
   async run(): Promise<void> {
-    const { bucketId, familyId, workerId } = this.parse(OperatorAcceptInvitation).flags
+    const { bucketId, workerId } = this.parse(OperatorAcceptInvitation).flags
     const workerKey = await this.getDistributorWorkerRoleKey(workerId)
 
-    this.log(`Accepting distribution bucket operator invitation (bucket: ${bucketId}, worker: ${workerId})...`)
+    this.log(`Accepting distribution bucket operator invitation...`, {
+      bucketId: bucketId.toHuman(),
+      workerId,
+    })
     await this.sendAndFollowTx(
       await this.getDecodedPair(workerKey),
-      this.api.tx.storage.acceptDistributionBucketInvitation(workerId, familyId, bucketId)
+      this.api.tx.storage.acceptDistributionBucketInvitation(workerId, bucketId)
     )
     this.log('Invitation succesfully accepted!')
   }

+ 7 - 11
distributor-node/src/commands/operator/set-metadata.ts

@@ -9,14 +9,7 @@ export default class OperatorSetMetadata extends AccountsCommandBase {
   Requires active distribution bucket operator worker role key.`
 
   static flags = {
-    bucketId: flags.integer({
-      char: 'B',
-      description: 'Distribution bucket id',
-      required: true,
-    }),
-    familyId: flags.integer({
-      char: 'f',
-      description: 'Distribution bucket family id',
+    bucketId: flags.bucketId({
       required: true,
     }),
     workerId: flags.integer({
@@ -38,7 +31,7 @@ export default class OperatorSetMetadata extends AccountsCommandBase {
   }
 
   async run(): Promise<void> {
-    const { bucketId, familyId, workerId, input, endpoint } = this.parse(OperatorSetMetadata).flags
+    const { bucketId, workerId, input, endpoint } = this.parse(OperatorSetMetadata).flags
     const workerKey = await this.getDistributorWorkerRoleKey(workerId)
 
     const validation = new ValidationService()
@@ -46,12 +39,15 @@ export default class OperatorSetMetadata extends AccountsCommandBase {
       ? validation.validate('OperatorMetadata', JSON.parse(fs.readFileSync(input).toString()))
       : { endpoint }
 
-    this.log(`Setting bucket operator metadata (bucket: ${bucketId}, worker: ${workerId})...`, metadata)
+    this.log(`Setting bucket operator metadata...`, {
+      bucketId: bucketId.toHuman(),
+      workerId,
+      metadata,
+    })
     await this.sendAndFollowTx(
       await this.getDecodedPair(workerKey),
       this.api.tx.storage.setDistributionOperatorMetadata(
         workerId,
-        familyId,
         bucketId,
         '0x' + Buffer.from(DistributionBucketOperatorMetadata.encode(metadata).finish()).toString('hex')
       )

+ 6 - 3
distributor-node/src/schemas/configSchema.ts

@@ -1,6 +1,7 @@
 import { JSONSchema4 } from 'json-schema'
 import winston from 'winston'
 import { MAX_CONCURRENT_RESPONSE_TIME_CHECKS } from '../services/networking/NetworkingService'
+import { BucketIdParserService } from '../services/parsers/BucketIdParserService'
 import { objectSchema } from './utils'
 
 export const bytesizeUnits = ['B', 'K', 'M', 'G', 'T']
@@ -236,11 +237,13 @@ export const configSchema: JSONSchema4 = objectSchema({
     },
     buckets: {
       description:
-        'Set of bucket ids distributed by the node. If not specified, all buckets currently assigned to worker specified in `config.workerId` will be distributed.',
-      title: 'Bucket ids',
+        'Set of bucket ids distributed by the node. ' +
+        'If not specified, all buckets currently assigned to worker specified in `config.workerId` will be distributed. ' +
+        'Expected bucket id format is: {familyId}:{bucketIndex}',
+      title: "Distributed buckets' ids",
       type: 'array',
       uniqueItems: true,
-      items: { type: 'integer', minimum: 0 },
+      items: { type: 'string', pattern: BucketIdParserService.bucketIdStrRegex.source },
       minItems: 1,
     },
     workerId: {

+ 21 - 0
distributor-node/src/services/parsers/BucketIdParserService.ts

@@ -0,0 +1,21 @@
+import { DistributionBucketId } from '@joystream/types/storage'
+import { createType } from '@joystream/types'
+
+export class BucketIdParserService {
+  static readonly bucketIdStrRegex = /^[0-9]+:[0-9]+$/
+
+  public static parseBucketId(bucketIdStr: string): DistributionBucketId {
+    if (!BucketIdParserService.bucketIdStrRegex.test(bucketIdStr)) {
+      throw new Error(`Invalid bucket id! Expected format: {familyId}:{bucketIndex}`)
+    }
+    const [familyId, bucketIndex] = bucketIdStr.split(':')
+    return createType<DistributionBucketId, 'DistributionBucketId'>('DistributionBucketId', {
+      distribution_bucket_family_id: parseInt(familyId),
+      distribution_bucket_index: parseInt(bucketIndex),
+    })
+  }
+
+  public static formatBucketId(bucketId: DistributionBucketId): string {
+    return `${bucketId.distribution_bucket_family_id.toString()}:${bucketId.distribution_bucket_index.toString()}`
+  }
+}

+ 3 - 3
distributor-node/src/types/generated/ConfigJson.d.ts

@@ -6,9 +6,9 @@
  */
 
 /**
- * Set of bucket ids distributed by the node. If not specified, all buckets currently assigned to worker specified in `config.workerId` will be distributed.
+ * Set of bucket ids distributed by the node. If not specified, all buckets currently assigned to worker specified in `config.workerId` will be distributed. Expected bucket id format is: {familyId}:{bucketIndex}
  */
-export type BucketIds = number[]
+export type DistributedBucketsIds = string[]
 
 /**
  * Configuration schema for distirubtor CLI and node
@@ -128,7 +128,7 @@ export interface DistributorNodeConfiguration {
    * Specifies the keys available within distributor node CLI.
    */
   keys?: (SubstrateUri | MnemonicPhrase | JSONBackupFile)[]
-  buckets?: BucketIds
+  buckets?: DistributedBucketsIds
   /**
    * ID of the node operator (distribution working group worker)
    */

+ 1 - 1
distributor-node/src/types/generated/OperatorApi.ts

@@ -33,7 +33,7 @@ export interface components {
     }
     'SetBucketsOperation': {
       /** Set of bucket ids to be distributed by the node. If not provided - all buckets assigned to currently configured worker will be distributed. */
-      'buckets'?: number[]
+      'buckets'?: string[]
     }
   }
 }

+ 1 - 5
distributor-node/src/types/generated/PublicApi.ts

@@ -22,10 +22,6 @@ export interface paths {
 
 export interface components {
   schemas: {
-    'SetConfigBody': {
-      /** Config setting path (ie. limits.storage) */
-      'path'?: string
-    }
     'ErrorResponse': {
       'type'?: string
       'message': string
@@ -40,7 +36,7 @@ export interface components {
     }
     'BucketsResponse':
       | {
-          'bucketIds': number[]
+          'bucketIds': string[]
         }
       | {
           'allByWorkerId': number