123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- import Debug from 'debug'
- import { DB, SubstrateEvent } from '../../generated/indexer'
- import { NextEntityId } from '../../generated/graphql-server/src/modules/next-entity-id/next-entity-id.model'
- import { ClassEntity } from '../../generated/graphql-server/src/modules/class-entity/class-entity.model'
- import { decode } from './decode'
- import {
- ClassEntityMap,
- IBatchOperation,
- ICategory,
- IChannel,
- ICreateEntityOperation,
- IDBBlockId,
- IEntity,
- IFeaturedVideo,
- IHttpMediaLocation,
- IJoystreamMediaLocation,
- IKnownLicense,
- ILanguage,
- ILicense,
- IMediaLocation,
- IProperty,
- IUserDefinedLicense,
- IVideo,
- IVideoMedia,
- IVideoMediaEncoding,
- IWhereCond,
- } from '../types'
- import {
- categoryPropertyNamesWithId,
- channelPropertyNamesWithId,
- knownLicensePropertyNamesWIthId,
- userDefinedLicensePropertyNamesWithId,
- joystreamMediaLocationPropertyNamesWithId,
- httpMediaLocationPropertyNamesWithId,
- videoMediaPropertyNamesWithId,
- videoMediaEncodingPropertyNamesWithId,
- videoPropertyNamesWithId,
- languagePropertyNamesWIthId,
- ContentDirectoryKnownClasses,
- licensePropertyNamesWithId,
- mediaLocationPropertyNamesWithId,
- featuredVideoPropertyNamesWithId,
- } from './content-dir-consts'
- import { getClassName, createBlockOrGetFromDatabase } from './entity/create'
- import { getOrCreate } from './get-or-create'
- import {
- addSchemaToCategory,
- addSchemaToChannel,
- addSchemaToFeaturedVideo,
- addSchemaToHttpMediaLocation,
- addSchemaToJoystreamMediaLocation,
- addSchemaToKnownLicense,
- addSchemaToLanguage,
- addSchemaToLicense,
- addSchemaToMediaLocation,
- addSchemaToUserDefinedLicense,
- addSchemaToVideo,
- addSchemaToVideoMedia,
- addSchemaToVideoMediaEncoding,
- } from './entity/addSchema'
- import { createDefaultSchema } from './default-schemas'
- const debug = Debug('mappings:cd:transaction')
- async function getNextEntityId(db: DB): Promise<number> {
- const e = await db.get(NextEntityId, { where: { id: '1' } })
- // Entity creation happens before addSchemaSupport so this should never happen
- if (!e) throw Error(`NextEntityId table doesn't have any record`)
- return e.nextId
- }
- // eslint-disable-next-line @typescript-eslint/naming-convention
- export async function contentDirectory_TransactionFailed(db: DB, event: SubstrateEvent): Promise<void> {
- debug(`TransactionFailed event: ${JSON.stringify(event)}`)
- const failedOperationIndex = event.params[1].value as number
- const operations = decode.getOperations(event)
- const successfulOperations = operations.toArray().slice(0, failedOperationIndex)
- if (!successfulOperations.length) return // No succesfull operations
- await applyOperations(decode.getOperationsByTypes(successfulOperations), db, event)
- }
- // eslint-disable-next-line @typescript-eslint/naming-convention
- export async function contentDirectory_TransactionCompleted(db: DB, event: SubstrateEvent): Promise<void> {
- debug(`TransactionCompleted event: ${JSON.stringify(event)}`)
- const operations = decode.getOperations(event)
- await applyOperations(decode.getOperationsByTypes(operations), db, event)
- }
- async function applyOperations(operations: IBatchOperation, db: DB, event: SubstrateEvent) {
- const { addSchemaSupportToEntityOperations, createEntityOperations, updatePropertyValuesOperations } = operations
- // Create entities before adding schema support
- // We need this to know which entity belongs to which class(we will need to know to update/create
- // Channel, Video etc.). For example if there is a property update operation there is no class id
- await batchCreateClassEntities(db, event.blockNumber, createEntityOperations)
- await batchAddSchemaSupportToEntity(db, createEntityOperations, addSchemaSupportToEntityOperations, event.blockNumber)
- await batchUpdatePropertyValue(db, createEntityOperations, updatePropertyValuesOperations)
- }
- async function batchCreateClassEntities(db: DB, block: number, operations: ICreateEntityOperation[]): Promise<void> {
- const nextEntityIdFromDb = await getOrCreate.nextEntityId(db)
- let entityId = nextEntityIdFromDb.nextId
- for (const { classId } of operations) {
- const c = new ClassEntity({
- id: entityId.toString(),
- classId: classId,
- version: block,
- happenedIn: await createBlockOrGetFromDatabase(db, block),
- })
- await db.save<ClassEntity>(c)
- // Create default schema for the entity
- await createDefaultSchema(db, c)
- entityId++
- }
- // Update database for next entity id
- nextEntityIdFromDb.nextId = entityId
- await db.save<NextEntityId>(nextEntityIdFromDb)
- }
- /**
- *
- * @param db database connection
- * @param createEntityOperations: Entity creations with in the same transaction
- * @param entities List of entities that schema support is added for
- * @param block block number
- */
- async function batchAddSchemaSupportToEntity(
- db: DB,
- createEntityOperations: ICreateEntityOperation[],
- entities: IEntity[],
- block: number
- ) {
- const classEntityMap: ClassEntityMap = new Map<string, IEntity[]>()
- for (const entity of entities) {
- const className = await getClassName(db, entity, createEntityOperations)
- if (className !== undefined) {
- const es = classEntityMap.get(className)
- classEntityMap.set(className, es ? [...es, entity] : [entity])
- }
- }
- // This is a copy of classEntityMap, we will use it to keep track of items.
- // We will remove items from this list whenever we insert them into db
- // const doneList: ClassEntityMap = new Map(classEntityMap.entries())
- const nextEntityIdBeforeTransaction = (await getNextEntityId(db)) - createEntityOperations.length
- for (const [className, entities] of classEntityMap) {
- for (const entity of entities) {
- const { entityId, indexOf, properties } = entity
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const id = entityId !== undefined ? entityId : indexOf! + nextEntityIdBeforeTransaction
- // const arg: IDBBlockId = { db, block, id: id.toString() }
- await addSchemaSupportToEntity(db, className, id, nextEntityIdBeforeTransaction, properties)
- }
- }
- }
- /**
- * Batch update operations for entity properties values update
- * @param db database connection
- * @param createEntityOperations Entity creations with in the same transaction
- * @param entities list of entities those properties values updated
- */
- async function batchUpdatePropertyValue(db: DB, createEntityOperations: ICreateEntityOperation[], entities: IEntity[]) {
- const entityIdBeforeTransaction = (await getNextEntityId(db)) - createEntityOperations.length
- for (const entity of entities) {
- const { entityId, indexOf, properties } = entity
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- const id = entityId !== undefined ? entityId : entityIdBeforeTransaction - indexOf!
- // const where: IWhereCond = { where: { id: id.toString() } }
- const className = await getClassName(db, entity, createEntityOperations)
- if (!className) {
- debug(`Can not update entity properties values. Unknown class name`)
- return
- }
- await addSchemaSupportToEntity(db, className, id, entityIdBeforeTransaction, properties)
- }
- }
- async function addSchemaSupportToEntity(
- db: DB,
- className: string,
- entityId: number,
- nextEntityId: number,
- properties: IProperty[]
- ) {
- switch (className) {
- case ContentDirectoryKnownClasses.CATEGORY:
- await addSchemaToCategory({
- db,
- entityId,
- nextEntityId,
- props: decode.setEntityPropertyValues<ICategory>(properties, categoryPropertyNamesWithId),
- })
- break
- case ContentDirectoryKnownClasses.CHANNEL:
- await addSchemaToChannel({
- db,
- entityId,
- nextEntityId,
- props: decode.setEntityPropertyValues<IChannel>(properties, channelPropertyNamesWithId),
- })
- break
- case ContentDirectoryKnownClasses.KNOWNLICENSE:
- await addSchemaToKnownLicense({
- db,
- entityId,
- nextEntityId,
- props: decode.setEntityPropertyValues<IKnownLicense>(properties, knownLicensePropertyNamesWIthId),
- })
- break
- case ContentDirectoryKnownClasses.USERDEFINEDLICENSE:
- await addSchemaToUserDefinedLicense({
- db,
- entityId,
- nextEntityId,
- props: decode.setEntityPropertyValues<IUserDefinedLicense>(properties, userDefinedLicensePropertyNamesWithId),
- })
- break
- case ContentDirectoryKnownClasses.JOYSTREAMMEDIALOCATION:
- await addSchemaToJoystreamMediaLocation({
- db,
- entityId,
- nextEntityId,
- props: decode.setEntityPropertyValues<IJoystreamMediaLocation>(
- properties,
- joystreamMediaLocationPropertyNamesWithId
- ),
- })
- break
- case ContentDirectoryKnownClasses.HTTPMEDIALOCATION:
- await addSchemaToHttpMediaLocation({
- db,
- entityId,
- nextEntityId,
- props: decode.setEntityPropertyValues<IHttpMediaLocation>(properties, httpMediaLocationPropertyNamesWithId),
- })
- break
- case ContentDirectoryKnownClasses.VIDEOMEDIA:
- await addSchemaToVideoMedia({
- db,
- entityId,
- nextEntityId,
- props: decode.setEntityPropertyValues<IVideoMedia>(properties, videoMediaPropertyNamesWithId),
- })
- break
- case ContentDirectoryKnownClasses.VIDEO:
- await addSchemaToVideo({
- db,
- entityId,
- nextEntityId,
- props: decode.setEntityPropertyValues<IVideo>(properties, videoPropertyNamesWithId),
- })
- break
- case ContentDirectoryKnownClasses.LANGUAGE:
- await addSchemaToLanguage({
- db,
- entityId,
- nextEntityId,
- props: decode.setEntityPropertyValues<ILanguage>(properties, languagePropertyNamesWIthId),
- })
- break
- case ContentDirectoryKnownClasses.VIDEOMEDIAENCODING:
- await addSchemaToVideoMediaEncoding({
- db,
- entityId,
- nextEntityId,
- props: decode.setEntityPropertyValues<IVideoMediaEncoding>(properties, videoMediaEncodingPropertyNamesWithId),
- })
- break
- case ContentDirectoryKnownClasses.LICENSE:
- await addSchemaToLicense({
- db,
- entityId,
- nextEntityId,
- props: decode.setEntityPropertyValues<ILicense>(properties, licensePropertyNamesWithId),
- })
- break
- case ContentDirectoryKnownClasses.MEDIALOCATION:
- await addSchemaToMediaLocation({
- db,
- entityId,
- nextEntityId,
- props: decode.setEntityPropertyValues<IMediaLocation>(properties, mediaLocationPropertyNamesWithId),
- })
- break
- case ContentDirectoryKnownClasses.FEATUREDVIDEOS:
- await addSchemaToFeaturedVideo({
- db,
- entityId,
- nextEntityId,
- props: decode.setEntityPropertyValues<IFeaturedVideo>(properties, featuredVideoPropertyNamesWithId),
- })
- break
- default:
- debug(`Unknown class name: ${className}`)
- break
- }
- }
|