get-or-create.ts 15 KB

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