Bladeren bron

query-node: add default schema to all newly created entities

metmirr 4 jaren geleden
bovenliggende
commit
874999c584

+ 302 - 0
query-node/mappings/content-directory/default-schemas.ts

@@ -0,0 +1,302 @@
+import Debug from 'debug'
+import { nanoid } from 'nanoid'
+
+import { DB } from '../../generated/indexer'
+import { Channel } from '../../generated/graphql-server/src/modules/channel/channel.model'
+import { Language } from '../../generated/graphql-server/src/modules/language/language.model'
+import { ClassEntity } from '../../generated/graphql-server/src/modules/class-entity/class-entity.model'
+import { Video } from '../../generated/graphql-server/src/modules/video/video.model'
+import { Category } from '../../generated/graphql-server/src/modules/category/category.model'
+import { VideoMedia } from '../../generated/graphql-server/src/modules/video-media/video-media.model'
+import { LicenseEntity } from '../../generated/graphql-server/src/modules/license-entity/license-entity.model'
+import { VideoMediaEncoding } from '../../generated/graphql-server/src/modules/video-media-encoding/video-media-encoding.model'
+import { MediaLocationEntity } from '../../generated/graphql-server/src/modules/media-location-entity/media-location-entity.model'
+import { HttpMediaLocationEntity } from '../../generated/graphql-server/src/modules/http-media-location-entity/http-media-location-entity.model'
+import { JoystreamMediaLocationEntity } from '../../generated/graphql-server/src/modules/joystream-media-location-entity/joystream-media-location-entity.model'
+import { KnownLicenseEntity } from '../../generated/graphql-server/src/modules/known-license-entity/known-license-entity.model'
+import { UserDefinedLicenseEntity } from '../../generated/graphql-server/src/modules/user-defined-license-entity/user-defined-license-entity.model'
+import { FeaturedVideo } from '../../generated/graphql-server/src/modules/featured-video/featured-video.model'
+import { JoystreamMediaLocation } from '../../generated/graphql-server/src/modules/variants/variants.model'
+
+import { contentDirectoryClassNamesWithId, ContentDirectoryKnownClasses } from './content-dir-consts'
+
+const debug = Debug('mappings:default-schema')
+
+/**
+ * Inserts a default schema for every newly created entity
+ * @param db DB
+ * @param ce ClassEntity
+ */
+export async function createDefaultSchema(db: DB, ce: ClassEntity): Promise<void> {
+  const knownClass = contentDirectoryClassNamesWithId.find((k) => k.classId === ce.classId)
+  // Its not a known class for query node so we simply return
+  if (!knownClass) {
+    debug(`Not a known class. ClassId: ${ce.classId} EntityId: ${ce.id}`)
+    return
+  }
+
+  switch (knownClass.name) {
+    case ContentDirectoryKnownClasses.CHANNEL:
+      await channelDefaultSchema(db, ce)
+      break
+    case ContentDirectoryKnownClasses.CATEGORY:
+      await categoryDefaultSchema(db, ce)
+      break
+    case ContentDirectoryKnownClasses.HTTPMEDIALOCATION:
+      await httpMediaLocationDefaultSchema(db, ce)
+      break
+    case ContentDirectoryKnownClasses.JOYSTREAMMEDIALOCATION:
+      await joystreamMediaLocationDefaultSchema(db, ce)
+      break
+    case ContentDirectoryKnownClasses.KNOWNLICENSE:
+      await knownLicenseDefaultSchema(db, ce)
+      break
+    case ContentDirectoryKnownClasses.LANGUAGE:
+      await languageDefaultSchema(db, ce)
+      break
+    case ContentDirectoryKnownClasses.LICENSE:
+      await licenseDefaultSchema(db, ce)
+      break
+    case ContentDirectoryKnownClasses.MEDIALOCATION:
+      await mediaLocationDefaultSchema(db, ce)
+      break
+    case ContentDirectoryKnownClasses.USERDEFINEDLICENSE:
+      await userDefinedLicenseDefaultSchema(db, ce)
+      break
+    case ContentDirectoryKnownClasses.VIDEOMEDIAENCODING:
+      await videoMediaEncodingDefaultSchema(db, ce)
+      break
+    case ContentDirectoryKnownClasses.FEATUREDVIDEOS:
+      await featuredVideoDefaultSchema(db, ce)
+      break
+    case ContentDirectoryKnownClasses.VIDEO:
+      await videoDefaultSchema(db, ce)
+      break
+    case ContentDirectoryKnownClasses.VIDEOMEDIA:
+      await videoMediaDefaultSchema(db, ce)
+      break
+
+    default:
+      break
+  }
+}
+
+function commonProperties(ce: ClassEntity) {
+  return { id: ce.id, happenedIn: ce.happenedIn, version: ce.happenedIn.block }
+}
+
+export async function categoryDefaultSchema(db: DB, ce: ClassEntity): Promise<void> {
+  await db.save<Category>(
+    new Category({
+      ...commonProperties(ce),
+      name: ce.id,
+    })
+  )
+}
+
+export async function channelDefaultSchema(db: DB, ce: ClassEntity): Promise<void> {
+  const defaultSchemaFromDb = await db.get(Channel, { where: { id: '0' } })
+  if (!defaultSchemaFromDb) throw Error(`Channel(0) not found`)
+
+  const c = new Channel({
+    ...commonProperties(ce),
+    handle: `${ce.id} - ${nanoid()}`,
+    description: `${ce.id}`,
+    isPublic: defaultSchemaFromDb.isPublic,
+    isCurated: defaultSchemaFromDb.isCurated,
+  })
+  await db.save<Channel>(c)
+}
+
+export async function httpMediaLocationDefaultSchema(db: DB, ce: ClassEntity): Promise<void> {
+  const defaultSchemaFromDb = await db.get(HttpMediaLocationEntity, { where: { id: '0' } })
+  if (!defaultSchemaFromDb) throw Error(`HttpMediaLocationEntity(0) not found`)
+
+  await db.save<HttpMediaLocationEntity>(
+    new HttpMediaLocationEntity({
+      ...commonProperties(ce),
+      url: defaultSchemaFromDb.url,
+    })
+  )
+}
+
+export async function joystreamMediaLocationDefaultSchema(db: DB, ce: ClassEntity): Promise<void> {
+  const defaultSchemaFromDb = await db.get(JoystreamMediaLocationEntity, { where: { id: '0' } })
+  if (!defaultSchemaFromDb) throw Error(`JoystreamMediaLocationEntity(0) not found`)
+
+  await db.save<JoystreamMediaLocationEntity>(
+    new JoystreamMediaLocationEntity({
+      ...commonProperties(ce),
+      dataObjectId: defaultSchemaFromDb.dataObjectId,
+    })
+  )
+}
+
+export async function knownLicenseDefaultSchema(db: DB, ce: ClassEntity): Promise<void> {
+  const defaultSchemaFromDb = await db.get(KnownLicenseEntity, { where: { id: '0' } })
+  if (!defaultSchemaFromDb) throw Error(`KnownLicenseEntity(0) not found`)
+
+  await db.save<KnownLicenseEntity>(
+    new KnownLicenseEntity({
+      ...commonProperties(ce),
+      code: `${ce.id}-${defaultSchemaFromDb.code}`,
+      name: defaultSchemaFromDb.name,
+    })
+  )
+}
+
+export async function userDefinedLicenseDefaultSchema(db: DB, ce: ClassEntity): Promise<void> {
+  await db.save<UserDefinedLicenseEntity>(
+    new UserDefinedLicenseEntity({
+      ...commonProperties(ce),
+      content: `${ce.id}`,
+    })
+  )
+}
+
+export async function languageDefaultSchema(db: DB, ce: ClassEntity): Promise<void> {
+  await db.save<Language>(
+    new Language({
+      ...commonProperties(ce),
+      code: `${ce.id}`,
+      name: `${ce.id}`,
+    })
+  )
+}
+
+export async function licenseDefaultSchema(db: DB, ce: ClassEntity): Promise<void> {
+  const defaultSchemaFromDb = await db.get(LicenseEntity, { where: { id: '0' } })
+  if (!defaultSchemaFromDb) throw Error(`LicenseEntity(0) not found`)
+
+  await db.save<LicenseEntity>(
+    new LicenseEntity({
+      ...commonProperties(ce),
+      type: defaultSchemaFromDb.type,
+    })
+  )
+}
+
+export async function mediaLocationDefaultSchema(db: DB, ce: ClassEntity): Promise<void> {
+  const defaultSchemaFromDb = await db.get(MediaLocationEntity, {
+    where: { id: '0' },
+    relations: ['joystreamMediaLocation'],
+  })
+  if (!defaultSchemaFromDb) throw Error(`MediaLocationEntity(0) not found`)
+
+  const m = new MediaLocationEntity()
+  m.joystreamMediaLocation = defaultSchemaFromDb.joystreamMediaLocation
+  await db.save<MediaLocationEntity>(
+    new MediaLocationEntity({
+      ...commonProperties(ce),
+      joystreamMediaLocation: defaultSchemaFromDb.joystreamMediaLocation,
+    })
+  )
+}
+
+export async function videoMediaEncodingDefaultSchema(db: DB, ce: ClassEntity): Promise<void> {
+  const defaultSchemaFromDb = await db.get(VideoMediaEncoding, { where: { id: '0' } })
+  if (!defaultSchemaFromDb) throw Error(`VideoMediaEncoding(0) not found`)
+
+  await db.save<VideoMediaEncoding>(
+    new VideoMediaEncoding({
+      ...commonProperties(ce),
+      name: defaultSchemaFromDb.name,
+    })
+  )
+}
+
+export async function featuredVideoDefaultSchema(db: DB, ce: ClassEntity): Promise<void> {
+  const video = await db.get(Video, { where: { id: '0' } })
+  if (!video) throw Error(`Video(0) not found for FeaturedVideo(${ce.id}) creation`)
+
+  await db.save<FeaturedVideo>(
+    new FeaturedVideo({
+      ...commonProperties(ce),
+      video,
+    })
+  )
+}
+
+export async function videoDefaultSchema(db: DB, ce: ClassEntity): Promise<void> {
+  const defaultSchemaFromDb = await db.get(Video, { where: { id: '0' } })
+  if (!defaultSchemaFromDb) throw Error(`Video(0) not found`)
+
+  const v = new Video({
+    ...commonProperties(ce),
+  })
+
+  // ///// default relations ///////
+  /* eslint-disable @typescript-eslint/no-non-null-assertion */
+  const filmAnimation = await db.get(Category, { where: { name: 'Film & Animation' } })
+  v.category = filmAnimation || (await db.get(Category, { where: { id: '0' } }))!
+  v.channel = (await db.get(Channel, { where: { id: '0' } }))!
+  v.license = (await db.get(LicenseEntity, { where: { id: '0' } }))!
+
+  // const media = (await db.get(VideoMedia, { where: { id: '0' } }))!
+
+  // const encoding = new VideoMediaEncoding({
+  //   name: `${Date.now()}`,
+  //   ...commonProperties(ce),
+  //   id: `${Date.now()}`,
+  // })
+  // await db.save<VideoMediaEncoding>(encoding)
+  const encoding = await db.get(VideoMediaEncoding, { where: { id: 0 } })
+
+  const mediaLoc = await db.get(MediaLocationEntity, { where: { id: '0' }, relations: ['joystreamMediaLocation'] })
+  if (!mediaLoc) throw Error(`MediaLocationEntity(0) not found while creating default schema for video`)
+  const location = new JoystreamMediaLocation()
+  location.dataObjectId = mediaLoc.joystreamMediaLocation!.dataObjectId
+
+  const media = await db.get(VideoMedia, { where: { id: '0' } })
+  if (!media) throw Error(`VideoMedia(0) not found while creating default schema for video`)
+
+  const newMedia = new VideoMedia({
+    ...commonProperties(ce),
+    location,
+    encoding,
+    id: `${Date.now()}`, // override id
+    pixelHeight: media.pixelHeight,
+    pixelWidth: media.pixelWidth,
+  })
+  await db.save<VideoMedia>(newMedia)
+
+  v.media = newMedia
+  // ///// default relations ///////
+
+  // Get default schema for video entity
+  // const defaultSchemaFromDb = (await db.get(Video, { where: { id: '0' } }))!
+  /* eslint-enable @typescript-eslint/no-non-null-assertion */
+
+  v.title = ce.id
+  v.description = ce.id
+  v.duration = defaultSchemaFromDb.duration
+  v.thumbnailUrl = defaultSchemaFromDb.thumbnailUrl
+  v.isPublic = defaultSchemaFromDb.isPublic
+  v.isCurated = defaultSchemaFromDb.isCurated
+  v.isExplicit = defaultSchemaFromDb.isExplicit
+  v.isFeatured = defaultSchemaFromDb.isFeatured
+
+  await db.save<Video>(v)
+}
+
+export async function videoMediaDefaultSchema(db: DB, ce: ClassEntity): Promise<void> {
+  const media = await db.get(VideoMedia, { where: { id: '0' } })
+  if (!media) throw Error(`VideoMedia(0) not found`)
+
+  const encoding = await db.get(VideoMediaEncoding, { where: { id: '0' } })
+
+  const mediaLoc = await db.get(MediaLocationEntity, { where: { id: '0' }, relations: ['joystreamMediaLocation'] })
+  if (!mediaLoc) throw Error(`MediaLocationEntity(0) not found while creating default schema for video`)
+  const location = new JoystreamMediaLocation()
+  location.dataObjectId = mediaLoc.joystreamMediaLocation!.dataObjectId
+
+  const vm = new VideoMedia({
+    ...commonProperties(ce),
+    pixelHeight: media.pixelHeight,
+    pixelWidth: media.pixelWidth,
+    encoding,
+    location,
+  })
+
+  await db.save<VideoMedia>(vm)
+}

