Browse Source

deleteChannel, deleteVideo, removeChannelAssets

Leszek Wiesner 3 years ago
parent
commit
8f1c8f0345

+ 5 - 0
cli/src/Api.ts

@@ -61,6 +61,7 @@ import {
 import ExitCodes from './ExitCodes'
 import { URL } from 'url'
 import fetch from 'cross-fetch'
+import { BagId, DataObject, DataObjectId } from '@joystream/types/storage'
 
 export const DEFAULT_API_URI = 'ws://localhost:9944/'
 
@@ -632,6 +633,10 @@ export default class Api {
     return video
   }
 
+  async dataObjectsByIds(bagId: BagId, ids: DataObjectId[]): Promise<DataObject[]> {
+    return this._api.query.storage.dataObjectsById.multi(ids.map((id) => [bagId, id]))
+  }
+
   async channelCategoryIds(): Promise<ChannelCategoryId[]> {
     // There is currently no way to differentiate between unexisting and existing category
     // other than fetching all existing category ids (event the .size() trick does not work, as the object is empty)

+ 44 - 0
cli/src/commands/content/deleteChannel.ts

@@ -0,0 +1,44 @@
+import ContentDirectoryCommandBase from '../../base/ContentDirectoryCommandBase'
+import { flags } from '@oclif/command'
+import chalk from 'chalk'
+
+export default class DeleteChannelCommand extends ContentDirectoryCommandBase {
+  static description = 'Delete the channel (it cannot have any associated assets and videos).'
+
+  static flags = {
+    channelId: flags.integer({
+      char: 'c',
+      required: true,
+      description: 'ID of the Channel',
+    }),
+  }
+
+  async run(): Promise<void> {
+    const {
+      flags: { channelId },
+    } = this.parse(DeleteChannelCommand)
+    // Context
+    const account = await this.getRequiredSelectedAccount()
+    const channel = await this.getApi().channelById(channelId)
+    const actor = await this.getChannelOwnerActor(channel)
+    await this.requestAccountDecoding(account)
+
+    if (channel.num_assets.toNumber()) {
+      this.error(
+        `This channel still has ${channel.num_assets.toNumber()} associated asset(s)!\n` +
+          `Delete the assets first using ${chalk.magentaBright('content:removeChannelAssets')} command`
+      )
+    }
+
+    if (channel.num_videos.toNumber()) {
+      this.error(
+        `This channel still has ${channel.num_videos.toNumber()} associated video(s)!\n` +
+          `Delete the videos first using ${chalk.magentaBright('content:deleteVideo')} command`
+      )
+    }
+
+    await this.requireConfirmation(`Are you sure you want to remove the channel with ID ${channelId.toString()}?`)
+
+    await this.sendAndFollowNamedTx(account, 'content', 'deleteChannel', [actor, channelId])
+  }
+}

+ 51 - 0
cli/src/commands/content/deleteVideo.ts

@@ -0,0 +1,51 @@
+import ContentDirectoryCommandBase from '../../base/ContentDirectoryCommandBase'
+import { flags } from '@oclif/command'
+import BN from 'bn.js'
+import chalk from 'chalk'
+import { formatBalance } from '@polkadot/util'
+import { createTypeFromConstructor } from '@joystream/types'
+import { BagId } from '@joystream/types/storage'
+
+export default class DeleteChannelCommand extends ContentDirectoryCommandBase {
+  static description = 'Delete the video (it cannot have any associated data objects).'
+
+  static flags = {
+    videoId: flags.integer({
+      char: 'v',
+      required: true,
+      description: 'ID of the Video',
+    }),
+  }
+
+  async run(): Promise<void> {
+    const {
+      flags: { videoId },
+    } = this.parse(DeleteChannelCommand)
+    // Context
+    const account = await this.getRequiredSelectedAccount()
+    const video = await this.getApi().videoById(videoId)
+    const channel = await this.getApi().channelById(video.in_channel.toNumber())
+    const actor = await this.getChannelOwnerActor(channel)
+    await this.requestAccountDecoding(account)
+
+    const bagId = createTypeFromConstructor(BagId, { Dynamic: { Channel: video.in_channel } })
+
+    const dataObjects = await this.getApi().dataObjectsByIds(
+      bagId,
+      Array.from(video.maybe_data_objects_id_set.unwrapOr([]))
+    )
+
+    if (dataObjects.length) {
+      const deletionPrize = dataObjects.reduce((a, b) => a.add(b.deletion_prize), new BN(0))
+      this.log(
+        `Data objects deletion prize of ${chalk.cyanBright(
+          formatBalance(deletionPrize)
+        )} will be transferred to ${chalk.magentaBright(channel.deletion_prize_source_account_id.toString())}`
+      )
+    }
+
+    await this.requireConfirmation(`Are you sure you want to remove the video with ID ${videoId.toString()}?`)
+
+    await this.sendAndFollowNamedTx(account, 'content', 'deleteVideo', [actor, videoId])
+  }
+}

+ 44 - 0
cli/src/commands/content/removeChannelAssets.ts

@@ -0,0 +1,44 @@
+import ContentDirectoryCommandBase from '../../base/ContentDirectoryCommandBase'
+import { flags } from '@oclif/command'
+import { JoyBTreeSet } from '@joystream/types/common'
+import { DataObjectId } from '@joystream/types/storage'
+import { registry } from '@joystream/types'
+
+export default class RemoveChannelAssetsCommand extends ContentDirectoryCommandBase {
+  static description = 'Remove data objects associated with the channel or any of its videos.'
+
+  static flags = {
+    channelId: flags.integer({
+      char: 'c',
+      required: true,
+      description: 'ID of the Channel',
+    }),
+    objectId: flags.integer({
+      char: 'o',
+      required: true,
+      multiple: true,
+      description: 'ID of the object to remove',
+    }),
+  }
+
+  async run(): Promise<void> {
+    const {
+      flags: { channelId, objectId: objectIds },
+    } = this.parse(RemoveChannelAssetsCommand)
+    // Context
+    const account = await this.getRequiredSelectedAccount()
+    const channel = await this.getApi().channelById(channelId)
+    const actor = await this.getChannelOwnerActor(channel)
+    await this.requestAccountDecoding(account)
+
+    this.log('Channel id:', channelId)
+    this.log('List of assets to remove:', objectIds)
+    await this.requireConfirmation('Do you confirm the provided input?', true)
+
+    await this.sendAndFollowNamedTx(account, 'content', 'removeChannelAssets', [
+      actor,
+      channelId,
+      new (JoyBTreeSet(DataObjectId))(registry, objectIds),
+    ])
+  }
+}