get-or-create.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. import { Channel } from '../../generated/graphql-server/src/modules/channel/channel.model'
  2. import { Category } from '../../generated/graphql-server/src/modules/category/category.model'
  3. import { KnownLicenseEntity } from '../../generated/graphql-server/src/modules/known-license-entity/known-license-entity.model'
  4. import { UserDefinedLicenseEntity } from '../../generated/graphql-server/src/modules/user-defined-license-entity/user-defined-license-entity.model'
  5. import { JoystreamMediaLocationEntity } from '../../generated/graphql-server/src/modules/joystream-media-location-entity/joystream-media-location-entity.model'
  6. import { HttpMediaLocationEntity } from '../../generated/graphql-server/src/modules/http-media-location-entity/http-media-location-entity.model'
  7. import { VideoMedia } from '../../generated/graphql-server/src/modules/video-media/video-media.model'
  8. import { Language } from '../../generated/graphql-server/src/modules/language/language.model'
  9. import { VideoMediaEncoding } from '../../generated/graphql-server/src/modules/video-media-encoding/video-media-encoding.model'
  10. import { LicenseEntity } from '../../generated/graphql-server/src/modules/license-entity/license-entity.model'
  11. import { MediaLocationEntity } from '../../generated/graphql-server/src/modules/media-location-entity/media-location-entity.model'
  12. import { Video } from '../../generated/graphql-server/src/modules/video/video.model'
  13. import { NextEntityId } from '../../generated/graphql-server/src/modules/next-entity-id/next-entity-id.model'
  14. import { ClassEntity } from '../../generated/graphql-server/src/modules/class-entity/class-entity.model'
  15. import { decode } from './decode'
  16. import {
  17. categoryPropertyNamesWithId,
  18. channelPropertyNamesWithId,
  19. contentDirectoryClassNamesWithId,
  20. httpMediaLocationPropertyNamesWithId,
  21. joystreamMediaLocationPropertyNamesWithId,
  22. knownLicensePropertyNamesWIthId,
  23. languagePropertyNamesWIthId,
  24. licensePropertyNamesWithId,
  25. mediaLocationPropertyNamesWithId,
  26. userDefinedLicensePropertyNamesWithId,
  27. videoMediaEncodingPropertyNamesWithId,
  28. videoPropertyNamesWithId,
  29. } from './content-dir-consts'
  30. import {
  31. ClassEntityMap,
  32. ICategory,
  33. IChannel,
  34. IDBBlockId,
  35. IEntity,
  36. IHttpMediaLocation,
  37. IJoystreamMediaLocation,
  38. IKnownClass,
  39. IKnownLicense,
  40. ILanguage,
  41. ILicense,
  42. IMediaLocation,
  43. IReference,
  44. IUserDefinedLicense,
  45. IVideo,
  46. IVideoMedia,
  47. IVideoMediaEncoding,
  48. IWhereCond,
  49. } from '../types'
  50. import {
  51. createCategory,
  52. createChannel,
  53. createVideoMedia,
  54. createUserDefinedLicense,
  55. createKnownLicense,
  56. createHttpMediaLocation,
  57. createJoystreamMediaLocation,
  58. createLanguage,
  59. createVideoMediaEncoding,
  60. createLicense,
  61. createMediaLocation,
  62. createVideo,
  63. } from './entity/create'
  64. import { DB } from '../../generated/indexer'
  65. // Keep track of the next entity id
  66. async function nextEntityId(db: DB): Promise<NextEntityId> {
  67. let e = await db.get(NextEntityId, { where: { id: '1' } })
  68. if (!e) {
  69. e = new NextEntityId({ id: '1', nextId: 1 })
  70. await db.save<NextEntityId>(e)
  71. }
  72. return e
  73. }
  74. function generateEntityIdFromIndex(index: number): string {
  75. return `${index}`
  76. }
  77. function findEntity(entityId: number, className: string, classEntityMap: ClassEntityMap): IEntity {
  78. const newlyCreatedEntities = classEntityMap.get(className)
  79. if (newlyCreatedEntities === undefined) throw Error(`Couldn't find '${className}' entities in the classEntityMap`)
  80. const entity = newlyCreatedEntities.find((e) => e.indexOf === entityId)
  81. if (!entity) throw Error(`Unknown ${className} entity id: ${entityId}`)
  82. // Remove the inserted entity from the list
  83. classEntityMap.set(
  84. className,
  85. newlyCreatedEntities.filter((e) => e.entityId !== entityId)
  86. )
  87. return entity
  88. }
  89. async function language(
  90. { db, block }: IDBBlockId,
  91. classEntityMap: ClassEntityMap,
  92. language: IReference,
  93. nextEntityIdBeforeTransaction: number
  94. ): Promise<Language> {
  95. let lang
  96. const { entityId, existing } = language
  97. if (existing) {
  98. lang = await db.get(Language, { where: { id: entityId.toString() } })
  99. if (!lang) throw Error(`Language entity not found`)
  100. return lang
  101. }
  102. const id = generateEntityIdFromIndex(nextEntityIdBeforeTransaction + entityId)
  103. // could be created in the transaction
  104. lang = await db.get(Language, { where: { id } })
  105. if (lang) return lang
  106. // get the entity from list of newly created entities and insert into db
  107. const { properties } = findEntity(entityId, 'Language', classEntityMap)
  108. return await createLanguage(
  109. { db, block, id },
  110. decode.setEntityPropertyValues<ILanguage>(properties, languagePropertyNamesWIthId)
  111. )
  112. }
  113. async function videoMediaEncoding(
  114. { db, block }: IDBBlockId,
  115. classEntityMap: ClassEntityMap,
  116. encoding: IReference,
  117. nextEntityIdBeforeTransaction: number
  118. ): Promise<VideoMediaEncoding> {
  119. let vmEncoding
  120. const { entityId, existing } = encoding
  121. if (existing) {
  122. vmEncoding = await db.get(VideoMediaEncoding, { where: { id: entityId.toString() } })
  123. if (!vmEncoding) throw Error(`VideoMediaEncoding entity not found`)
  124. return vmEncoding
  125. }
  126. const id = generateEntityIdFromIndex(nextEntityIdBeforeTransaction + entityId)
  127. // could be created in the transaction
  128. vmEncoding = await db.get(VideoMediaEncoding, { where: { id } })
  129. if (vmEncoding) return vmEncoding
  130. const { properties } = findEntity(entityId, 'VideoMediaEncoding', classEntityMap)
  131. return await createVideoMediaEncoding(
  132. { db, block, id },
  133. decode.setEntityPropertyValues<IVideoMediaEncoding>(properties, videoMediaEncodingPropertyNamesWithId)
  134. )
  135. }
  136. async function videoMedia(
  137. { db, block }: IDBBlockId,
  138. classEntityMap: ClassEntityMap,
  139. media: IReference,
  140. nextEntityIdBeforeTransaction: number
  141. ): Promise<VideoMedia> {
  142. let videoM: VideoMedia | undefined
  143. const { entityId, existing } = media
  144. if (existing) {
  145. videoM = await db.get(VideoMedia, { where: { id: entityId.toString() } })
  146. if (!videoM) throw Error(`VideoMedia entity not found`)
  147. return videoM
  148. }
  149. const id = generateEntityIdFromIndex(nextEntityIdBeforeTransaction + entityId)
  150. // could be created in the transaction
  151. videoM = await db.get(VideoMedia, { where: { id } })
  152. if (videoM) return videoM
  153. const { properties } = findEntity(entityId, 'VideoMedia', classEntityMap)
  154. return await createVideoMedia(
  155. { db, block, id },
  156. classEntityMap,
  157. decode.setEntityPropertyValues<IVideoMedia>(properties, videoPropertyNamesWithId),
  158. nextEntityIdBeforeTransaction
  159. )
  160. }
  161. async function knownLicense(
  162. { db, block }: IDBBlockId,
  163. classEntityMap: ClassEntityMap,
  164. knownLicense: IReference,
  165. nextEntityIdBeforeTransaction: number
  166. ): Promise<KnownLicenseEntity> {
  167. let kLicense: KnownLicenseEntity | undefined
  168. const { entityId, existing } = knownLicense
  169. if (existing) {
  170. kLicense = await db.get(KnownLicenseEntity, { where: { id: entityId.toString() } })
  171. if (!kLicense) throw Error(`KnownLicense entity not found`)
  172. return kLicense
  173. }
  174. const id = generateEntityIdFromIndex(nextEntityIdBeforeTransaction + entityId)
  175. // could be created in the transaction
  176. kLicense = await db.get(KnownLicenseEntity, { where: { id } })
  177. if (kLicense) return kLicense
  178. const { properties } = findEntity(entityId, 'KnownLicense', classEntityMap)
  179. return await createKnownLicense(
  180. { db, block, id },
  181. decode.setEntityPropertyValues<IKnownLicense>(properties, knownLicensePropertyNamesWIthId)
  182. )
  183. }
  184. async function userDefinedLicense(
  185. { db, block }: IDBBlockId,
  186. classEntityMap: ClassEntityMap,
  187. userDefinedLicense: IReference,
  188. nextEntityIdBeforeTransaction: number
  189. ): Promise<UserDefinedLicenseEntity> {
  190. let udLicense: UserDefinedLicenseEntity | undefined
  191. const { entityId, existing } = userDefinedLicense
  192. if (existing) {
  193. udLicense = await db.get(UserDefinedLicenseEntity, { where: { id: entityId.toString() } })
  194. if (!udLicense) throw Error(`UserDefinedLicense entity not found`)
  195. return udLicense
  196. }
  197. const id = generateEntityIdFromIndex(nextEntityIdBeforeTransaction + entityId)
  198. // could be created in the transaction
  199. udLicense = await db.get(UserDefinedLicenseEntity, {
  200. where: { id },
  201. })
  202. if (udLicense) return udLicense
  203. const { properties } = findEntity(entityId, 'UserDefinedLicense', classEntityMap)
  204. return await createUserDefinedLicense(
  205. { db, block, id },
  206. decode.setEntityPropertyValues<IUserDefinedLicense>(properties, userDefinedLicensePropertyNamesWithId)
  207. )
  208. }
  209. async function channel(
  210. { db, block }: IDBBlockId,
  211. classEntityMap: ClassEntityMap,
  212. channel: IReference,
  213. nextEntityIdBeforeTransaction: number
  214. ): Promise<Channel> {
  215. let chann: Channel | undefined
  216. const { entityId, existing } = channel
  217. if (existing) {
  218. chann = await db.get(Channel, { where: { id: entityId.toString() } })
  219. if (!chann) throw Error(`Channel entity not found`)
  220. return chann
  221. }
  222. const id = generateEntityIdFromIndex(nextEntityIdBeforeTransaction + entityId)
  223. // could be created in the transaction
  224. chann = await db.get(Channel, { where: { id } })
  225. if (chann) return chann
  226. const { properties } = findEntity(entityId, 'Channel', classEntityMap)
  227. return await createChannel(
  228. { db, block, id },
  229. classEntityMap,
  230. decode.setEntityPropertyValues<IChannel>(properties, channelPropertyNamesWithId),
  231. nextEntityIdBeforeTransaction
  232. )
  233. }
  234. async function category(
  235. { db, block }: IDBBlockId,
  236. classEntityMap: ClassEntityMap,
  237. category: IReference,
  238. nextEntityIdBeforeTransaction: number
  239. ): Promise<Category> {
  240. let cat: Category | undefined
  241. const { entityId, existing } = category
  242. if (existing) {
  243. cat = await db.get(Category, { where: { id: entityId.toString() } })
  244. if (!cat) throw Error(`Category entity not found`)
  245. return cat
  246. }
  247. const id = generateEntityIdFromIndex(nextEntityIdBeforeTransaction + entityId)
  248. // could be created in the transaction
  249. cat = await db.get(Category, { where: { id } })
  250. if (cat) return cat
  251. const { properties } = findEntity(entityId, 'Category', classEntityMap)
  252. return await createCategory(
  253. { db, block, id },
  254. decode.setEntityPropertyValues<ICategory>(properties, categoryPropertyNamesWithId)
  255. )
  256. }
  257. async function httpMediaLocation(
  258. { db, block }: IDBBlockId,
  259. classEntityMap: ClassEntityMap,
  260. httpMediaLoc: IReference,
  261. nextEntityIdBeforeTransaction: number
  262. ): Promise<HttpMediaLocationEntity | undefined> {
  263. let loc: HttpMediaLocationEntity | undefined
  264. const { entityId, existing } = httpMediaLoc
  265. if (existing) {
  266. loc = await db.get(HttpMediaLocationEntity, { where: { id: entityId.toString() } })
  267. if (!loc) throw Error(`HttpMediaLocation entity not found`)
  268. return loc
  269. }
  270. const id = generateEntityIdFromIndex(nextEntityIdBeforeTransaction + entityId)
  271. // could be created in the transaction
  272. loc = await db.get(HttpMediaLocationEntity, {
  273. where: { id },
  274. })
  275. if (loc) return loc
  276. const { properties } = findEntity(entityId, 'HttpMediaLocation', classEntityMap)
  277. return await createHttpMediaLocation(
  278. { db, block, id },
  279. decode.setEntityPropertyValues<IHttpMediaLocation>(properties, httpMediaLocationPropertyNamesWithId)
  280. )
  281. }
  282. async function joystreamMediaLocation(
  283. { db, block }: IDBBlockId,
  284. classEntityMap: ClassEntityMap,
  285. joyMediaLoc: IReference,
  286. nextEntityIdBeforeTransaction: number
  287. ): Promise<JoystreamMediaLocationEntity | undefined> {
  288. let loc: JoystreamMediaLocationEntity | undefined
  289. const { entityId, existing } = joyMediaLoc
  290. if (existing) {
  291. loc = await db.get(JoystreamMediaLocationEntity, { where: { id: entityId.toString() } })
  292. if (!loc) throw Error(`JoystreamMediaLocation entity not found`)
  293. return loc
  294. }
  295. const id = generateEntityIdFromIndex(nextEntityIdBeforeTransaction + entityId)
  296. // could be created in the transaction
  297. loc = await db.get(JoystreamMediaLocationEntity, {
  298. where: { id },
  299. })
  300. if (loc) return loc
  301. const { properties } = findEntity(entityId, 'JoystreamMediaLocation', classEntityMap)
  302. return await createJoystreamMediaLocation(
  303. { db, block, id },
  304. decode.setEntityPropertyValues<IJoystreamMediaLocation>(properties, joystreamMediaLocationPropertyNamesWithId)
  305. )
  306. }
  307. async function license(
  308. { db, block }: IDBBlockId,
  309. classEntityMap: ClassEntityMap,
  310. license: IReference,
  311. nextEntityIdBeforeTransaction: number
  312. ): Promise<LicenseEntity> {
  313. let lic: LicenseEntity | undefined
  314. const { entityId, existing } = license
  315. if (existing) {
  316. lic = await db.get(LicenseEntity, { where: { id: entityId.toString() } })
  317. if (!lic) throw Error(`License entity not found`)
  318. return lic
  319. }
  320. const id = generateEntityIdFromIndex(nextEntityIdBeforeTransaction + entityId)
  321. // could be created in the transaction
  322. lic = await db.get(LicenseEntity, { where: { id } })
  323. if (lic) return lic
  324. const { properties } = findEntity(entityId, 'License', classEntityMap)
  325. return await createLicense(
  326. { db, block, id },
  327. classEntityMap,
  328. decode.setEntityPropertyValues<ILicense>(properties, licensePropertyNamesWithId),
  329. nextEntityIdBeforeTransaction
  330. )
  331. }
  332. async function mediaLocation(
  333. { db, block }: IDBBlockId,
  334. classEntityMap: ClassEntityMap,
  335. location: IReference,
  336. nextEntityIdBeforeTransaction: number
  337. ): Promise<MediaLocationEntity> {
  338. const { entityId, existing } = location
  339. // Relationships to be loaded
  340. const relations = ['httpMediaLocation', 'joystreamMediaLocation']
  341. if (existing) {
  342. const loc = await db.get(MediaLocationEntity, { where: { id: entityId.toString() }, relations })
  343. if (!loc) throw Error(`MediaLocation entity not found`)
  344. return loc
  345. }
  346. // Could be created in the same transaction so try to query
  347. const id = generateEntityIdFromIndex(nextEntityIdBeforeTransaction + entityId)
  348. const loc = await db.get(MediaLocationEntity, { where: { id }, relations })
  349. if (loc) return loc
  350. // Create entity
  351. const { properties } = findEntity(entityId, 'MediaLocation', classEntityMap)
  352. return await createMediaLocation(
  353. { db, block, id },
  354. classEntityMap,
  355. decode.setEntityPropertyValues<IMediaLocation>(properties, mediaLocationPropertyNamesWithId),
  356. nextEntityIdBeforeTransaction
  357. )
  358. }
  359. async function video(
  360. { db, block }: IDBBlockId,
  361. classEntityMap: ClassEntityMap,
  362. video: IReference,
  363. nextEntityIdBeforeTransaction: number
  364. ): Promise<Video> {
  365. const { existing, entityId } = video
  366. if (existing) {
  367. const v = await db.get(Video, { where: { id: entityId.toString() } })
  368. if (!v) throw Error(`Video not found. id ${entityId}`)
  369. return v
  370. }
  371. const id = generateEntityIdFromIndex(nextEntityIdBeforeTransaction + entityId)
  372. const v = await db.get(Video, { where: { id } })
  373. if (v) return v
  374. const { properties } = findEntity(entityId, 'MediaVideo', classEntityMap)
  375. return await createVideo(
  376. { db, block, id },
  377. classEntityMap,
  378. decode.setEntityPropertyValues<IVideo>(properties, videoPropertyNamesWithId),
  379. nextEntityIdBeforeTransaction
  380. )
  381. }
  382. export async function getKnownClass(db: DB, where: IWhereCond): Promise<[IKnownClass | undefined, ClassEntity]> {
  383. const ce = await db.get(ClassEntity, where)
  384. if (!ce) {
  385. throw Error(`Class not found for the EntityId: ${where.where.id} or the entity has not been created.`)
  386. }
  387. const knownClass = contentDirectoryClassNamesWithId.find((c) => c.classId === ce.classId)
  388. if (!knownClass) console.log('Unknown class')
  389. return [knownClass, ce]
  390. }
  391. export const getOrCreate = {
  392. language,
  393. videoMediaEncoding,
  394. videoMedia,
  395. knownLicense,
  396. userDefinedLicense,
  397. channel,
  398. category,
  399. joystreamMediaLocation,
  400. httpMediaLocation,
  401. license,
  402. mediaLocation,
  403. nextEntityId,
  404. video,
  405. }