+ 299 - 0
query-node/mappings/content-directory/entity/addSchema.ts

@@ -0,0 +1,299 @@
+import { DB } from '../../../generated/indexer'
+import { Channel } from '../../../generated/graphql-server/src/modules/channel/channel.model'
+import { Category } from '../../../generated/graphql-server/src/modules/category/category.model'
+import { KnownLicenseEntity } from '../../../generated/graphql-server/src/modules/known-license-entity/known-license-entity.model'
+import { UserDefinedLicenseEntity } from '../../../generated/graphql-server/src/modules/user-defined-license-entity/user-defined-license-entity.model'
+import { JoystreamMediaLocationEntity } from '../../../generated/graphql-server/src/modules/joystream-media-location-entity/joystream-media-location-entity.model'
+import { HttpMediaLocationEntity } from '../../../generated/graphql-server/src/modules/http-media-location-entity/http-media-location-entity.model'
+import { VideoMedia } from '../../../generated/graphql-server/src/modules/video-media/video-media.model'
+import { Video } from '../../../generated/graphql-server/src/modules/video/video.model'
+import { Language } from '../../../generated/graphql-server/src/modules/language/language.model'
+import { VideoMediaEncoding } from '../../../generated/graphql-server/src/modules/video-media-encoding/video-media-encoding.model'
+import { LicenseEntity } from '../../../generated/graphql-server/src/modules/license-entity/license-entity.model'
+import { MediaLocationEntity } from '../../../generated/graphql-server/src/modules/media-location-entity/media-location-entity.model'
+import { FeaturedVideo } from '../../../generated/graphql-server/src/modules/featured-video/featured-video.model'
+
+import {
+  ICategory,
+  IChannel,
+  IFeaturedVideo,
+  IHttpMediaLocation,
+  IJoystreamMediaLocation,
+  IKnownLicense,
+  ILanguage,
+  ILicense,
+  IMediaLocation,
+  IReference,
+  IUserDefinedLicense,
+  IVideo,
+  IVideoMedia,
+  IVideoMediaEncoding,
+} from '../../types'
+import {
+  HttpMediaLocation,
+  JoystreamMediaLocation,
+  KnownLicense,
+  UserDefinedLicense,
+} from '../../../generated/graphql-server/src/modules/variants/variants.model'
+
+type SchemaSupport<T> = { db: DB; entityId: number; props: T; nextEntityId: number }
+
+function getEntityIdFromRef(ref: IReference, nextEntityId: number) {
+  const { entityId, existing } = ref
+  return (existing ? entityId : entityId + nextEntityId).toString()
+}
+
+export async function addSchemaToChannel(param: SchemaSupport<IChannel>): Promise<void> {
+  const { entityId, nextEntityId, props, db } = param
+  const c = await db.get(Channel, { where: { id: entityId.toString() } })
+  if (!c) throw Error(`Channel(${entityId}) has never been created!`)
+
+  c.handle = props.handle || c.handle
+  c.description = props.description || c.description
+  c.isCurated = !!props.isCurated
+  c.isPublic = props.isPublic || c.isPublic
+  c.coverPhotoUrl = props.coverPhotoUrl || c.coverPhotoUrl
+  c.avatarPhotoUrl = props.avatarPhotoUrl || c.avatarPhotoUrl
+
+  const { language } = props
+  if (language) {
+    c.language = await db.get(Language, { where: { id: getEntityIdFromRef(language, nextEntityId) } })
+  }
+  await db.save<Channel>(c)
+}
+
+export async function addSchemaToCategory(param: SchemaSupport<ICategory>): Promise<void> {
+  const { entityId, props, db } = param
+  const c = await db.get(Category, { where: { id: entityId.toString() } })
+  if (!c) throw Error(`Category(${entityId}) has never been created!`)
+
+  c.name = props.name || c.name
+  c.description = props.description || c.description
+  await db.save<Category>(c)
+}
+
+export async function addSchemaToHttpMediaLocation(param: SchemaSupport<IHttpMediaLocation>): Promise<void> {
+  const { entityId, props, db } = param
+  const c = await db.get(HttpMediaLocationEntity, { where: { id: entityId.toString() } })
+  if (!c) throw Error(`HttpMediaLocationEntity(${entityId}) has never been created!`)
+
+  c.url = props.url || c.url
+  c.port = props.port || c.port
+
+  await db.save<HttpMediaLocationEntity>(c)
+}
+
+export async function addSchemaToJoystreamMediaLocation(param: SchemaSupport<IJoystreamMediaLocation>): Promise<void> {
+  const { entityId, props, db } = param
+  const j = await db.get(JoystreamMediaLocationEntity, { where: { id: entityId.toString() } })
+  if (!j) throw Error(`JoystreamMediaLocationEntity(${entityId}) has never been created!`)
+
+  j.dataObjectId = props.dataObjectId || j.dataObjectId
+  await db.save<JoystreamMediaLocationEntity>(j)
+}
+
+export async function addSchemaToKnownLicense(param: SchemaSupport<IKnownLicense>): Promise<void> {
+  const { entityId, props, db } = param
+  const k = await db.get(KnownLicenseEntity, { where: { id: entityId.toString() } })
+  if (!k) throw Error(`KnownLicenseEntity(${entityId}) has never been created!`)
+
+  k.code = props.code || k.code
+  k.description = props.description || k.description
+  k.name = props.name || k.name
+  k.url = props.url || k.url
+
+  await db.save<KnownLicenseEntity>(k)
+}
+
+export async function addSchemaToLanguage(param: SchemaSupport<ILanguage>): Promise<void> {
+  const { entityId, props, db } = param
+  const l = await db.get(Language, { where: { id: entityId.toString() } })
+  if (!l) throw Error(`Language(${entityId}) has never been created!`)
+
+  l.code = props.code || l.code
+  l.name = props.name || l.name
+
+  await db.save<Language>(l)
+}
+
+export async function addSchemaToLicense(param: SchemaSupport<ILicense>): Promise<void> {
+  const { entityId, props, db, nextEntityId } = param
+  const l = await db.get(LicenseEntity, { where: { id: entityId.toString() } })
+  if (!l) throw Error(`LicenseEntity(${entityId}) has never been created!`)
+
+  const { knownLicense, userDefinedLicense } = props
+
+  let licenseEntityId
+
+  if (knownLicense) {
+    licenseEntityId = getEntityIdFromRef(knownLicense, nextEntityId)
+    const k = await db.get(KnownLicenseEntity, { where: { id: licenseEntityId } })
+    if (!k) throw Error(`KnownLicenseEntity(${licenseEntityId}) not found`)
+
+    const licenseType = new KnownLicense()
+    licenseType.code = k.code
+    licenseType.description = k.description
+    licenseType.name = k.name
+    licenseType.url = k.url
+    l.type = licenseType
+  }
+
+  if (userDefinedLicense) {
+    licenseEntityId = getEntityIdFromRef(userDefinedLicense, nextEntityId)
+    const u = await db.get(UserDefinedLicenseEntity, {
+      where: { id: licenseEntityId },
+    })
+    if (!u) throw Error(`UserDefinedLicenseEntity(${licenseEntityId}) not found`)
+    const licenseType = new UserDefinedLicense()
+    licenseType.content = u.content
+    l.type = licenseType
+  }
+
+  l.attribution = props.attribution || l.attribution
+  await db.save<LicenseEntity>(l)
+}
+
+export async function addSchemaToMediaLocation(param: SchemaSupport<IMediaLocation>): Promise<void> {
+  const { entityId, props, db, nextEntityId } = param
+  const m = await db.get(MediaLocationEntity, { where: { id: entityId.toString() } })
+  if (!m) throw Error(`MediaLocationEntity(${entityId}) has never been created!`)
+
+  const { httpMediaLocation, joystreamMediaLocation } = props
+
+  if (httpMediaLocation) {
+    m.httpMediaLocation =
+      (await db.get(HttpMediaLocationEntity, {
+        where: { id: getEntityIdFromRef(httpMediaLocation, nextEntityId) },
+      })) || m.httpMediaLocation
+  }
+  if (joystreamMediaLocation) {
+    m.joystreamMediaLocation =
+      (await db.get(JoystreamMediaLocationEntity, {
+        where: { id: getEntityIdFromRef(joystreamMediaLocation, nextEntityId) },
+      })) || m.joystreamMediaLocation
+  }
+  await db.save<MediaLocationEntity>(m)
+}
+
+export async function addSchemaToUserDefinedLicense(param: SchemaSupport<IUserDefinedLicense>): Promise<void> {
+  const { entityId, props, db } = param
+  const u = await db.get(UserDefinedLicenseEntity, { where: { id: entityId.toString() } })
+  if (!u) throw Error(`UserDefinedLicenseEntity(${entityId}) has never been created!`)
+
+  u.content = props.content || u.content
+  await db.save<UserDefinedLicenseEntity>(u)
+}
+
+export async function addSchemaToVideoMedia(param: SchemaSupport<IVideoMedia>): Promise<void> {
+  const { entityId, props, db, nextEntityId } = param
+  const v = await db.get(VideoMedia, { where: { id: entityId.toString() } })
+  if (!v) throw Error(`VideoMedia(${entityId}) has never been created!`)
+
+  v.pixelHeight = props.pixelHeight || v.pixelHeight
+  v.pixelWidth = props.pixelWidth || v.pixelWidth
+  v.size = props.size || v.size
+
+  const { location, encoding } = props
+
+  if (encoding) {
+    const encodingId = getEntityIdFromRef(encoding, nextEntityId)
+    const encod = await db.get(VideoMediaEncoding, { where: { id: encodingId } })
+    if (!encod) throw Error(`VideoMediaEncoding(${encodingId}) has never been created!`)
+    v.encoding = encod
+  }
+
+  if (location) {
+    const mediaLocation = await db.get(MediaLocationEntity, {
+      where: { id: getEntityIdFromRef(location, nextEntityId) },
+      relations: ['httpMediaLocation', 'joystreamMediaLocation'],
+    })
+    v.locationEntity = mediaLocation
+
+    if (mediaLocation) {
+      const { httpMediaLocation, joystreamMediaLocation } = mediaLocation
+      if (httpMediaLocation) {
+        const mediaLoc = new HttpMediaLocation()
+        mediaLoc.port = httpMediaLocation.port
+        mediaLoc.url = httpMediaLocation.url
+        v.location = mediaLoc
+      }
+      if (joystreamMediaLocation) {
+        const mediaLoc = new JoystreamMediaLocation()
+        mediaLoc.dataObjectId = joystreamMediaLocation.dataObjectId
+        v.location = mediaLoc
+      }
+    }
+  }
+  await db.save<VideoMedia>(v)
+}
+
+export async function addSchemaToVideoMediaEncoding(param: SchemaSupport<IVideoMediaEncoding>): Promise<void> {
+  const { entityId, props, db } = param
+  const e = await db.get(VideoMediaEncoding, { where: { id: entityId.toString() } })
+  if (!e) throw Error(`VideoMediaEncoding(${entityId}) has never been created!`)
+
+  e.name = props.name || e.name
+  await db.save<VideoMediaEncoding>(e)
+}
+
+export async function addSchemaToFeaturedVideo(param: SchemaSupport<IFeaturedVideo>): Promise<void> {
+  const { entityId, props, db, nextEntityId } = param
+  const f = await db.get(FeaturedVideo, { where: { id: entityId.toString() } })
+  if (!f) throw Error(`FeaturedVideo(${entityId}) has never been created!`)
+
+  if (props.video) {
+    const videoId = getEntityIdFromRef(props.video, nextEntityId)
+    const v = await db.get(Video, { where: { id: videoId } })
+    if (!v) throw Error(`Video(${videoId}) has never been created!`)
+    f.video = v
+  }
+  await db.save<FeaturedVideo>(f)
+}
+
+export async function addSchemaToVideo(param: SchemaSupport<IVideo>): Promise<void> {
+  const { entityId, nextEntityId, props, db } = param
+
+  const v = await db.get(Video, { where: { id: entityId.toString() } })
+  if (!v) throw Error(`Video(${entityId}) has never been created!`)
+
+  v.title = props.title || v.title
+  v.description = props.description || v.description
+  v.duration = props.duration || v.duration
+  v.hasMarketing = props.hasMarketing || v.hasMarketing
+  v.isCurated = !props.isCurated || v.isCurated
+  v.isExplicit = props.isExplicit || v.isExplicit
+  v.isPublic = props.isPublic || v.isPublic
+  v.publishedBeforeJoystream = props.publishedBeforeJoystream || v.publishedBeforeJoystream
+  v.skippableIntroDuration = props.skippableIntroDuration || v.skippableIntroDuration
+  v.thumbnailUrl = props.thumbnailUrl || v.thumbnailUrl
+  v.isFeatured = !!v.isFeatured
+
+  const { language, license, category, channel, media } = props
+
+  if (language) {
+    v.language = await db.get(Language, { where: { id: getEntityIdFromRef(language, nextEntityId) } })
+  }
+  if (license) {
+    const licenseId = getEntityIdFromRef(license, nextEntityId)
+    const l = await db.get(LicenseEntity, { where: { id: licenseId } })
+    if (!l) throw Error(`LicenseEntity(${licenseId}) has never been created!`)
+    v.license = l
+  }
+  if (category) {
+    const categoryId = getEntityIdFromRef(category, nextEntityId)
+    const c = await db.get(Category, { where: { id: categoryId } })
+    if (!c) throw Error(`Category(${categoryId}) has never been created!`)
+    v.category = c
+  }
+  if (channel) {
+    const channelId = getEntityIdFromRef(channel, nextEntityId)
+    const c = await db.get(Channel, { where: { id: channelId } })
+    if (!c) throw Error(`Channel(${channelId}) has never been created!`)
+    v.channel = c
+  }
+  if (media) {
+    v.media = await db.get(VideoMedia, { where: { id: getEntityIdFromRef(media, nextEntityId) } })
+  }
+  /* eslint-enable @typescript-eslint/no-non-null-assertion */
+  await db.save<Video>(v)
+}

