@datastax/astra-db-ts
Version:
Data API TypeScript client
202 lines (201 loc) • 9.23 kB
JavaScript
// Copyright Datastax, Inc
// SPDX-License-Identifier: Apache-2.0
// noinspection ExceptionCaughtLocallyJS
var _DataAPIHttpClient_props;
import { __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
import { EmbeddingAPIKeyHeaderProvider, HeadersProvider, RerankingAPIKeyHeaderProvider, TokenProvider, } from '../../../lib/index.js';
import { Collection, DataAPIHttpError, DataAPIResponseError, DataAPITimeoutError } from '../../../documents/index.js';
import { HttpClient } from '../../../lib/api/clients/http-client.js';
import { HttpMethods } from '../../../lib/api/constants.js';
import { Timeouts } from '../../../lib/api/timeouts/timeouts.js';
import { NonErrorError } from '../../../lib/errors.js';
import { isNonEmpty } from '../../../lib/utils.js';
export const EmissionStrategy = {
Normal: (logger) => ({
emitCommandStarted(reqId, info, opts) {
logger.commandStarted?.(reqId, info, opts.extraLogInfo);
},
emitCommandFailed(reqId, info, resp, error, started, opts) {
logger.commandFailed?.(reqId, info, opts.extraLogInfo, resp, error, started);
},
emitCommandSucceeded(reqId, info, resp, started, opts) {
logger.commandSucceeded?.(reqId, info, opts.extraLogInfo, resp, started);
},
emitCommandWarnings(reqId, info, warnings, opts) {
logger.commandWarnings?.(reqId, info, opts.extraLogInfo, warnings);
},
}),
Admin: (logger) => ({
emitCommandStarted(reqId, info, opts) {
logger.adminCommandStarted?.(reqId, '', adaptInfo4Devops(info, opts.methodName), true, null); // TODO
},
emitCommandFailed(reqId, info, _, error, started, opts) {
logger.adminCommandFailed?.(reqId, '', adaptInfo4Devops(info, opts.methodName), true, error, started);
},
emitCommandSucceeded(reqId, info, resp, started, opts) {
logger.adminCommandSucceeded?.(reqId, '', adaptInfo4Devops(info, opts.methodName), true, resp, started);
},
emitCommandWarnings(reqId, info, warnings, opts) {
logger.adminCommandWarnings?.(reqId, '', adaptInfo4Devops(info, opts.methodName), true, warnings);
},
}),
};
const adaptInfo4Devops = (info, methodName) => ({
method: 'POST',
data: info.command,
path: info.url,
methodName,
});
export class DataAPIHttpClient extends HttpClient {
constructor(opts) {
super('data-api', {
...opts,
additionalHeaders: HeadersProvider.opts.fromObj.concat([
opts.additionalHeaders,
opts.tokenProvider.toHeadersProvider(),
]),
mkTimeoutError: DataAPITimeoutError.mk,
});
Object.defineProperty(this, "collectionName", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "tableName", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "keyspace", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "emissionStrategy", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "bigNumHack", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
_DataAPIHttpClient_props.set(this, void 0);
this.keyspace = opts.keyspace;
__classPrivateFieldSet(this, _DataAPIHttpClient_props, opts, "f");
this.emissionStrategy = opts.emissionStrategy(opts.logger.internal);
}
forTableSlashCollectionOrWhateverWeWouldCallTheUnionOfTheseTypes(tSlashC, opts, bigNumHack) {
const clone = new DataAPIHttpClient({
...__classPrivateFieldGet(this, _DataAPIHttpClient_props, "f"),
emissionStrategy: EmissionStrategy.Normal,
keyspace: { ref: tSlashC.keyspace },
logger: tSlashC,
additionalHeaders: HeadersProvider.opts.monoid.concat([
__classPrivateFieldGet(this, _DataAPIHttpClient_props, "f").additionalHeaders,
HeadersProvider.opts.fromStr(EmbeddingAPIKeyHeaderProvider).parse(opts?.embeddingApiKey),
HeadersProvider.opts.fromStr(RerankingAPIKeyHeaderProvider).parse(opts?.rerankingApiKey),
]),
});
if (tSlashC instanceof Collection) {
clone.collectionName = tSlashC.name;
}
else {
clone.tableName = tSlashC.name;
}
clone.bigNumHack = bigNumHack;
clone.tm = new Timeouts(DataAPITimeoutError.mk, Timeouts.cfg.parse({ ...this.tm.baseTimeouts, ...opts?.timeoutDefaults }));
return clone;
}
forDbAdmin(dbAdmin, opts) {
const clone = new DataAPIHttpClient({
...__classPrivateFieldGet(this, _DataAPIHttpClient_props, "f"),
tokenProvider: TokenProvider.opts.concat([opts.adminToken, __classPrivateFieldGet(this, _DataAPIHttpClient_props, "f").tokenProvider]),
baseUrl: opts?.endpointUrl ?? __classPrivateFieldGet(this, _DataAPIHttpClient_props, "f").baseUrl,
baseApiPath: opts?.endpointUrl ? '' : __classPrivateFieldGet(this, _DataAPIHttpClient_props, "f").baseApiPath,
emissionStrategy: EmissionStrategy.Admin,
logger: dbAdmin,
});
clone.collectionName = undefined;
clone.tableName = undefined;
clone.tm = new Timeouts(DataAPITimeoutError.mk, { ...this.tm.baseTimeouts, ...opts?.timeoutDefaults });
return clone;
}
async executeCommand(command, options) {
if (options?.collection && options.table) {
throw new Error('Can\'t provide both `table` and `collection` as options to DataAPIHttpClient.executeCommand()');
}
const tOrC = options.collection || options.table || this.collectionName || this.tableName;
const keyspace = options.keyspace === undefined ? this.keyspace?.ref : options.keyspace;
if (keyspace === undefined) {
throw new Error('Db is missing a required keyspace; be sure to set one with client.db(..., { keyspace }), or db.useKeyspace()');
}
if (keyspace === null && tOrC) {
throw new Error('Keyspace may not be `null` when a table or collection is provided to DataAPIHttpClient.executeCommand()');
}
const info = {
url: this.baseUrl,
tOrC: tOrC,
tOrCType: !tOrC ? undefined : tOrC === (options.table || this.tableName) ? 'table' : 'collection',
keyspace: keyspace,
command: command,
timeoutManager: options.timeoutManager,
bigNumsPresent: options.bigNumsPresent,
};
const keyspacePath = info.keyspace ? `/${info.keyspace}` : '';
const collectionPath = info.tOrC ? `/${info.tOrC}` : '';
info.url += keyspacePath + collectionPath;
const requestId = this.logger.internal.generateCommandRequestId();
this.emissionStrategy.emitCommandStarted?.(requestId, info, options);
const started = performance.now();
let clonedData;
try {
const serialized = (info.bigNumsPresent)
? this.bigNumHack?.parser.stringify(info.command)
: JSON.stringify(info.command);
const resp = await this._request({
url: info.url,
data: serialized,
timeoutManager: info.timeoutManager,
method: HttpMethods.Post,
});
if (resp.status >= 400 && resp.status !== 401) {
throw new DataAPIHttpError(resp);
}
const data = (resp.body)
? (this.bigNumHack?.parseWithBigNumbers(resp.body))
? this.bigNumHack?.parser.parse(resp.body)
: JSON.parse(resp.body)
: {};
clonedData = requestId
? structuredClone(data)
: undefined;
const warnings = data?.status?.warnings ?? [];
if (warnings.length) {
this.emissionStrategy.emitCommandWarnings?.(requestId, info, warnings, options);
}
if (data.errors && isNonEmpty(data.errors)) {
throw new DataAPIResponseError(info.command, data);
}
const respData = {
data: data.data,
status: data.status,
errors: data.errors,
};
this.emissionStrategy.emitCommandSucceeded?.(requestId, info, clonedData, started, options);
return respData;
}
catch (thrown) {
const err = NonErrorError.asError(thrown);
this.emissionStrategy.emitCommandFailed?.(requestId, info, clonedData, err, started, options);
throw err;
}
}
}
_DataAPIHttpClient_props = new WeakMap();