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