@configurator/ravendb
Version:
RavenDB client for Node.js
203 lines • 9.37 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DatabaseSmuggler = void 0;
const StringUtil_1 = require("../../Utility/StringUtil");
const DatabaseSmugglerImportOptions_1 = require("./DatabaseSmugglerImportOptions");
const Exceptions_1 = require("../../Exceptions");
const HttpUtil_1 = require("../../Utility/HttpUtil");
const fs = require("fs");
const StreamUtil = require("../../Utility/StreamUtil");
const LengthUnawareFormData_1 = require("../../Utility/LengthUnawareFormData");
const path = require("path");
const BackupUtils_1 = require("./BackupUtils");
const OperationCompletionAwaiter_1 = require("../Operations/OperationCompletionAwaiter");
const GetNextOperationIdCommand_1 = require("../Commands/GetNextOperationIdCommand");
const RavenCommand_1 = require("../../Http/RavenCommand");
class DatabaseSmuggler {
constructor(store, databaseName) {
this._store = store;
this._databaseName = databaseName ?? store.database;
if (this._databaseName) {
this._requestExecutor = store.getRequestExecutor(this._databaseName);
}
else {
this._requestExecutor = null;
}
}
forDatabase(databaseName) {
if (StringUtil_1.StringUtil.equalsIgnoreCase(databaseName, this._databaseName)) {
return this;
}
return new DatabaseSmuggler(this._store, databaseName);
}
async export(options, toFileOrToDatabase) {
if (toFileOrToDatabase instanceof DatabaseSmuggler) {
const importOptions = new DatabaseSmugglerImportOptions_1.DatabaseSmugglerImportOptions(options);
return await this._export(options, async (response) => {
const importOperation = await toFileOrToDatabase.import(importOptions, response);
await importOperation.waitForCompletion();
});
}
else {
const directory = path.dirname(path.resolve(toFileOrToDatabase));
if (!fs.existsSync(directory)) {
fs.mkdirSync(directory, { recursive: true });
}
return await this._export(options, async (response) => {
const fileStream = fs.createWriteStream(toFileOrToDatabase);
await StreamUtil.pipelineAsync(response, fileStream);
});
}
}
async _export(options, handleStreamResponse) {
if (!options) {
(0, Exceptions_1.throwError)("InvalidArgumentException", "Options cannot be null");
}
if (!this._requestExecutor) {
(0, Exceptions_1.throwError)("InvalidOperationException", "Cannot use smuggler without a database defined, did you forget to call 'forDatabase'?");
}
const getOperationIdCommand = new GetNextOperationIdCommand_1.GetNextOperationIdCommand();
await this._requestExecutor.execute(getOperationIdCommand);
const operationId = getOperationIdCommand.result;
const command = new ExportCommand(this._requestExecutor.conventions, options, handleStreamResponse, operationId, getOperationIdCommand.nodeTag);
await this._requestExecutor.execute(command);
return new OperationCompletionAwaiter_1.OperationCompletionAwaiter(this._requestExecutor, this._requestExecutor.conventions, operationId, getOperationIdCommand.nodeTag);
}
async importIncremental(options, fromDirectory) {
const files = fs.readdirSync(fromDirectory)
.filter(x => BackupUtils_1.BackupUtils.BACKUP_FILE_SUFFIXES.includes("." + path.extname(x)))
.sort(BackupUtils_1.BackupUtils.comparator);
if (!files.length) {
return;
}
const oldOperateOnTypes = DatabaseSmuggler.configureOptionsFromIncrementalImport(options);
for (let i = 0; i < files.length - 1; i++) {
const filePath = files[i];
await this.import(options, path.resolve(filePath));
}
options.operateOnTypes = oldOperateOnTypes;
const lastFile = files.slice(-1).pop();
await this.import(options, path.resolve(lastFile));
}
static configureOptionsFromIncrementalImport(options) {
options.operateOnTypes.push("Tombstones");
options.operateOnTypes.push("CompareExchangeTombstones");
const oldOperateOnTypes = [...options.operateOnTypes];
options.operateOnTypes = options.operateOnTypes.filter(x => x !== "Indexes" && x !== "Subscriptions");
return oldOperateOnTypes;
}
async import(options, fileOrStream) {
if (typeof fileOrStream === "string") {
let countOfFileParts = 0;
let result;
let fromFile = fileOrStream;
do {
const fos = fs.createReadStream(fromFile);
result = await this._import(options, fos);
countOfFileParts++;
fromFile = StringUtil_1.StringUtil.format("{0}.part{1}", fromFile, countOfFileParts);
} while (fs.existsSync(fromFile));
return result;
}
else {
return await this._import(options, fileOrStream);
}
}
async _import(options, stream) {
if (!options) {
(0, Exceptions_1.throwError)("InvalidArgumentException", "Options cannot be null");
}
if (!stream) {
(0, Exceptions_1.throwError)("InvalidArgumentException", "Stream cannot be null");
}
if (!this._requestExecutor) {
(0, Exceptions_1.throwError)("InvalidOperationException", "Cannot use smuggler without a database defined, did you forget to call 'forDatabase'?");
}
const getOperationIdCommand = new GetNextOperationIdCommand_1.GetNextOperationIdCommand();
await this._requestExecutor.execute(getOperationIdCommand);
const operationId = getOperationIdCommand.result;
const command = new ImportCommand(this._requestExecutor.conventions, options, stream, operationId, getOperationIdCommand.nodeTag);
await this._requestExecutor.execute(command);
return new OperationCompletionAwaiter_1.OperationCompletionAwaiter(this._requestExecutor, this._requestExecutor.conventions, operationId, getOperationIdCommand.nodeTag);
}
}
exports.DatabaseSmuggler = DatabaseSmuggler;
class ExportCommand extends RavenCommand_1.RavenCommand {
constructor(conventions, options, handleStreamResponse, operationId, nodeTag) {
super();
if (!conventions) {
(0, Exceptions_1.throwError)("InvalidArgumentException", "Conventions cannot be null");
}
if (!options) {
(0, Exceptions_1.throwError)("InvalidArgumentException", "Options cannot be null");
}
if (!handleStreamResponse) {
(0, Exceptions_1.throwError)("InvalidArgumentException", "HandleStreamResponse cannot be null");
}
this._handleStreamResponse = handleStreamResponse;
const { operateOnTypes, ...restOptions } = options;
this._options = conventions.objectMapper.toObjectLiteral({
operateOnTypes: operateOnTypes.join(","),
...restOptions
});
this._operationId = operationId;
this._selectedNodeTag = nodeTag;
}
get isReadRequest() {
return false;
}
createRequest(node) {
const uri = node.url + "/databases/" + node.database + "/smuggler/export?operationId=" + this._operationId;
const body = this._serializer.serialize(this._options);
const headers = HttpUtil_1.HeadersBuilder.create()
.typeAppJson().build();
return {
method: "POST",
uri,
body,
headers
};
}
async processResponse(cache, response, bodyStream, url) {
await this._handleStreamResponse(bodyStream);
return "Automatic";
}
}
class ImportCommand extends RavenCommand_1.RavenCommand {
get isReadRequest() {
return false;
}
constructor(conventions, options, stream, operationId, nodeTag) {
super();
this._responseType = "Empty";
if (!stream) {
(0, Exceptions_1.throwError)("InvalidArgumentException", "Stream cannot be null");
}
if (!conventions) {
(0, Exceptions_1.throwError)("InvalidArgumentException", "Conventions cannot be null");
}
if (!options) {
(0, Exceptions_1.throwError)("InvalidArgumentException", "Options cannot be null");
}
this._stream = stream;
const { operateOnTypes, ...restOptions } = options;
this._options = conventions.objectMapper.toObjectLiteral({
operateOnTypes: operateOnTypes.join(","),
...restOptions
});
this._operationId = operationId;
this._selectedNodeTag = nodeTag;
}
createRequest(node) {
const uri = node.url + "/databases/" + node.database + "/smuggler/import?operationId=" + this._operationId;
const multipart = new LengthUnawareFormData_1.LengthUnawareFormData();
multipart.append("importOptions", this._serializer.serialize(this._options));
multipart.append("file", this._stream, { filename: "name" });
return {
method: "POST",
uri,
body: multipart,
};
}
}
//# sourceMappingURL=DatabaseSmuggler.js.map