Browse Source

Merge branch 'babylon' into integration-tests-updates

Mokhtar Naamani 4 years ago
parent
commit
57e8694ea5

+ 2 - 2
Cargo.lock

@@ -1993,7 +1993,7 @@ dependencies = [
 
 [[package]]
 name = "joystream-node"
-version = "3.4.1"
+version = "3.5.0"
 dependencies = [
  "frame-benchmarking",
  "frame-benchmarking-cli",
@@ -2053,7 +2053,7 @@ dependencies = [
 
 [[package]]
 name = "joystream-node-runtime"
-version = "7.7.0"
+version = "7.9.0"
 dependencies = [
  "frame-benchmarking",
  "frame-executive",

+ 2 - 2
content-directory-schemas/inputs/classes/ChannelClass.json

@@ -1,7 +1,7 @@
 {
   "name": "Channel",
   "description": "A channel belonging to certain member. Members can publish certain type of content (ie. videos) through channels.",
-  "maximum_entities_count": 400,
-  "default_entity_creation_voucher_upper_bound": 50,
+  "maximum_entities_count": 5000,
+  "default_entity_creation_voucher_upper_bound": 25,
   "class_permissions": { "any_member": true }
 }

+ 2 - 2
content-directory-schemas/inputs/classes/ContentCategoryClass.json

@@ -1,6 +1,6 @@
 {
   "name": "ContentCategory",
   "description": "A category the content may be published under",
-  "maximum_entities_count": 100,
-  "default_entity_creation_voucher_upper_bound": 50
+  "maximum_entities_count": 500,
+  "default_entity_creation_voucher_upper_bound": 500
 }

+ 2 - 2
content-directory-schemas/inputs/classes/FeaturedVideoClass.json

@@ -1,6 +1,6 @@
 {
   "name": "FeaturedVideo",
   "description": "Featured video references",
-  "maximum_entities_count": 400,
-  "default_entity_creation_voucher_upper_bound": 50
+  "maximum_entities_count": 10,
+  "default_entity_creation_voucher_upper_bound": 10
 }

+ 2 - 2
content-directory-schemas/inputs/classes/HttpMediaLocationClass.json

@@ -1,7 +1,7 @@
 {
   "name": "HttpMediaLocation",
   "description": "An object describing http location of media object",
-  "maximum_entities_count": 400,
-  "default_entity_creation_voucher_upper_bound": 50,
+  "maximum_entities_count": 5000,
+  "default_entity_creation_voucher_upper_bound": 100,
   "class_permissions": { "any_member": true }
 }

+ 2 - 2
content-directory-schemas/inputs/classes/JoystreamMediaLocationClass.json

@@ -1,7 +1,7 @@
 {
   "name": "JoystreamMediaLocation",
   "description": "An object describing location of media object in a format specific to Joystream platform",
-  "maximum_entities_count": 400,
-  "default_entity_creation_voucher_upper_bound": 50,
+  "maximum_entities_count": 5000,
+  "default_entity_creation_voucher_upper_bound": 100,
   "class_permissions": { "any_member": true }
 }

+ 2 - 2
content-directory-schemas/inputs/classes/KnownLicenseClass.json

@@ -1,6 +1,6 @@
 {
   "name": "KnownLicense",
   "description": "A commonly recognized license (ie. CC_BY_SA)",
-  "maximum_entities_count": 100,
-  "default_entity_creation_voucher_upper_bound": 50
+  "maximum_entities_count": 500,
+  "default_entity_creation_voucher_upper_bound": 500
 }

+ 2 - 2
content-directory-schemas/inputs/classes/LanguageClass.json

@@ -1,6 +1,6 @@
 {
   "name": "Language",
   "description": "A language in which the content on the platform may be published",
-  "maximum_entities_count": 100,
-  "default_entity_creation_voucher_upper_bound": 50
+  "maximum_entities_count": 500,
+  "default_entity_creation_voucher_upper_bound": 500
 }

+ 2 - 2
content-directory-schemas/inputs/classes/LicenseClass.json

@@ -1,7 +1,7 @@
 {
   "name": "License",
   "description": "Describes a license the media can be published under",
-  "maximum_entities_count": 400,
-  "default_entity_creation_voucher_upper_bound": 50,
+  "maximum_entities_count": 5000,
+  "default_entity_creation_voucher_upper_bound": 100,
   "class_permissions": { "any_member": true }
 }

+ 2 - 2
content-directory-schemas/inputs/classes/MediaLocationClass.json

@@ -1,7 +1,7 @@
 {
   "name": "MediaLocation",
   "description": "An object describing how the related media object can be accessed",
-  "maximum_entities_count": 400,
-  "default_entity_creation_voucher_upper_bound": 50,
+  "maximum_entities_count": 5000,
+  "default_entity_creation_voucher_upper_bound": 100,
   "class_permissions": { "any_member": true }
 }

+ 2 - 2
content-directory-schemas/inputs/classes/UserDefinedLicenseClass.json

@@ -1,7 +1,7 @@
 {
   "name": "UserDefinedLicense",
   "description": "Custom license defined by the user",
-  "maximum_entities_count": 400,
-  "default_entity_creation_voucher_upper_bound": 50,
+  "maximum_entities_count": 5000,
+  "default_entity_creation_voucher_upper_bound": 100,
   "class_permissions": { "any_member": true }
 }

+ 2 - 2
content-directory-schemas/inputs/classes/VideoClass.json

@@ -1,7 +1,7 @@
 {
   "name": "Video",
   "description": "Describes a Video",
-  "maximum_entities_count": 400,
-  "default_entity_creation_voucher_upper_bound": 50,
+  "maximum_entities_count": 5000,
+  "default_entity_creation_voucher_upper_bound": 100,
   "class_permissions": { "any_member": true }
 }

+ 2 - 2
content-directory-schemas/inputs/classes/VideoMediaClass.json

@@ -1,7 +1,7 @@
 {
   "name": "VideoMedia",
   "description": "Describes a video media object",
-  "maximum_entities_count": 400,
-  "default_entity_creation_voucher_upper_bound": 50,
+  "maximum_entities_count": 5000,
+  "default_entity_creation_voucher_upper_bound": 100,
   "class_permissions": { "any_member": true }
 }

+ 8 - 0
content-directory-schemas/inputs/entityBatches/KnownLicenseBatch.json

@@ -4,48 +4,56 @@
     {
       "code": "PDM",
       "name": "Public Domain",
+      "description": "For items which are not protected by copyright. This is not a license, but rather a copyright status. Some government-produced works, items with expired copyrights, and those which are ineligible for copyright protection may be included in this category.",
       "url": "https://creativecommons.org/share-your-work/public-domain/pdm",
       "attributionRequired": false
     },
     {
       "code": "CC0",
       "name": "Public Domain Dedication",
+      "description": "The CC0 (Public Domain Dedication) License allows creators to waive all rights to their creations and release them into the Public Domain.",
       "url": "https://creativecommons.org/share-your-work/public-domain/cc0",
       "attributionRequired": true
     },
     {
       "code": "CC_BY",
       "name": "Creative Commons Attribution License",
+      "description": "Sharing and adapting this content is permitted, but attribution must be provided. Read the License Deed for more information.",
       "url": "https://creativecommons.org/licenses/by/4.0",
       "attributionRequired": true
     },
     {
       "code": "CC_BY_SA",
       "name": "Creative Commons Attribution-ShareAlike License",
+      "description": "Sharing and adapting this content is permitted, but attribution must be provided. Any derivative works must be distributed under the same license. Read the License Deed for more information.",
       "url": "https://creativecommons.org/licenses/by-sa/4.0",
       "attributionRequired": true
     },
     {
       "code": "CC_BY_ND",
       "name": "Creative Commons Attribution-NoDerivs License",
+      "description": "Sharing this content is permitted, but attribution must be provided. You may not remix, transform, or build upon the material. Read the License Deed for more information.",
       "url": "https://creativecommons.org/licenses/by-nd/4.0",
       "attributionRequired": true
     },
     {
       "code": "CC_BY_NC",
       "name": "Creative Commons Attribution-NonCommercial License",
+      "description": "Sharing and adapting this content is permitted, but attribution must be provided. Commercial use is not permitted. Read the License Deed for more information.",
       "url": "https://creativecommons.org/licenses/by-nc/4.0",
       "attributionRequired": true
     },
     {
       "code": "CC_BY_NC_SA",
       "name": "Creative Commons Attribution-NonCommercial-ShareAlike License",
+      "description": "Sharing and adapting this content is permitted, but attribution must be provided. Any derivative works must be distributed under the same license. Commercial use is not permitted. Read the License Deed for more information.",
       "url": "https://creativecommons.org/licenses/by-nc-sa/4.0",
       "attributionRequired": true
     },
     {
       "code": "CC_BY_NC_ND",
       "name": "Creative Commons Attribution-NonCommercial-NoDerivs License",
+      "description": "Sharing this content is permitted, but attribution must be provided. You may not remix, transform, or build upon the material. Commercial use is not permitted. Read the License Deed for more information.",
       "url": "https://creativecommons.org/licenses/by-nc-nd/4.0",
       "attributionRequired": true
     }

+ 1 - 1
node/Cargo.toml

@@ -3,7 +3,7 @@ authors = ['Joystream contributors']
 build = 'build.rs'
 edition = '2018'
 name = 'joystream-node'
-version = '3.4.1'
+version = '3.5.0'
 default-run = "joystream-node"
 
 [[bin]]

+ 2 - 2
query-node/mappings/content-directory/content-dir-consts.ts

@@ -1,4 +1,4 @@
-import { IPropertyWithId } from '../types'
+import { IKnownClass, IPropertyWithId } from '../types'
 
 // Content directory predefined class names
 export enum ContentDirectoryKnownClasses {
@@ -18,7 +18,7 @@ export enum ContentDirectoryKnownClasses {
 }
 
 // Predefined content-directory classes, classId may change after the runtime seeding
-export const contentDirectoryClassNamesWithId: { classId: number; name: string }[] = [
+export const contentDirectoryClassNamesWithId: IKnownClass[] = [
   { name: ContentDirectoryKnownClasses.CHANNEL, classId: 1 },
   { name: ContentDirectoryKnownClasses.CATEGORY, classId: 2 },
   { name: ContentDirectoryKnownClasses.HTTPMEDIALOCATION, classId: 3 },

+ 15 - 37
query-node/mappings/content-directory/entity/index.ts

@@ -57,7 +57,6 @@ import {
   userDefinedLicensePropertyNamesWithId,
   videoMediaEncodingPropertyNamesWithId,
   videoPropertyNamesWithId,
-  contentDirectoryClassNamesWithId,
   ContentDirectoryKnownClasses,
   featuredVideoPropertyNamesWithId,
 } from '../content-dir-consts'
@@ -80,7 +79,7 @@ import {
   IMediaLocation,
   IFeaturedVideo,
 } from '../../types'
-import { getOrCreate } from '../get-or-create'
+import { getOrCreate, getKnownClass } from '../get-or-create'
 
 const debug = Debug('mappings:content-directory')
 
@@ -91,22 +90,13 @@ async function contentDirectory_EntitySchemaSupportAdded(db: DB, event: Substrat
 
   const { blockNumber: block } = event
   const entityId = decode.stringIfyEntityId(event)
-  const classEntity = await db.get(ClassEntity, { where: { id: entityId } })
 
-  if (classEntity === undefined) {
-    console.log(`Class not found for the EntityId: ${entityId}`)
-    return
-  }
-
-  const cls = contentDirectoryClassNamesWithId.find((c) => c.classId === classEntity.classId)
-  if (cls === undefined) {
-    console.log('Not recognized class')
-    return
-  }
+  const [knownClass] = await getKnownClass(db, { where: { id: entityId } })
+  if (!knownClass) return
 
   const arg: IDBBlockId = { db, block, id: entityId }
 
-  switch (cls.name) {
+  switch (knownClass.name) {
     case ContentDirectoryKnownClasses.CHANNEL:
       await createChannel(
         arg,
@@ -183,7 +173,7 @@ async function contentDirectory_EntitySchemaSupportAdded(db: DB, event: Substrat
       break
 
     default:
-      throw new Error(`Unknown class name: ${cls.name}`)
+      throw new Error(`Unknown class name: ${knownClass.name}`)
   }
 }
 
@@ -194,19 +184,10 @@ async function contentDirectory_EntityRemoved(db: DB, event: SubstrateEvent): Pr
   const entityId = decode.stringIfyEntityId(event)
   const where: IWhereCond = { where: { id: entityId } }
 
-  const classEntity = await db.get(ClassEntity, where)
-  if (classEntity === undefined) {
-    console.log(`Class not found for the EntityId: ${entityId}`)
-    return
-  }
-
-  const cls = contentDirectoryClassNamesWithId.find((c) => c.classId === classEntity.classId)
-  if (cls === undefined) {
-    console.log('Unknown class')
-    return
-  }
+  const [knownClass, classEntity] = await getKnownClass(db, where)
+  if (!knownClass) return
 
-  switch (cls.name) {
+  switch (knownClass.name) {
     case ContentDirectoryKnownClasses.CHANNEL:
       await removeChannel(db, where)
       break
@@ -259,8 +240,9 @@ async function contentDirectory_EntityRemoved(db: DB, event: SubstrateEvent): Pr
       break
 
     default:
-      throw new Error(`Unknown class name: ${cls.name}`)
+      throw new Error(`Unknown class name: ${knownClass.name}`)
   }
+  await db.remove<ClassEntity>(classEntity)
 }
 
 // eslint-disable-next-line @typescript-eslint/naming-convention
@@ -290,21 +272,17 @@ async function contentDirectory_EntityPropertyValuesUpdated(db: DB, event: Subst
 
   const { 2: newPropertyValues } = extrinsic.args
   const entityId = decode.stringIfyEntityId(event)
-
-  const ce = await db.get(ClassEntity, { where: { id: entityId } })
-  if (ce === undefined) throw Error(`Class not found for the entity id: ${entityId}`)
-
-  const cls = contentDirectoryClassNamesWithId.find((c) => c.classId === ce.classId)
-  if (cls === undefined) throw Error(`Not known class id: ${ce.classId}`)
-
   const where: IWhereCond = { where: { id: entityId } }
 
+  const [knownClass] = await getKnownClass(db, where)
+  if (!knownClass) return
+
   // TODO: change setProperties method signature to accecpt SubstrateExtrinsic, then remove the following
   // line. The reason we push the same arg is beacuse of the setProperties method check the 3rd indices
   // to get properties values
   extrinsic.args.push(newPropertyValues)
 
-  switch (cls.name) {
+  switch (knownClass.name) {
     case ContentDirectoryKnownClasses.CHANNEL:
       updateChannelEntityPropertyValues(db, where, decode.setProperties<IChannel>(event, channelPropertyNamesWithId), 0)
       break
@@ -406,7 +384,7 @@ async function contentDirectory_EntityPropertyValuesUpdated(db: DB, event: Subst
       break
 
     default:
-      throw new Error(`Unknown class name: ${cls.name}`)
+      throw new Error(`Unknown class name: ${knownClass.name}`)
   }
 }
 

+ 21 - 13
query-node/mappings/content-directory/entity/remove.ts

@@ -1,4 +1,5 @@
 import assert from 'assert'
+import Debug from 'debug'
 
 import { DB } from '../../../generated/indexer'
 import { Channel } from '../../../generated/graphql-server/src/modules/channel/channel.model'
@@ -17,63 +18,70 @@ import { FeaturedVideo } from '../../../generated/graphql-server/src/modules/fea
 
 import { IWhereCond } from '../../types'
 
+const debug = Debug(`mappings:remove-entity`)
+
 function assertKeyViolation(entityName: string, entityId: string) {
   assert(false, `Can not remove ${entityName}(${entityId})! There are references to this entity`)
 }
 
+function logEntityNotFound(className: string, where: IWhereCond) {
+  debug(`${className}(${where.where.id}) not found. This happen when schema support is not added for the entity.`)
+}
+
 async function removeChannel(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(Channel, where)
-  if (!record) throw Error(`Channel(${where.where.id}) not found`)
+  if (!record) return logEntityNotFound(`Channel`, where)
   if (record.videos && record.videos.length) assertKeyViolation(`Channel`, record.id)
   await db.remove<Channel>(record)
 }
 
 async function removeCategory(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(Category, where)
-  if (!record) throw Error(`Category(${where.where.id}) not found`)
+  if (!record) return logEntityNotFound(`Category`, where)
   if (record.videos && record.videos.length) assertKeyViolation(`Category`, record.id)
   await db.remove<Category>(record)
 }
 async function removeVideoMedia(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(VideoMedia, where)
-  if (!record) throw Error(`VideoMedia(${where.where.id}) not found`)
+  if (!record) return logEntityNotFound(`VideoMedia`, where)
   if (record.video) assertKeyViolation(`VideoMedia`, record.id)
   await db.remove<VideoMedia>(record)
 }
+
 async function removeVideo(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(Video, where)
-  if (!record) throw Error(`Video(${where.where.id}) not found`)
+  if (!record) return logEntityNotFound(`Video`, where)
   await db.remove<Video>(record)
 }
 
 async function removeLicense(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(LicenseEntity, where)
-  if (!record) throw Error(`License(${where.where.id}) not found`)
+  if (!record) return logEntityNotFound(`License`, where)
   if (record.videolicense && record.videolicense.length) assertKeyViolation(`License`, record.id)
   await db.remove<LicenseEntity>(record)
 }
 
 async function removeUserDefinedLicense(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(UserDefinedLicenseEntity, where)
-  if (!record) throw Error(`UserDefinedLicense(${where.where.id}) not found`)
+  if (!record) return logEntityNotFound(`UserDefinedLicense`, where)
   await db.remove<UserDefinedLicenseEntity>(record)
 }
 
 async function removeKnownLicense(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(KnownLicenseEntity, where)
-  if (!record) throw Error(`KnownLicense(${where.where.id}) not found`)
+  if (!record) return logEntityNotFound(`KnownLicense`, where)
   await db.remove<KnownLicenseEntity>(record)
 }
 async function removeMediaLocation(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(MediaLocationEntity, where)
-  if (!record) throw Error(`MediaLocation(${where.where.id}) not found`)
+  if (!record) return logEntityNotFound(`MediaLocation`, where)
   if (record.videoMedia) assertKeyViolation('MediaLocation', record.id)
   await db.remove<MediaLocationEntity>(record)
 }
 
 async function removeHttpMediaLocation(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(HttpMediaLocationEntity, where)
-  if (!record) throw Error(`HttpMediaLocation(${where.where.id}) not found`)
+  if (!record) return logEntityNotFound(`HttpMediaLocation`, where)
   if (record.medialocationentityhttpMediaLocation && record.medialocationentityhttpMediaLocation.length) {
     assertKeyViolation('HttpMediaLocation', record.id)
   }
@@ -82,7 +90,7 @@ async function removeHttpMediaLocation(db: DB, where: IWhereCond): Promise<void>
 
 async function removeJoystreamMediaLocation(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(JoystreamMediaLocationEntity, where)
-  if (!record) throw Error(`JoystreamMediaLocation(${where.where.id}) not found`)
+  if (!record) return logEntityNotFound(`JoystreamMediaLocation`, where)
   if (record.medialocationentityjoystreamMediaLocation && record.medialocationentityjoystreamMediaLocation.length) {
     assertKeyViolation('JoystreamMediaLocation', record.id)
   }
@@ -91,7 +99,7 @@ async function removeJoystreamMediaLocation(db: DB, where: IWhereCond): Promise<
 
 async function removeLanguage(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(Language, where)
-  if (!record) throw Error(`Language(${where.where.id}) not found`)
+  if (!record) return logEntityNotFound(`Language`, where)
   if (record.channellanguage && record.channellanguage.length) assertKeyViolation('Language', record.id)
   if (record.videolanguage && record.videolanguage.length) assertKeyViolation('Language', record.id)
   await db.remove<Language>(record)
@@ -99,13 +107,13 @@ async function removeLanguage(db: DB, where: IWhereCond): Promise<void> {
 
 async function removeVideoMediaEncoding(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(VideoMediaEncoding, where)
-  if (!record) throw Error(`VideoMediaEncoding(${where.where.id}) not found`)
+  if (!record) return logEntityNotFound(`VideoMediaEncoding`, where)
   await db.remove<VideoMediaEncoding>(record)
 }
 
 async function removeFeaturedVideo(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(FeaturedVideo, { ...where, relations: ['video'] })
-  if (!record) throw Error(`FeaturedVideo(${where.where.id}) not found`)
+  if (!record) return logEntityNotFound(`FeaturedVideo`, where)
 
   record.video.isFeatured = false
   record.video.featured = undefined

+ 15 - 0
query-node/mappings/content-directory/get-or-create.ts

@@ -11,11 +11,13 @@ import { LicenseEntity } from '../../generated/graphql-server/src/modules/licens
 import { MediaLocationEntity } from '../../generated/graphql-server/src/modules/media-location-entity/media-location-entity.model'
 import { Video } from '../../generated/graphql-server/src/modules/video/video.model'
 import { NextEntityId } from '../../generated/graphql-server/src/modules/next-entity-id/next-entity-id.model'
+import { ClassEntity } from '../../generated/graphql-server/src/modules/class-entity/class-entity.model'
 
 import { decode } from './decode'
 import {
   categoryPropertyNamesWithId,
   channelPropertyNamesWithId,
+  contentDirectoryClassNamesWithId,
   httpMediaLocationPropertyNamesWithId,
   joystreamMediaLocationPropertyNamesWithId,
   knownLicensePropertyNamesWIthId,
@@ -34,6 +36,7 @@ import {
   IEntity,
   IHttpMediaLocation,
   IJoystreamMediaLocation,
+  IKnownClass,
   IKnownLicense,
   ILanguage,
   ILicense,
@@ -43,6 +46,7 @@ import {
   IVideo,
   IVideoMedia,
   IVideoMediaEncoding,
+  IWhereCond,
 } from '../types'
 
 import {
@@ -423,6 +427,17 @@ async function video(
   )
 }
 
+export async function getKnownClass(db: DB, where: IWhereCond): Promise<[IKnownClass | undefined, ClassEntity]> {
+  const ce = await db.get(ClassEntity, where)
+  if (!ce) {
+    throw Error(`Class not found for the EntityId: ${where.where.id} or the entity has not been created.`)
+  }
+
+  const knownClass = contentDirectoryClassNamesWithId.find((c) => c.classId === ce.classId)
+  if (!knownClass) console.log('Unknown class')
+  return [knownClass, ce]
+}
+
 export const getOrCreate = {
   language,
   videoMediaEncoding,

+ 5 - 0
query-node/mappings/types.ts

@@ -202,3 +202,8 @@ export type ClassEntityMap = Map<string, IEntity[]>
 export interface IFeaturedVideo {
   video?: IReference
 }
+
+export interface IKnownClass {
+  name: string
+  classId: number
+}

+ 8 - 0
runtime-modules/content-directory/src/lib.rs

@@ -2864,6 +2864,14 @@ impl<T: Trait> Module<T> {
     }
 }
 
+impl<T: Trait> Module<T> {
+    pub fn set_initial_ids_to_one() {
+        <NextEntityId<T>>::put(T::EntityId::one());
+        <NextClassId<T>>::put(T::ClassId::one());
+        <NextCuratorGroupId<T>>::put(T::CuratorGroupId::one());
+    }
+}
+
 decl_event!(
     pub enum Event<T>
     where

+ 1 - 1
runtime/Cargo.toml

@@ -4,7 +4,7 @@ edition = '2018'
 name = 'joystream-node-runtime'
 # Follow convention: https://github.com/Joystream/substrate-runtime-joystream/issues/1
 # {Authoring}.{Spec}.{Impl} of the RuntimeVersion
-version = '7.7.0'
+version = '7.9.0'
 
 [dependencies]
 # Third-party dependencies

+ 3 - 3
runtime/src/lib.rs

@@ -75,7 +75,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
     spec_name: create_runtime_str!("joystream-node"),
     impl_name: create_runtime_str!("joystream-node"),
     authoring_version: 7,
-    spec_version: 7,
+    spec_version: 9,
     impl_version: 0,
     apis: crate::runtime_api::EXPORTED_RUNTIME_API_VERSIONS,
     transaction_version: 1,
@@ -396,13 +396,13 @@ parameter_types! {
     pub const MaxNumberOfMaintainersPerClass: MaxNumber = 10;
     pub const MaxNumberOfSchemasPerClass: MaxNumber = 20;
     pub const MaxNumberOfPropertiesPerSchema: MaxNumber = 40;
-    pub const MaxNumberOfEntitiesPerClass: MaxNumber = 400;
+    pub const MaxNumberOfEntitiesPerClass: MaxNumber = 5000;
     pub const MaxNumberOfCuratorsPerGroup: MaxNumber = 50;
     pub const MaxNumberOfOperationsDuringAtomicBatching: MaxNumber = 500;
     pub const VecMaxLengthConstraint: VecMaxLength = 200;
     pub const TextMaxLengthConstraint: TextMaxLength = 5000;
     pub const HashedTextMaxLengthConstraint: HashedTextMaxLength = Some(25000);
-    pub const IndividualEntitiesCreationLimit: EntityId = 50;
+    pub const IndividualEntitiesCreationLimit: EntityId = 500;
 }
 
 impl content_directory::Trait for Runtime {

+ 7 - 2
runtime/src/runtime_api.rs

@@ -13,8 +13,8 @@ use sp_std::vec::Vec;
 use crate::constants::PRIMARY_PROBABILITY;
 use crate::integration::content_directory::ContentDirectoryWorkingGroup;
 use crate::{
-    AccountId, AuthorityDiscoveryId, Balance, BlockNumber, EpochDuration, GrandpaAuthorityList,
-    GrandpaId, Hash, Index, RuntimeVersion, Signature, VERSION,
+    content_directory, AccountId, AuthorityDiscoveryId, Balance, BlockNumber, EpochDuration,
+    GrandpaAuthorityList, GrandpaId, Hash, Index, RuntimeVersion, Signature, VERSION,
 };
 use crate::{
     AllModules, AuthorityDiscovery, Babe, Call, Grandpa, Historical, InherentDataExt,
@@ -70,6 +70,11 @@ impl OnRuntimeUpgrade for CustomOnRuntimeUpgrade {
             default_content_working_group_mint_capacity,
         );
 
+        // Next Id's are configured at genesis. Applications and tools are harcoded to expect initial
+        // values of the ids to start at 1. With a runtime upgrade the initial values will not be
+        // configured and get an initial default value of zero. This corrects this problem.
+        content_directory::Module::<Runtime>::set_initial_ids_to_one();
+
         10_000_000 // TODO: adjust weight
     }
 }