Browse Source

Merge branch 'giza-protobuf-and-query-node' into giza-cli

Leszek Wiesner 3 years ago
parent
commit
12a1b6e1e2

+ 3 - 0
query-node/manifest.yml

@@ -83,6 +83,7 @@ typegen:
     - content.VideoDeleted
     - content.VideoCensorshipStatusUpdated
     - content.FeaturedVideosSet
+    - content.ChannelDeleted
 
     # working groups (we're using "storage_working_group" as a reference module)
     - storage_working_group.WorkerStorageUpdated
@@ -172,6 +173,8 @@ mappings:
       handler: content_VideoCensorshipStatusUpdated
     - event: content.FeaturedVideosSet
       handler: content_FeaturedVideosSet
+    - event: content.ChannelDeleted
+      handler: content_ChannelDeleted
 
     # working groups
     ## storage - workers

+ 17 - 7
query-node/mappings/content/channel.ts

@@ -4,7 +4,7 @@ eslint-disable @typescript-eslint/naming-convention
 import { EventContext, StoreContext } from '@joystream/hydra-common'
 import { Content } from '../generated/types'
 import { convertContentActorToChannelOwner, processChannelMetadata } from './utils'
-import { AssetNone, Channel, ChannelCategory, StorageDataObject } from 'query-node/dist/model'
+import { Channel, ChannelCategory, StorageDataObject } from 'query-node/dist/model'
 import { deserializeMetadata, inconsistentState, logger } from '../common'
 import { ChannelCategoryMetadata, ChannelMetadata } from '@joystream/metadata-protobuf'
 import { integrateMeta } from '@joystream/metadata-protobuf/utils'
@@ -27,9 +27,6 @@ export async function content_ChannelCreated(ctx: EventContext & StoreContext):
     createdInBlock: event.blockNumber,
     rewardAccount: channelCreationParameters.reward_account.unwrapOr(undefined)?.toString(),
     deletionPrizeDestAccount: runtimeChannel.deletion_prize_source_account_id.toString(),
-    // assets
-    coverPhoto: new AssetNone(),
-    avatarPhoto: new AssetNone(),
     // fill in auto-generated fields
     createdAt: new Date(event.blockTimestamp),
     updatedAt: new Date(event.blockTimestamp),
@@ -38,8 +35,10 @@ export async function content_ChannelCreated(ctx: EventContext & StoreContext):
   })
 
   // deserialize & process metadata
-  const metadata = deserializeMetadata(ChannelMetadata, channelCreationParameters.meta) || {}
-  await processChannelMetadata(ctx, channel, metadata, channelCreationParameters.assets)
+  if (channelCreationParameters.meta.isSome) {
+    const metadata = deserializeMetadata(ChannelMetadata, channelCreationParameters.meta.unwrap()) || {}
+    await processChannelMetadata(ctx, channel, metadata, channelCreationParameters.assets.unwrapOr(undefined))
+  }
 
   // save entity
   await store.save<Channel>(channel)
@@ -67,7 +66,12 @@ export async function content_ChannelUpdated(ctx: EventContext & StoreContext):
   //  update metadata if it was changed
   if (newMetadataBytes) {
     const newMetadata = deserializeMetadata(ChannelMetadata, newMetadataBytes) || {}
-    await processChannelMetadata(ctx, channel, newMetadata, channelUpdateParameters.assets.unwrapOr(undefined))
+    await processChannelMetadata(
+      ctx,
+      channel,
+      newMetadata,
+      channelUpdateParameters.assets_to_upload.unwrapOr(undefined)
+    )
   }
 
   // prepare changed reward account
@@ -209,3 +213,9 @@ export async function content_ChannelCategoryDeleted({ store, event }: EventCont
   // emit log event
   logger.info('Channel category has been deleted', { id: channelCategory.id })
 }
+
+export async function content_ChannelDeleted({ store, event }: EventContext & StoreContext): Promise<void> {
+  const [, channelId] = new Content.ChannelDeletedEvent(event).params
+
+  await store.remove<Channel>(new Channel({ id: channelId.toString() }))
+}

+ 119 - 113
query-node/mappings/content/utils.ts

@@ -8,7 +8,7 @@ import {
   IChannelMetadata,
 } from '@joystream/metadata-protobuf'
 import { integrateMeta, isSet, isValidLanguageCode } from '@joystream/metadata-protobuf/utils'
