bitcore-node
Version:
A blockchain indexing node with extended capabilities using bitcore
246 lines • 9.16 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Storage = exports.StorageService = void 0;
const events_1 = require("events");
const mongodb_1 = require("mongodb");
const mongodb_2 = require("mongodb");
const Loggify_1 = require("../decorators/Loggify");
const logger_1 = __importDefault(require("../logger"));
require("../models");
const utils_1 = require("../utils");
const config_1 = require("./config");
let StorageService = class StorageService {
constructor({ configService = config_1.Config } = {}) {
this.connected = false;
this.connection = new events_1.EventEmitter();
this.modelsConnected = new Array();
this.configService = configService;
this.connection.setMaxListeners(30);
}
start(args = {}) {
return new Promise((resolve, reject) => {
let options = Object.assign({}, this.configService.get(), args);
let { dbUrl, dbName, dbHost, dbPort, dbUser, dbPass, dbReadPreference } = options;
let auth = dbUser !== '' && dbPass !== '' ? `${dbUser}:${dbPass}@` : '';
const connectUrl = dbUrl
? dbUrl
: `mongodb://${auth}${dbHost}:${dbPort}/${dbName}?socketTimeoutMS=3600000&noDelay=true${dbReadPreference ? `?readPreference=${dbReadPreference}` : ''}`;
let attemptConnect = async () => {
return mongodb_2.MongoClient.connect(connectUrl, {
keepAlive: true,
poolSize: options.maxPoolSize,
useNewUrlParser: true
});
};
let attempted = 0;
let attemptConnectId = setInterval(async () => {
try {
this.client = await attemptConnect();
this.db = this.client.db(dbName);
this.connected = true;
clearInterval(attemptConnectId);
this.connection.emit('CONNECTED');
resolve(this.client);
}
catch (err) {
logger_1.default.error('%o', err);
attempted++;
if (attempted > 5) {
clearInterval(attemptConnectId);
reject(new Error('Failed to connect to database'));
}
}
}, 5000);
});
}
async stop() {
if (this.client) {
logger_1.default.info('Stopping Storage Service');
await (0, utils_1.wait)(5000);
this.connected = false;
await Promise.all(this.modelsConnected);
await this.client.close();
this.connection.emit('DISCONNECTED');
}
}
validPagingProperty(model, property) {
const defaultCase = property === '_id';
return defaultCase || model.allowedPaging.some(prop => prop.key === property);
}
/**
* castForDb
*
* For a given model, return the typecasted value based on a key and the type associated with that key
*/
typecastForDb(model, modelKey, modelValue) {
let typecastedValue = modelValue;
if (modelKey) {
let oldValue = modelValue;
let optionsType = model.allowedPaging.find(prop => prop.key === modelKey);
if (optionsType) {
switch (optionsType.type) {
case 'number':
typecastedValue = Number(oldValue);
break;
case 'string':
typecastedValue = (oldValue || '').toString();
break;
case 'date':
typecastedValue = new Date(oldValue);
break;
}
}
else if (modelKey == '_id') {
typecastedValue = new mongodb_1.ObjectID(oldValue);
}
}
return typecastedValue;
}
stream(input, req, res) {
let closed = false;
req.on('close', function () {
closed = true;
});
res.on('close', function () {
closed = true;
});
input.on('error', function (err) {
if (!closed) {
closed = true;
return res.status(500).end(err.message);
}
return;
});
let isFirst = true;
res.type('json');
input.on('data', function (data) {
if (!closed) {
if (isFirst) {
res.write('[\n');
isFirst = false;
}
else {
res.write(',\n');
}
res.write(JSON.stringify(data));
}
});
input.on('end', function () {
if (!closed) {
if (isFirst) {
// there was no data
res.write('[]');
}
else {
res.write('\n]');
}
res.end();
}
});
}
apiStream(cursor, req, res) {
let closed = false;
req.on('close', function () {
closed = true;
cursor.close();
});
res.on('close', function () {
closed = true;
cursor.close();
});
cursor.on('error', function (err) {
if (!closed) {
closed = true;
return res.status(500).end(err.message);
}
return;
});
let isFirst = true;
res.type('json');
cursor.on('data', function (data) {
if (!closed) {
if (isFirst) {
res.write('[\n');
isFirst = false;
}
else {
res.write(',\n');
}
res.write(data);
}
else {
cursor.close();
}
});
cursor.on('end', function () {
if (!closed) {
if (isFirst) {
// there was no data
res.write('[]');
}
else {
res.write('\n]');
}
res.end();
}
});
}
getFindOptions(model, originalOptions) {
let query = {};
let since = null;
let options = {};
if (originalOptions.sort) {
options.sort = originalOptions.sort;
}
if (originalOptions.paging && this.validPagingProperty(model, originalOptions.paging)) {
if (originalOptions.since !== undefined) {
since = this.typecastForDb(model, originalOptions.paging, originalOptions.since);
}
if (originalOptions.direction && Number(originalOptions.direction) === 1) {
if (since) {
query[originalOptions.paging] = { $gt: since };
}
options.sort = Object.assign({}, originalOptions.sort, { [originalOptions.paging]: 1 });
}
else {
if (since) {
query[originalOptions.paging] = { $lt: since };
}
options.sort = Object.assign({}, originalOptions.sort, { [originalOptions.paging]: -1 });
}
}
if (originalOptions.limit) {
options.limit = Number(originalOptions.limit);
}
return { query, options };
}
apiStreamingFind(model, originalQuery, originalOptions, req, res, transform) {
const { query, options } = this.getFindOptions(model, originalOptions);
const finalQuery = Object.assign({}, originalQuery, query);
let cursor = model.collection
.find(finalQuery, options)
.addCursorFlag('noCursorTimeout', true)
.stream({
transform: transform || model._apiTransform.bind(model)
});
if (options.sort) {
cursor = cursor.sort(options.sort);
}
return this.apiStream(cursor, req, res);
}
};
exports.StorageService = StorageService;
exports.StorageService = StorageService = __decorate([
Loggify_1.LoggifyClass
], StorageService);
exports.Storage = new StorageService();
//# sourceMappingURL=storage.js.map