decode.ts 6.1 KB

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