decode.ts 5.8 KB

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