AvroSchemaDecoder.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.AvroSchemaDecoder = void 0;
  4. const Reader_1 = require("@jsonjoy.com/buffers/lib/Reader");
  5. const AvroDecoder_1 = require("./AvroDecoder");
  6. const AvroSchemaValidator_1 = require("./AvroSchemaValidator");
  7. class AvroSchemaDecoder {
  8. constructor(reader = new Reader_1.Reader()) {
  9. this.reader = reader;
  10. this.namedSchemas = new Map();
  11. this.decoder = new AvroDecoder_1.AvroDecoder();
  12. this.decoder.reader = reader;
  13. this.validator = new AvroSchemaValidator_1.AvroSchemaValidator();
  14. }
  15. decode(data, schema) {
  16. this.reader.reset(data);
  17. this.namedSchemas.clear();
  18. if (!this.validator.validateSchema(schema)) {
  19. throw new Error('Invalid Avro schema');
  20. }
  21. this.collectNamedSchemas(schema);
  22. return this.readValue(schema);
  23. }
  24. readValue(schema) {
  25. const resolvedSchema = this.resolveSchema(schema);
  26. if (typeof resolvedSchema === 'string') {
  27. switch (resolvedSchema) {
  28. case 'null':
  29. return this.decoder.readNull();
  30. case 'boolean':
  31. return this.decoder.readBoolean();
  32. case 'int':
  33. return this.decoder.readInt();
  34. case 'long':
  35. return this.decoder.readLong();
  36. case 'float':
  37. return this.decoder.readFloat();
  38. case 'double':
  39. return this.decoder.readDouble();
  40. case 'bytes':
  41. return this.decoder.readBytes();
  42. case 'string':
  43. return this.decoder.readString();
  44. default:
  45. throw new Error(`Unknown primitive type: ${resolvedSchema}`);
  46. }
  47. }
  48. if (Array.isArray(resolvedSchema)) {
  49. return this.readUnion(resolvedSchema);
  50. }
  51. switch (resolvedSchema.type) {
  52. case 'record':
  53. return this.readRecord(resolvedSchema);
  54. case 'enum':
  55. return this.readEnum(resolvedSchema);
  56. case 'array':
  57. return this.readArray(resolvedSchema);
  58. case 'map':
  59. return this.readMap(resolvedSchema);
  60. case 'fixed':
  61. return this.readFixed(resolvedSchema);
  62. default:
  63. throw new Error(`Unknown schema type: ${resolvedSchema.type}`);
  64. }
  65. }
  66. readRecord(schema) {
  67. const result = {};
  68. for (let i = 0; i < schema.fields.length; i++) {
  69. const field = schema.fields[i];
  70. try {
  71. result[field.name] = this.readValue(field.type);
  72. }
  73. catch (error) {
  74. throw new Error(`Error reading field '${field.name}': ${error.message}`);
  75. }
  76. }
  77. return result;
  78. }
  79. readEnum(schema) {
  80. const index = this.decoder.readEnum();
  81. if (index < 0 || index >= schema.symbols.length) {
  82. throw new Error(`Invalid enum index ${index} for enum with ${schema.symbols.length} symbols`);
  83. }
  84. return schema.symbols[index];
  85. }
  86. readArray(schema) {
  87. return this.decoder.readArray(() => this.readValue(schema.items));
  88. }
  89. readMap(schema) {
  90. return this.decoder.readMap(() => this.readValue(schema.values));
  91. }
  92. readUnion(schema) {
  93. const schemaReaders = schema.map((subSchema) => () => this.readValue(subSchema));
  94. const result = this.decoder.readUnion(schemaReaders);
  95. return result.value;
  96. }
  97. readFixed(schema) {
  98. return this.decoder.readFixed(schema.size);
  99. }
  100. readNull(schema) {
  101. this.validateSchemaType(schema, 'null');
  102. return this.decoder.readNull();
  103. }
  104. readBoolean(schema) {
  105. this.validateSchemaType(schema, 'boolean');
  106. return this.decoder.readBoolean();
  107. }
  108. readInt(schema) {
  109. this.validateSchemaType(schema, 'int');
  110. const value = this.decoder.readInt();
  111. if (!Number.isInteger(value) || value < -2147483648 || value > 2147483647) {
  112. throw new Error('Decoded value is not a valid 32-bit integer');
  113. }
  114. return value;
  115. }
  116. readLong(schema) {
  117. this.validateSchemaType(schema, 'long');
  118. return this.decoder.readLong();
  119. }
  120. readFloat(schema) {
  121. this.validateSchemaType(schema, 'float');
  122. return this.decoder.readFloat();
  123. }
  124. readDouble(schema) {
  125. this.validateSchemaType(schema, 'double');
  126. return this.decoder.readDouble();
  127. }
  128. readBytes(schema) {
  129. this.validateSchemaType(schema, 'bytes');
  130. return this.decoder.readBytes();
  131. }
  132. readString(schema) {
  133. this.validateSchemaType(schema, 'string');
  134. return this.decoder.readString();
  135. }
  136. validateSchemaType(schema, expectedType) {
  137. const resolvedSchema = this.resolveSchema(schema);
  138. const actualType = typeof resolvedSchema === 'string'
  139. ? resolvedSchema
  140. : Array.isArray(resolvedSchema)
  141. ? 'union'
  142. : resolvedSchema.type;
  143. if (actualType !== expectedType) {
  144. throw new Error(`Expected schema type ${expectedType}, got ${actualType}`);
  145. }
  146. }
  147. resolveSchema(schema) {
  148. if (typeof schema === 'string') {
  149. const namedSchema = this.namedSchemas.get(schema);
  150. return namedSchema || schema;
  151. }
  152. return schema;
  153. }
  154. collectNamedSchemas(schema) {
  155. if (typeof schema === 'string' || Array.isArray(schema)) {
  156. return;
  157. }
  158. if (typeof schema === 'object' && schema !== null) {
  159. switch (schema.type) {
  160. case 'record':
  161. const recordSchema = schema;
  162. const recordFullName = this.getFullName(recordSchema.name, recordSchema.namespace);
  163. this.namedSchemas.set(recordFullName, recordSchema);
  164. recordSchema.fields.forEach((field) => this.collectNamedSchemas(field.type));
  165. break;
  166. case 'enum':
  167. const enumSchema = schema;
  168. const enumFullName = this.getFullName(enumSchema.name, enumSchema.namespace);
  169. this.namedSchemas.set(enumFullName, enumSchema);
  170. break;
  171. case 'fixed':
  172. const fixedSchema = schema;
  173. const fixedFullName = this.getFullName(fixedSchema.name, fixedSchema.namespace);
  174. this.namedSchemas.set(fixedFullName, fixedSchema);
  175. break;
  176. case 'array':
  177. this.collectNamedSchemas(schema.items);
  178. break;
  179. case 'map':
  180. this.collectNamedSchemas(schema.values);
  181. break;
  182. }
  183. }
  184. }
  185. getFullName(name, namespace) {
  186. return namespace ? `${namespace}.${name}` : name;
  187. }
  188. }
  189. exports.AvroSchemaDecoder = AvroSchemaDecoder;
  190. //# sourceMappingURL=AvroSchemaDecoder.js.map