inputSchemasToEntitySchemas.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import fs from 'fs'
  2. import path from 'path'
  3. import {
  4. AddClassSchema,
  5. HashProperty,
  6. Property,
  7. ReferenceProperty,
  8. SinglePropertyVariant,
  9. TextProperty,
  10. VecPropertyVariant,
  11. } from '../types/extrinsics/AddClassSchema'
  12. import _ from 'lodash'
  13. import { schemaFilenameToEntitySchemaName, classIdToEntitySchemaName } from './helpers/entitySchemas'
  14. import PRIMITIVE_PROPERTY_DEFS from '../schemas/propertyValidationDefs.schema.json'
  15. import { getInputsLocation } from './helpers/inputs'
  16. const INPUTS_LOCATION = getInputsLocation('schemas')
  17. const SINGLE_ENTITY_SCHEMAS_LOCATION = path.join(__dirname, '../schemas/entities')
  18. const BATCH_OF_ENITIES_SCHEMAS_LOCATION = path.join(__dirname, '../schemas/entityBatches')
  19. const ENTITY_REFERENCE_SCHEMAS_LOCATION = path.join(__dirname, '../schemas/entityReferences')
  20. const inputFilenames = fs.readdirSync(INPUTS_LOCATION)
  21. const strictObjectDef = (def: Record<string, any>) => ({
  22. type: 'object',
  23. additionalProperties: false,
  24. ...def,
  25. })
  26. const onePropertyObjectDef = (propertyName: string, propertyDef: Record<string, any>) =>
  27. strictObjectDef({
  28. required: [propertyName],
  29. properties: {
  30. [propertyName]: propertyDef,
  31. },
  32. })
  33. const TextPropertyDef = ({ Text: maxLength }: TextProperty) => ({
  34. type: 'string',
  35. maxLength,
  36. })
  37. const HashPropertyDef = ({ Hash: maxLength }: HashProperty) => ({
  38. type: 'string',
  39. maxLength,
  40. })
  41. const ReferencePropertyDef = ({ Reference: ref }: ReferenceProperty) => ({
  42. 'oneOf': [
  43. onePropertyObjectDef('new', { '$ref': `./${classIdToEntitySchemaName(ref[0], 'Entity')}.schema.json` }),
  44. onePropertyObjectDef('existing', {
  45. '$ref': `../entityReferences/${classIdToEntitySchemaName(ref[0], 'Ref')}.schema.json`,
  46. }),
  47. ],
  48. })
  49. const SinglePropertyDef = ({ Single: singlePropType }: SinglePropertyVariant) => {
  50. if (typeof singlePropType === 'string') {
  51. return PRIMITIVE_PROPERTY_DEFS.definitions[singlePropType]
  52. } else if ((singlePropType as TextProperty).Text) {
  53. return TextPropertyDef(singlePropType as TextProperty)
  54. } else if ((singlePropType as HashProperty).Hash) {
  55. return HashPropertyDef(singlePropType as HashProperty)
  56. } else if ((singlePropType as ReferenceProperty).Reference) {
  57. return ReferencePropertyDef(singlePropType as ReferenceProperty)
  58. }
  59. throw new Error(`Unknown single proprty type: ${JSON.stringify(singlePropType)}`)
  60. }
  61. const VecPropertyDef = ({ Vector: vec }: VecPropertyVariant) => ({
  62. type: 'array',
  63. maxItems: vec.max_length,
  64. 'items': SinglePropertyDef({ Single: vec.vec_type }),
  65. })
  66. const PropertyDef = ({ property_type: propertyType, description }: Property) => ({
  67. ...((propertyType as SinglePropertyVariant).Single
  68. ? SinglePropertyDef(propertyType as SinglePropertyVariant)
  69. : VecPropertyDef(propertyType as VecPropertyVariant)),
  70. description,
  71. })
  72. // Mkdir entity schemas directories if they do not exist
  73. const entitySchemasDirs = [
  74. SINGLE_ENTITY_SCHEMAS_LOCATION,
  75. BATCH_OF_ENITIES_SCHEMAS_LOCATION,
  76. ENTITY_REFERENCE_SCHEMAS_LOCATION,
  77. ]
  78. entitySchemasDirs.forEach((dir) => {
  79. if (!fs.existsSync(dir)) {
  80. fs.mkdirSync(dir)
  81. }
  82. })
  83. // Run schema conversion:
  84. inputFilenames.forEach((fileName) => {
  85. const inputFilePath = path.join(INPUTS_LOCATION, fileName)
  86. const inputJson = fs.readFileSync(inputFilePath).toString()
  87. const inputData = JSON.parse(inputJson) as AddClassSchema
  88. const schemaName = schemaFilenameToEntitySchemaName(fileName)
  89. if (inputData.newProperties && !inputData.existingProperties) {
  90. const properites = inputData.newProperties
  91. const propertiesObj = properites.reduce((pObj, p) => {
  92. pObj[p.name] = PropertyDef(p)
  93. return pObj
  94. }, {} as Record<string, ReturnType<typeof PropertyDef>>)
  95. const EntitySchema = {
  96. '$schema': 'http://json-schema.org/draft-07/schema',
  97. '$id': `https://joystream.org/${schemaName}Entity.schema.json`,
  98. 'title': `${schemaName}Entity`,
  99. 'description': `JSON schema for entities based on ${schemaName} runtime schema`,
  100. ...strictObjectDef({
  101. required: properites.filter((p) => p.required).map((p) => p.name),
  102. properties: propertiesObj,
  103. }),
  104. }
  105. const ReferenceSchema = {
  106. '$schema': 'http://json-schema.org/draft-07/schema',
  107. '$id': `https://joystream.org/${schemaName}Reference.schema.json`,
  108. 'title': `${schemaName}Reference`,
  109. 'description': `JSON schema for reference to ${schemaName} entity based on runtime schema`,
  110. 'anyOf': [
  111. ...properites.filter((p) => p.required && p.unique).map((p) => onePropertyObjectDef(p.name, PropertyDef(p))),
  112. PRIMITIVE_PROPERTY_DEFS.definitions.Uint64,
  113. ],
  114. }
  115. const BatchSchema = {
  116. '$schema': 'http://json-schema.org/draft-07/schema',
  117. '$id': `https://joystream.org/${schemaName}Batch.schema.json`,
  118. 'title': `${schemaName}Batch`,
  119. 'description': `JSON schema for batch of entities based on ${schemaName} runtime schema`,
  120. 'type': 'array',
  121. 'items': { '$ref': `../entities/${schemaName}Entity.schema.json` },
  122. }
  123. const entitySchemaPath = path.join(SINGLE_ENTITY_SCHEMAS_LOCATION, `${schemaName}Entity.schema.json`)
  124. fs.writeFileSync(entitySchemaPath, JSON.stringify(EntitySchema, undefined, 4))
  125. console.log(`${entitySchemaPath} succesfully generated!`)
  126. const entityReferenceSchemaPath = path.join(ENTITY_REFERENCE_SCHEMAS_LOCATION, `${schemaName}Ref.schema.json`)
  127. fs.writeFileSync(entityReferenceSchemaPath, JSON.stringify(ReferenceSchema, undefined, 4))
  128. console.log(`${entityReferenceSchemaPath} succesfully generated!`)
  129. const batchOfEntitiesSchemaPath = path.join(BATCH_OF_ENITIES_SCHEMAS_LOCATION, `${schemaName}Batch.schema.json`)
  130. fs.writeFileSync(batchOfEntitiesSchemaPath, JSON.stringify(BatchSchema, undefined, 4))
  131. console.log(`${batchOfEntitiesSchemaPath} succesfully generated!`)
  132. } else {
  133. console.log('WARNING: Schemas with "existingProperties" not supported yet!')
  134. console.log('Skipping...')
  135. }
  136. })