decode.ts 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import { SubstrateEvent } from '../../generated/indexer'
  2. import {
  3. IPropertyIdWithName,
  4. IClassEntity,
  5. IProperty,
  6. IBatchOperation,
  7. ICreateEntityOperation,
  8. IEntity,
  9. IReference,
  10. } from '../types'
  11. import Debug from 'debug'
  12. import { ParametrizedClassPropertyValue, UpdatePropertyValuesOperation } from '@joystream/types/content-directory'
  13. import { createType } from '@joystream/types'
  14. const debug = Debug('mappings:cd:decode')
  15. function stringIfyEntityId(event: SubstrateEvent): string {
  16. const { 1: entityId } = event.params
  17. return entityId.value as string
  18. }
  19. function setProperties<T>({ extrinsic, blockNumber }: SubstrateEvent, propNamesWithId: IPropertyIdWithName): T {
  20. if (extrinsic === undefined) throw Error('Undefined extrinsic')
  21. const { 3: newPropertyValues } = extrinsic!.args
  22. const properties: { [key: string]: any; reference?: IReference } = {}
  23. for (const [k, v] of Object.entries(newPropertyValues.value)) {
  24. const propertyName = propNamesWithId[k]
  25. const singlePropVal = createType('InputPropertyValue', v as any).asType('Single')
  26. properties[propertyName] = singlePropVal.isOfType('Reference')
  27. ? { entityId: singlePropVal.asType('Reference').toJSON(), existing: true }
  28. : singlePropVal.value.toJSON()
  29. }
  30. properties.version = blockNumber
  31. debug(`Entity properties: ${JSON.stringify(properties)}`)
  32. return properties as T
  33. }
  34. function getClassEntity(event: SubstrateEvent): IClassEntity {
  35. const { 0: classId } = event.extrinsic!.args
  36. const { 1: entityId } = event.params
  37. return {
  38. entityId: (entityId.value as unknown) as number,
  39. classId: (classId.value as unknown) as number,
  40. }
  41. }
  42. /**
  43. * When entity is creation through `transaction` extrinsic we use this function to parse
  44. * entity properties it looks quite similar to `setProperties` function
  45. * @param properties
  46. * @param propertyNamesWithId
  47. */
  48. function setEntityPropertyValues<T>(properties: IProperty[], propertyNamesWithId: IPropertyIdWithName): T {
  49. const entityProperties: { [key: string]: any; reference?: IReference } = {}
  50. for (const [propId, propName] of Object.entries(propertyNamesWithId)) {
  51. // get the property value by id
  52. const p = properties.find((p) => p.id === propId)
  53. if (!p) continue
  54. entityProperties[propName] = p.reference ? p.reference : p.value
  55. }
  56. // debug(`Entity properties ${JSON.stringify(entityProperties)}`)
  57. return entityProperties as T
  58. }
  59. // Decode entity property values
  60. function getEntityProperties(propertyValues: ParametrizedClassPropertyValue[]): IProperty[] {
  61. const properties: IProperty[] = []
  62. const entityPropertyValues = createType('Vec<ParametrizedClassPropertyValue>', propertyValues)
  63. entityPropertyValues.map((pv: ParametrizedClassPropertyValue) => {
  64. const v = createType('ParametrizedPropertyValue', pv.value)
  65. const propertyId = pv.in_class_index.toJSON()
  66. let reference
  67. let value
  68. if (v.isOfType('InputPropertyValue')) {
  69. const inputPropVal = v.asType('InputPropertyValue')
  70. value = inputPropVal.isOfType('Single')
  71. ? inputPropVal.asType('Single').value.toJSON()
  72. : inputPropVal.asType('Vector').value.toJSON()
  73. if (inputPropVal.isOfType('Single')) {
  74. if (inputPropVal.asType('Single').isOfType('Reference')) {
  75. reference = { entityId: value as number, existing: true }
  76. }
  77. }
  78. } else if (v.isOfType('InternalEntityJustAdded')) {
  79. value = v.asType('InternalEntityJustAdded').toJSON()
  80. reference = { entityId: value as number, existing: false }
  81. } else {
  82. // TODO: Add support for v.asType('InternalEntityVec')
  83. throw Error('InternalEntityVec property type is not supported yet!')
  84. }
  85. properties.push({ id: `${propertyId}`, value, reference })
  86. })
  87. return properties
  88. }
  89. function getOperations({ extrinsic }: SubstrateEvent): IBatchOperation {
  90. const operations = createType('Vec<OperationType>', extrinsic!.args[1].value as any)
  91. const updatePropertyValuesOperations: IEntity[] = []
  92. const addSchemaSupportToEntityOperations: IEntity[] = []
  93. const createEntityOperations: ICreateEntityOperation[] = []
  94. for (const operation of operations) {
  95. if (operation.isOfType('CreateEntity')) {
  96. const cep = operation.asType('CreateEntity')
  97. createEntityOperations.push({ classId: cep.class_id.toJSON() })
  98. } else if (operation.isOfType('AddSchemaSupportToEntity')) {
  99. const op = operation.asType('AddSchemaSupportToEntity')
  100. const pe = createType('ParameterizedEntity', op.entity_id)
  101. const entity: IEntity = {
  102. properties: getEntityProperties(op.parametrized_property_values),
  103. }
  104. if (pe.isOfType('InternalEntityJustAdded')) {
  105. entity.indexOf = pe.asType('InternalEntityJustAdded').toJSON()
  106. } else {
  107. entity.entityId = pe.asType('ExistingEntity').toJSON()
  108. }
  109. addSchemaSupportToEntityOperations.push(entity)
  110. } else {
  111. updatePropertyValuesOperations.push(makeEntity(operation.asType('UpdatePropertyValues')))
  112. }
  113. }
  114. return {
  115. updatePropertyValuesOperations,
  116. addSchemaSupportToEntityOperations,
  117. createEntityOperations,
  118. }
  119. }
  120. function makeEntity(upv: UpdatePropertyValuesOperation): IEntity {
  121. const entity: IEntity = {
  122. properties: getEntityProperties(upv.new_parametrized_property_values),
  123. }
  124. const pe = createType('ParameterizedEntity', upv.entity_id)
  125. if (pe.isOfType('InternalEntityJustAdded')) {
  126. entity.indexOf = pe.asType('InternalEntityJustAdded').toJSON()
  127. } else {
  128. entity.entityId = pe.asType('ExistingEntity').toJSON()
  129. }
  130. return entity
  131. }
  132. export const decode = {
  133. stringIfyEntityId,
  134. getClassEntity,
  135. setEntityPropertyValues,
  136. getEntityProperties,
  137. getOperations,
  138. setProperties,
  139. }