ravendb
Version:
RavenDB client for Node.js
211 lines • 8.17 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MultiGetCommand = void 0;
const RavenCommand_js_1 = require("../../../Http/RavenCommand.js");
const GetResponse_js_1 = require("./GetResponse.js");
const StatusCode_js_1 = require("../../../Http/StatusCode.js");
const HttpUtil_js_1 = require("../../../Utility/HttpUtil.js");
const index_js_1 = require("../../../Exceptions/index.js");
const Constants_js_1 = require("../../../Constants.js");
const ObjectUtil_js_1 = require("../../../Utility/ObjectUtil.js");
class MultiGetCommand extends RavenCommand_js_1.RavenCommand {
_requestExecutor;
_httpCache;
_commands;
_sessionInfo;
_conventions;
_baseUrl;
_cached;
aggressivelyCached;
constructor(requestExecutor, conventions, commands, sessionInfo) {
super();
this._requestExecutor = requestExecutor;
if (!requestExecutor) {
(0, index_js_1.throwError)("InvalidArgumentException", "RequestExecutor cannot be null");
}
this._httpCache = requestExecutor.cache;
if (!commands) {
(0, index_js_1.throwError)("InvalidArgumentException", "Commands cannot be null");
}
this._commands = commands;
this._conventions = conventions;
this._sessionInfo = sessionInfo;
this._responseType = "Raw";
}
_getCacheKey(command) {
const url = this._baseUrl + command.urlAndQuery;
return (command.method || "GET") + "-" + url;
}
createRequest(node) {
this._baseUrl = node.url + "/databases/" + node.database;
const requests = [];
const bodyObj = { Requests: requests };
const request = {
uri: this._baseUrl + "/multi_get",
method: "POST",
headers: this._headers().typeAppJson().build(),
};
if ((!this._sessionInfo || !this._sessionInfo.noCaching) && this._maybeReadAllFromCache(this._requestExecutor.aggressiveCaching)) {
this.aggressivelyCached = true;
return null; // aggressively cached
}
for (const command of this._commands) {
const req = {
Url: "/databases/" + node.database + command.url,
Query: command.query,
Method: command.method || "GET",
Headers: command.headers,
Content: command.body
};
requests.push(req);
}
request.body = JSON.stringify(bodyObj);
return request;
}
_maybeReadAllFromCache(options) {
this.closeCache();
let readAllFromCache = !!options;
for (let i = 0; i < this._commands.length; i++) {
const command = this._commands[i];
if (Constants_js_1.HEADERS.IF_NONE_MATCH in command.headers) {
continue; // command already explicitly handling setting this, let's not touch it.
}
const cacheKey = this._getCacheKey(command);
let changeVector;
let cachedRef;
const cachedItem = this._httpCache.get(cacheKey, c => {
changeVector = c.changeVector;
cachedRef = c.response;
});
if (!cachedItem.item) {
readAllFromCache = false;
continue;
}
if (readAllFromCache && cachedItem.age > options.duration || !command.canCacheAggressively) {
readAllFromCache = false;
}
command.headers[Constants_js_1.HEADERS.IF_NONE_MATCH] = changeVector;
if (!this._cached) {
this._cached = new Cached(this._commands.length);
}
this._cached.values[i] = [cachedItem, cachedRef];
}
if (readAllFromCache) {
try {
this.result = [];
for (let i = 0; i < this._commands.length; i++) {
const itemAndCached = this._cached.values[i];
const getResponse = new GetResponse_js_1.GetResponse();
getResponse.result = itemAndCached[1];
getResponse.statusCode = StatusCode_js_1.StatusCodes.NotModified;
this.result.push(getResponse);
}
}
finally {
this._cached.dispose();
}
this._cached = null;
}
return readAllFromCache;
}
async setResponseAsync(bodyStream, fromCache) {
if (!bodyStream) {
this._throwInvalidResponse();
}
try {
const result = await this._pipeline()
.parseJsonSync()
.process(bodyStream);
const responses = result.Results.map(item => MultiGetCommand._mapToLocalObject(item));
this.result = [];
for (let i = 0; i < responses.length; i++) {
const res = responses[i];
const command = this._commands[i];
this._maybeSetCache(res, command, i);
if (this._cached && res.statusCode === StatusCode_js_1.StatusCodes.NotModified) {
const clonedResponse = new GetResponse_js_1.GetResponse();
clonedResponse.result = this._cached.values[i][1];
clonedResponse.statusCode = StatusCode_js_1.StatusCodes.NotModified;
this.result.push(clonedResponse);
}
else {
this.result.push(GetResponse_js_1.GetResponse.create(res));
}
}
return null;
}
finally {
if (this._cached) {
this._cached.dispose();
}
}
}
_maybeSetCache(getResponse, command, cachedIndex) {
if (getResponse.statusCode === StatusCode_js_1.StatusCodes.NotModified) {
// if not modified - update age
if (this._cached) {
this._cached.values[cachedIndex][0].notModified();
}
return;
}
const cacheKey = this._getCacheKey(command);
const result = getResponse.result;
if (!result) {
this._httpCache.setNotFound(cacheKey);
return;
}
const changeVector = (0, HttpUtil_js_1.getEtagHeader)(getResponse.headers);
if (!changeVector) {
return;
}
this._httpCache.set(cacheKey, changeVector, result);
}
get isReadRequest() {
return false;
}
dispose() {
this.closeCache();
}
closeCache() {
//If _cached is not null - it means that the client approached with this multitask request to node and the request failed.
//and now client tries to send it to another node.
if (this._cached) {
this._cached.dispose();
this._cached = null;
// The client sends the commands.
// Some of which could be saved in cache with a response
// that includes the change vector that received from the old fallen node.
// The client can't use those responses because their URLs are different
// (include the IP and port of the old node), because of that the client
// needs to get those docs again from the new node.
for (const command of this._commands) {
delete command.headers[Constants_js_1.HEADERS.IF_NONE_MATCH];
}
}
}
static _mapToLocalObject(json) {
// convert from Pascal to camel on top level only
const item = {};
for (const [key, value] of Object.entries(json)) {
item[ObjectUtil_js_1.ObjectUtil.camelCase(key)] = value;
}
item.result = item.result ? JSON.stringify(item.result) : null;
return item;
}
}
exports.MultiGetCommand = MultiGetCommand;
class Cached {
_size;
values;
constructor(size) {
this._size = size;
this.values = new Array(size);
}
dispose() {
if (!this.values) {
return;
}
this.values = null;
}
}
//# sourceMappingURL=MultiGetCommand.js.map