get-or-create.ts 15 KB

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