@datastax/astra-db-ts
Version:
Data API TypeScript client
172 lines (171 loc) • 6.73 kB
JavaScript
// Copyright Datastax, Inc
// SPDX-License-Identifier: Apache-2.0
import { SerDes } from '../../../lib/api/ser-des/ser-des.js';
import { TableCodecs } from '../../../documents/tables/ser-des/codecs.js';
import { NEVERMIND, SerDesTarget } from '../../../lib/api/ser-des/ctx.js';
import { $SerializeForTable } from '../../../documents/tables/ser-des/constants.js';
import { isBigNumber } from '../../../lib/utils.js';
import { UnexpectedDataAPIResponseError } from '../../../client/index.js';
import { TableSerDesCfgHandler } from '../../../documents/tables/ser-des/cfg-handler.js';
import { pathMatches } from '../../../lib/api/ser-des/utils.js';
export class TableSerDes extends SerDes {
constructor(cfg) {
super(TableSerDes.cfg.concat([codecs, cfg]), serialize, deserialize);
}
adaptSerCtx(ctx) {
ctx.bigNumsPresent = false;
return ctx;
}
adaptDesCtx(ctx) {
const rawDataApiResp = ctx.rawDataApiResp;
const status = UnexpectedDataAPIResponseError.require(rawDataApiResp.status, 'No `status` found in response.', rawDataApiResp);
if (ctx.target === SerDesTarget.InsertedId) {
ctx.tableSchema = UnexpectedDataAPIResponseError.require(status.primaryKeySchema, 'No `status.primaryKeySchema` found in response.\n\n**Did you accidentally use a `Table` object on a Collection?** If so, your document was successfully inserted, but the client cannot properly deserialize the response. Please use a `Collection` object instead.', rawDataApiResp);
}
else {
ctx.tableSchema = UnexpectedDataAPIResponseError.require(status.projectionSchema, 'No `status.projectionSchema` found in response.\n\n**Did you accidentally use a `Table` object on a Collection?** If so, documents may\'ve been found, but the client cannot properly deserialize the response. Please use a `Collection` object instead.', rawDataApiResp);
}
if (ctx.target === SerDesTarget.InsertedId) {
ctx.rootObj = Object.fromEntries(Object.keys(ctx.tableSchema).map((key, i) => {
return [key, ctx.rootObj[i]];
}));
}
if (this._cfg.sparseData !== true) {
populateSparseData(ctx);
}
return ctx;
}
bigNumsPresent(ctx) {
return ctx.bigNumsPresent;
}
}
Object.defineProperty(TableSerDes, "cfg", {
enumerable: true,
configurable: true,
writable: true,
value: TableSerDesCfgHandler
});
const serialize = (value, ctx) => {
let resp = null;
// Path-based serializers
for (const pathSer of ctx.serializers.forPath[ctx.path.length] ?? []) {
if (pathMatches(pathSer.path, ctx.path) && pathSer.fns.find((fns) => (resp = fns(value, ctx))[0] !== NEVERMIND)) {
return resp;
}
}
// Name-based serializers
const key = ctx.path[ctx.path.length - 1] ?? '';
const nameSer = ctx.serializers.forName[key];
if (nameSer?.find((fns) => (resp = fns(value, ctx))[0] !== NEVERMIND)) {
return resp;
}
// Type-based & custom serializers
for (const guardSer of ctx.serializers.forGuard) {
if (guardSer.guard(value, ctx) && (resp = guardSer.fn(value, ctx))[0] !== NEVERMIND) {
return resp;
}
}
if (typeof value === 'number') {
if (!isFinite(value)) {
return ctx.done(value.toString());
}
}
else if (typeof value === 'object' && value !== null) {
// Delegate serializer
if (value[$SerializeForTable] && (resp = value[$SerializeForTable](ctx))[0] !== NEVERMIND) {
return resp;
}
// Class-based serializers
const classSer = ctx.serializers.forClass.find((c) => value instanceof c.class);
if (classSer?.fns.find((fns) => (resp = fns(value, ctx))[0] !== NEVERMIND)) {
return resp;
}
// Enable using json-bigint
if (isBigNumber(value)) {
ctx.bigNumsPresent = true;
return ctx.done();
}
}
else if (typeof value === 'bigint') {
ctx.bigNumsPresent = true;
}
return ctx.recurse();
};
const deserialize = (value, ctx) => {
let resp = null;
// Path-based deserializers
for (const pathDes of ctx.deserializers.forPath[ctx.path.length] ?? []) {
if (pathMatches(pathDes.path, ctx.path) && pathDes.fns.find((fns) => (resp = fns(value, ctx))[0] !== NEVERMIND)) {
return resp;
}
}
// Name-based deserializers
const key = ctx.path[ctx.path.length - 1] ?? '';
const nameDes = ctx.deserializers.forName[key];
if (nameDes?.find((fns) => (resp = fns(value, ctx))[0] !== NEVERMIND)) {
return resp;
}
// Custom deserializers
for (const guardDes of ctx.deserializers.forGuard) {
if (guardDes.guard(value, ctx) && (resp = guardDes.fn(value, ctx))[0] !== NEVERMIND) {
return resp;
}
}
if (ctx.path.length === 0 || value === null) {
return ctx.recurse(value);
}
// Type-based deserializers
const type = resolveAbsType(ctx);
const typeDes = type && ctx.deserializers.forType[type];
if (typeDes && typeDes.find((fns) => (resp = fns(value, ctx))[0] !== NEVERMIND)) {
return resp;
}
return ctx.recurse(value);
};
const codecs = TableSerDes.cfg.parse({ codecs: Object.values(TableCodecs.Defaults) });
function populateSparseData(ctx) {
for (const key in ctx.tableSchema) {
if (Object.prototype.hasOwnProperty.call(ctx.rootObj, key)) {
continue;
}
const type = resolveType(ctx.tableSchema[key]);
if (type === 'map') {
ctx.rootObj[key] = new Map();
}
else if (type === 'set') {
ctx.rootObj[key] = new Set();
}
else if (type === 'list') {
ctx.rootObj[key] = [];
}
else {
ctx.rootObj[key] = null;
}
}
}
function resolveAbsType({ path, tableSchema }) {
const column = tableSchema[path[0]];
const type = column ? resolveType(column) : undefined;
if (path.length === 1 || !column) {
return type;
}
if (type === 'map') {
if (typeof path[1] === 'number') {
if (path.length === 3) {
return (path[2] === 0 ? column.keyType : column.valueType);
}
}
else if (path.length === 2) {
return column.valueType;
}
}
else if ((type === 'set' || type === 'list') && path.length === 2) {
return column.valueType;
}
return undefined;
}
function resolveType(column) {
return (column.type === 'UNSUPPORTED')
? column.apiSupport.cqlDefinition
: column.type;
}