+ 139 - 197
query-node/mappings/content-directory/entity/index.ts

@@ -3,21 +3,6 @@ import { DB, SubstrateEvent } from '../../../generated/indexer'
 import { ClassEntity } from '../../../generated/graphql-server/src/modules/class-entity/class-entity.model'
 
 import { decode } from '../decode'
-import {
-  updateCategoryEntityPropertyValues,
-  updateChannelEntityPropertyValues,
-  updateVideoMediaEntityPropertyValues,
-  updateVideoEntityPropertyValues,
-  updateUserDefinedLicenseEntityPropertyValues,
-  updateHttpMediaLocationEntityPropertyValues,
-  updateJoystreamMediaLocationEntityPropertyValues,
-  updateKnownLicenseEntityPropertyValues,
-  updateLanguageEntityPropertyValues,
-  updateVideoMediaEncodingEntityPropertyValues,
-  updateLicenseEntityPropertyValues,
-  updateMediaLocationEntityPropertyValues,
-  updateFeaturedVideoEntityPropertyValues,
-} from './update'
 import {
   removeCategory,
   removeChannel,
@@ -33,20 +18,7 @@ import {
   removeMediaLocation,
   removeFeaturedVideo,
 } from './remove'
-import {
-  createCategory,
-  createChannel,
-  createVideoMedia,
-  createVideo,
-  createUserDefinedLicense,
-  createKnownLicense,
-  createHttpMediaLocation,
-  createJoystreamMediaLocation,
-  createLanguage,
-  createVideoMediaEncoding,
-  createBlockOrGetFromDatabase,
-  createFeaturedVideo,
-} from './create'
+import { createBlockOrGetFromDatabase } from './create'
 import {
   categoryPropertyNamesWithId,
   channelPropertyNamesWithId,
@@ -59,6 +31,8 @@ import {
   videoPropertyNamesWithId,
   ContentDirectoryKnownClasses,
   featuredVideoPropertyNamesWithId,
+  licensePropertyNamesWithId,
+  mediaLocationPropertyNamesWithId,
 } from '../content-dir-consts'
 
 import {
@@ -72,111 +46,177 @@ import {
   IVideo,
   ILanguage,
   IVideoMediaEncoding,
-  IDBBlockId,
   IWhereCond,
-  IEntity,
   ILicense,
   IMediaLocation,
   IFeaturedVideo,
 } from '../../types'
 import { getOrCreate, getKnownClass } from '../get-or-create'
+import { createDefaultSchema } from '../default-schemas'
+import {
+  addSchemaToCategory,
+  addSchemaToChannel,
+  addSchemaToFeaturedVideo,
+  addSchemaToHttpMediaLocation,
+  addSchemaToJoystreamMediaLocation,
+  addSchemaToKnownLicense,
+  addSchemaToLanguage,
+  addSchemaToLicense,
+  addSchemaToMediaLocation,
+  addSchemaToUserDefinedLicense,
+  addSchemaToVideo,
+  addSchemaToVideoMedia,
+  addSchemaToVideoMediaEncoding,
+} from './addSchema'
+import { NextEntityId } from '../../../generated/graphql-server/src/modules/next-entity-id/next-entity-id.model'
 
 const debug = Debug('mappings:content-directory')
 
-// eslint-disable-next-line @typescript-eslint/naming-convention
-async function contentDirectory_EntitySchemaSupportAdded(db: DB, event: SubstrateEvent): Promise<void> {
-  if (event.extrinsic && event.extrinsic.method === 'transaction') return
-  debug(`EntitySchemaSupportAdded event: ${JSON.stringify(event)}`)
-
-  const { blockNumber: block } = event
-  const entityId = decode.stringIfyEntityId(event)
-
-  const [knownClass] = await getKnownClass(db, { where: { id: entityId } })
-  if (!knownClass) return
-
-  const arg: IDBBlockId = { db, block, id: entityId }
-
-  switch (knownClass.name) {
+async function addSchemSupportToEntity(
+  event: SubstrateEvent,
+  className: string,
+  db: DB,
+  entityId: number,
+  nextEntityId = 0
+) {
+  switch (className) {
     case ContentDirectoryKnownClasses.CHANNEL:
-      await createChannel(
-        arg,
-        new Map<string, IEntity[]>(),
-        decode.setProperties<IChannel>(event, channelPropertyNamesWithId),
-        0 // ignored
-      )
+      addSchemaToChannel({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setProperties<IChannel>(event, channelPropertyNamesWithId),
+      })
       break
 
     case ContentDirectoryKnownClasses.CATEGORY:
-      await createCategory(arg, decode.setProperties<ICategory>(event, categoryPropertyNamesWithId))
+      await addSchemaToCategory({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setProperties<ICategory>(event, categoryPropertyNamesWithId),
+      })
       break
 
     case ContentDirectoryKnownClasses.KNOWNLICENSE:
-      await createKnownLicense(arg, decode.setProperties<IKnownLicense>(event, knownLicensePropertyNamesWIthId))
+      await addSchemaToKnownLicense({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setProperties<IKnownLicense>(event, knownLicensePropertyNamesWIthId),
+      })
       break
 
     case ContentDirectoryKnownClasses.USERDEFINEDLICENSE:
-      await createUserDefinedLicense(
-        arg,
-        decode.setProperties<IUserDefinedLicense>(event, userDefinedLicensePropertyNamesWithId)
-      )
+      await addSchemaToUserDefinedLicense({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setProperties<IUserDefinedLicense>(event, userDefinedLicensePropertyNamesWithId),
+      })
       break
 
     case ContentDirectoryKnownClasses.JOYSTREAMMEDIALOCATION:
-      await createJoystreamMediaLocation(
-        arg,
-        decode.setProperties<IJoystreamMediaLocation>(event, joystreamMediaLocationPropertyNamesWithId)
-      )
+      await addSchemaToJoystreamMediaLocation({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setProperties<IJoystreamMediaLocation>(event, joystreamMediaLocationPropertyNamesWithId),
+      })
       break
 
     case ContentDirectoryKnownClasses.HTTPMEDIALOCATION:
-      await createHttpMediaLocation(
-        arg,
-        decode.setProperties<IHttpMediaLocation>(event, httpMediaLocationPropertyNamesWithId)
-      )
+      await addSchemaToHttpMediaLocation({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setProperties<IHttpMediaLocation>(event, httpMediaLocationPropertyNamesWithId),
+      })
       break
 
     case ContentDirectoryKnownClasses.VIDEOMEDIA:
-      await createVideoMedia(
-        arg,
-        new Map<string, IEntity[]>(),
-        decode.setProperties<IVideoMedia>(event, videoPropertyNamesWithId),
-        0 // ignored
-      )
+      await addSchemaToVideoMedia({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setProperties<IVideoMedia>(event, videoPropertyNamesWithId),
+      })
       break
 
     case ContentDirectoryKnownClasses.VIDEO:
-      await createVideo(
-        arg,
-        new Map<string, IEntity[]>(),
-        decode.setProperties<IVideo>(event, videoPropertyNamesWithId),
-        0 // ignored
-      )
+      await addSchemaToVideo({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setProperties<IVideo>(event, videoPropertyNamesWithId),
+      })
       break
 
     case ContentDirectoryKnownClasses.LANGUAGE:
-      await createLanguage(arg, decode.setProperties<ILanguage>(event, languagePropertyNamesWIthId))
+      await addSchemaToLanguage({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setProperties<ILanguage>(event, languagePropertyNamesWIthId),
+      })
       break
 
     case ContentDirectoryKnownClasses.VIDEOMEDIAENCODING:
-      await createVideoMediaEncoding(
-        arg,
-        decode.setProperties<IVideoMediaEncoding>(event, videoMediaEncodingPropertyNamesWithId)
-      )
+      await addSchemaToVideoMediaEncoding({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setProperties<IVideoMediaEncoding>(event, videoMediaEncodingPropertyNamesWithId),
+      })
       break
+
     case ContentDirectoryKnownClasses.FEATUREDVIDEOS:
-      await createFeaturedVideo(
-        arg,
-        new Map<string, IEntity[]>(),
-        decode.setProperties<IFeaturedVideo>(event, featuredVideoPropertyNamesWithId),
-        0
-      )
+      await addSchemaToFeaturedVideo({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setProperties<IFeaturedVideo>(event, featuredVideoPropertyNamesWithId),
+      })
+      break
+
+    case ContentDirectoryKnownClasses.LICENSE:
+      await addSchemaToLicense({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setProperties<ILicense>(event, licensePropertyNamesWithId),
+      })
+      break
+
+    case ContentDirectoryKnownClasses.MEDIALOCATION:
+      await addSchemaToMediaLocation({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setProperties<IMediaLocation>(event, mediaLocationPropertyNamesWithId),
+      })
       break
 
     default:
-      throw new Error(`Unknown class name: ${knownClass.name}`)
+      debug(`Unknown class name: ${className}`)
+      break
   }
 }
 
+// eslint-disable-next-line @typescript-eslint/naming-convention
+async function contentDirectory_EntitySchemaSupportAdded(db: DB, event: SubstrateEvent): Promise<void> {
+  if (event.extrinsic && event.extrinsic.method === 'transaction') return
+  debug(`EntitySchemaSupportAdded event: ${JSON.stringify(event)}`)
+
+  const { params } = event
+  const entityId = params[1].value as number
+
+  const [knownClass] = await getKnownClass(db, { where: { id: entityId.toString() } })
+  if (!knownClass) return
+
+  await addSchemSupportToEntity(event, knownClass.name, db, entityId)
+}
+
 // eslint-disable-next-line @typescript-eslint/naming-convention
 async function contentDirectory_EntityRemoved(db: DB, event: SubstrateEvent): Promise<void> {
   debug(`EntityRemoved event: ${JSON.stringify(event)}`)
@@ -259,7 +299,11 @@ async function contentDirectory_EntityCreated(db: DB, event: SubstrateEvent): Pr
   classEntity.happenedIn = await createBlockOrGetFromDatabase(db, event.blockNumber)
   await db.save<ClassEntity>(classEntity)
 
-  await getOrCreate.nextEntityId(db, c.entityId + 1)
+  const nextEntityIdFromDb = await getOrCreate.nextEntityId(db)
+  nextEntityIdFromDb.nextId = c.entityId + 1
+  await db.save<NextEntityId>(nextEntityIdFromDb)
+
+  await createDefaultSchema(db, classEntity)
 }
 
 // eslint-disable-next-line @typescript-eslint/naming-convention
@@ -271,10 +315,11 @@ async function contentDirectory_EntityPropertyValuesUpdated(db: DB, event: Subst
   debug(`EntityPropertyValuesUpdated event: ${JSON.stringify(event)}`)
 
   const { 2: newPropertyValues } = extrinsic.args
-  const entityId = decode.stringIfyEntityId(event)
-  const where: IWhereCond = { where: { id: entityId } }
+  // const entityId = decode.stringIfyEntityId(event)
+  // const where: IWhereCond = { where: { id: entityId } }
+  const entityId = event.params[1].value as number
 
-  const [knownClass] = await getKnownClass(db, where)
+  const [knownClass] = await getKnownClass(db, { where: { id: entityId.toString() } })
   if (!knownClass) return
 
   // TODO: change setProperties method signature to accecpt SubstrateExtrinsic, then remove the following
@@ -282,110 +327,7 @@ async function contentDirectory_EntityPropertyValuesUpdated(db: DB, event: Subst
   // to get properties values
   extrinsic.args.push(newPropertyValues)
 
-  switch (knownClass.name) {
-    case ContentDirectoryKnownClasses.CHANNEL:
-      updateChannelEntityPropertyValues(db, where, decode.setProperties<IChannel>(event, channelPropertyNamesWithId), 0)
-      break
-
-    case ContentDirectoryKnownClasses.CATEGORY:
-      await updateCategoryEntityPropertyValues(
-        db,
-        where,
-        decode.setProperties<ICategory>(event, categoryPropertyNamesWithId)
-      )
-      break
-
-    case ContentDirectoryKnownClasses.KNOWNLICENSE:
-      await updateKnownLicenseEntityPropertyValues(
-        db,
-        where,
-        decode.setProperties<IKnownLicense>(event, knownLicensePropertyNamesWIthId)
-      )
-      break
-
-    case ContentDirectoryKnownClasses.USERDEFINEDLICENSE:
-      await updateUserDefinedLicenseEntityPropertyValues(
-        db,
-        where,
-        decode.setProperties<IUserDefinedLicense>(event, userDefinedLicensePropertyNamesWithId)
-      )
-      break
-
-    case ContentDirectoryKnownClasses.JOYSTREAMMEDIALOCATION:
-      await updateJoystreamMediaLocationEntityPropertyValues(
-        db,
-        where,
-        decode.setProperties<IJoystreamMediaLocation>(event, joystreamMediaLocationPropertyNamesWithId)
-      )
-      break
-
-    case ContentDirectoryKnownClasses.HTTPMEDIALOCATION:
-      await updateHttpMediaLocationEntityPropertyValues(
-        db,
-        where,
-        decode.setProperties<IHttpMediaLocation>(event, httpMediaLocationPropertyNamesWithId)
-      )
-      break
-
-    case ContentDirectoryKnownClasses.VIDEOMEDIA:
-      await updateVideoMediaEntityPropertyValues(
-        db,
-        where,
-        decode.setProperties<IVideoMedia>(event, videoPropertyNamesWithId),
-        0
-      )
-      break
-
-    case ContentDirectoryKnownClasses.VIDEO:
-      await updateVideoEntityPropertyValues(db, where, decode.setProperties<IVideo>(event, videoPropertyNamesWithId), 0)
-      break
-
-    case ContentDirectoryKnownClasses.LANGUAGE:
-      await updateLanguageEntityPropertyValues(
-        db,
-        where,
-        decode.setProperties<ILanguage>(event, languagePropertyNamesWIthId)
-      )
-      break
-
-    case ContentDirectoryKnownClasses.VIDEOMEDIAENCODING:
-      await updateVideoMediaEncodingEntityPropertyValues(
-        db,
-        where,
-        decode.setProperties<IVideoMediaEncoding>(event, videoMediaEncodingPropertyNamesWithId)
-      )
-      break
-
-    case ContentDirectoryKnownClasses.LICENSE:
-      await updateLicenseEntityPropertyValues(
-        db,
-        where,
-        decode.setProperties<ILicense>(event, videoMediaEncodingPropertyNamesWithId),
-        0
-      )
-      break
-
-    case ContentDirectoryKnownClasses.MEDIALOCATION:
-      await updateMediaLocationEntityPropertyValues(
-        db,
-        where,
-        decode.setProperties<IMediaLocation>(event, videoMediaEncodingPropertyNamesWithId),
-        0
-      )
-      break
-
-    case ContentDirectoryKnownClasses.FEATUREDVIDEOS:
-      await updateFeaturedVideoEntityPropertyValues(
-        db,
-        where,
-        decode.setProperties<IFeaturedVideo>(event, featuredVideoPropertyNamesWithId),
-        0
-      )
-      break
-
-    default:
-      throw new Error(`Unknown class name: ${knownClass.name}`)
-  }
+  await addSchemSupportToEntity(event, knownClass.name, db, entityId)
 }
 
 export {

+ 6 - 4
query-node/mappings/content-directory/get-or-create.ts

@@ -67,11 +67,13 @@ import {
 import { DB } from '../../generated/indexer'
 
 // Keep track of the next entity id
-async function nextEntityId(db: DB, nextEntityId: number): Promise<void> {
+async function nextEntityId(db: DB): Promise<NextEntityId> {
   let e = await db.get(NextEntityId, { where: { id: '1' } })
-  if (!e) e = new NextEntityId({ id: '1' })
-  e.nextId = nextEntityId
-  await db.save<NextEntityId>(e)
+  if (!e) {
+    e = new NextEntityId({ id: '1', nextId: 1 })
+    await db.save<NextEntityId>(e)
+  }
+  return e
 }
 
 function generateEntityIdFromIndex(index: number): string {

+ 127 - 0
query-node/mappings/content-directory/initializeDefaultSchemas.ts

@@ -0,0 +1,127 @@
+import { DB, SubstrateEvent } from '../../generated/indexer'
+import { Channel } from '../../generated/graphql-server/src/modules/channel/channel.model'
+import { Video } from '../../generated/graphql-server/src/modules/video/video.model'
+import { Category } from '../../generated/graphql-server/src/modules/category/category.model'
+import { VideoMedia } from '../../generated/graphql-server/src/modules/video-media/video-media.model'
+import { LicenseEntity } from '../../generated/graphql-server/src/modules/license-entity/license-entity.model'
+import { VideoMediaEncoding } from '../../generated/graphql-server/src/modules/video-media-encoding/video-media-encoding.model'
+import { MediaLocationEntity } from '../../generated/graphql-server/src/modules/media-location-entity/media-location-entity.model'
+import { HttpMediaLocationEntity } from '../../generated/graphql-server/src/modules/http-media-location-entity/http-media-location-entity.model'
+import { JoystreamMediaLocationEntity } from '../../generated/graphql-server/src/modules/joystream-media-location-entity/joystream-media-location-entity.model'
+import { KnownLicenseEntity } from '../../generated/graphql-server/src/modules/known-license-entity/known-license-entity.model'
+import { Block, Network } from '../../generated/graphql-server/src/modules/block/block.model'
+import BN from 'bn.js'
+import {
+  JoystreamMediaLocation,
+  KnownLicense,
+} from '../../generated/graphql-server/src/modules/variants/variants.model'
+import { nanoid } from 'nanoid'
+
+let done = false
+
+// eslint-disable-next-line @typescript-eslint/naming-convention
+export async function system_ExtrinsicSuccess(db: DB, event: SubstrateEvent): Promise<void> {
+  if (done) {
+    console.log(`Database initialization is done.`)
+    process.exit()
+  }
+
+  const id = '0'
+
+  // ///////// Block /////////////////
+  const happenedIn = new Block()
+  happenedIn.block = 0
+  happenedIn.timestamp = new BN(Date.now())
+  happenedIn.network = Network.BABYLON
+  await db.save<Block>(happenedIn)
+  // ///////// Block /////////////////
+
+  const commonProperties = { id, happenedIn }
+
+  // ///////// HttpMediaLocationEntity /////////////////
+  const httpMediaLocation = new HttpMediaLocationEntity({
+    ...commonProperties,
+    url: '5FyzfM2YtZa75hHYCAo5evNS8bH8P4Kw8EyXqKkC5upVSDBQ',
+  })
+  await db.save<HttpMediaLocationEntity>(httpMediaLocation)
+  // ///////// HttpMediaLocationEntity /////////////////
+
+  // ///////// JoystreamMediaLocationEntity /////////////////
+  const joyMediaLocation = new JoystreamMediaLocationEntity({
+    ...commonProperties,
+    dataObjectId: '5FyzfM2YtZa75hHYCAo5evNS8bH8P4Kw8EyXqKkC5upVSDBQ',
+  })
+  await db.save<JoystreamMediaLocationEntity>(joyMediaLocation)
+  // ///////// JoystreamMediaLocationEntity /////////////////
+
+  // ///////// KnownLicenseEntity /////////////////
+  const knownLicense = new KnownLicenseEntity({ ...commonProperties, code: 'NA' })
+  await db.save<KnownLicenseEntity>(knownLicense)
+  // ///////// KnownLicenseEntity /////////////////
+
+  // ///////// License /////////////////
+  const k = new KnownLicense()
+  k.code = knownLicense.code
+  const license = new LicenseEntity({ ...commonProperties, type: k })
+  await db.save<LicenseEntity>(license)
+  // ///////// License /////////////////
+
+  // ///////// MediaLocationEntity /////////////////
+  const mediaLocEntity = new MediaLocationEntity({ ...commonProperties, joystreamMediaLocation: joyMediaLocation })
+  await db.save<MediaLocationEntity>(mediaLocEntity)
+  // ///////// MediaLocationEntity /////////////////
+
+  // ///////// Channel /////////////////
+  const channel = new Channel({
+    ...commonProperties,
+    handle: `Channel(0) - ${nanoid()}`,
+    description: `Channel 0`,
+    isPublic: false,
+    isCurated: false,
+  })
+  await db.save<Channel>(channel)
+  // ///////// Channel /////////////////
+
+  // ///////// Category /////////////////
+  const category = new Category({ ...commonProperties, name: `Category(0) ${nanoid()}` })
+  await db.save<Category>(category)
+  // ///////// Category /////////////////
+
+  // ///////// VideoMediaEncoding /////////////////
+  const videoMediaEncod = new VideoMediaEncoding({ ...commonProperties, name: 'NA' })
+  await db.save<VideoMediaEncoding>(videoMediaEncod)
+  // ///////// VideoMediaEncoding /////////////////
+
+  // ///////// VideoMedia /////////////////
+  const location = new JoystreamMediaLocation()
+  location.dataObjectId = joyMediaLocation.dataObjectId
+  const videoMedia = new VideoMedia({
+    ...commonProperties,
+    location,
+    locationEntity: mediaLocEntity,
+    encoding: videoMediaEncod,
+    pixelHeight: 0,
+    pixelWidth: 0,
+  })
+  await db.save<VideoMedia>(videoMedia)
+  // ///////// VideoMedia /////////////////
+
+  // ///////// Video /////////////////
+  const v = new Video({ ...commonProperties })
+  v.category = category
+  v.channel = channel
+  v.media = videoMedia
+  v.license = license
+  v.title = `Video(0)`
+  v.description = `Video(0)`
+  v.duration = 0
+  v.thumbnailUrl = 'https://eu-central-1.linodeobjects.com/joystream/1.png'
+  v.isPublic = false
+  v.isCurated = false
+  v.isExplicit = true
+  v.isFeatured = false
+  await db.save<Video>(v)
+  // ///////// Video /////////////////
+
+  done = true
+}

+ 3 - 0
query-node/mappings/content-directory/mapping.ts

@@ -5,3 +5,6 @@ export {
   contentDirectory_EntityPropertyValuesUpdated,
 } from './entity'
 export { contentDirectory_TransactionCompleted, contentDirectory_TransactionFailed } from './transaction'
+
+// Only one time to seed the database
+// export { system_ExtrinsicSuccess } from './initializeDefaultSchemas'

+ 171 - 260
query-node/mappings/content-directory/transaction.ts

@@ -20,6 +20,7 @@ import {
   ILanguage,
   ILicense,
   IMediaLocation,
+  IProperty,
   IUserDefinedLicense,
   IVideo,
   IVideoMedia,
@@ -42,40 +43,25 @@ import {
   mediaLocationPropertyNamesWithId,
   featuredVideoPropertyNamesWithId,
 } from './content-dir-consts'
-import {
-  updateCategoryEntityPropertyValues,
-  updateChannelEntityPropertyValues,
-  updateVideoMediaEntityPropertyValues,
-  updateVideoEntityPropertyValues,
-  updateUserDefinedLicenseEntityPropertyValues,
-  updateHttpMediaLocationEntityPropertyValues,
-  updateJoystreamMediaLocationEntityPropertyValues,
-  updateKnownLicenseEntityPropertyValues,
-  updateLanguageEntityPropertyValues,
-  updateVideoMediaEncodingEntityPropertyValues,
-  updateLicenseEntityPropertyValues,
-  updateMediaLocationEntityPropertyValues,
-  updateFeaturedVideoEntityPropertyValues,
-} from './entity/update'
 
-import {
-  createCategory,
-  createChannel,
-  createVideoMedia,
-  createVideo,
-  createUserDefinedLicense,
-  createKnownLicense,
-  createHttpMediaLocation,
-  createJoystreamMediaLocation,
-  createLanguage,
-  createVideoMediaEncoding,
-  getClassName,
-  createLicense,
-  createMediaLocation,
-  createBlockOrGetFromDatabase,
-  createFeaturedVideo,
-} from './entity/create'
+import { getClassName, createBlockOrGetFromDatabase } from './entity/create'
 import { getOrCreate } from './get-or-create'
+import {
+  addSchemaToCategory,
+  addSchemaToChannel,
+  addSchemaToFeaturedVideo,
+  addSchemaToHttpMediaLocation,
+  addSchemaToJoystreamMediaLocation,
+  addSchemaToKnownLicense,
+  addSchemaToLanguage,
+  addSchemaToLicense,
+  addSchemaToMediaLocation,
+  addSchemaToUserDefinedLicense,
+  addSchemaToVideo,
+  addSchemaToVideoMedia,
+  addSchemaToVideoMediaEncoding,
+} from './entity/addSchema'
+import { createDefaultSchema } from './default-schemas'
 
 const debug = Debug('mappings:cd:transaction')
 
@@ -119,21 +105,26 @@ async function applyOperations(operations: IBatchOperation, db: DB, event: Subst
 }
 
 async function batchCreateClassEntities(db: DB, block: number, operations: ICreateEntityOperation[]): Promise<void> {
-  const nId = await db.get(NextEntityId, { where: { id: '1' } })
-  let nextId = nId ? nId.nextId : 1 // start entity id from 1
+  const nextEntityIdFromDb = await getOrCreate.nextEntityId(db)
 
+  let entityId = nextEntityIdFromDb.nextId
   for (const { classId } of operations) {
     const c = new ClassEntity({
-      id: nextId.toString(), // entity id
+      id: entityId.toString(),
       classId: classId,
       version: block,
       happenedIn: await createBlockOrGetFromDatabase(db, block),
     })
     await db.save<ClassEntity>(c)
-    nextId++
+
+    // Create default schema for the entity
+    await createDefaultSchema(db, c)
+    entityId++
   }
 
-  await getOrCreate.nextEntityId(db, nextId)
+  // Update database for next entity id
+  nextEntityIdFromDb.nextId = entityId
+  await db.save<NextEntityId>(nextEntityIdFromDb)
 }
 
 /**
@@ -161,7 +152,7 @@ async function batchAddSchemaSupportToEntity(
 
   // This is a copy of classEntityMap, we will use it to keep track of items.
   // We will remove items from this list whenever we insert them into db
-  const doneList: ClassEntityMap = new Map(classEntityMap.entries())
+  // const doneList: ClassEntityMap = new Map(classEntityMap.entries())
 
   const nextEntityIdBeforeTransaction = (await getNextEntityId(db)) - createEntityOperations.length
 
@@ -170,112 +161,9 @@ async function batchAddSchemaSupportToEntity(
       const { entityId, indexOf, properties } = entity
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
       const id = entityId !== undefined ? entityId : indexOf! + nextEntityIdBeforeTransaction
-      const arg: IDBBlockId = { db, block, id: id.toString() }
-
-      switch (className) {
-        case ContentDirectoryKnownClasses.CATEGORY:
-          await createCategory(arg, decode.setEntityPropertyValues<ICategory>(properties, categoryPropertyNamesWithId))
-          break
-
-        case ContentDirectoryKnownClasses.CHANNEL:
-          await createChannel(
-            arg,
-            doneList,
-            decode.setEntityPropertyValues<IChannel>(properties, channelPropertyNamesWithId),
-            nextEntityIdBeforeTransaction
-          )
-          break
-
-        case ContentDirectoryKnownClasses.KNOWNLICENSE:
-          await createKnownLicense(
-            arg,
-            decode.setEntityPropertyValues<IKnownLicense>(properties, knownLicensePropertyNamesWIthId)
-          )
-          break
-
-        case ContentDirectoryKnownClasses.USERDEFINEDLICENSE:
-          await createUserDefinedLicense(
-            arg,
-            decode.setEntityPropertyValues<IUserDefinedLicense>(properties, userDefinedLicensePropertyNamesWithId)
-          )
-          break
-
-        case ContentDirectoryKnownClasses.JOYSTREAMMEDIALOCATION:
-          await createJoystreamMediaLocation(
-            arg,
-            decode.setEntityPropertyValues<IJoystreamMediaLocation>(
-              properties,
-              joystreamMediaLocationPropertyNamesWithId
-            )
-          )
-          break
-
-        case ContentDirectoryKnownClasses.HTTPMEDIALOCATION:
-          await createHttpMediaLocation(
-            arg,
-            decode.setEntityPropertyValues<IHttpMediaLocation>(properties, httpMediaLocationPropertyNamesWithId)
-          )
-          break
-
-        case ContentDirectoryKnownClasses.VIDEOMEDIA:
-          await createVideoMedia(
-            arg,
-            doneList,
-            decode.setEntityPropertyValues<IVideoMedia>(properties, videoMediaPropertyNamesWithId),
-            nextEntityIdBeforeTransaction
-          )
-          break
-
-        case ContentDirectoryKnownClasses.VIDEO:
-          await createVideo(
-            arg,
-            doneList,
-            decode.setEntityPropertyValues<IVideo>(properties, videoPropertyNamesWithId),
-            nextEntityIdBeforeTransaction
-          )
-          break
-
-        case ContentDirectoryKnownClasses.LANGUAGE:
-          await createLanguage(arg, decode.setEntityPropertyValues<ILanguage>(properties, languagePropertyNamesWIthId))
-          break
-
-        case ContentDirectoryKnownClasses.VIDEOMEDIAENCODING:
-          await createVideoMediaEncoding(
-            arg,
-            decode.setEntityPropertyValues<IVideoMediaEncoding>(properties, videoMediaEncodingPropertyNamesWithId)
-          )
-          break
-
-        case ContentDirectoryKnownClasses.LICENSE:
-          await createLicense(
-            arg,
-            classEntityMap,
-            decode.setEntityPropertyValues<ILicense>(properties, licensePropertyNamesWithId),
-            nextEntityIdBeforeTransaction
-          )
-          break
-        case ContentDirectoryKnownClasses.MEDIALOCATION:
-          await createMediaLocation(
-            arg,
-            classEntityMap,
-            decode.setEntityPropertyValues<IMediaLocation>(properties, mediaLocationPropertyNamesWithId),
-            nextEntityIdBeforeTransaction
-          )
-          break
-
-        case ContentDirectoryKnownClasses.FEATUREDVIDEOS:
-          await createFeaturedVideo(
-            arg,
-            classEntityMap,
-            decode.setEntityPropertyValues<IFeaturedVideo>(properties, featuredVideoPropertyNamesWithId),
-            nextEntityIdBeforeTransaction
-          )
-          break
-
-        default:
-          console.log(`Unknown class name: ${className}`)
-          break
-      }
+      // const arg: IDBBlockId = { db, block, id: id.toString() }
+
+      await addSchemaSupportToEntity(db, className, id, nextEntityIdBeforeTransaction, properties)
     }
   }
 }
@@ -292,126 +180,149 @@ async function batchUpdatePropertyValue(db: DB, createEntityOperations: ICreateE
   for (const entity of entities) {
     const { entityId, indexOf, properties } = entity
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    const id = entityId ? entityId.toString() : entityIdBeforeTransaction - indexOf!
+    const id = entityId !== undefined ? entityId : entityIdBeforeTransaction - indexOf!
 
-    const where: IWhereCond = { where: { id: id.toString() } }
+    // const where: IWhereCond = { where: { id: id.toString() } }
     const className = await getClassName(db, entity, createEntityOperations)
-    if (className === undefined) {
-      console.log(`Can not update entity properties values. Unknown class name`)
+    if (!className) {
+      debug(`Can not update entity properties values. Unknown class name`)
       return
     }
 
-    switch (className) {
-      case ContentDirectoryKnownClasses.CHANNEL:
-        await updateChannelEntityPropertyValues(
-          db,
-          where,
-          decode.setEntityPropertyValues<IChannel>(properties, channelPropertyNamesWithId),
-          entityIdBeforeTransaction
-        )
-        break
-
-      case ContentDirectoryKnownClasses.CATEGORY:
-        await updateCategoryEntityPropertyValues(
-          db,
-          where,
-          decode.setEntityPropertyValues<ICategory>(properties, categoryPropertyNamesWithId)
-        )
-        break
-
-      case ContentDirectoryKnownClasses.KNOWNLICENSE:
-        await updateKnownLicenseEntityPropertyValues(
-          db,
-          where,
-          decode.setEntityPropertyValues<IKnownLicense>(properties, knownLicensePropertyNamesWIthId)
-        )
-        break
-
-      case ContentDirectoryKnownClasses.USERDEFINEDLICENSE:
-        await updateUserDefinedLicenseEntityPropertyValues(
-          db,
-          where,
-          decode.setEntityPropertyValues<IUserDefinedLicense>(properties, userDefinedLicensePropertyNamesWithId)
-        )
-        break
-
-      case ContentDirectoryKnownClasses.JOYSTREAMMEDIALOCATION:
-        await updateJoystreamMediaLocationEntityPropertyValues(
-          db,
-          where,
-          decode.setEntityPropertyValues<IJoystreamMediaLocation>(properties, joystreamMediaLocationPropertyNamesWithId)
-        )
-        break
-
-      case ContentDirectoryKnownClasses.HTTPMEDIALOCATION:
-        await updateHttpMediaLocationEntityPropertyValues(
-          db,
-          where,
-          decode.setEntityPropertyValues<IHttpMediaLocation>(properties, httpMediaLocationPropertyNamesWithId)
-        )
-        break
-
-      case ContentDirectoryKnownClasses.VIDEOMEDIA:
-        await updateVideoMediaEntityPropertyValues(
-          db,
-          where,
-          decode.setEntityPropertyValues<IVideoMedia>(properties, videoPropertyNamesWithId),
-          entityIdBeforeTransaction
-        )
-        break
-
-      case ContentDirectoryKnownClasses.VIDEO:
-        await updateVideoEntityPropertyValues(
-          db,
-          where,
-          decode.setEntityPropertyValues<IVideo>(properties, videoPropertyNamesWithId),
-          entityIdBeforeTransaction
-        )
-        break
-
-      case ContentDirectoryKnownClasses.LANGUAGE:
-        await updateLanguageEntityPropertyValues(
-          db,
-          where,
-          decode.setEntityPropertyValues<ILanguage>(properties, languagePropertyNamesWIthId)
-        )
-        break
-
-      case ContentDirectoryKnownClasses.VIDEOMEDIAENCODING:
-        await updateVideoMediaEncodingEntityPropertyValues(
-          db,
-          where,
-          decode.setEntityPropertyValues<IVideoMediaEncoding>(properties, videoMediaEncodingPropertyNamesWithId)
-        )
-        break
-      case ContentDirectoryKnownClasses.LICENSE:
-        await updateLicenseEntityPropertyValues(
-          db,
-          where,
-          decode.setEntityPropertyValues<ILicense>(properties, licensePropertyNamesWithId),
-          entityIdBeforeTransaction
-        )
-        break
-      case ContentDirectoryKnownClasses.MEDIALOCATION:
-        await updateMediaLocationEntityPropertyValues(
-          db,
-          where,
-          decode.setEntityPropertyValues<IMediaLocation>(properties, mediaLocationPropertyNamesWithId),
-          entityIdBeforeTransaction
-        )
-        break
-      case ContentDirectoryKnownClasses.FEATUREDVIDEOS:
-        await updateFeaturedVideoEntityPropertyValues(
-          db,
-          where,
-          decode.setEntityPropertyValues<IFeaturedVideo>(properties, featuredVideoPropertyNamesWithId),
-          entityIdBeforeTransaction
-        )
-        break
-
-      default:
-        console.log(`Unknown class name: ${className}`)
-        break
-    }
+    await addSchemaSupportToEntity(db, className, id, entityIdBeforeTransaction, properties)
+  }
+}
+
+async function addSchemaSupportToEntity(
+  db: DB,
+  className: string,
+  entityId: number,
+  nextEntityId: number,
+  properties: IProperty[]
+) {
+  switch (className) {
+    case ContentDirectoryKnownClasses.CATEGORY:
+      await addSchemaToCategory({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setEntityPropertyValues<ICategory>(properties, categoryPropertyNamesWithId),
+      })
+      break
+
+    case ContentDirectoryKnownClasses.CHANNEL:
+      await addSchemaToChannel({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setEntityPropertyValues<IChannel>(properties, channelPropertyNamesWithId),
+      })
+      break
+
+    case ContentDirectoryKnownClasses.KNOWNLICENSE:
+      await addSchemaToKnownLicense({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setEntityPropertyValues<IKnownLicense>(properties, knownLicensePropertyNamesWIthId),
+      })
+      break
+
+    case ContentDirectoryKnownClasses.USERDEFINEDLICENSE:
+      await addSchemaToUserDefinedLicense({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setEntityPropertyValues<IUserDefinedLicense>(properties, userDefinedLicensePropertyNamesWithId),
+      })
+      break
+
+    case ContentDirectoryKnownClasses.JOYSTREAMMEDIALOCATION:
+      await addSchemaToJoystreamMediaLocation({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setEntityPropertyValues<IJoystreamMediaLocation>(
+          properties,
+          joystreamMediaLocationPropertyNamesWithId
+        ),
+      })
+      break
+
+    case ContentDirectoryKnownClasses.HTTPMEDIALOCATION:
+      await addSchemaToHttpMediaLocation({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setEntityPropertyValues<IHttpMediaLocation>(properties, httpMediaLocationPropertyNamesWithId),
+      })
+      break
+
+    case ContentDirectoryKnownClasses.VIDEOMEDIA:
+      await addSchemaToVideoMedia({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setEntityPropertyValues<IVideoMedia>(properties, videoMediaPropertyNamesWithId),
+      })
+      break
+
+    case ContentDirectoryKnownClasses.VIDEO:
+      await addSchemaToVideo({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setEntityPropertyValues<IVideo>(properties, videoPropertyNamesWithId),
+      })
+      break
+
+    case ContentDirectoryKnownClasses.LANGUAGE:
+      await addSchemaToLanguage({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setEntityPropertyValues<ILanguage>(properties, languagePropertyNamesWIthId),
+      })
+      break
+
+    case ContentDirectoryKnownClasses.VIDEOMEDIAENCODING:
+      await addSchemaToVideoMediaEncoding({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setEntityPropertyValues<IVideoMediaEncoding>(properties, videoMediaEncodingPropertyNamesWithId),
+      })
+      break
+
+    case ContentDirectoryKnownClasses.LICENSE:
+      await addSchemaToLicense({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setEntityPropertyValues<ILicense>(properties, licensePropertyNamesWithId),
+      })
+      break
+
+    case ContentDirectoryKnownClasses.MEDIALOCATION:
+      await addSchemaToMediaLocation({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setEntityPropertyValues<IMediaLocation>(properties, mediaLocationPropertyNamesWithId),
+      })
+      break
+
+    case ContentDirectoryKnownClasses.FEATUREDVIDEOS:
+      await addSchemaToFeaturedVideo({
+        db,
+        entityId,
+        nextEntityId,
+        props: decode.setEntityPropertyValues<IFeaturedVideo>(properties, featuredVideoPropertyNamesWithId),
+      })
+      break
+
+    default:
+      debug(`Unknown class name: ${className}`)
+      break
   }
 }

+ 1 - 0
query-node/package.json

@@ -34,6 +34,7 @@
 		"bn.js": "^5.1.2",
 		"debug": "^4.2.0",
 		"dotenvi": "^0.9.1",
+		"nanoid": "^3.1.20",
 		"tslib": "^2.0.0"
 	},
 	"volta": {

+ 2 - 2
query-node/schema.graphql

@@ -185,7 +185,7 @@ type JoystreamMediaLocationEntity @entity {
   id: ID!
 
   "Id of the data object in the Joystream runtime dataDirectory module"
-  dataObjectId: String! @unique
+  dataObjectId: String!
 
   happenedIn: Block!
 }
@@ -258,7 +258,7 @@ type Video @entity {
   language: Language
 
   "Reference to VideoMedia"
-  media: VideoMedia!
+  media: VideoMedia
 
   "Whether or not Video contains marketing"
   hasMarketing: Boolean