AvroSchemaValidator.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.AvroSchemaValidator = void 0;
  4. class AvroSchemaValidator {
  5. constructor() {
  6. this.namedSchemas = new Map();
  7. }
  8. validateSchema(schema) {
  9. this.namedSchemas.clear();
  10. return this.validateSchemaInternal(schema);
  11. }
  12. validateValue(value, schema) {
  13. this.namedSchemas.clear();
  14. this.validateSchemaInternal(schema);
  15. return this.validateValueAgainstSchema(value, schema);
  16. }
  17. validateSchemaInternal(schema) {
  18. if (typeof schema === 'string') {
  19. return this.validateStringSchema(schema);
  20. }
  21. if (Array.isArray(schema)) {
  22. return this.validateUnionSchema(schema);
  23. }
  24. if (typeof schema === 'object' && schema !== null) {
  25. switch (schema.type) {
  26. case 'null':
  27. return this.validateNullSchema(schema);
  28. case 'boolean':
  29. return this.validateBooleanSchema(schema);
  30. case 'int':
  31. return this.validateIntSchema(schema);
  32. case 'long':
  33. return this.validateLongSchema(schema);
  34. case 'float':
  35. return this.validateFloatSchema(schema);
  36. case 'double':
  37. return this.validateDoubleSchema(schema);
  38. case 'bytes':
  39. return this.validateBytesSchema(schema);
  40. case 'string':
  41. return this.validateStringTypeSchema(schema);
  42. case 'record':
  43. return this.validateRecordSchema(schema);
  44. case 'enum':
  45. return this.validateEnumSchema(schema);
  46. case 'array':
  47. return this.validateArraySchema(schema);
  48. case 'map':
  49. return this.validateMapSchema(schema);
  50. case 'fixed':
  51. return this.validateFixedSchema(schema);
  52. default:
  53. return false;
  54. }
  55. }
  56. return false;
  57. }
  58. validateStringSchema(schema) {
  59. const primitiveTypes = ['null', 'boolean', 'int', 'long', 'float', 'double', 'bytes', 'string'];
  60. return primitiveTypes.includes(schema) || this.namedSchemas.has(schema);
  61. }
  62. validateUnionSchema(schema) {
  63. if (schema.length === 0)
  64. return false;
  65. const typeSet = new Set();
  66. for (const subSchema of schema) {
  67. if (!this.validateSchemaInternal(subSchema))
  68. return false;
  69. const typeName = this.getSchemaTypeName(subSchema);
  70. if (typeSet.has(typeName))
  71. return false;
  72. typeSet.add(typeName);
  73. }
  74. return true;
  75. }
  76. validateNullSchema(schema) {
  77. return schema.type === 'null';
  78. }
  79. validateBooleanSchema(schema) {
  80. return schema.type === 'boolean';
  81. }
  82. validateIntSchema(schema) {
  83. return schema.type === 'int';
  84. }
  85. validateLongSchema(schema) {
  86. return schema.type === 'long';
  87. }
  88. validateFloatSchema(schema) {
  89. return schema.type === 'float';
  90. }
  91. validateDoubleSchema(schema) {
  92. return schema.type === 'double';
  93. }
  94. validateBytesSchema(schema) {
  95. return schema.type === 'bytes';
  96. }
  97. validateStringTypeSchema(schema) {
  98. return schema.type === 'string';
  99. }
  100. validateRecordSchema(schema) {
  101. if (schema.type !== 'record' || !schema.name || !Array.isArray(schema.fields))
  102. return false;
  103. const fullName = this.getFullName(schema.name, schema.namespace);
  104. if (this.namedSchemas.has(fullName))
  105. return false;
  106. this.namedSchemas.set(fullName, schema);
  107. const fieldNames = new Set();
  108. for (const field of schema.fields) {
  109. if (!this.validateRecordField(field))
  110. return false;
  111. if (fieldNames.has(field.name))
  112. return false;
  113. fieldNames.add(field.name);
  114. }
  115. return true;
  116. }
  117. validateRecordField(field) {
  118. return typeof field.name === 'string' && field.name.length > 0 && this.validateSchemaInternal(field.type);
  119. }
  120. validateEnumSchema(schema) {
  121. if (schema.type !== 'enum' || !schema.name || !Array.isArray(schema.symbols))
  122. return false;
  123. const fullName = this.getFullName(schema.name, schema.namespace);
  124. if (this.namedSchemas.has(fullName))
  125. return false;
  126. this.namedSchemas.set(fullName, schema);
  127. if (schema.symbols.length === 0)
  128. return false;
  129. const symbolSet = new Set();
  130. for (const symbol of schema.symbols) {
  131. if (typeof symbol !== 'string' || symbolSet.has(symbol))
  132. return false;
  133. symbolSet.add(symbol);
  134. }
  135. if (schema.default !== undefined && !schema.symbols.includes(schema.default))
  136. return false;
  137. return true;
  138. }
  139. validateArraySchema(schema) {
  140. return schema.type === 'array' && this.validateSchemaInternal(schema.items);
  141. }
  142. validateMapSchema(schema) {
  143. return schema.type === 'map' && this.validateSchemaInternal(schema.values);
  144. }
  145. validateFixedSchema(schema) {
  146. if (schema.type !== 'fixed' || !schema.name || typeof schema.size !== 'number')
  147. return false;
  148. if (schema.size < 0)
  149. return false;
  150. const fullName = this.getFullName(schema.name, schema.namespace);
  151. if (this.namedSchemas.has(fullName))
  152. return false;
  153. this.namedSchemas.set(fullName, schema);
  154. return true;
  155. }
  156. validateValueAgainstSchema(value, schema) {
  157. if (typeof schema === 'string') {
  158. return this.validateValueAgainstStringSchema(value, schema);
  159. }
  160. if (Array.isArray(schema)) {
  161. return schema.some((subSchema) => this.validateValueAgainstSchema(value, subSchema));
  162. }
  163. if (typeof schema === 'object' && schema !== null) {
  164. switch (schema.type) {
  165. case 'null':
  166. return value === null;
  167. case 'boolean':
  168. return typeof value === 'boolean';
  169. case 'int':
  170. return typeof value === 'number' && Number.isInteger(value) && value >= -2147483648 && value <= 2147483647;
  171. case 'long':
  172. return (typeof value === 'number' && Number.isInteger(value)) || typeof value === 'bigint';
  173. case 'float':
  174. case 'double':
  175. return typeof value === 'number';
  176. case 'bytes':
  177. return value instanceof Uint8Array;
  178. case 'string':
  179. return typeof value === 'string';
  180. case 'record':
  181. return this.validateValueAgainstRecord(value, schema);
  182. case 'enum':
  183. return this.validateValueAgainstEnum(value, schema);
  184. case 'array':
  185. return this.validateValueAgainstArray(value, schema);
  186. case 'map':
  187. return this.validateValueAgainstMap(value, schema);
  188. case 'fixed':
  189. return this.validateValueAgainstFixed(value, schema);
  190. default:
  191. return false;
  192. }
  193. }
  194. return false;
  195. }
  196. validateValueAgainstStringSchema(value, schema) {
  197. switch (schema) {
  198. case 'null':
  199. return value === null;
  200. case 'boolean':
  201. return typeof value === 'boolean';
  202. case 'int':
  203. return typeof value === 'number' && Number.isInteger(value) && value >= -2147483648 && value <= 2147483647;
  204. case 'long':
  205. return (typeof value === 'number' && Number.isInteger(value)) || typeof value === 'bigint';
  206. case 'float':
  207. case 'double':
  208. return typeof value === 'number';
  209. case 'bytes':
  210. return value instanceof Uint8Array;
  211. case 'string':
  212. return typeof value === 'string';
  213. default:
  214. const namedSchema = this.namedSchemas.get(schema);
  215. return namedSchema ? this.validateValueAgainstSchema(value, namedSchema) : false;
  216. }
  217. }
  218. validateValueAgainstRecord(value, schema) {
  219. if (typeof value !== 'object' || value === null)
  220. return false;
  221. const obj = value;
  222. for (const field of schema.fields) {
  223. const fieldValue = obj[field.name];
  224. if (fieldValue === undefined && field.default === undefined)
  225. return false;
  226. if (fieldValue !== undefined && !this.validateValueAgainstSchema(fieldValue, field.type))
  227. return false;
  228. }
  229. return true;
  230. }
  231. validateValueAgainstEnum(value, schema) {
  232. return typeof value === 'string' && schema.symbols.includes(value);
  233. }
  234. validateValueAgainstArray(value, schema) {
  235. if (!Array.isArray(value))
  236. return false;
  237. return value.every((item) => this.validateValueAgainstSchema(item, schema.items));
  238. }
  239. validateValueAgainstMap(value, schema) {
  240. if (typeof value !== 'object' || value === null)
  241. return false;
  242. const obj = value;
  243. return Object.values(obj).every((val) => this.validateValueAgainstSchema(val, schema.values));
  244. }
  245. validateValueAgainstFixed(value, schema) {
  246. return value instanceof Uint8Array && value.length === schema.size;
  247. }
  248. getSchemaTypeName(schema) {
  249. if (typeof schema === 'string')
  250. return schema;
  251. if (Array.isArray(schema))
  252. return 'union';
  253. return schema.type;
  254. }
  255. getFullName(name, namespace) {
  256. return namespace ? `${namespace}.${name}` : name;
  257. }
  258. }
  259. exports.AvroSchemaValidator = AvroSchemaValidator;
  260. //# sourceMappingURL=AvroSchemaValidator.js.map