Browse Source

Merge pull request #1847 from metmirr/qnode-license-attribution

Query node add attribution field to License entity
Mokhtar Naamani 4 years ago
parent
commit
7b4f85cc67

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

@@ -52,6 +52,7 @@ export const channelPropertyNamesWithId: IPropertyWithId = {
 export const licensePropertyNamesWithId: IPropertyWithId = {
   0: { name: 'knownLicense', type: 'number', required: false },
   1: { name: 'userDefinedLicense', type: 'number', required: false },
+  2: { name: 'attribution', type: 'string', required: false },
 }
 
 export const knownLicensePropertyNamesWIthId: IPropertyWithId = {

+ 25 - 33
query-node/mappings/content-directory/entity/create.ts

@@ -205,14 +205,12 @@ async function createVideoMedia(
     const { httpMediaLocation, joystreamMediaLocation } = m
     if (httpMediaLocation) {
       const mediaLoc = new HttpMediaLocation()
-      mediaLoc.isTypeOf = 'HttpMediaLocation'
       mediaLoc.port = httpMediaLocation.port
       mediaLoc.url = httpMediaLocation.url
       videoMedia.location = mediaLoc
     }
     if (joystreamMediaLocation) {
       const mediaLoc = new JoystreamMediaLocation()
-      mediaLoc.isTypeOf = 'JoystreamMediaLocation'
       mediaLoc.dataObjectId = joystreamMediaLocation.dataObjectId
       videoMedia.location = mediaLoc
     }
@@ -257,28 +255,9 @@ async function createVideo(
       nextEntityIdBeforeTransaction
     )
   }
-  if (license !== undefined) {
-    const { knownLicense, userdefinedLicense } = await getOrCreate.license(
-      { db, block, id },
-      classEntityMap,
-      license,
-      nextEntityIdBeforeTransaction
-    )
-    if (knownLicense) {
-      const lic = new KnownLicense()
-      lic.code = knownLicense.code
-      lic.description = knownLicense.description
-      lic.isTypeOf = 'KnownLicense'
-      lic.name = knownLicense.name
-      lic.url = knownLicense.url
-      video.license = lic
-    }
-    if (userdefinedLicense) {
-      const lic = new UserDefinedLicense()
-      lic.content = userdefinedLicense.content
-      lic.isTypeOf = 'UserDefinedLicense'
-      video.license = lic
-    }
+  if (license) {
+    const lic = await getOrCreate.license({ db, block, id }, classEntityMap, license, nextEntityIdBeforeTransaction)
+    video.license = lic
   }
   if (category !== undefined) {
     video.category = await getOrCreate.category(
@@ -340,27 +319,40 @@ async function createLicense(
   const record = await db.get(LicenseEntity, { where: { id } })
   if (record) return record
 
-  const { knownLicense, userDefinedLicense } = p
-
   const license = new LicenseEntity()
-  license.id = id
-  if (knownLicense !== undefined) {
-    license.knownLicense = await getOrCreate.knownLicense(
+
+  if (p.knownLicense) {
+    const kLicense = await getOrCreate.knownLicense(
       { db, block, id },
       classEntityMap,
-      knownLicense,
+      p.knownLicense,
       nextEntityIdBeforeTransaction
     )
+    const k = new KnownLicense()
+    k.code = kLicense.code
+    k.description = kLicense.description
+    k.name = kLicense.name
+    k.url = kLicense.url
+    // Set the license type
+    license.type = k
   }
-  if (userDefinedLicense !== undefined) {
-    license.userdefinedLicense = await getOrCreate.userDefinedLicense(
+  if (p.userDefinedLicense) {
+    const { content } = await getOrCreate.userDefinedLicense(
       { db, block, id },
       classEntityMap,
-      userDefinedLicense,
+      p.userDefinedLicense,
       nextEntityIdBeforeTransaction
     )
+    const u = new UserDefinedLicense()
+    u.content = content
+    // Set the license type
+    license.type = u
   }
+
+  license.id = id
+  license.attribution = p.attribution
   license.happenedIn = await createBlockOrGetFromDatabase(db, block)
+
   await db.save<LicenseEntity>(license)
   return license
 }

+ 36 - 68
query-node/mappings/content-directory/entity/remove.ts

@@ -1,4 +1,4 @@
-import Debug from 'debug'
+import assert from 'assert'
 
 import { DB } from '../../../generated/indexer'
 import { Channel } from '../../../generated/graphql-server/src/modules/channel/channel.model'
@@ -17,127 +17,95 @@ 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`)
+}
 
 async function removeChannel(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(Channel, where)
-  if (record === undefined) throw Error(`Channel not found`)
-  if (record.videos) record.videos.map(async (v) => await removeVideo(db, { where: { id: v.id } }))
+  if (!record) throw Error(`Channel(${where.where.id}) not found`)
+  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 === undefined) throw Error(`Category not found`)
-  if (record.videos) record.videos.map(async (v) => await removeVideo(db, { where: { id: v.id } }))
+  if (!record) throw Error(`Category(${where.where.id}) not found`)
+  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 === undefined) throw Error(`VideoMedia not found`)
-  if (record.video) await db.remove<Video>(record.video)
+  if (!record) throw Error(`VideoMedia(${where.where.id}) not found`)
+  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 === undefined) throw Error(`Video not found`)
+  if (!record) throw Error(`Video(${where.where.id}) not found`)
   await db.remove<Video>(record)
 }
 
 async function removeLicense(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(LicenseEntity, where)
-  if (record === undefined) throw Error(`License not found`)
-
-  const { knownLicense, userdefinedLicense } = record
-  let videos: Video[] = []
-
-  if (knownLicense) {
-    videos = await db.getMany(Video, {
-      where: {
-        license: {
-          isTypeOf: 'KnownLicense',
-          code: knownLicense.code,
-          description: knownLicense.description,
-          name: knownLicense.name,
-          url: knownLicense.url,
-        },
-      },
-    })
-  }
-  if (userdefinedLicense) {
-    videos = await db.getMany(Video, {
-      where: { license: { isTypeOf: 'UserDefinedLicense', content: userdefinedLicense.content } },
-    })
-  }
-  // Remove all the videos under this license
-  videos.map(async (v) => await removeVideo(db, { where: { id: v.id } }))
+  if (!record) throw Error(`License(${where.where.id}) not found`)
+  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 === undefined) throw Error(`UserDefinedLicense not found`)
-  if (record.licenseentityuserdefinedLicense)
-    record.licenseentityuserdefinedLicense.map(async (l) => await removeLicense(db, { where: { id: l.id } }))
+  if (!record) throw Error(`UserDefinedLicense(${where.where.id}) not found`)
   await db.remove<UserDefinedLicenseEntity>(record)
 }
+
 async function removeKnownLicense(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(KnownLicenseEntity, where)
-  if (record === undefined) throw Error(`KnownLicense not found`)
-  if (record.licenseentityknownLicense)
-    record.licenseentityknownLicense.map(async (k) => await removeLicense(db, { where: { id: k.id } }))
+  if (!record) throw Error(`KnownLicense(${where.where.id}) not found`)
   await db.remove<KnownLicenseEntity>(record)
 }
 async function removeMediaLocation(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(MediaLocationEntity, where)
-  if (record === undefined) throw Error(`MediaLocation not found`)
-  if (record.videoMedia) await removeVideo(db, { where: { id: record.videoMedia.id } })
-
-  const { httpMediaLocation, joystreamMediaLocation } = record
-
-  let videoMedia: VideoMedia | undefined
-  if (httpMediaLocation) {
-    videoMedia = await db.get(VideoMedia, {
-      where: { location: { isTypeOf: 'HttpMediaLocation', url: httpMediaLocation.url, port: httpMediaLocation.port } },
-    })
-  }
-  if (joystreamMediaLocation) {
-    videoMedia = await db.get(VideoMedia, {
-      where: { location: { isTypeOf: 'JoystreamMediaLocation', dataObjectId: joystreamMediaLocation.dataObjectId } },
-    })
-  }
-  if (videoMedia) await db.remove<VideoMedia>(videoMedia)
+  if (!record) throw Error(`MediaLocation(${where.where.id}) not found`)
+  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 === undefined) throw Error(`HttpMediaLocation not found`)
-  if (record.medialocationentityhttpMediaLocation)
-    record.medialocationentityhttpMediaLocation.map(async (v) => await removeMediaLocation(db, { where: { id: v.id } }))
+  if (!record) throw Error(`HttpMediaLocation(${where.where.id}) not found`)
+  if (record.medialocationentityhttpMediaLocation && record.medialocationentityhttpMediaLocation.length) {
+    assertKeyViolation('HttpMediaLocation', record.id)
+  }
   await db.remove<HttpMediaLocationEntity>(record)
 }
+
 async function removeJoystreamMediaLocation(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(JoystreamMediaLocationEntity, where)
-  if (record === undefined) throw Error(`JoystreamMediaLocation not found`)
-  if (record.medialocationentityjoystreamMediaLocation)
-    record.medialocationentityjoystreamMediaLocation.map(async (v) => await removeVideo(db, { where: { id: v.id } }))
+  if (!record) throw Error(`JoystreamMediaLocation(${where.where.id}) not found`)
+  if (record.medialocationentityjoystreamMediaLocation && record.medialocationentityjoystreamMediaLocation.length) {
+    assertKeyViolation('JoystreamMediaLocation', record.id)
+  }
   await db.remove<JoystreamMediaLocationEntity>(record)
 }
+
 async function removeLanguage(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(Language, where)
-  if (record === undefined) throw Error(`Language not found`)
-  if (record.channellanguage) record.channellanguage.map(async (c) => await removeChannel(db, { where: { id: c.id } }))
-  if (record.videolanguage) record.videolanguage.map(async (v) => await removeVideo(db, { where: { id: v.id } }))
+  if (!record) throw Error(`Language(${where.where.id}) not found`)
+  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)
 }
+
 async function removeVideoMediaEncoding(db: DB, where: IWhereCond): Promise<void> {
   const record = await db.get(VideoMediaEncoding, where)
-  if (record === undefined) throw Error(`Language not found`)
+  if (!record) throw Error(`VideoMediaEncoding(${where.where.id}) not found`)
   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 not found. id: ${where.where.id}`)
+  if (!record) throw Error(`FeaturedVideo(${where.where.id}) not found`)
 
   record.video.isFeatured = false
   record.video.featured = undefined

+ 29 - 24
query-node/mappings/content-directory/entity/update.ts

@@ -76,12 +76,29 @@ async function updateLicenseEntityPropertyValues(
   const { knownLicense, userDefinedLicense } = props
   if (knownLicense) {
     const id = getEntityIdFromReferencedField(knownLicense, entityIdBeforeTransaction)
-    record.knownLicense = await db.get(KnownLicenseEntity, { where: { id } })
+    const kLicense = await db.get(KnownLicenseEntity, { where: { id } })
+    if (!kLicense) throw Error(`KnownLicense not found ${id}`)
+
+    const k = new KnownLicense()
+    k.code = kLicense.code
+    k.description = kLicense.description
+    k.name = kLicense.name
+    k.url = kLicense.url
+    // Set the license type
+    record.type = k
   }
   if (userDefinedLicense) {
     const id = getEntityIdFromReferencedField(userDefinedLicense, entityIdBeforeTransaction)
-    record.userdefinedLicense = await db.get(UserDefinedLicenseEntity, { where: { id } })
+    const udl = await db.get(UserDefinedLicenseEntity, { where: { id } })
+    if (!udl) throw Error(`UserDefinedLicense not found ${id}`)
+
+    const u = new UserDefinedLicense()
+    u.content = udl.content
+    // Set the license type
+    record.type = u
   }
+
+  record.attribution = props.attribution || record.attribution
   await db.save<LicenseEntity>(record)
 }
 
@@ -91,6 +108,7 @@ async function updateCategoryEntityPropertyValues(db: DB, where: IWhereCond, pro
   Object.assign(record, props)
   await db.save<Category>(record)
 }
+
 async function updateChannelEntityPropertyValues(
   db: DB,
   where: IWhereCond,
@@ -112,6 +130,7 @@ async function updateChannelEntityPropertyValues(
   record.language = lang
   await db.save<Channel>(record)
 }
+
 async function updateVideoMediaEntityPropertyValues(
   db: DB,
   where: IWhereCond,
@@ -139,13 +158,11 @@ async function updateVideoMediaEntityPropertyValues(
 
     if (httpMediaLocation) {
       mediaLoc = new HttpMediaLocation()
-      mediaLoc.isTypeOf = typeof HttpMediaLocation
       mediaLoc.url = httpMediaLocation.url
       mediaLoc.port = httpMediaLocation.port
     }
     if (joystreamMediaLocation) {
       mediaLoc = new JoystreamMediaLocation()
-      mediaLoc.isTypeOf = typeof JoystreamMediaLocation
       mediaLoc.dataObjectId = joystreamMediaLocation.dataObjectId
     }
     props.location = undefined
@@ -156,6 +173,7 @@ async function updateVideoMediaEntityPropertyValues(
   record.location = mediaLoc
   await db.save<VideoMedia>(record)
 }
+
 async function updateVideoEntityPropertyValues(
   db: DB,
   where: IWhereCond,
@@ -169,7 +187,6 @@ async function updateVideoEntityPropertyValues(
   let cat: Category | undefined
   let lang: Language | undefined
   let vMedia: VideoMedia | undefined
-  let lic: KnownLicense | UserDefinedLicense = record.license
 
   const { channel, category, language, media, license } = props
   if (channel) {
@@ -192,25 +209,9 @@ async function updateVideoEntityPropertyValues(
   }
   if (license) {
     const id = getEntityIdFromReferencedField(license, entityIdBeforeTransaction)
-    const licenseEntity = await db.get(LicenseEntity, {
-      where: { id },
-      relations: ['knownLicense', 'userdefinedLicense'],
-    })
+    const licenseEntity = await db.get(LicenseEntity, { where: { id } })
     if (!licenseEntity) throw Error(`License entity not found: ${id}`)
-    const { knownLicense, userdefinedLicense } = licenseEntity
-    if (knownLicense) {
-      lic = new KnownLicense()
-      lic.code = knownLicense.code
-      lic.description = knownLicense.description
-      lic.isTypeOf = 'KnownLicense'
-      lic.name = knownLicense.name
-      lic.url = knownLicense.url
-    }
-    if (userdefinedLicense) {
-      lic = new UserDefinedLicense()
-      lic.content = userdefinedLicense.content
-      lic.isTypeOf = 'UserDefinedLicense'
-    }
+    record.license = licenseEntity
     props.license = undefined
   }
   if (language) {
@@ -225,11 +226,11 @@ async function updateVideoEntityPropertyValues(
   record.channel = chann || record.channel
   record.category = cat || record.category
   record.media = vMedia || record.media
-  record.license = lic
   record.language = lang
 
   await db.save<Video>(record)
 }
+
 async function updateUserDefinedLicenseEntityPropertyValues(
   db: DB,
   where: IWhereCond,
@@ -240,12 +241,14 @@ async function updateUserDefinedLicenseEntityPropertyValues(
   Object.assign(record, props)
   await db.save<UserDefinedLicenseEntity>(record)
 }
+
 async function updateKnownLicenseEntityPropertyValues(db: DB, where: IWhereCond, props: IKnownLicense): Promise<void> {
   const record = await db.get(KnownLicenseEntity, where)
   if (record === undefined) throw Error(`Entity not found: ${where.where.id}`)
   Object.assign(record, props)
   await db.save<KnownLicenseEntity>(record)
 }
+
 async function updateHttpMediaLocationEntityPropertyValues(
   db: DB,
   where: IWhereCond,
@@ -267,12 +270,14 @@ async function updateJoystreamMediaLocationEntityPropertyValues(
   Object.assign(record, props)
   await db.save<JoystreamMediaLocationEntity>(record)
 }
+
 async function updateLanguageEntityPropertyValues(db: DB, where: IWhereCond, props: ILanguage): Promise<void> {
   const record = await db.get(Language, where)
   if (record === undefined) throw Error(`Entity not found: ${where.where.id}`)
   Object.assign(record, props)
   await db.save<Language>(record)
 }
+
 async function updateVideoMediaEncodingEntityPropertyValues(
   db: DB,
   where: IWhereCond,

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

@@ -352,7 +352,7 @@ async function license(
 
   const id = generateEntityIdFromIndex(nextEntityIdBeforeTransaction + entityId)
   // could be created in the transaction
-  lic = await db.get(LicenseEntity, { where: { id }, relations: ['knownLicense', 'userdefinedLicense'] })
+  lic = await db.get(LicenseEntity, { where: { id } })
   if (lic) return lic
 
   const { properties } = findEntity(entityId, 'License', classEntityMap)

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

@@ -115,6 +115,7 @@ export interface IVideo {
 export interface ILicense {
   knownLicense?: IReference
   userDefinedLicense?: IReference
+  attribution?: string
 }
 
 export interface IMediaLocation {

+ 15 - 18
query-node/schema.graphql

@@ -163,21 +163,6 @@ type UserDefinedLicenseEntity @entity {
   happenedIn: Block!
 }
 
-type LicenseEntity @entity {
-  "Runtime entity identifier (EntityId)"
-  id: ID!
-
-  # One of the following field will be non-null
-
-  "Reference to a known license"
-  knownLicense: KnownLicenseEntity
-
-  "Reference to user-defined license"
-  userdefinedLicense: UserDefinedLicenseEntity
-
-  happenedIn: Block!
-}
-
 type MediaLocationEntity @entity {
   "Runtime entity identifier (EntityId)"
   id: ID!
@@ -290,7 +275,7 @@ type Video @entity {
   "Whether the Video contains explicit material."
   isExplicit: Boolean!
 
-  license: License!
+  license: LicenseEntity!
 
   happenedIn: Block!
 
@@ -313,6 +298,8 @@ type HttpMediaLocation @variant {
   port: Int
 }
 
+union MediaLocation = HttpMediaLocation | JoystreamMediaLocation
+
 type KnownLicense @variant {
   "Short, commonly recognized code of the licence (ie. CC_BY_SA)"
   code: String!
@@ -332,10 +319,20 @@ type UserDefinedLicense @variant {
   content: String!
 }
 
-union MediaLocation = HttpMediaLocation | JoystreamMediaLocation
-
 union License = KnownLicense | UserDefinedLicense
 
+type LicenseEntity @entity {
+  "Runtime entity identifier (EntityId)"
+  id: ID!
+
+  type: License!
+
+  "Attribution (if required by the license)"
+  attribution: String
+
+  happenedIn: Block!
+}
+
 type FeaturedVideo @entity {
   "Runtime entity identifier (EntityId)"
   id: ID!