UNPKG

@aws-lambda-powertools/kafka

Version:

Utility to easily handle message deserialization and parsing of Kafka events in AWS Lambda functions

81 lines (80 loc) 3.53 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.deserialize = void 0; const protobufjs_1 = require("protobufjs"); const errors_js_1 = require("../errors.js"); /** * Default order of varint types used in Protobuf to attempt deserializing Confluent Schema Registry messages. */ const varintOrder = ['int32', 'sint32']; /** * Deserialize a Protobuf message from a base64-encoded string. * * @template T - The type of the deserialized message object. * * @param data - The base64-encoded string representing the Protobuf binary data. * @param messageType - The Protobuf message type definition - see {@link Message | `Message`} from {@link https://www.npmjs.com/package/protobufjs | `protobufjs`}. */ const deserialize = (data, messageType, schemaMetadata) => { const buffer = Buffer.from(data, 'base64'); try { if (schemaMetadata.schemaId === undefined) { return messageType.decode(buffer, buffer.length); } /** * If `schemaId` is longer than 10 chars, it's an UUID, otherwise it's a numeric ID. * * When this is the case, we know the schema is coming from Glue Schema Registry, * and the first byte of the buffer is a magic byte that we need to remove before * decoding the message. */ if (schemaMetadata.schemaId.length > 10) { // remove the first byte from the buffer const reader = new protobufjs_1.BufferReader(buffer); reader.uint32(); return messageType.decode(reader); } } catch (error) { throw new errors_js_1.KafkaConsumerDeserializationError(`Failed to deserialize Protobuf message: ${error}, message: ${data}, messageType: ${JSON.stringify(messageType)}`); } /** * If schemaId is numeric, inferred from its length, we know it's coming from Confluent Schema Registry, * so we need to remove the MessageIndex bytes. * We don't know the type of the index, so we try both `int32` and `sint32`. If both fail, we throw an error. */ try { const newBuffer = clipConfluentSchemaRegistryBuffer(buffer, varintOrder[0]); return messageType.decode(newBuffer); } catch (error) { try { const newBuffer = clipConfluentSchemaRegistryBuffer(buffer, varintOrder[1]); const decoded = messageType.decode(newBuffer); // swap varint order if the first attempt failed so we can use the correct one for subsequent messages varintOrder.reverse(); return decoded; } catch { throw new errors_js_1.KafkaConsumerDeserializationError(`Failed to deserialize Protobuf message: ${error}, message: ${data}, messageType: ${JSON.stringify(messageType)}`); } } }; exports.deserialize = deserialize; /** * Clip the Confluent Schema Registry buffer to remove the index bytes. * * @param buffer - The buffer to clip. * @param intType - The type of the integer to read from the buffer, either 'int32' or 'sint32'. */ const clipConfluentSchemaRegistryBuffer = (buffer, intType) => { const reader = new protobufjs_1.BufferReader(buffer); /** * Read the first varint byte to get the index count or 0. * Doing so, also advances the reader position to the next byte after the index count. */ const indexCount = intType === 'int32' ? reader.int32() : reader.sint32(); // Skip the index bytes reader.skip(indexCount); return reader; };