-import { invalidMetadata, inconsistentState, unexpectedData, logger } from '../common'
+import { invalidMetadata, inconsistentState, logger } from '../common'
 import {
   // primary entities
   CuratorGroup,
@@ -20,54 +20,121 @@ import {
   License,
   VideoMediaMetadata,
   // asset
-  Asset,
   Membership,
   VideoMediaEncoding,
   ChannelCategory,
-  AssetNone,
-  AssetExternal,
-  AssetJoystreamStorage,
   StorageDataObject,
+  DataObjectTypeChannelAvatar,
+  DataObjectTypeChannelCoverPhoto,
+  DataObjectTypeVideoMedia,
+  DataObjectTypeVideoThumbnail,
 } from 'query-node/dist/model'
 // Joystream types
-import { NewAssets, ContentActor } from '@joystream/types/augment'
+import { ContentActor, StorageAssets } from '@joystream/types/augment'
 import { DecodedMetadataObject } from '@joystream/metadata-protobuf/types'
 import BN from 'bn.js'
 import { getMostRecentlyCreatedDataObjects } from '../storage/utils'
-import { DataObjectCreationParameters as ObjectCreationParams } from '@joystream/types/storage'
-import { registry } from '@joystream/types'
+
+const ASSET_TYPES = {
+  channel: [
+    {
+      DataObjectTypeConstructor: DataObjectTypeChannelCoverPhoto,
+      metaFieldName: 'coverPhoto',
+      schemaFieldName: 'coverPhoto',
+    },
+    {
+      DataObjectTypeConstructor: DataObjectTypeChannelAvatar,
+      metaFieldName: 'avatarPhoto',
+      schemaFieldName: 'avatarPhoto',
+    },
+  ],
+  video: [
+    {
+      DataObjectTypeConstructor: DataObjectTypeVideoMedia,
+      metaFieldName: 'video',
+      schemaFieldName: 'media',
+    },
+    {
+      DataObjectTypeConstructor: DataObjectTypeVideoThumbnail,
+      metaFieldName: 'thumbnailPhoto',
+      schemaFieldName: 'thumbnailPhoto',
+    },
+  ],
+} as const
+
+async function processChannelAssets(
+  { event, store }: EventContext & StoreContext,
+  assets: StorageDataObject[],
+  channel: Channel,
+  meta: DecodedMetadataObject<IChannelMetadata>
+) {
+  await Promise.all(
+    ASSET_TYPES.channel.map(async ({ metaFieldName, schemaFieldName, DataObjectTypeConstructor }) => {
+      const newAssetIndex = meta[metaFieldName]
+      const currentAsset = channel[schemaFieldName]
+      if (isSet(newAssetIndex)) {
+        const asset = findAssetByIndex(assets, newAssetIndex)
+        if (asset) {
+          if (currentAsset) {
+            currentAsset.unsetAt = new Date(event.blockTimestamp)
+            await store.save<StorageDataObject>(currentAsset)
+          }
+          const dataObjectType = new DataObjectTypeConstructor()
+          dataObjectType.channelId = channel.id
+          asset.type = dataObjectType
+          channel[schemaFieldName] = asset
+          await store.save<StorageDataObject>(asset)
+        }
+      }
+    })
+  )
+}
+
+async function processVideoAssets(
+  { event, store }: EventContext & StoreContext,
+  assets: StorageDataObject[],
+  video: Video,
+  meta: DecodedMetadataObject<IVideoMetadata>
+) {
+  await Promise.all(
+    ASSET_TYPES.video.map(async ({ metaFieldName, schemaFieldName, DataObjectTypeConstructor }) => {
+      const newAssetIndex = meta[metaFieldName]
+      const currentAsset = video[schemaFieldName]
+      if (isSet(newAssetIndex)) {
+        const asset = findAssetByIndex(assets, newAssetIndex)
+        if (asset) {
+          if (currentAsset) {
+            currentAsset.unsetAt = new Date(event.blockTimestamp)
+            await store.save<StorageDataObject>(currentAsset)
+          }
+          const dataObjectType = new DataObjectTypeConstructor()
+          dataObjectType.videoId = video.id
+          asset.type = dataObjectType
+          video[schemaFieldName] = asset
+          await store.save<StorageDataObject>(asset)
+        }
+      }
+    })
+  )
+}
 
 export async function processChannelMetadata(
   ctx: EventContext & StoreContext,
   channel: Channel,
   meta: DecodedMetadataObject<IChannelMetadata>,
-  assets?: NewAssets
+  assetsParams?: StorageAssets
 ): Promise<Channel> {
-  const processedAssets = assets ? await processNewAssets(ctx, assets) : []
+  const assets = assetsParams ? await processNewAssets(ctx, assetsParams) : []
 
   integrateMeta(channel, meta, ['title', 'description', 'isPublic'])
 
+  await processChannelAssets(ctx, assets, channel, meta)
+
   // prepare channel category if needed
   if (isSet(meta.category)) {
     channel.category = await processChannelCategory(ctx, channel.category, parseInt(meta.category))
   }
 
-  // prepare cover photo asset if needed
-  if (isSet(meta.coverPhoto)) {
-    const asset = findAssetByIndex(processedAssets, meta.coverPhoto, 'channel cover photo')
-    if (asset) {
-      channel.coverPhoto = asset
-    }
-  }
-
-  // prepare avatar photo asset if needed
-  if (isSet(meta.avatarPhoto)) {
-    const asset = findAssetByIndex(processedAssets, meta.avatarPhoto, 'channel avatar photo')
-    if (asset) {
-      channel.avatarPhoto = asset
-    }
-  }
-
   // prepare language if needed
   if (isSet(meta.language)) {
     channel.language = await processLanguage(ctx, channel.language, meta.language)
@@ -80,21 +147,23 @@ export async function processVideoMetadata(
   ctx: EventContext & StoreContext,
   video: Video,
   meta: DecodedMetadataObject<IVideoMetadata>,
-  assets?: NewAssets
+  assetsParams?: StorageAssets
 ): Promise<Video> {
-  const processedAssets = assets ? await processNewAssets(ctx, assets) : []
+  const assets = assetsParams ? await processNewAssets(ctx, assetsParams) : []
 
   integrateMeta(video, meta, ['title', 'description', 'duration', 'hasMarketing', 'isExplicit', 'isPublic'])
 
+  await processVideoAssets(ctx, assets, video, meta)
+
   // prepare video category if needed
   if (meta.category) {
     video.category = await processVideoCategory(ctx, video.category, parseInt(meta.category))
   }
 
   // prepare media meta information if needed
-  if (isSet(meta.mediaType) || isSet(meta.mediaPixelWidth) || isSet(meta.mediaPixelHeight)) {
+  if (isSet(meta.video) || isSet(meta.mediaType) || isSet(meta.mediaPixelWidth) || isSet(meta.mediaPixelHeight)) {
     // prepare video file size if poosible
-    const videoSize = extractVideoSize(assets, meta.video)
+    const videoSize = extractVideoSize(assets)
     video.mediaMetadata = await processVideoMediaMetadata(ctx, video.mediaMetadata, meta, videoSize)
   }
 
@@ -103,22 +172,6 @@ export async function processVideoMetadata(
     await updateVideoLicense(ctx, video, meta.license)
   }
 
-  // prepare thumbnail photo asset if needed
-  if (isSet(meta.thumbnailPhoto)) {
-    const asset = findAssetByIndex(processedAssets, meta.thumbnailPhoto, 'thumbnail photo')
-    if (asset) {
-      video.thumbnailPhoto = asset
-    }
-  }
-
-  // prepare video asset if needed
-  if (isSet(meta.video)) {
-    const asset = findAssetByIndex(processedAssets, meta.video, 'video')
-    if (asset) {
-      video.media = asset
-    }
-  }
-
   // prepare language if needed
   if (isSet(meta.language)) {
     video.language = await processLanguage(ctx, video.language, meta.language)
@@ -135,7 +188,7 @@ export async function processVideoMetadata(
   return video
 }
 
-function findAssetByIndex(assets: typeof Asset[], index: number, name?: string): typeof Asset | null {
+function findAssetByIndex(assets: StorageDataObject[], index: number, name?: string): StorageDataObject | null {
   if (assets[index]) {
     return assets[index]
   }
@@ -172,7 +225,7 @@ async function processVideoMediaMetadata(
   ctx: StoreContext & EventContext,
   existingVideoMedia: VideoMediaMetadata | undefined,
   metadata: DecodedMetadataObject<IVideoMetadata>,
-  videoSize: number | undefined
+  videoSize: BN | undefined
 ): Promise<VideoMediaMetadata> {
   const { store, event } = ctx
   const videoMedia =
@@ -272,65 +325,16 @@ function processPublishedBeforeJoystream(
   return new Date(timestamp)
 }
 
-async function processNewAssets(ctx: EventContext & StoreContext, assets: NewAssets): Promise<Array<typeof Asset>> {
-  if (assets.isUrls) {
-    return assets.asUrls.map((assetUrls) => {
-      const resultAsset = new AssetExternal()
-      resultAsset.urls = JSON.stringify(assetUrls.map((u) => u.toString()))
-      return resultAsset
-    })
-  }
-
-  if (assets.isUpload) {
-    const assetsUploaded = assets.asUpload.object_creation_list.length
-    // FIXME: Ideally the runtime would provide object ids in ChannelCreated/VideoCreated/ChannelUpdated(...) events
-    const objects = await getMostRecentlyCreatedDataObjects(ctx.store, assetsUploaded)
-    return objects.map((o) => {
-      const resultAsset = new AssetJoystreamStorage()
-      resultAsset.dataObjectId = o.id
-      return resultAsset
-    })
-  }
-
-  unexpectedData('Unrecognized assets type', assets.type)
+async function processNewAssets(ctx: EventContext & StoreContext, assets: StorageAssets): Promise<StorageDataObject[]> {
+  const assetsUploaded = assets.object_creation_list.length
+  // FIXME: Ideally the runtime would provide object ids in ChannelCreated/VideoCreated/ChannelUpdated(...) events
+  const objects = await getMostRecentlyCreatedDataObjects(ctx.store, assetsUploaded)
+  return objects
 }
 
-function extractVideoSize(assets: NewAssets | undefined, assetIndex: number | null | undefined): number | undefined {
-  // escape if no assetIndex is set
-  if (!isSet(assetIndex)) {
-    return undefined
-  }
-
-  // index provided, but there are no assets
-  if (!assets) {
-    invalidMetadata(`Non-existing asset video size extraction requested - no assets were uploaded!`, {
-      assetIndex,
-    })
-    return undefined
-  }
-
-  // cannot extract size from other asset types than "Upload"
-  if (!assets.isUpload) {
-    return undefined
-  }
-
-  const dataObjectsParams = assets.asUpload.object_creation_list
-
-  // ensure asset index is valid
-  if (assetIndex >= dataObjectsParams.length) {
-    invalidMetadata(`Non-existing asset video size extraction requested`, {
-      assetsProvided: dataObjectsParams.length,
-      assetIndex,
-    })
-    return undefined
-  }
-
-  // extract video size from objectParams
-  const objectParams = assets.asUpload.object_creation_list[assetIndex]
-  const params = new ObjectCreationParams(registry, objectParams.toJSON() as any)
-  const videoSize = params.getField('size').toNumber()
-
-  return videoSize
+function extractVideoSize(assets: StorageDataObject[]): BN | undefined {
+  const mediaAsset = assets.find((a) => a.type?.isTypeOf === DataObjectTypeVideoMedia.name)
+  return mediaAsset ? mediaAsset.size : undefined
 }
 
 async function processLanguage(
@@ -476,23 +480,25 @@ export async function unsetAssetRelations(store: DatabaseManager, dataObject: St
   // is allowed to be associated only with one channel/video in runtime
   const channel = await store.get(Channel, {
     where: channelAssets.map((assetName) => ({
-      [assetName]: Raw((alias) => `${alias} ->> 'dataObjectId' = :id`, {
+      [assetName]: {
         id: dataObject.id,
-      }),
+      },
     })),
+    relations: [...channelAssets],
   })
   const video = await store.get(Video, {
     where: videoAssets.map((assetName) => ({
-      [assetName]: Raw((alias) => `${alias} ->> 'dataObjectId' = :id`, {
+      [assetName]: {
         id: dataObject.id,
-      }),
+      },
     })),
+    relations: [...videoAssets],
   })
 
   if (channel) {
     channelAssets.forEach((assetName) => {
-      if (channel[assetName] && (channel[assetName] as AssetJoystreamStorage).dataObjectId === dataObject.id) {
-        channel[assetName] = new AssetNone()
+      if (channel[assetName] && channel[assetName]?.id === dataObject.id) {
+        channel[assetName] = null as any
       }
     })
     await store.save<Channel>(channel)
@@ -506,8 +512,8 @@ export async function unsetAssetRelations(store: DatabaseManager, dataObject: St
 
   if (video) {
     videoAssets.forEach((assetName) => {
-      if (video[assetName] && (video[assetName] as AssetJoystreamStorage).dataObjectId === dataObject.id) {
-        video[assetName] = new AssetNone()
+      if (video[assetName] && video[assetName]?.id === dataObject.id) {
+        video[assetName] = null as any
       }
     })
     await store.save<Video>(video)

+ 6 - 6
query-node/mappings/content/video.ts

@@ -6,7 +6,7 @@ import { In } from 'typeorm'
 import { Content } from '../generated/types'
 import { deserializeMetadata, inconsistentState, logger } from '../common'
 import { processVideoMetadata } from './utils'
-import { Channel, Video, VideoCategory, AssetNone } from 'query-node/dist/model'
+import { Channel, Video, VideoCategory } from 'query-node/dist/model'
 import { VideoMetadata, VideoCategoryMetadata } from '@joystream/metadata-protobuf'
 import { integrateMeta } from '@joystream/metadata-protobuf/utils'
 import _ from 'lodash'
@@ -107,14 +107,14 @@ export async function content_VideoCreated(ctx: EventContext & StoreContext): Pr
     isCensored: false,
     isFeatured: false,
     createdInBlock: event.blockNumber,
-    thumbnailPhoto: new AssetNone(),
-    media: new AssetNone(),
     createdAt: new Date(event.blockTimestamp),
     updatedAt: new Date(event.blockTimestamp),
   })
   // deserialize & process metadata
-  const metadata = deserializeMetadata(VideoMetadata, videoCreationParameters.meta) || {}
-  await processVideoMetadata(ctx, video, metadata, videoCreationParameters.assets)
+  if (videoCreationParameters.meta.isSome) {
+    const metadata = deserializeMetadata(VideoMetadata, videoCreationParameters.meta.unwrap()) || {}
+    await processVideoMetadata(ctx, video, metadata, videoCreationParameters.assets.unwrapOr(undefined))
+  }
 
   // save video
   await store.save<Video>(video)
@@ -145,7 +145,7 @@ export async function content_VideoUpdated(ctx: EventContext & StoreContext): Pr
   // update metadata if it was changed
   if (newMetadataBytes) {
     const newMetadata = deserializeMetadata(VideoMetadata, newMetadataBytes) || {}
-    await processVideoMetadata(ctx, video, newMetadata, videoUpdateParameters.assets.unwrapOr(undefined))
+    await processVideoMetadata(ctx, video, newMetadata, videoUpdateParameters.assets_to_upload.unwrapOr(undefined))
   }
 
   // set last update time

+ 21 - 155
query-node/mappings/storage/index.ts

@@ -1,7 +1,7 @@
 /*
 eslint-disable @typescript-eslint/naming-convention
 */
-import { DatabaseManager, EventContext, StoreContext } from '@joystream/hydra-common'
+import { EventContext, StoreContext } from '@joystream/hydra-common'
 import { Storage } from '../generated/types/storage'
 import {
   DistributionBucket,
@@ -11,11 +11,6 @@ import {
   DistributionBucketOperatorStatus,
   NodeLocationMetadata,
   StorageBag,
-  StorageBagOwner,
-  StorageBagOwnerChannel,
-  StorageBagOwnerCouncil,
-  StorageBagOwnerMember,
-  StorageBagOwnerWorkingGroup,
   StorageBucket,
   StorageBucketOperatorStatusActive,
   StorageBucketOperatorStatusInvited,
@@ -28,156 +23,25 @@ import {
 } from 'query-node/dist/model'
 import BN from 'bn.js'
 import { getById } from '../common'
-import { BTreeSet } from '@polkadot/types'
 import { In } from 'typeorm'
-import _ from 'lodash'
-import { DataObjectId, BagId, DynamicBagId, StaticBagId } from '@joystream/types/augment/all'
 import {
   processDistributionBucketFamilyMetadata,
   processDistributionOperatorMetadata,
   processStorageOperatorMetadata,
 } from './metadata'
-import { createDataObjects, getStorageSystem, removeDataObject } from './utils'
-
-async function getDataObjectsInBag(
-  store: DatabaseManager,
-  bagId: BagId,
-  dataObjectIds: BTreeSet<DataObjectId>
-): Promise<StorageDataObject[]> {
-  const dataObjects = await store.getMany(StorageDataObject, {
-    where: {
-      id: In(Array.from(dataObjectIds).map((id) => id.toString())),
-      storageBag: { id: getBagId(bagId) },
-    },
-  })
-  if (dataObjects.length !== Array.from(dataObjectIds).length) {
-    throw new Error(
-      `Missing data objects: ${_.difference(
-        Array.from(dataObjectIds).map((id) => id.toString()),
-        dataObjects.map((o) => o.id)
-      )} in bag ${getBagId(bagId)}`
-    )
-  }
-  return dataObjects
-}
-
-function getStaticBagOwner(bagId: StaticBagId): typeof StorageBagOwner {
-  if (bagId.isCouncil) {
-    return new StorageBagOwnerCouncil()
-  } else if (bagId.isWorkingGroup) {
-    const owner = new StorageBagOwnerWorkingGroup()
-    owner.workingGroupId = bagId.asWorkingGroup.toString().toLowerCase()
-    return owner
-  } else {
-    throw new Error(`Unexpected static bag type: ${bagId.type}`)
-  }
-}
-
-function getDynamicBagOwner(bagId: DynamicBagId) {
-  if (bagId.isChannel) {
-    const owner = new StorageBagOwnerChannel()
-    owner.channelId = bagId.asChannel.toNumber()
-    return owner
-  } else if (bagId.isMember) {
-    const owner = new StorageBagOwnerMember()
-    owner.memberId = bagId.asMember.toNumber()
-    return owner
-  } else {
-    throw new Error(`Unexpected dynamic bag type: ${bagId.type}`)
-  }
-}
-
-function getStaticBagId(bagId: StaticBagId): string {
-  if (bagId.isCouncil) {
-    return `static:council`
-  } else if (bagId.isWorkingGroup) {
-    return `static:wg:${bagId.asWorkingGroup.type.toLowerCase()}`
-  } else {
-    throw new Error(`Unexpected static bag type: ${bagId.type}`)
-  }
-}
-
-function getDynamicBagId(bagId: DynamicBagId): string {
-  if (bagId.isChannel) {
-    return `dynamic:channel:${bagId.asChannel.toString()}`
-  } else if (bagId.isMember) {
-    return `dynamic:member:${bagId.asMember.toString()}`
-  } else {
-    throw new Error(`Unexpected dynamic bag type: ${bagId.type}`)
-  }
-}
-
-function getBagId(bagId: BagId) {
-  return bagId.isStatic ? getStaticBagId(bagId.asStatic) : getDynamicBagId(bagId.asDynamic)
-}
-
-async function getDynamicBag(
-  store: DatabaseManager,
-  bagId: DynamicBagId,
-  relations?: 'objects'[]
-): Promise<StorageBag> {
-  return getById(store, StorageBag, getDynamicBagId(bagId), relations)
-}
-
-async function getStaticBag(store: DatabaseManager, bagId: StaticBagId, relations?: 'objects'[]): Promise<StorageBag> {
-  const id = getStaticBagId(bagId)
-  const bag = await store.get(StorageBag, { where: { id }, relations })
-  if (!bag) {
-    console.log(`Creating new static bag: ${id}`)
-    const newBag = new StorageBag({
-      id,
-      owner: getStaticBagOwner(bagId),
-    })
-    await store.save<StorageBag>(newBag)
-    return newBag
-  }
-  return bag
-}
-
-async function getBag(store: DatabaseManager, bagId: BagId, relations?: 'objects'[]): Promise<StorageBag> {
-  return bagId.isStatic
-    ? getStaticBag(store, bagId.asStatic, relations)
-    : getDynamicBag(store, bagId.asDynamic, relations)
-}
-
-async function getDistributionBucketOperatorWithMetadata(
-  store: DatabaseManager,
-  id: string
-): Promise<DistributionBucketOperator> {
-  const operator = await store.get(DistributionBucketOperator, {
-    where: { id },
-    relations: ['metadata', 'metadata.nodeLocation', 'metadata.nodeLocation.coordinates'],
-  })
-  if (!operator) {
-    throw new Error(`DistributionBucketOperator not found by id: ${id}`)
-  }
-  return operator
-}
-
-async function getStorageBucketWithOperatorMetadata(store: DatabaseManager, id: string): Promise<StorageBucket> {
-  const bucket = await store.get(StorageBucket, {
-    where: { id },
-    relations: ['operatorMetadata', 'operatorMetadata.nodeLocation', 'operatorMetadata.nodeLocation.coordinates'],
-  })
-  if (!bucket) {
-    throw new Error(`StorageBucket not found by id: ${id}`)
-  }
-  return bucket
-}
-
-async function getDistributionBucketFamilyWithMetadata(
-  store: DatabaseManager,
-  id: string
-): Promise<DistributionBucketFamily> {
-  const family = await store.get(DistributionBucketFamily, {
-    where: { id },
-    relations: ['metadata', 'metadata.areas'],
-  })
-  if (!family) {
-    throw new Error(`DistributionBucketFamily not found by id: ${id}`)
-  }
-  return family
-}
+import {
+  createDataObjects,
+  getStorageSystem,
+  removeDataObject,
+  getStorageBucketWithOperatorMetadata,
+  getBag,
+  getDynamicBagId,
+  getDynamicBagOwner,
+  getDataObjectsInBag,
+  getDynamicBag,
+  getDistributionBucketFamilyWithMetadata,
+  getDistributionBucketOperatorWithMetadata,
+} from './utils'
 
 // STORAGE BUCKETS
 
@@ -363,7 +227,11 @@ export async function storage_DynamicBagCreated({ event, store }: EventContext &
 export async function storage_DynamicBagDeleted({ event, store }: EventContext & StoreContext): Promise<void> {
   const [, bagId] = new Storage.DynamicBagDeletedEvent(event).params
   const storageBag = await getDynamicBag(store, bagId, ['objects'])
-  // The bag should already be empty, so no cascade-remove required
+  // TODO: Cascade remove on db level (would require changes in Hydra / comitting autogenerated files)
+  const storageAssignments = await store.getMany(StorageBagStorageAssignment, { where: { storageBag } })
+  const distributionAssignments = await store.getMany(StorageBagDistributionAssignment, { where: { storageBag } })
+  await Promise.all(storageAssignments.map((a) => store.remove<StorageBagStorageAssignment>(a)))
+  await Promise.all(distributionAssignments.map((a) => store.remove<StorageBagDistributionAssignment>(a)))
   await store.remove<StorageBag>(storageBag)
 }
 
@@ -371,10 +239,8 @@ export async function storage_DynamicBagDeleted({ event, store }: EventContext &
 
 // Note: "Uploaded" here actually means "created" (the real upload happens later)
 export async function storage_DataObjectsUploaded({ event, store }: EventContext & StoreContext): Promise<void> {
-  const [dataObjectIds, uploadParams] = new Storage.DataObjectsUploadedEvent(event).params
-  const { bagId, objectCreationList } = uploadParams
-  const storageBag = await getBag(store, bagId)
-  await createDataObjects(store, objectCreationList, storageBag, dataObjectIds)
+  const [dataObjectIds, uploadParams, deletionPrize] = new Storage.DataObjectsUploadedEvent(event).params
+  await createDataObjects(store, uploadParams, deletionPrize, dataObjectIds)
 }
 
 export async function storage_PendingDataObjectsAccepted({ event, store }: EventContext & StoreContext): Promise<void> {

+ 173 - 7
query-node/mappings/storage/utils.ts

@@ -1,13 +1,175 @@
 import { DatabaseManager } from '@joystream/hydra-common'
-import { DataObjectCreationParameters } from '@joystream/types/augment'
+import { UploadParameters } from '@joystream/types/augment'
 import { registry } from '@joystream/types'
 import { DataObjectCreationParameters as ObjectCreationParams } from '@joystream/types/storage'
-import { StorageBag, StorageDataObject, StorageSystemParameters } from 'query-node/dist/model'
+import {
+  DataObjectTypeUnknown,
+  StorageBag,
+  StorageDataObject,
+  StorageSystemParameters,
+  StorageBagOwner,
+  StorageBagOwnerChannel,
+  StorageBagOwnerCouncil,
+  StorageBagOwnerMember,
+  StorageBagOwnerWorkingGroup,
+  StorageBucket,
+  DistributionBucketOperator,
+  DistributionBucketFamily,
+} from 'query-node/dist/model'
 import BN from 'bn.js'
-import { bytesToString, inconsistentState } from '../common'
+import { bytesToString, inconsistentState, getById } from '../common'
 import { In } from 'typeorm'
 import { unsetAssetRelations } from '../content/utils'
 
+import { BTreeSet } from '@polkadot/types'
+import _ from 'lodash'
+import { DataObjectId, BagId, DynamicBagId, StaticBagId } from '@joystream/types/augment/all'
+import { Balance } from '@polkadot/types/interfaces'
+
+export async function getDataObjectsInBag(
+  store: DatabaseManager,
+  bagId: BagId,
+  dataObjectIds: BTreeSet<DataObjectId>
+): Promise<StorageDataObject[]> {
+  const dataObjects = await store.getMany(StorageDataObject, {
+    where: {
+      id: In(Array.from(dataObjectIds).map((id) => id.toString())),
+      storageBag: { id: getBagId(bagId) },
+    },
+  })
+  if (dataObjects.length !== Array.from(dataObjectIds).length) {
+    throw new Error(
+      `Missing data objects: ${_.difference(
+        Array.from(dataObjectIds).map((id) => id.toString()),
+        dataObjects.map((o) => o.id)
+      )} in bag ${getBagId(bagId)}`
+    )
+  }
+  return dataObjects
+}
+
+export function getStaticBagOwner(bagId: StaticBagId): typeof StorageBagOwner {
+  if (bagId.isCouncil) {
+    return new StorageBagOwnerCouncil()
+  } else if (bagId.isWorkingGroup) {
+    const owner = new StorageBagOwnerWorkingGroup()
+    owner.workingGroupId = bagId.asWorkingGroup.toString().toLowerCase()
+    return owner
+  } else {
+    throw new Error(`Unexpected static bag type: ${bagId.type}`)
+  }
+}
+
+export function getDynamicBagOwner(bagId: DynamicBagId) {
+  if (bagId.isChannel) {
+    const owner = new StorageBagOwnerChannel()
+    owner.channelId = bagId.asChannel.toNumber()
+    return owner
+  } else if (bagId.isMember) {
+    const owner = new StorageBagOwnerMember()
+    owner.memberId = bagId.asMember.toNumber()
+    return owner
+  } else {
+    throw new Error(`Unexpected dynamic bag type: ${bagId.type}`)
+  }
+}
+
+export function getStaticBagId(bagId: StaticBagId): string {
+  if (bagId.isCouncil) {
+    return `static:council`
+  } else if (bagId.isWorkingGroup) {
+    return `static:wg:${bagId.asWorkingGroup.type.toLowerCase()}`
+  } else {
+    throw new Error(`Unexpected static bag type: ${bagId.type}`)
+  }
+}
+
+export function getDynamicBagId(bagId: DynamicBagId): string {
+  if (bagId.isChannel) {
+    return `dynamic:channel:${bagId.asChannel.toString()}`
+  } else if (bagId.isMember) {
+    return `dynamic:member:${bagId.asMember.toString()}`
+  } else {
+    throw new Error(`Unexpected dynamic bag type: ${bagId.type}`)
+  }
+}
+
+export function getBagId(bagId: BagId) {
+  return bagId.isStatic ? getStaticBagId(bagId.asStatic) : getDynamicBagId(bagId.asDynamic)
+}
+
+export async function getDynamicBag(
+  store: DatabaseManager,
+  bagId: DynamicBagId,
+  relations?: 'objects'[]
+): Promise<StorageBag> {
+  return getById(store, StorageBag, getDynamicBagId(bagId), relations)
+}
+
+export async function getStaticBag(
+  store: DatabaseManager,
+  bagId: StaticBagId,
+  relations?: 'objects'[]
+): Promise<StorageBag> {
+  const id = getStaticBagId(bagId)
+  const bag = await store.get(StorageBag, { where: { id }, relations })
+  if (!bag) {
+    console.log(`Creating new static bag: ${id}`)
+    const newBag = new StorageBag({
+      id,
+      owner: getStaticBagOwner(bagId),
+    })
+    await store.save<StorageBag>(newBag)
+    return newBag
+  }
+  return bag
+}
+
+export async function getBag(store: DatabaseManager, bagId: BagId, relations?: 'objects'[]): Promise<StorageBag> {
+  return bagId.isStatic
+    ? getStaticBag(store, bagId.asStatic, relations)
+    : getDynamicBag(store, bagId.asDynamic, relations)
+}
+
+export async function getDistributionBucketOperatorWithMetadata(
+  store: DatabaseManager,
+  id: string
+): Promise<DistributionBucketOperator> {
+  const operator = await store.get(DistributionBucketOperator, {
+    where: { id },
+    relations: ['metadata', 'metadata.nodeLocation', 'metadata.nodeLocation.coordinates'],
+  })
+  if (!operator) {
+    throw new Error(`DistributionBucketOperator not found by id: ${id}`)
+  }
+  return operator
+}
+
+export async function getStorageBucketWithOperatorMetadata(store: DatabaseManager, id: string): Promise<StorageBucket> {
+  const bucket = await store.get(StorageBucket, {
+    where: { id },
+    relations: ['operatorMetadata', 'operatorMetadata.nodeLocation', 'operatorMetadata.nodeLocation.coordinates'],
+  })
+  if (!bucket) {
+    throw new Error(`StorageBucket not found by id: ${id}`)
+  }
+  return bucket
+}
+
+export async function getDistributionBucketFamilyWithMetadata(
+  store: DatabaseManager,
+  id: string
+): Promise<DistributionBucketFamily> {
+  const family = await store.get(DistributionBucketFamily, {
+    where: { id },
+    relations: ['metadata', 'metadata.areas'],
+  })
+  if (!family) {
+    throw new Error(`DistributionBucketFamily not found by id: ${id}`)
+  }
+  return family
+}
+
 export async function getStorageSystem(store: DatabaseManager): Promise<StorageSystemParameters> {
   const storageSystem = await store.get(StorageSystemParameters, {})
   if (!storageSystem) {
@@ -19,13 +181,15 @@ export async function getStorageSystem(store: DatabaseManager): Promise<StorageS
 
 export async function createDataObjects(
   store: DatabaseManager,
-  objectsParams: DataObjectCreationParameters[],
-  storageBag: StorageBag,
+  uploadParams: UploadParameters,
+  deletionPrize: Balance,
   objectIds?: BN[]
 ): Promise<StorageDataObject[]> {
   const storageSystem = await getStorageSystem(store)
+  const { objectCreationList, bagId } = uploadParams
+  const storageBag = await getBag(store, bagId)
 
-  const dataObjects = objectsParams.map((objectParams, i) => {
+  const dataObjects = objectCreationList.map((objectParams, i) => {
     const params = new ObjectCreationParams(registry, objectParams.toJSON() as any)
     const objectId = objectIds ? objectIds[i] : storageSystem.nextDataObjectId
     const object = new StorageDataObject({
@@ -33,6 +197,8 @@ export async function createDataObjects(
       isAccepted: false,
       ipfsHash: bytesToString(objectParams.ipfsContentId),
       size: new BN(params.getField('size').toString()),
+      type: new DataObjectTypeUnknown(),
+      deletionPrize,
       storageBag,
     })
     if (objectId.gte(storageSystem.nextDataObjectId)) {
@@ -67,5 +233,5 @@ export async function getMostRecentlyCreatedDataObjects(
 
 export async function removeDataObject(store: DatabaseManager, object: StorageDataObject): Promise<void> {
   await unsetAssetRelations(store, object)
-  await store.save<StorageDataObject>(object)
+  await store.remove<StorageDataObject>(object)
 }

+ 4 - 25
query-node/schemas/content.graphql

@@ -1,21 +1,3 @@
-type AssetExternal @variant {
-  # FIXME: [String!] currnetly not supported in variants
-  "JSON array of the urls"
-  urls: String!
-}
-
-type AssetJoystreamStorage @variant {
-  "Related data object"
-  dataObject: StorageDataObject!
-}
-
-# FIXME: https://github.com/Joystream/hydra/issues/434
-type AssetNone @variant {
-  _phantom: Int
-}
-
-union Asset = AssetExternal | AssetJoystreamStorage | AssetNone
-
 "Category of media channel"
 type ChannelCategory @entity {
   id: ID!
@@ -62,12 +44,11 @@ type Channel @entity {
   "The description of a Channel"
   description: String
 
-  # FIXME: Due to https://github.com/Joystream/hydra/issues/434, Asset is currently non-optional (use AssetNone to unset it)
   "Channel's cover (background) photo asset. Recommended ratio: 16:9."
-  coverPhoto: Asset!
+  coverPhoto: StorageDataObject
 
   "Channel's avatar photo asset."
-  avatarPhoto: Asset!
+  avatarPhoto: StorageDataObject
 
   ##########################
 
@@ -129,9 +110,8 @@ type Video @entity {
   "Video duration in seconds"
   duration: Int
 
-# FIXME: Due to https://github.com/Joystream/hydra/issues/434, Asset is currently non-optional (use AssetNone to unset it)
   "Video thumbnail asset (recommended ratio: 16:9)"
-  thumbnailPhoto: Asset!
+  thumbnailPhoto: StorageDataObject
 
   ##########################
 
@@ -156,9 +136,8 @@ type Video @entity {
   "License under the video is published"
   license: License
 
-  # FIXME: Due to https://github.com/Joystream/hydra/issues/434, Asset is currently non-optional (use AssetNone to unset it)
   "Video media asset"
-  media: Asset!
+  media: StorageDataObject
 
   ##########################
 

+ 36 - 0
query-node/schemas/storage.graphql

@@ -191,6 +191,32 @@ type StorageBagDistributionAssignment @entity {
   distributionBucketId: ID
 }
 
+type DataObjectTypeChannelAvatar @variant {
+  "Related channel entity"
+  channel: Channel!
+}
+
+type DataObjectTypeChannelCoverPhoto @variant {
+  "Related channel entity"
+  channel: Channel!
+}
+
+type DataObjectTypeVideoMedia @variant {
+  "Related video entity"
+  video: Video!
+}
+
+type DataObjectTypeVideoThumbnail @variant {
+  "Related video entity"
+  video: Video!
+}
+
+type DataObjectTypeUnknown @variant {
+  _phantom: Int
+}
+
+union DataObjectType = DataObjectTypeChannelAvatar | DataObjectTypeChannelCoverPhoto | DataObjectTypeVideoMedia | DataObjectTypeVideoThumbnail | DataObjectTypeUnknown
+
 type StorageDataObject @entity {
   "Data object runtime id"
   id: ID!
@@ -206,6 +232,16 @@ type StorageDataObject @entity {
 
   "IPFS content hash"
   ipfsHash: String!
+
+  # FIXME: Cannot be optional because: https://github.com/Joystream/hydra/issues/434
+  "The type of the asset that the data object represents (if known)"
+  type: DataObjectType!
+
+  "Prize for removing the data object"
+  deletionPrize: BigInt!
+
+  "If the object is no longer used as an asset - the time at which it was unset (if known)"
+  unsetAt: DateTime
 }
 
 type DistributionBucketFamilyGeographicArea @entity {

+ 4 - 0
runtime-modules/content/src/errors.rs

@@ -76,5 +76,9 @@ decl_error! {
         /// Channel Contains Assets
         ChannelContainsAssets,
 
+        /// Bag Size specified is not valid
+        InvalidBagSizeSpecified,
+
+
     }
 }

+ 21 - 3
runtime-modules/content/src/lib.rs

@@ -652,6 +652,7 @@ decl_module! {
                     &sender,
                 )?;
             }
+
             //
             // == MUTATION SAFE ==
             //
@@ -727,7 +728,7 @@ decl_module! {
             origin,
             actor: ContentActor<T::CuratorGroupId, T::CuratorId, T::MemberId>,
             channel_id: T::ChannelId,
-            assets_to_remove: BTreeSet<DataObjectId<T>>,
+            num_objects_to_delete: u64,
         ) -> DispatchResult {
             // check that channel exists
             let channel = Self::ensure_channel_exists(&channel_id)?;
@@ -742,11 +743,28 @@ decl_module! {
             // check that channel videos are 0
             ensure!(channel.num_videos == 0, Error::<T>::ChannelContainsVideos);
 
+            // get bag id for the channel
+            let dyn_bag = DynamicBagIdType::<T::MemberId, T::ChannelId>::Channel(channel_id);
+            let bag_id = storage::BagIdType::from(dyn_bag.clone());
+
+            // ensure that bag size provided is valid
+            ensure!(
+                storage::Bags::<T>::get(&bag_id).objects_number == num_objects_to_delete,
+                Error::<T>::InvalidBagSizeSpecified
+            );
+
+            // construct collection of assets to be removed
+            let assets_to_remove: BTreeSet<DataObjectId<T>> =
+                storage::DataObjectsById::<T>::iter_prefix(&bag_id).map(|x| x.0).collect();
+
             // remove specified assets from storage
-            Self::remove_assets_from_storage(&assets_to_remove, &channel_id, &channel.deletion_prize_source_account_id)?;
+            Self::remove_assets_from_storage(
+                &assets_to_remove,
+                &channel_id,
+                &channel.deletion_prize_source_account_id
+            )?;
 
             // delete channel dynamic bag
-            let dyn_bag = DynamicBagIdType::<T::MemberId, T::ChannelId>::Channel(channel_id);
             Storage::<T>::delete_dynamic_bag(
                 channel.deletion_prize_source_account_id,
                 dyn_bag

+ 5 - 9
runtime-modules/content/src/tests/channels.rs

@@ -19,7 +19,6 @@ fn successful_channel_deletion() {
         );
 
         // 3 assets added at creation
-        let first_obj_id = Storage::<Test>::next_data_object_id();
         let assets = StorageAssetsRecord {
             object_creation_list: vec![
                 DataObjectCreationParameters {
@@ -51,17 +50,14 @@ fn successful_channel_deletion() {
             Ok(()),
         );
 
-        // retrieve objs id set
-        let obj_ids =
-            (first_obj_id..Storage::<Test>::next_data_object_id()).collect::<BTreeSet<_>>();
-
-        // attempt to delete channel with non zero assets should result in error
+        // attempt to delete channel with non zero assets should result in error: objects
+        // are misspecified
         delete_channel_mock(
             FIRST_MEMBER_ORIGIN,
             ContentActor::Member(FIRST_MEMBER_ID),
             channel_id,
-            BTreeSet::new(),
-            Err(storage::Error::<Test>::CannotDeleteNonEmptyDynamicBag.into()),
+            2u64,
+            Err(Error::<Test>::InvalidBagSizeSpecified.into()),
         );
 
         // successful deletion because we empty the bag first
@@ -69,7 +65,7 @@ fn successful_channel_deletion() {
             FIRST_MEMBER_ORIGIN,
             ContentActor::Member(FIRST_MEMBER_ID),
             channel_id,
-            obj_ids,
+            3u64,
             Ok(()),
         );
     })

+ 2 - 2
runtime-modules/content/src/tests/mock.rs

@@ -507,7 +507,7 @@ pub fn delete_channel_mock(
     sender: u64,
     actor: ContentActor<CuratorGroupId, CuratorId, MemberId>,
     channel_id: ChannelId,
-    assets: BTreeSet<<Test as storage::Trait>::DataObjectId>,
+    objects_num: u64,
     result: DispatchResult,
 ) {
     assert_eq!(
@@ -515,7 +515,7 @@ pub fn delete_channel_mock(
             Origin::signed(sender),
             actor.clone(),
             channel_id.clone(),
-            assets.clone()
+            objects_num,
         ),
         result.clone(),
     );

File diff suppressed because it is too large
+ 1 - 1
types/augment-codec/all.ts


+ 4 - 0
types/augment-codec/augment-api-errors.ts

@@ -138,6 +138,10 @@ declare module '@polkadot/api/types/errors' {
        * Channel assets feasibility
        **/
       InvalidAssetsProvided: AugmentedError<ApiType>;
+      /**
+       * Bag Size specified is not valid
+       **/
+      InvalidBagSizeSpecified: AugmentedError<ApiType>;
       /**
        * Lead authentication failed
        **/

+ 7 - 6
types/augment-codec/augment-api-events.ts

@@ -2,7 +2,7 @@
 /* eslint-disable */
 
 import type { BTreeMap, BTreeSet, Bytes, Option, Vec, bool, u32, u64 } from '@polkadot/types';
-import type { ApplicationId, ApplicationIdToWorkerIdMap, BagId, CategoryId, Channel, ChannelCategory, ChannelCategoryCreationParameters, ChannelCategoryId, ChannelCategoryUpdateParameters, ChannelCreationParameters, ChannelId, ChannelOwnershipTransferRequest, ChannelOwnershipTransferRequestId, ChannelUpdateParameters, Cid, ContentActor, CuratorGroupId, CuratorId, DataObjectId, DistributionBucketFamilyId, DistributionBucketId, DynamicBagDeletionPrizeRecord, DynamicBagId, DynamicBagType, EntryMethod, IsCensored, MemberId, MintBalanceOf, MintId, NewAssets, OpeningId, PersonCreationParameters, PersonId, PersonUpdateParameters, PlaylistCreationParameters, PlaylistId, PlaylistUpdateParameters, PostId, ProposalId, ProposalStatus, RationaleText, Series, SeriesId, SeriesParameters, StorageBucketId, ThreadId, UploadParameters, VideoCategoryCreationParameters, VideoCategoryId, VideoCategoryUpdateParameters, VideoCreationParameters, VideoId, VideoUpdateParameters, VoteKind, Voucher, WorkerId } from './all';
+import type { ApplicationId, ApplicationIdToWorkerIdMap, BagId, CategoryId, Channel, ChannelCategory, ChannelCategoryCreationParameters, ChannelCategoryId, ChannelCategoryUpdateParameters, ChannelCreationParameters, ChannelId, ChannelOwnershipTransferRequest, ChannelOwnershipTransferRequestId, ChannelUpdateParameters, Cid, ContentActor, CuratorGroupId, CuratorId, DataObjectId, DistributionBucketFamilyId, DistributionBucketId, DynamicBagDeletionPrizeRecord, DynamicBagId, DynamicBagType, EntryMethod, IsCensored, MemberId, MintBalanceOf, MintId, OpeningId, PersonCreationParameters, PersonId, PersonUpdateParameters, PlaylistCreationParameters, PlaylistId, PlaylistUpdateParameters, PostId, ProposalId, ProposalStatus, RationaleText, Series, SeriesId, SeriesParameters, StorageAssets, StorageBucketId, ThreadId, UploadParameters, VideoCategoryCreationParameters, VideoCategoryId, VideoCategoryUpdateParameters, VideoCreationParameters, VideoId, VideoUpdateParameters, VoteKind, Voucher, WorkerId } from './all';
 import type { BalanceStatus } from '@polkadot/types/interfaces/balances';
 import type { AuthorityId } from '@polkadot/types/interfaces/consensus';
 import type { AuthorityList } from '@polkadot/types/interfaces/grandpa';
@@ -69,15 +69,15 @@ declare module '@polkadot/api/types/events' {
       CuratorGroupStatusSet: AugmentedEvent<ApiType, [CuratorGroupId, bool]>;
       CuratorRemoved: AugmentedEvent<ApiType, [CuratorGroupId, CuratorId]>;
       FeaturedVideosSet: AugmentedEvent<ApiType, [ContentActor, Vec<VideoId>]>;
-      PersonCreated: AugmentedEvent<ApiType, [ContentActor, PersonId, NewAssets, PersonCreationParameters]>;
+      PersonCreated: AugmentedEvent<ApiType, [ContentActor, PersonId, StorageAssets, PersonCreationParameters]>;
       PersonDeleted: AugmentedEvent<ApiType, [ContentActor, PersonId]>;
-      PersonUpdated: AugmentedEvent<ApiType, [ContentActor, PersonId, NewAssets, PersonUpdateParameters]>;
+      PersonUpdated: AugmentedEvent<ApiType, [ContentActor, PersonId, StorageAssets, PersonUpdateParameters]>;
       PlaylistCreated: AugmentedEvent<ApiType, [ContentActor, PlaylistId, PlaylistCreationParameters]>;
       PlaylistDeleted: AugmentedEvent<ApiType, [ContentActor, PlaylistId]>;
       PlaylistUpdated: AugmentedEvent<ApiType, [ContentActor, PlaylistId, PlaylistUpdateParameters]>;
-      SeriesCreated: AugmentedEvent<ApiType, [ContentActor, SeriesId, NewAssets, SeriesParameters, Series]>;
+      SeriesCreated: AugmentedEvent<ApiType, [ContentActor, SeriesId, StorageAssets, SeriesParameters, Series]>;
       SeriesDeleted: AugmentedEvent<ApiType, [ContentActor, SeriesId]>;
-      SeriesUpdated: AugmentedEvent<ApiType, [ContentActor, SeriesId, NewAssets, SeriesParameters, Series]>;
+      SeriesUpdated: AugmentedEvent<ApiType, [ContentActor, SeriesId, StorageAssets, SeriesParameters, Series]>;
       VideoCategoryCreated: AugmentedEvent<ApiType, [ContentActor, VideoCategoryId, VideoCategoryCreationParameters]>;
       VideoCategoryDeleted: AugmentedEvent<ApiType, [ContentActor, VideoCategoryId]>;
       VideoCategoryUpdated: AugmentedEvent<ApiType, [ContentActor, VideoCategoryId, VideoCategoryUpdateParameters]>;
@@ -1098,8 +1098,9 @@ declare module '@polkadot/api/types/events' {
        * Params
        * - data objects IDs
        * - initial uploading parameters
+       * - deletion prize for objects
        **/
-      DataObjectsUploaded: AugmentedEvent<ApiType, [Vec<DataObjectId>, UploadParameters]>;
+      DataObjectsUploaded: AugmentedEvent<ApiType, [Vec<DataObjectId>, UploadParameters, Balance]>;
       /**
        * Emits on creating distribution bucket.
        * Params

+ 4 - 8
types/augment-codec/augment-api-tx.ts

@@ -137,17 +137,13 @@ declare module '@polkadot/api/types/submittable' {
       createSeries: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: SeriesParameters | { assets?: any; seasons?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId, SeriesParameters]>;
       createVideo: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: VideoCreationParameters | { assets?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId, VideoCreationParameters]>;
       createVideoCategory: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, params: VideoCategoryCreationParameters | { meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, VideoCategoryCreationParameters]>;
-      deleteChannel: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId]>;
+      deleteChannel: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, numObjectsToDelete: u64 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId, u64]>;
       deleteChannelCategory: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, categoryId: ChannelCategoryId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelCategoryId]>;
       deletePerson: AugmentedSubmittable<(actor: PersonActor | { Member: any } | { Curator: any } | string | Uint8Array, person: PersonId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [PersonActor, PersonId]>;
       deletePlaylist: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, playlist: PlaylistId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId, PlaylistId]>;
       deleteSeries: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, series: SeriesId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, SeriesId]>;
-      deleteVideo: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, VideoId]>;
+      deleteVideo: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, assetsToRemove: BTreeSet<DataObjectId>) => SubmittableExtrinsic<ApiType>, [ContentActor, VideoId, BTreeSet<DataObjectId>]>;
       deleteVideoCategory: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, categoryId: VideoCategoryId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, VideoCategoryId]>;
-      /**
-       * Remove assets of a channel from storage
-       **/
-      removeChannelAssets: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, assets: BTreeSet<DataObjectId>) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId, BTreeSet<DataObjectId>]>;
       /**
        * Remove curator from a given curator group
        **/
@@ -159,13 +155,13 @@ declare module '@polkadot/api/types/submittable' {
        **/
       setCuratorGroupStatus: AugmentedSubmittable<(curatorGroupId: CuratorGroupId | AnyNumber | Uint8Array, isActive: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [CuratorGroupId, bool]>;
       setFeaturedVideos: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, list: Vec<VideoId> | (VideoId | AnyNumber | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [ContentActor, Vec<VideoId>]>;
-      updateChannel: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: ChannelUpdateParameters | { assets?: any; new_meta?: any; reward_account?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId, ChannelUpdateParameters]>;
+      updateChannel: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: ChannelUpdateParameters | { assets_to_upload?: any; new_meta?: any; reward_account?: any; assets_to_remove?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId, ChannelUpdateParameters]>;
       updateChannelCategory: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, categoryId: ChannelCategoryId | AnyNumber | Uint8Array, params: ChannelCategoryUpdateParameters | { new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelCategoryId, ChannelCategoryUpdateParameters]>;
       updateChannelCensorshipStatus: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, isCensored: bool | boolean | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId, bool, Bytes]>;
       updatePerson: AugmentedSubmittable<(actor: PersonActor | { Member: any } | { Curator: any } | string | Uint8Array, person: PersonId | AnyNumber | Uint8Array, params: PersonUpdateParameters | { assets?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PersonActor, PersonId, PersonUpdateParameters]>;
       updatePlaylist: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, playlist: PlaylistId | AnyNumber | Uint8Array, params: PlaylistUpdateParameters | { new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, PlaylistId, PlaylistUpdateParameters]>;
       updateSeries: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: SeriesParameters | { assets?: any; seasons?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId, SeriesParameters]>;
-      updateVideo: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, params: VideoUpdateParameters | { assets?: any; new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, VideoId, VideoUpdateParameters]>;
+      updateVideo: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, params: VideoUpdateParameters | { assets_to_upload?: any; new_meta?: any; assets_to_remove?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, VideoId, VideoUpdateParameters]>;
       updateVideoCategory: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, categoryId: VideoCategoryId | AnyNumber | Uint8Array, params: VideoCategoryUpdateParameters | { new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, VideoCategoryId, VideoCategoryUpdateParameters]>;
       updateVideoCensorshipStatus: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, isCensored: bool | boolean | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, VideoId, bool, Bytes]>;
     };

File diff suppressed because it is too large
+ 0 - 0
types/augment-codec/augment-types.ts


+ 17 - 24
types/augment/all/defs.json

@@ -765,24 +765,16 @@
             "Lead": "Null"
         }
     },
-    "CreationUploadParameters": {
+    "StorageAssets": {
         "object_creation_list": "Vec<DataObjectCreationParameters>",
         "expected_data_size_fee": "u128"
     },
-    "AssetUrls": "Vec<Url>",
-    "NewAssets": {
-        "_enum": {
-            "Upload": "CreationUploadParameters",
-            "Urls": "Vec<AssetUrls>"
-        }
-    },
     "Channel": {
         "owner": "ChannelOwner",
         "num_videos": "u64",
         "is_censored": "bool",
         "reward_account": "Option<GenericAccountId>",
-        "deletion_prize_source_account_id": "GenericAccountId",
-        "num_assets": "u64"
+        "deletion_prize_source_account_id": "GenericAccountId"
     },
     "ChannelOwner": {
         "_enum": {
@@ -799,14 +791,15 @@
         "new_meta": "Bytes"
     },
     "ChannelCreationParameters": {
-        "assets": "NewAssets",
-        "meta": "Bytes",
+        "assets": "Option<StorageAssets>",
+        "meta": "Option<Bytes>",
         "reward_account": "Option<GenericAccountId>"
     },
     "ChannelUpdateParameters": {
-        "assets": "Option<NewAssets>",
+        "assets_to_upload": "Option<StorageAssets>",
         "new_meta": "Option<Bytes>",
-        "reward_account": "Option<Option<GenericAccountId>>"
+        "reward_account": "Option<Option<GenericAccountId>>",
+        "assets_to_remove": "Vec<DataObjectId>"
     },
     "ChannelOwnershipTransferRequestId": "u64",
     "ChannelOwnershipTransferRequest": {
@@ -818,8 +811,7 @@
     "Video": {
         "in_channel": "ChannelId",
         "in_series": "Option<SeriesId>",
-        "is_censored": "bool",
-        "maybe_data_objects_id_set": "Option<Vec<DataObjectId>>"
+        "is_censored": "bool"
     },
     "VideoId": "u64",
     "VideoCategoryId": "u64",
@@ -831,12 +823,13 @@
         "new_meta": "Bytes"
     },
     "VideoCreationParameters": {
-        "assets": "NewAssets",
-        "meta": "Bytes"
+        "assets": "Option<StorageAssets>",
+        "meta": "Option<Bytes>"
     },
     "VideoUpdateParameters": {
-        "assets": "Option<NewAssets>",
-        "new_meta": "Option<Bytes>"
+        "assets_to_upload": "Option<StorageAssets>",
+        "new_meta": "Option<Bytes>",
+        "assets_to_remove": "Vec<DataObjectId>"
     },
     "Person": {
         "controlled_by": "PersonController"
@@ -855,11 +848,11 @@
         }
     },
     "PersonCreationParameters": {
-        "assets": "NewAssets",
+        "assets": "StorageAssets",
         "meta": "Bytes"
     },
     "PersonUpdateParameters": {
-        "assets": "Option<NewAssets>",
+        "assets": "Option<StorageAssets>",
         "meta": "Option<Bytes>"
     },
     "Playlist": {
@@ -881,12 +874,12 @@
         "episodes": "Vec<VideoId>"
     },
     "SeriesParameters": {
-        "assets": "Option<NewAssets>",
+        "assets": "Option<StorageAssets>",
         "seasons": "Option<Vec<Option<SeasonParameters>>>",
         "meta": "Option<Bytes>"
     },
     "SeasonParameters": {
-        "assets": "Option<NewAssets>",
+        "assets": "Option<StorageAssets>",
         "episodes": "Option<Vec<Option<EpisodeParemters>>>",
         "meta": "Option<Bytes>"
     },

+ 18 - 29
types/augment/all/types.ts

@@ -139,9 +139,6 @@ export interface Approved extends Enum {
   readonly asExecutionFailed: ExecutionFailed;
 }
 
-/** @name AssetUrls */
-export interface AssetUrls extends Vec<Url> {}
-
 /** @name Backer */
 export interface Backer extends Struct {
   readonly member: GenericAccountId;
@@ -209,7 +206,6 @@ export interface Channel extends Struct {
   readonly is_censored: bool;
   readonly reward_account: Option<GenericAccountId>;
   readonly deletion_prize_source_account_id: GenericAccountId;
-  readonly num_assets: u64;
 }
 
 /** @name ChannelCategory */
@@ -233,8 +229,8 @@ export interface ChannelContentType extends Null {}
 
 /** @name ChannelCreationParameters */
 export interface ChannelCreationParameters extends Struct {
-  readonly assets: NewAssets;
-  readonly meta: Bytes;
+  readonly assets: Option<StorageAssets>;
+  readonly meta: Option<Bytes>;
   readonly reward_account: Option<GenericAccountId>;
 }
 
@@ -268,9 +264,10 @@ export interface ChannelPublicationStatus extends Null {}
 
 /** @name ChannelUpdateParameters */
 export interface ChannelUpdateParameters extends Struct {
-  readonly assets: Option<NewAssets>;
+  readonly assets_to_upload: Option<StorageAssets>;
   readonly new_meta: Option<Bytes>;
   readonly reward_account: Option<Option<GenericAccountId>>;
+  readonly assets_to_remove: Vec<DataObjectId>;
 }
 
 /** @name ChildPositionInParentCategory */
@@ -315,12 +312,6 @@ export interface ContentIdSet extends BTreeSet<Cid> {}
 /** @name CreateEntityOperation */
 export interface CreateEntityOperation extends Null {}
 
-/** @name CreationUploadParameters */
-export interface CreationUploadParameters extends Struct {
-  readonly object_creation_list: Vec<DataObjectCreationParameters>;
-  readonly expected_data_size_fee: u128;
-}
-
 /** @name Credential */
 export interface Credential extends Null {}
 
@@ -655,14 +646,6 @@ export interface ModerationAction extends Struct {
   readonly rationale: Text;
 }
 
-/** @name NewAssets */
-export interface NewAssets extends Enum {
-  readonly isUpload: boolean;
-  readonly asUpload: CreationUploadParameters;
-  readonly isUrls: boolean;
-  readonly asUrls: Vec<AssetUrls>;
-}
-
 /** @name NextAdjustment */
 export interface NextAdjustment extends Struct {
   readonly adjustment: AdjustOnInterval;
@@ -782,7 +765,7 @@ export interface PersonController extends Enum {
 
 /** @name PersonCreationParameters */
 export interface PersonCreationParameters extends Struct {
-  readonly assets: NewAssets;
+  readonly assets: StorageAssets;
   readonly meta: Bytes;
 }
 
@@ -791,7 +774,7 @@ export interface PersonId extends u64 {}
 
 /** @name PersonUpdateParameters */
 export interface PersonUpdateParameters extends Struct {
-  readonly assets: Option<NewAssets>;
+  readonly assets: Option<StorageAssets>;
   readonly meta: Option<Bytes>;
 }
 
@@ -1080,7 +1063,7 @@ export interface Season extends Struct {
 
 /** @name SeasonParameters */
 export interface SeasonParameters extends Struct {
-  readonly assets: Option<NewAssets>;
+  readonly assets: Option<StorageAssets>;
   readonly episodes: Option<Vec<Option<EpisodeParemters>>>;
   readonly meta: Option<Bytes>;
 }
@@ -1106,7 +1089,7 @@ export interface SeriesId extends u64 {}
 
 /** @name SeriesParameters */
 export interface SeriesParameters extends Struct {
-  readonly assets: Option<NewAssets>;
+  readonly assets: Option<StorageAssets>;
   readonly seasons: Option<Vec<Option<SeasonParameters>>>;
   readonly meta: Option<Bytes>;
 }
@@ -1206,6 +1189,12 @@ export interface StaticBagId extends Enum {
 /** @name Status */
 export interface Status extends Null {}
 
+/** @name StorageAssets */
+export interface StorageAssets extends Struct {
+  readonly object_creation_list: Vec<DataObjectCreationParameters>;
+  readonly expected_data_size_fee: u128;
+}
+
 /** @name StorageBucket */
 export interface StorageBucket extends Struct {
   readonly operator_status: StorageBucketOperatorStatus;
@@ -1330,7 +1319,6 @@ export interface Video extends Struct {
   readonly in_channel: ChannelId;
   readonly in_series: Option<SeriesId>;
   readonly is_censored: bool;
-  readonly maybe_data_objects_id_set: Option<Vec<DataObjectId>>;
 }
 
 /** @name VideoCategory */
@@ -1351,8 +1339,8 @@ export interface VideoCategoryUpdateParameters extends Struct {
 
 /** @name VideoCreationParameters */
 export interface VideoCreationParameters extends Struct {
-  readonly assets: NewAssets;
-  readonly meta: Bytes;
+  readonly assets: Option<StorageAssets>;
+  readonly meta: Option<Bytes>;
 }
 
 /** @name VideoId */
@@ -1360,8 +1348,9 @@ export interface VideoId extends u64 {}
 
 /** @name VideoUpdateParameters */
 export interface VideoUpdateParameters extends Struct {
-  readonly assets: Option<NewAssets>;
+  readonly assets_to_upload: Option<StorageAssets>;
   readonly new_meta: Option<Bytes>;
+  readonly assets_to_remove: Vec<DataObjectId>;
 }
 
 /** @name VoteKind */

+ 4 - 0
types/augment/augment-api-errors.ts

@@ -138,6 +138,10 @@ declare module '@polkadot/api/types/errors' {
        * Channel assets feasibility
        **/
       InvalidAssetsProvided: AugmentedError<ApiType>;
+      /**
+       * Bag Size specified is not valid
+       **/
+      InvalidBagSizeSpecified: AugmentedError<ApiType>;
       /**
        * Lead authentication failed
        **/

+ 7 - 6
types/augment/augment-api-events.ts

@@ -2,7 +2,7 @@
 /* eslint-disable */
 
 import type { BTreeMap, BTreeSet, Bytes, Option, Vec, bool, u32, u64 } from '@polkadot/types';
-import type { ApplicationId, ApplicationIdToWorkerIdMap, BagId, CategoryId, Channel, ChannelCategory, ChannelCategoryCreationParameters, ChannelCategoryId, ChannelCategoryUpdateParameters, ChannelCreationParameters, ChannelId, ChannelOwnershipTransferRequest, ChannelOwnershipTransferRequestId, ChannelUpdateParameters, Cid, ContentActor, CuratorGroupId, CuratorId, DataObjectId, DistributionBucketFamilyId, DistributionBucketId, DynamicBagDeletionPrizeRecord, DynamicBagId, DynamicBagType, EntryMethod, IsCensored, MemberId, MintBalanceOf, MintId, NewAssets, OpeningId, PersonCreationParameters, PersonId, PersonUpdateParameters, PlaylistCreationParameters, PlaylistId, PlaylistUpdateParameters, PostId, ProposalId, ProposalStatus, RationaleText, Series, SeriesId, SeriesParameters, StorageBucketId, ThreadId, UploadParameters, VideoCategoryCreationParameters, VideoCategoryId, VideoCategoryUpdateParameters, VideoCreationParameters, VideoId, VideoUpdateParameters, VoteKind, Voucher, WorkerId } from './all';
+import type { ApplicationId, ApplicationIdToWorkerIdMap, BagId, CategoryId, Channel, ChannelCategory, ChannelCategoryCreationParameters, ChannelCategoryId, ChannelCategoryUpdateParameters, ChannelCreationParameters, ChannelId, ChannelOwnershipTransferRequest, ChannelOwnershipTransferRequestId, ChannelUpdateParameters, Cid, ContentActor, CuratorGroupId, CuratorId, DataObjectId, DistributionBucketFamilyId, DistributionBucketId, DynamicBagDeletionPrizeRecord, DynamicBagId, DynamicBagType, EntryMethod, IsCensored, MemberId, MintBalanceOf, MintId, OpeningId, PersonCreationParameters, PersonId, PersonUpdateParameters, PlaylistCreationParameters, PlaylistId, PlaylistUpdateParameters, PostId, ProposalId, ProposalStatus, RationaleText, Series, SeriesId, SeriesParameters, StorageAssets, StorageBucketId, ThreadId, UploadParameters, VideoCategoryCreationParameters, VideoCategoryId, VideoCategoryUpdateParameters, VideoCreationParameters, VideoId, VideoUpdateParameters, VoteKind, Voucher, WorkerId } from './all';
 import type { BalanceStatus } from '@polkadot/types/interfaces/balances';
 import type { AuthorityId } from '@polkadot/types/interfaces/consensus';
 import type { AuthorityList } from '@polkadot/types/interfaces/grandpa';
@@ -69,15 +69,15 @@ declare module '@polkadot/api/types/events' {
       CuratorGroupStatusSet: AugmentedEvent<ApiType, [CuratorGroupId, bool]>;
       CuratorRemoved: AugmentedEvent<ApiType, [CuratorGroupId, CuratorId]>;
       FeaturedVideosSet: AugmentedEvent<ApiType, [ContentActor, Vec<VideoId>]>;
-      PersonCreated: AugmentedEvent<ApiType, [ContentActor, PersonId, NewAssets, PersonCreationParameters]>;
+      PersonCreated: AugmentedEvent<ApiType, [ContentActor, PersonId, StorageAssets, PersonCreationParameters]>;
       PersonDeleted: AugmentedEvent<ApiType, [ContentActor, PersonId]>;
-      PersonUpdated: AugmentedEvent<ApiType, [ContentActor, PersonId, NewAssets, PersonUpdateParameters]>;
+      PersonUpdated: AugmentedEvent<ApiType, [ContentActor, PersonId, StorageAssets, PersonUpdateParameters]>;
       PlaylistCreated: AugmentedEvent<ApiType, [ContentActor, PlaylistId, PlaylistCreationParameters]>;
       PlaylistDeleted: AugmentedEvent<ApiType, [ContentActor, PlaylistId]>;
       PlaylistUpdated: AugmentedEvent<ApiType, [ContentActor, PlaylistId, PlaylistUpdateParameters]>;
-      SeriesCreated: AugmentedEvent<ApiType, [ContentActor, SeriesId, NewAssets, SeriesParameters, Series]>;
+      SeriesCreated: AugmentedEvent<ApiType, [ContentActor, SeriesId, StorageAssets, SeriesParameters, Series]>;
       SeriesDeleted: AugmentedEvent<ApiType, [ContentActor, SeriesId]>;
-      SeriesUpdated: AugmentedEvent<ApiType, [ContentActor, SeriesId, NewAssets, SeriesParameters, Series]>;
+      SeriesUpdated: AugmentedEvent<ApiType, [ContentActor, SeriesId, StorageAssets, SeriesParameters, Series]>;
       VideoCategoryCreated: AugmentedEvent<ApiType, [ContentActor, VideoCategoryId, VideoCategoryCreationParameters]>;
       VideoCategoryDeleted: AugmentedEvent<ApiType, [ContentActor, VideoCategoryId]>;
       VideoCategoryUpdated: AugmentedEvent<ApiType, [ContentActor, VideoCategoryId, VideoCategoryUpdateParameters]>;
@@ -1098,8 +1098,9 @@ declare module '@polkadot/api/types/events' {
        * Params
        * - data objects IDs
        * - initial uploading parameters
+       * - deletion prize for objects
        **/
-      DataObjectsUploaded: AugmentedEvent<ApiType, [Vec<DataObjectId>, UploadParameters]>;
+      DataObjectsUploaded: AugmentedEvent<ApiType, [Vec<DataObjectId>, UploadParameters, Balance]>;
       /**
        * Emits on creating distribution bucket.
        * Params

+ 4 - 8
types/augment/augment-api-tx.ts

@@ -137,17 +137,13 @@ declare module '@polkadot/api/types/submittable' {
       createSeries: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: SeriesParameters | { assets?: any; seasons?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId, SeriesParameters]>;
       createVideo: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: VideoCreationParameters | { assets?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId, VideoCreationParameters]>;
       createVideoCategory: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, params: VideoCategoryCreationParameters | { meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, VideoCategoryCreationParameters]>;
-      deleteChannel: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId]>;
+      deleteChannel: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, numObjectsToDelete: u64 | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId, u64]>;
       deleteChannelCategory: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, categoryId: ChannelCategoryId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelCategoryId]>;
       deletePerson: AugmentedSubmittable<(actor: PersonActor | { Member: any } | { Curator: any } | string | Uint8Array, person: PersonId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [PersonActor, PersonId]>;
       deletePlaylist: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, playlist: PlaylistId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId, PlaylistId]>;
       deleteSeries: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, series: SeriesId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, SeriesId]>;
-      deleteVideo: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, VideoId]>;
+      deleteVideo: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, assetsToRemove: BTreeSet<DataObjectId>) => SubmittableExtrinsic<ApiType>, [ContentActor, VideoId, BTreeSet<DataObjectId>]>;
       deleteVideoCategory: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, categoryId: VideoCategoryId | AnyNumber | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, VideoCategoryId]>;
-      /**
-       * Remove assets of a channel from storage
-       **/
-      removeChannelAssets: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, assets: BTreeSet<DataObjectId>) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId, BTreeSet<DataObjectId>]>;
       /**
        * Remove curator from a given curator group
        **/
@@ -159,13 +155,13 @@ declare module '@polkadot/api/types/submittable' {
        **/
       setCuratorGroupStatus: AugmentedSubmittable<(curatorGroupId: CuratorGroupId | AnyNumber | Uint8Array, isActive: bool | boolean | Uint8Array) => SubmittableExtrinsic<ApiType>, [CuratorGroupId, bool]>;
       setFeaturedVideos: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, list: Vec<VideoId> | (VideoId | AnyNumber | Uint8Array)[]) => SubmittableExtrinsic<ApiType>, [ContentActor, Vec<VideoId>]>;
-      updateChannel: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: ChannelUpdateParameters | { assets?: any; new_meta?: any; reward_account?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId, ChannelUpdateParameters]>;
+      updateChannel: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: ChannelUpdateParameters | { assets_to_upload?: any; new_meta?: any; reward_account?: any; assets_to_remove?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId, ChannelUpdateParameters]>;
       updateChannelCategory: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, categoryId: ChannelCategoryId | AnyNumber | Uint8Array, params: ChannelCategoryUpdateParameters | { new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelCategoryId, ChannelCategoryUpdateParameters]>;
       updateChannelCensorshipStatus: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, isCensored: bool | boolean | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId, bool, Bytes]>;
       updatePerson: AugmentedSubmittable<(actor: PersonActor | { Member: any } | { Curator: any } | string | Uint8Array, person: PersonId | AnyNumber | Uint8Array, params: PersonUpdateParameters | { assets?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [PersonActor, PersonId, PersonUpdateParameters]>;
       updatePlaylist: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, playlist: PlaylistId | AnyNumber | Uint8Array, params: PlaylistUpdateParameters | { new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, PlaylistId, PlaylistUpdateParameters]>;
       updateSeries: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, channelId: ChannelId | AnyNumber | Uint8Array, params: SeriesParameters | { assets?: any; seasons?: any; meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, ChannelId, SeriesParameters]>;
-      updateVideo: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, params: VideoUpdateParameters | { assets?: any; new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, VideoId, VideoUpdateParameters]>;
+      updateVideo: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, params: VideoUpdateParameters | { assets_to_upload?: any; new_meta?: any; assets_to_remove?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, VideoId, VideoUpdateParameters]>;
       updateVideoCategory: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, categoryId: VideoCategoryId | AnyNumber | Uint8Array, params: VideoCategoryUpdateParameters | { new_meta?: any } | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, VideoCategoryId, VideoCategoryUpdateParameters]>;
       updateVideoCensorshipStatus: AugmentedSubmittable<(actor: ContentActor | { Curator: any } | { Member: any } | { Lead: any } | string | Uint8Array, videoId: VideoId | AnyNumber | Uint8Array, isCensored: bool | boolean | Uint8Array, rationale: Bytes | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [ContentActor, VideoId, bool, Bytes]>;
     };

File diff suppressed because it is too large
+ 0 - 0
types/augment/augment-types.ts


+ 15 - 24
types/src/content/index.ts

@@ -1,7 +1,7 @@
 import { Vec, Option, Tuple } from '@polkadot/types'
 import { bool, u64, u32, u128, Null, Bytes } from '@polkadot/types/primitive'
 import { MemberId } from '../members'
-import { JoyStructDecorated, JoyEnum, ChannelId, JoyBTreeSet, Url } from '../common'
+import { JoyStructDecorated, JoyEnum, ChannelId, JoyBTreeSet } from '../common'
 import { GenericAccountId as AccountId } from '@polkadot/types/generic/AccountId'
 import { DataObjectId, DataObjectCreationParameters } from '../storage'
 
@@ -17,18 +17,11 @@ export class ChannelOwnershipTransferRequestId extends u64 {}
 export class MaxNumber extends u32 {}
 export class IsCensored extends bool {}
 
-export class AssetUrls extends Vec.with(Url) {}
-
-export class CreationUploadParameters extends JoyStructDecorated({
+export class StorageAssets extends JoyStructDecorated({
   object_creation_list: Vec.with(DataObjectCreationParameters),
   expected_data_size_fee: u128,
 }) {}
 
-export class NewAssets extends JoyEnum({
-  Upload: CreationUploadParameters,
-  Urls: Vec.with(AssetUrls),
-}) {}
-
 export class CuratorGroup extends JoyStructDecorated({
   curators: JoyBTreeSet(CuratorId),
   active: bool,
@@ -51,19 +44,19 @@ export class Channel extends JoyStructDecorated({
   is_censored: bool,
   reward_account: Option.with(AccountId),
   deletion_prize_source_account_id: AccountId,
-  num_assets: u64,
 }) {}
 
 export class ChannelCreationParameters extends JoyStructDecorated({
-  assets: NewAssets,
-  meta: Bytes,
+  assets: Option.with(StorageAssets),
+  meta: Option.with(Bytes),
   reward_account: Option.with(AccountId),
 }) {}
 
 export class ChannelUpdateParameters extends JoyStructDecorated({
-  assets: Option.with(NewAssets),
+  assets_to_upload: Option.with(StorageAssets),
   new_meta: Option.with(Bytes),
   reward_account: Option.with(Option.with(AccountId)),
+  assets_to_remove: JoyBTreeSet(DataObjectId),
 }) {}
 
 export class ChannelOwnershipTransferRequest extends JoyStructDecorated({
@@ -101,17 +94,17 @@ export class Video extends JoyStructDecorated({
   in_channel: ChannelId,
   in_series: Option.with(SeriesId),
   is_censored: bool,
-  maybe_data_objects_id_set: Option.with(JoyBTreeSet(DataObjectId)),
 }) {}
 
 export class VideoCreationParameters extends JoyStructDecorated({
-  assets: NewAssets,
-  meta: Bytes,
+  assets: Option.with(StorageAssets),
+  meta: Option.with(Bytes),
 }) {}
 
 export class VideoUpdateParameters extends JoyStructDecorated({
-  assets: Option.with(NewAssets),
+  assets_to_upload: Option.with(StorageAssets),
   new_meta: Option.with(Bytes),
+  assets_to_remove: JoyBTreeSet(DataObjectId),
 }) {}
 
 export class Playlist extends JoyStructDecorated({
@@ -136,7 +129,7 @@ export class Season extends JoyStructDecorated({
 }) {}
 
 export class SeasonParameters extends JoyStructDecorated({
-  assets: Option.with(NewAssets),
+  assets: Option.with(StorageAssets),
   episodes: Option.with(Vec.with(Option.with(EpisodeParemters))),
   meta: Option.with(Bytes),
 }) {}
@@ -147,7 +140,7 @@ export class Series extends JoyStructDecorated({
 }) {}
 
 export class SeriesParameters extends JoyStructDecorated({
-  assets: Option.with(NewAssets),
+  assets: Option.with(StorageAssets),
   seasons: Option.with(Vec.with(Option.with(SeasonParameters))),
   meta: Option.with(Bytes),
 }) {}
@@ -162,12 +155,12 @@ export class Person extends JoyStructDecorated({
 }) {}
 
 export class PersonCreationParameters extends JoyStructDecorated({
-  assets: NewAssets,
+  assets: StorageAssets,
   meta: Bytes,
 }) {}
 
 export class PersonUpdateParameters extends JoyStructDecorated({
-  assets: Option.with(NewAssets),
+  assets: Option.with(StorageAssets),
   meta: Option.with(Bytes),
 }) {}
 
@@ -181,9 +174,7 @@ export const contentTypes = {
   CuratorGroupId,
   CuratorGroup,
   ContentActor,
-  CreationUploadParameters,
-  AssetUrls,
-  NewAssets,
+  StorageAssets,
   Channel,
   ChannelOwner,
   ChannelCategoryId,

+ 4 - 2
types/src/index.ts

@@ -62,8 +62,8 @@ registry.register(types)
 // will create a type like: { a: string } | { b: number } | { c: Null } | "c"
 type EnumVariant<T> = keyof T extends infer K
   ? K extends keyof T
-    ? T[K] extends Null
-      ? K
+    ? T[K] extends Null | null
+      ? K | { [I in K]: T[I] }
       : { [I in K]: T[I] }
     : never
   : never
@@ -86,6 +86,8 @@ type CreateInterface_NoOption<T extends Codec> =
       ? CreateInterface<S>[]
       : T extends BTreeMap<infer K, infer V>
       ? Map<K, V>
+      : T extends Null
+      ? null
       : unknown)
 
 // Wrapper for CreateInterface_NoOption that includes resolving an Option

Some files were not shown because too many files changed in this diff