kaab4321's picture
first
9756872
Raw History Blame Contribute Delete
7.51 kB
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CursorResponse = exports.MongoDBResponse = exports.isErrorResponse = void 0;
const bson_1 = require("../../bson");
const error_1 = require("../../error");
const utils_1 = require("../../utils");
const document_1 = require("./on_demand/document");
/**
* Accepts a BSON payload and checks for na "ok: 0" element.
* This utility is intended to prevent calling response class constructors
* that expect the result to be a success and demand certain properties to exist.
*
* For example, a cursor response always expects a cursor embedded document.
* In order to write the class such that the properties reflect that assertion (non-null)
* we cannot invoke the subclass constructor if the BSON represents an error.
*
* @param bytes - BSON document returned from the server
*/
function isErrorResponse(bson) {
const elements = (0, bson_1.parseToElementsToArray)(bson, 0);
for (let eIdx = 0; eIdx < elements.length; eIdx++) {
const element = elements[eIdx];
if (element[2 /* BSONElementOffset.nameLength */] === 2) {
const nameOffset = element[1 /* BSONElementOffset.nameOffset */];
// 111 == "o", 107 == "k"
if (bson[nameOffset] === 111 && bson[nameOffset + 1] === 107) {
const valueOffset = element[3 /* BSONElementOffset.offset */];
const valueLength = element[4 /* BSONElementOffset.length */];
// If any byte in the length of the ok number (works for any type) is non zero,
// then it is considered "ok: 1"
for (let i = valueOffset; i < valueOffset + valueLength; i++) {
if (bson[i] !== 0x00)
return false;
}
return true;
}
}
}
return true;
}
exports.isErrorResponse = isErrorResponse;
/** @internal */
class MongoDBResponse extends document_1.OnDemandDocument {
static is(value) {
return value instanceof MongoDBResponse;
}
/** Indicates this document is a server error */
get isError() {
let isError = this.ok === 0;
isError ||= this.has('errmsg');
isError ||= this.has('code');
isError ||= this.has('$err'); // The '$err' field is used in OP_REPLY responses
return isError;
}
/**
* Drivers can safely assume that the `recoveryToken` field is always a BSON document but drivers MUST NOT modify the
* contents of the document.
*/
get recoveryToken() {
return (this.get('recoveryToken', bson_1.BSONType.object)?.toObject({
promoteValues: false,
promoteLongs: false,
promoteBuffers: false
}) ?? null);
}
/**
* The server creates a cursor in response to a snapshot find/aggregate command and reports atClusterTime within the cursor field in the response.
* For the distinct command the server adds a top-level atClusterTime field to the response.
* The atClusterTime field represents the timestamp of the read and is guaranteed to be majority committed.
*/
get atClusterTime() {
return (this.get('cursor', bson_1.BSONType.object)?.get('atClusterTime', bson_1.BSONType.timestamp) ??
this.get('atClusterTime', bson_1.BSONType.timestamp));
}
get operationTime() {
return this.get('operationTime', bson_1.BSONType.timestamp);
}
get ok() {
return this.getNumber('ok') ? 1 : 0;
}
get $err() {
return this.get('$err', bson_1.BSONType.string);
}
get errmsg() {
return this.get('errmsg', bson_1.BSONType.string);
}
get code() {
return this.getNumber('code');
}
get $clusterTime() {
if (!('clusterTime' in this)) {
const clusterTimeDoc = this.get('$clusterTime', bson_1.BSONType.object);
if (clusterTimeDoc == null) {
this.clusterTime = null;
return null;
}
const clusterTime = clusterTimeDoc.get('clusterTime', bson_1.BSONType.timestamp, true);
const signature = clusterTimeDoc.get('signature', bson_1.BSONType.object)?.toObject();
// @ts-expect-error: `signature` is incorrectly typed. It is public API.
this.clusterTime = { clusterTime, signature };
}
return this.clusterTime ?? null;
}
toObject(options) {
const exactBSONOptions = {
useBigInt64: options?.useBigInt64,
promoteLongs: options?.promoteLongs,
promoteValues: options?.promoteValues,
promoteBuffers: options?.promoteBuffers,
bsonRegExp: options?.bsonRegExp,
raw: options?.raw ?? false,
fieldsAsRaw: options?.fieldsAsRaw ?? {},
validation: this.parseBsonSerializationOptions(options)
};
return super.toObject(exactBSONOptions);
}
parseBsonSerializationOptions(options) {
const enableUtf8Validation = options?.enableUtf8Validation;
if (enableUtf8Validation === false) {
return { utf8: false };
}
return { utf8: { writeErrors: false } };
}
}
// {ok:1}
MongoDBResponse.empty = new MongoDBResponse(new Uint8Array([13, 0, 0, 0, 16, 111, 107, 0, 1, 0, 0, 0, 0]));
exports.MongoDBResponse = MongoDBResponse;
/** @internal */
class CursorResponse extends MongoDBResponse {
static is(value) {
return value instanceof CursorResponse || value === CursorResponse.emptyGetMore;
}
constructor(bytes, offset, isArray) {
super(bytes, offset, isArray);
this.ns = null;
this.batchSize = 0;
this.iterated = 0;
const cursor = this.get('cursor', bson_1.BSONType.object, true);
const id = cursor.get('id', bson_1.BSONType.long, true);
this.id = new bson_1.Long(Number(id & 0xffffffffn), Number((id >> 32n) & 0xffffffffn));
const namespace = cursor.get('ns', bson_1.BSONType.string);
if (namespace != null)
this.ns = (0, utils_1.ns)(namespace);
if (cursor.has('firstBatch'))
this.batch = cursor.get('firstBatch', bson_1.BSONType.array, true);
else if (cursor.has('nextBatch'))
this.batch = cursor.get('nextBatch', bson_1.BSONType.array, true);
else
throw new error_1.MongoUnexpectedServerResponseError('Cursor document did not contain a batch');
this.batchSize = this.batch.size();
}
get length() {
return Math.max(this.batchSize - this.iterated, 0);
}
shift(options) {
if (this.iterated >= this.batchSize) {
return null;
}
const result = this.batch.get(this.iterated, bson_1.BSONType.object, true) ?? null;
this.iterated += 1;
if (options?.raw) {
return result.toBytes();
}
else {
return result.toObject(options);
}
}
clear() {
this.iterated = this.batchSize;
}
pushMany() {
throw new Error('pushMany Unsupported method');
}
push() {
throw new Error('push Unsupported method');
}
}
/**
* This supports a feature of the FindCursor.
* It is an optimization to avoid an extra getMore when the limit has been reached
*/
CursorResponse.emptyGetMore = { id: new bson_1.Long(0), length: 0, shift: () => null };
exports.CursorResponse = CursorResponse;
//# sourceMappingURL=responses.js.map