sfcc-cip-analytics-client
Version:
SFCC Commerce Intelligence Platform Analytics Client
166 lines (165 loc) • 8.09 kB
JavaScript
;
/**
* Avatica protobuf utility functions for decoding and processing result data.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.decodeValue = decodeValue;
exports.processFrame = processFrame;
const protocol_1 = require("./protocol");
/**
* Converts an Avatica TypedValue into a standard JavaScript type.
* @param columnValue The Avatica ColumnValue object containing the typed value.
* @param columnMetadata Optional column metadata for enhanced type detection.
* @returns The corresponding JavaScript primitive or object.
*/
function decodeValue(columnValue, columnMetadata) {
// Check if this is using the new scalar_value field
if (columnValue.scalarValue) {
const typedValue = columnValue.scalarValue;
if (typedValue.null) {
return null;
}
// Check which field actually has a value based on the type
switch (typedValue.type) {
case protocol_1.Rep.BOOLEAN:
case protocol_1.Rep.PRIMITIVE_BOOLEAN:
return typedValue.boolValue;
case protocol_1.Rep.STRING:
return typedValue.stringValue;
case protocol_1.Rep.BYTE:
case protocol_1.Rep.PRIMITIVE_BYTE:
case protocol_1.Rep.SHORT:
case protocol_1.Rep.PRIMITIVE_SHORT:
case protocol_1.Rep.INTEGER:
case protocol_1.Rep.PRIMITIVE_INT:
case protocol_1.Rep.LONG:
case protocol_1.Rep.PRIMITIVE_LONG:
case protocol_1.Rep.BIG_INTEGER:
case protocol_1.Rep.NUMBER:
// Check if this is actually a date/timestamp based on column metadata
if (columnMetadata?.type) {
const avaticaType = columnMetadata.type;
if (avaticaType.name === 'DATE' || avaticaType.id === 91) {
// Date values stored as days since epoch (1970-01-01)
const numValue = typeof typedValue.numberValue === 'object' && typedValue.numberValue.toNumber
? typedValue.numberValue.toNumber()
: Number(typedValue.numberValue);
return new Date(numValue * 24 * 60 * 60 * 1000);
}
if (avaticaType.name === 'TIMESTAMP' || avaticaType.id === 93) {
// Timestamp values stored as milliseconds since epoch
const numValue = typeof typedValue.numberValue === 'object' && typedValue.numberValue.toNumber
? typedValue.numberValue.toNumber()
: Number(typedValue.numberValue);
return new Date(numValue);
}
}
// numberValue might be a Long object from protobuf
if (typedValue.numberValue && typeof typedValue.numberValue === 'object' && typedValue.numberValue.toNumber) {
return typedValue.numberValue.toNumber();
}
return Number(typedValue.numberValue);
case protocol_1.Rep.FLOAT:
case protocol_1.Rep.PRIMITIVE_FLOAT:
case protocol_1.Rep.DOUBLE:
case protocol_1.Rep.PRIMITIVE_DOUBLE:
return typedValue.doubleValue;
case protocol_1.Rep.BIG_DECIMAL:
// process from string
return parseFloat(typedValue.stringValue);
case protocol_1.Rep.BYTE_STRING:
return typedValue.bytesValue;
case protocol_1.Rep.ARRAY:
return typedValue.arrayValue;
case protocol_1.Rep.JAVA_SQL_DATE:
case protocol_1.Rep.JAVA_UTIL_DATE:
// Date values are typically stored as milliseconds since epoch
if (typedValue.numberValue !== undefined) {
const timestamp = typeof typedValue.numberValue === 'object' && typedValue.numberValue.toNumber
? typedValue.numberValue.toNumber()
: Number(typedValue.numberValue);
return new Date(timestamp);
}
if (typedValue.stringValue !== undefined) {
return new Date(typedValue.stringValue);
}
return null;
case protocol_1.Rep.JAVA_SQL_TIMESTAMP:
// Timestamp values are typically stored as milliseconds since epoch
if (typedValue.numberValue !== undefined) {
const timestamp = typeof typedValue.numberValue === 'object' && typedValue.numberValue.toNumber
? typedValue.numberValue.toNumber()
: Number(typedValue.numberValue);
return new Date(timestamp);
}
if (typedValue.stringValue !== undefined) {
return new Date(typedValue.stringValue);
}
return null;
case protocol_1.Rep.JAVA_SQL_TIME:
// Time values might be stored as milliseconds since midnight or as strings
if (typedValue.numberValue !== undefined) {
const timeMs = typeof typedValue.numberValue === 'object' && typedValue.numberValue.toNumber
? typedValue.numberValue.toNumber()
: Number(typedValue.numberValue);
// For time-only values, create a Date object for today with the given time
const today = new Date();
today.setHours(0, 0, 0, 0);
return new Date(today.getTime() + timeMs);
}
if (typedValue.stringValue !== undefined) {
return new Date(`1970-01-01T${typedValue.stringValue}`);
}
return null;
default:
// Try to find any defined value
if (typedValue.stringValue !== undefined && typedValue.stringValue !== '')
return typedValue.stringValue;
if (typedValue.numberValue !== undefined) {
if (typedValue.numberValue && typeof typedValue.numberValue === 'object' && typedValue.numberValue.toNumber) {
return typedValue.numberValue.toNumber();
}
return Number(typedValue.numberValue);
}
if (typedValue.doubleValue !== undefined)
return typedValue.doubleValue;
if (typedValue.bytesValue !== undefined)
return typedValue.bytesValue;
if (typedValue.arrayValue !== undefined)
return typedValue.arrayValue;
if (typedValue.boolValue !== undefined)
return typedValue.boolValue;
}
return null;
}
return null;
}
/**
* Processes an Avatica result frame into a more usable format (array of objects).
* @param signature The signature from the execute response, containing column info.
* @param frame The data frame containing rows of typed values.
* @returns An array of objects, where each object represents a row with column names as keys.
*/
function processFrame(signature, frame) {
if (!signature || !frame || !frame.rows) {
return [];
}
const columnNames = signature.columns?.map((c) => c.label || '') || [];
return frame.rows.map((row) => {
const rowObject = {};
// row.value is an array of ColumnValue objects
if (!row.value || !Array.isArray(row.value)) {
return rowObject;
}
const decodedValues = row.value.map((colValue, index) => {
const columnMetadata = signature.columns?.[index];
return decodeValue(colValue, columnMetadata);
});
columnNames.forEach((name, i) => {
if (name) {
rowObject[name] = decodedValues[i];
}
});
return rowObject;
});
}