cassandra-driver
Version:
DataStax Node.js Driver for Apache Cassandra
362 lines (302 loc) • 8.5 kB
JavaScript
/*
* Copyright DataStax, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
;
const types = require('../../types');
const utils = require('../../utils');
const { getTypeDefinitionByValue, getUdtTypeDefinitionByValue } = require('./complex-type-helper');
const { Point, Polygon, LineString } = require('../../geometry');
const { Edge } = require('./structure');
const { GraphTypeWrapper, UdtGraphWrapper } = require('./wrappers');
const { Tuple, dataTypes } = types;
const typeKey = '@type';
const valueKey = '@value';
class EdgeDeserializer {
constructor() {
this.key = 'g:Edge';
}
deserialize(obj) {
const value = obj[valueKey];
return new Edge(this.reader.read(value['id']), this.reader.read(value['outV']), value['outVLabel'], value['label'], this.reader.read(value['inV']), value['inVLabel'], this.reader.read(value['properties']));
}
}
/**
* Uses toString() instance method and fromString() static method to serialize and deserialize the value.
* @abstract
* @private
*/
class StringBasedTypeSerializer {
/**
* Creates a new instance of the deserializer.
* @param {String} key
* @param {Function} targetType
*/
constructor(key, targetType) {
if (!key) {
throw new Error('Deserializer must provide a type key');
}
if (!targetType) {
throw new Error('Deserializer must provide a target type');
}
this.key = key;
this.targetType = targetType;
}
deserialize(obj) {
let value = obj[valueKey];
if (typeof value !== 'string') {
value = value.toString();
}
return this.targetType.fromString(value);
}
serialize(value) {
return {
[typeKey]: this.key,
[valueKey]: value.toString()
};
}
canBeUsedFor(value) {
return value instanceof this.targetType;
}
}
class UuidSerializer extends StringBasedTypeSerializer {
constructor() {
super('g:UUID', types.Uuid);
}
}
class LongSerializer extends StringBasedTypeSerializer {
constructor() {
super('g:Int64', types.Long);
}
}
class BigDecimalSerializer extends StringBasedTypeSerializer {
constructor() {
super('gx:BigDecimal', types.BigDecimal);
}
}
class BigIntegerSerializer extends StringBasedTypeSerializer {
constructor() {
super('gx:BigInteger', types.Integer);
}
}
class InetAddressSerializer extends StringBasedTypeSerializer {
constructor() {
super('gx:InetAddress', types.InetAddress);
}
}
class LocalDateSerializer extends StringBasedTypeSerializer {
constructor() {
super('gx:LocalDate', types.LocalDate);
}
}
class LocalTimeSerializer extends StringBasedTypeSerializer {
constructor() {
super('gx:LocalTime', types.LocalTime);
}
}
class InstantSerializer extends StringBasedTypeSerializer {
constructor() {
super('gx:Instant', Date);
}
serialize(item) {
return {
[typeKey]: this.key,
[valueKey]: item.toISOString()
};
}
deserialize(obj) {
return new Date(obj[valueKey]);
}
}
class BlobSerializer extends StringBasedTypeSerializer {
constructor() {
super('dse:Blob', Buffer);
}
deserialize(obj) {
return utils.allocBufferFromString(obj[valueKey], 'base64');
}
serialize(item) {
return {
[typeKey]: this.key,
[valueKey]: item.toString('base64')
};
}
}
class PointSerializer extends StringBasedTypeSerializer {
constructor() {
super('dse:Point', Point);
}
}
class LineStringSerializer extends StringBasedTypeSerializer {
constructor() {
super('dse:LineString', LineString);
}
}
class PolygonSerializer extends StringBasedTypeSerializer {
constructor() {
super('dse:Polygon', Polygon);
}
}
class TupleSerializer {
constructor() {
this.key = 'dse:Tuple';
}
deserialize(obj) {
// Skip definitions and go to the value
const value = obj[valueKey]['value'];
if (!Array.isArray(value)) {
throw new Error('Expected Array, obtained: ' + value);
}
const result = [];
for (const element of value) {
result.push(this.reader.read(element));
}
return Tuple.fromArray(result);
}
/** @param {Tuple} tuple */
serialize(tuple) {
const result = {
'cqlType': 'tuple',
'definition': tuple.elements.map(getTypeDefinitionByValue),
'value': tuple.elements.map(e => this.writer.adaptObject(e))
};
return {
[typeKey]: this.key,
[valueKey]: result
};
}
canBeUsedFor(value) {
return value instanceof Tuple;
}
}
class DurationSerializer {
constructor() {
this.key = 'dse:Duration';
}
deserialize(obj) {
// Skip definitions and go to the value
const value = obj[valueKey];
return new types.Duration(
this.reader.read(value['months']), this.reader.read(value['days']), this.reader.read(value['nanos']));
}
/** @param {Duration} value */
serialize(value) {
return {
[typeKey]: this.key,
[valueKey]: {
'months': value['months'],
'days': value['days'],
'nanos': value['nanoseconds'],
}
};
}
canBeUsedFor(value) {
return value instanceof types.Duration;
}
}
class UdtSerializer {
constructor() {
this.key = 'dse:UDT';
}
deserialize(obj) {
// Skip definitions and go to the value
const valueRoot = obj[valueKey];
const result = {};
const value = valueRoot['value'];
valueRoot['definition'].forEach((definition, index) => {
result[definition.fieldName] = this.reader.read(value[index]);
});
return result;
}
serialize(udtWrapper) {
const serializedValue = getUdtTypeDefinitionByValue(udtWrapper);
// New properties can be added to the existing object without need to clone
// as getTypeDefinition() returns a new object each time
serializedValue['value'] = Object.entries(udtWrapper.value).map(([_, v]) => this.writer.adaptObject(v));
return {
[typeKey]: this.key,
[valueKey]: serializedValue
};
}
canBeUsedFor(value) {
return value instanceof UdtGraphWrapper;
}
}
class InternalSerializer {
constructor(name, transformFn) {
this._name = name;
this._transformFn = transformFn || (x => x);
}
serialize(item) {
return {
[typeKey]: this._name,
[valueKey]: this._transformFn(item)
};
}
}
// Associative array of graph type name by CQL type code, used by the type wrapper
const graphSONSerializerByCqlType = {
[dataTypes.int]: new InternalSerializer('g:Int32'),
[dataTypes.bigint]: new InternalSerializer('g:Int64'),
[dataTypes.double]: new InternalSerializer('g:Double'),
[dataTypes.float]: new InternalSerializer('g:Float'),
[dataTypes.timestamp]: new InternalSerializer('g:Timestamp', x => x.getTime())
};
class GraphTypeWrapperSerializer {
constructor() {
// Use a fixed name that doesn't conflict with TinkerPop and DS Graph
this.key = 'client:wrapper';
}
serialize(wrappedValue) {
const s = graphSONSerializerByCqlType[wrappedValue.typeInfo.code];
if (!s) {
throw new Error(`No serializer found for wrapped value ${wrappedValue}`);
}
return s.serialize(wrappedValue.value);
}
canBeUsedFor(value) {
return value instanceof GraphTypeWrapper;
}
}
const serializersArray = [
EdgeDeserializer,
UuidSerializer,
LongSerializer,
BigDecimalSerializer,
BigIntegerSerializer,
InetAddressSerializer,
LocalDateSerializer,
LocalTimeSerializer,
InstantSerializer,
BlobSerializer,
PointSerializer,
LineStringSerializer,
PolygonSerializer,
TupleSerializer,
UdtSerializer,
GraphTypeWrapperSerializer,
DurationSerializer
];
function getCustomSerializers() {
const customSerializers = {};
serializersArray.forEach(sConstructor => {
const instance = new sConstructor();
if (!instance.key) {
throw new TypeError(`Key for ${sConstructor} instance not set`);
}
customSerializers[instance.key] = instance;
});
return customSerializers;
}
module.exports = getCustomSerializers;