UNPKG

ottoman

Version:
562 lines 24 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __asyncValues = (this && this.__asyncValues) || function (o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.searchQuery = exports.model = exports.getCollection = exports.getModel = exports.start = exports.close = exports.connect = exports.getOttomanInstances = exports.getDefaultInstance = exports.Ottoman = exports.__ottomanInstances = exports.__ottoman = void 0; const couchbase = __importStar(require("couchbase")); const extract_connection_string_1 = require("../utils/extract-connection-string"); const create_model_1 = require("../model/create-model"); const constants_1 = require("../utils/constants"); const __1 = require(".."); const is_debug_mode_1 = require("../utils/is-debug-mode"); const ensure_n1ql_indexes_1 = require("../model/index/n1ql/ensure-n1ql-indexes"); const build_map_view_index_fn_1 = require("../model/index/view/build-map-view-index-fn"); const ensure_view_indexes_1 = require("../model/index/view/ensure-view-indexes"); const ottoman_errors_1 = require("../exceptions/ottoman-errors"); const parse_errors_1 = require("../utils/parse-errors"); const couchbase_1 = require("couchbase"); const generate_uuid_1 = require("../utils/generate-uuid"); exports.__ottomanInstances = []; class Ottoman { /** * Retrieve all register callbacks to "IndexOnline" event */ get indexReadyHooks() { return this.events['IndexOnline']; } /** * Register Ottoman events * @param event the name of the event you want to listen to. * @param fn callback function to be executed when the event trigger up. */ on(event, fn) { switch (event) { case 'IndexOnline': this.events.IndexOnline.push(fn); if (this.indexOnline) { setTimeout(() => fn(this.indexOnline instanceof Error ? this.indexOnline : null, this)); } break; } } /** * CollectionManager allows the management of collections within a Bucket. * * Check the * [Collection Manager Couchbase SDK API](https://docs.couchbase.com/sdk-api/couchbase-node-client/classes/CollectionManager.html) * documentation for more details. */ get collectionManager() { return this.bucket.collections(); } /** * Gets a bucket manager for this cluster. * * Check the * [Bucket Manager Couchbase SDK API](https://docs.couchbase.com/sdk-api/couchbase-node-client/classes/BucketManager.html) * documentation for more details. */ get bucketManager() { return this.cluster.buckets(); } /** * QueryIndexManager provides an interface for managing the query indexes on the cluster. * * Check the * [Query Index Manager Couchbase SDK API](https://docs.couchbase.com/sdk-api/couchbase-node-client/classes/QueryIndexManager.html) * documentation for more details. */ get queryIndexManager() { return this.cluster.queryIndexes(); } /** * ViewIndexManager is an interface which enables the management of view indexes on the cluster. * * @deprecated * Check the * [View Index Manager Couchbase SDK API](https://docs.couchbase.com/sdk-api/couchbase-node-client/classes/ViewIndexManager.html) * documentation for more details. */ get viewIndexManager() { return this.bucket.viewIndexes(); } /** * Cluster represents an entire Couchbase Server cluster. */ get cluster() { return this._cluster; } constructor(config = {}) { this.n1qlIndexes = {}; this.viewIndexes = {}; this.refdocIndexes = {}; this.events = { IndexOnline: [], }; this.indexOnline = false; /** * @ignore */ this.getRefdocIndexByKey = (key) => this.refdocIndexes[key]; /** * @ignore */ this.registerIndex = (indexName, fields, modelName) => { this.n1qlIndexes[indexName] = { fields, modelName }; }; /** * @ignore */ this.registerViewIndex = (designDocName, indexName, fields, metadata) => { const map = (0, build_map_view_index_fn_1.buildMapViewIndexFn)(metadata, fields); if (!this.viewIndexes[designDocName]) { this.viewIndexes[designDocName] = { views: {} }; } this.viewIndexes[designDocName].views[indexName] = { map }; }; /** * @ignore */ this.registerRefdocIndex = (fields, prefix) => { if (!this.refdocIndexes[prefix]) { this.refdocIndexes[prefix] = []; } this.refdocIndexes[prefix].push({ fields }); }; /** * Dictionary for all models registered on this connection. */ this.models = {}; /** * Contains the name of the current bucket. */ this.bucketName = ''; /** * Connect to Couchbase server. * @example * ```javascript * import { connect } from "ottoman"; * const connection = connect("couchbase://localhost/travel-sample@admin:password"); * ``` */ this.connect = (connectOptions) => __awaiter(this, void 0, void 0, function* () { const options = typeof connectOptions === 'object' ? connectOptions : (0, extract_connection_string_1.extractConnectionString)(connectOptions); const { connectionString, bucketName } = options, _options = __rest(options, ["connectionString", "bucketName"]); this._cluster = yield couchbase_1.Cluster.connect(connectionString, _options); this.bucketName = bucketName; this.couchbase = couchbase; this.bucket = this._cluster.bucket(bucketName); return this; }); if (config.keyGeneratorDelimiter) { (0, constants_1.validateDelimiter)(config.keyGeneratorDelimiter); } this.id = (0, generate_uuid_1.generateUUID)(); this.config = config; if (!exports.__ottoman) { // eslint-disable-next-line @typescript-eslint/no-this-alias exports.__ottoman = this; } exports.__ottomanInstances.push(this); } /** * Creates a Model on this connection. * @example * ```javascript * const User = connection.model('User', { name: String }, { collectionName: 'users' }); * ``` */ model(name, schema, options = {}) { if (this.models[name]) { throw new ottoman_errors_1.OttomanError(`A model with name '${name}' has already been registered.`); } if (options.keyGeneratorDelimiter) { (0, constants_1.validateDelimiter)(options.keyGeneratorDelimiter); } const modelOptions = options; modelOptions.collectionName = options.collectionName || this.config.collectionName || name; modelOptions.scopeName = options.scopeName || this.config.scopeName || constants_1.DEFAULT_SCOPE; modelOptions.keyGenerator = options.keyGenerator || this.config.keyGenerator || constants_1.KEY_GENERATOR; if (options.keyGeneratorDelimiter || options.keyGeneratorDelimiter === '') { modelOptions.keyGeneratorDelimiter = options.keyGeneratorDelimiter; } else if (this.config.keyGeneratorDelimiter || this.config.keyGeneratorDelimiter === '') { modelOptions.keyGeneratorDelimiter = this.config.keyGeneratorDelimiter; } else { modelOptions.keyGeneratorDelimiter = constants_1.KEY_GENERATOR_DELIMITER; } modelOptions.modelKey = options.modelKey || this.config.modelKey || constants_1.MODEL_KEY; modelOptions.idKey = options.idKey || this.config.idKey || constants_1.DEFAULT_ID_KEY; modelOptions.maxExpiry = options.maxExpiry || this.config.maxExpiry || constants_1.DEFAULT_MAX_EXPIRY; const ModelFactory = (0, create_model_1.createModel)({ name, schemaDraft: schema, options: modelOptions, ottoman: this }); this.models[name] = ModelFactory; return ModelFactory; } /** * dropCollection drops a collection from a scope in a bucket. * @param collectionName * @param scopeName * @param options */ dropCollection(collectionName, scopeName, options = {}) { return __awaiter(this, void 0, void 0, function* () { try { return yield this.collectionManager.dropCollection(collectionName, scopeName, options); } catch (e) { (0, parse_errors_1.parseError)(e, { collectionName, scopeName }); } }); } /** * dropScope drops a scope from a bucket. * @param scopeName * @param options */ dropScope(scopeName, options = {}) { return __awaiter(this, void 0, void 0, function* () { try { return yield this.collectionManager.dropScope(scopeName, options); } catch (e) { (0, parse_errors_1.parseError)(e, { scopeName }); } }); } /** * dropBucket drops a bucket from the cluster. * @param bucketName * @param options */ dropBucket(bucketName, options = {}) { return __awaiter(this, void 0, void 0, function* () { try { return yield this.bucketManager.dropBucket(bucketName, options); } catch (e) { (0, parse_errors_1.parseError)(e, { bucketName }); } }); } /** * Returns a Model constructor from the given model name. * * @example * ```javascript * const User = connection.getModel('User'); * ``` */ getModel(name) { return this.models[name]; } /** * Return a collection from the given collectionName in this bucket * or default collection if collectionName is missing. */ getCollection(collectionName = constants_1.DEFAULT_COLLECTION, scopeName = constants_1.DEFAULT_SCOPE) { return this.bucket.scope(scopeName === '_default' ? '' : scopeName).collection(collectionName === '_default' ? '' : collectionName); } getIndexes() { return this.cluster.searchIndexes().getAllIndexes(); } searchQuery(indexName, query, options) { return this.cluster.searchQuery(indexName, query, options); } /** * Closes the current connection. * * @example * ```javascript * connection.close().then(() => console.log('connection closed')); * ``` */ close() { return __awaiter(this, void 0, void 0, function* () { if (this.cluster) { yield this.cluster.close(); exports.__ottomanInstances = exports.__ottomanInstances.filter((instance) => instance.id !== this.id); if ((exports.__ottoman === null || exports.__ottoman === void 0 ? void 0 : exports.__ottoman.id) === this.id) { exports.__ottoman = undefined; } } }); } /** * Executes N1QL Queries. * * Ottoman provides a powerful [Query Builder](docs/basic/query-builder) system to create valid N1QL queries using method chaining. * See the example below: * * @example * ```js * const expr_where = { * $or: [ * { address: { $like: '%57-59%' } }, * { free_breakfast: true } * ] * }; * const query = new Query({}, 'travel-sample'); * const n1qlQuery = query.select([{ $field: 'address' }]) * .where(expr_where) * .build() * * connection.query(n1qlQuery).then(result => console.log( result )) * ``` * The above example will run this query: * ```sql * SELECT address * FROM `travel-sample` * WHERE (address LIKE '%57-59%' OR free_breakfast = true) * ``` */ query(query, options = {}) { return __awaiter(this, void 0, void 0, function* () { if (options.transactionContext) { return options.transactionContext.query(query, options); } return this.cluster.query(query, options); }); } /** * `ensureCollections` will attempt to create scopes and collections to map your models to in Couchbase Server. */ ensureCollections() { var _a, e_1, _b, _c, _d, e_2, _e, _f; return __awaiter(this, void 0, void 0, function* () { const scopePromises = new Map(); const collectionPromises = new Map(); for (const key in this.models) { if (this.models[key]) { const metadata = (0, __1.getModelMetadata)(this.models[key]); const { scopeName, collectionName, maxExpiry } = metadata; if (scopeName !== '_default') { scopePromises.set(scopeName, this.collectionManager .createScope(scopeName) .then(() => { if ((0, is_debug_mode_1.isDebugMode)()) { console.info('Scope created: ', scopeName); } }) .catch((e) => { if (!(e instanceof couchbase.ScopeExistsError)) { throw e; } })); } if (collectionName !== '_default') { collectionPromises.set(`${scopeName}${collectionName}`, tryCreateCollection({ collectionName, scopeName, maxExpiry, collectionManager: this.collectionManager }, this.config.retryCount)); } } } try { // eslint-disable-next-line no-unused-vars for (var _g = true, _h = __asyncValues(scopePromises.values()), _j; _j = yield _h.next(), _a = _j.done, !_a;) { _c = _j.value; _g = false; try { const scope = _c; } finally { _g = true; } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (!_g && !_a && (_b = _h.return)) yield _b.call(_h); } finally { if (e_1) throw e_1.error; } } try { // eslint-disable-next-line no-unused-vars for (var _k = true, _l = __asyncValues(collectionPromises.values()), _m; _m = yield _l.next(), _d = _m.done, !_d;) { _f = _m.value; _k = false; try { const collection = _f; } finally { _k = true; } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (!_k && !_d && (_e = _l.return)) yield _e.call(_l); } finally { if (e_2) throw e_2.error; } } }); } /** * `ensureIndexes` will attempt to create indexes defined in your schema if they do not exist. * * @param options * - `ignoreWatchIndexes`: by default `ensureIndexes` function will wait for indexes, but you can disabled it setting ignoreWatchIndexes to true. */ ensureIndexes(options = {}) { return __awaiter(this, void 0, void 0, function* () { yield (0, ensure_n1ql_indexes_1.ensureN1qlIndexes)(this, this.n1qlIndexes); yield (0, ensure_view_indexes_1.ensureViewIndexes)(this, this.viewIndexes); if (!options.ignoreWatchIndexes && this.indexOnlinePromise) { return this.indexOnlinePromise; } }); } /** * `start` method is just a shortcut to run `ensureCollections` and `ensureIndexes`. * @param options * - `ignoreWatchIndexes`: by default `start` function will wait for indexes, but you can disabled it setting ignoreWatchIndexes to true. * * Notice: It's not required to execute the `start` method in order for Ottoman work. */ start(options = {}) { return __awaiter(this, void 0, void 0, function* () { yield this.ensureCollections(); yield this.ensureIndexes(options); }); } /** * The `$transactions` function passes a `TransactionAttemptContext` object to the transaction function. * @param transactionFn The function to be executed as a transaction. * * @param config * - `durabilityLevel`: Specifies the level of synchronous durability level. * - `timeout`: Specifies the timeout for the transaction. * * @example * ```javascript * await otttoman.$transactions(async (ctx: TransactionAttemptContext) => { * const user = await User.create({ name: "John Doe" }, { transactionContext: ctx }); * }); * ``` * * Notice: You **MUST pass the `transactionContext` option in the model methods** to execute the operation(s) as a transaction. */ $transactions(transactionFn, config) { return __awaiter(this, void 0, void 0, function* () { if (Object.keys(this.refdocIndexes).length > 0) { console.warn('\x1b[4m\x1b[43mWARNING:\x1b[0m One or more RefDoc indexes have been detected in this project. RefDoc indexes are not currently supported in transactions. \nFor more information visit https://ottomanjs.com/docs/advanced/transactions#transactions-with-refdoc-indexes.'); } yield this.cluster.transactions().run(transactionFn, config); }); } } exports.Ottoman = Ottoman; const delay = (timems) => new Promise((resolve) => { setTimeout(() => { resolve(true); }, timems); }); const createCollection = ({ collectionManager, collectionName, scopeName, maxExpiry }) => { return collectionManager .createCollection({ name: collectionName, scopeName, maxExpiry: maxExpiry ? +maxExpiry : constants_1.DEFAULT_MAX_EXPIRY, }) .then(() => { if ((0, is_debug_mode_1.isDebugMode)()) { console.info(`collection created: ${scopeName}/${collectionName}`); } }); }; const tryCreateCollection = ({ collectionManager, collectionName, scopeName, maxExpiry }, retries = 3, delayMS = 5000) => __awaiter(void 0, void 0, void 0, function* () { let collectionDoesNotExists = true; let err = null; let tryDelay = 0; let tryCount = 0; while (collectionDoesNotExists && tryCount < retries + 1) { yield delay(tryDelay); try { yield createCollection({ collectionManager, collectionName, scopeName, maxExpiry }); } catch (e) { if (e instanceof couchbase.CollectionExistsError) collectionDoesNotExists = false; err = e; } tryDelay += Math.max(Math.min(tryDelay * 10, delayMS), 100); tryCount++; } if (collectionDoesNotExists && err) throw err; }); const getDefaultInstance = () => exports.__ottoman; exports.getDefaultInstance = getDefaultInstance; const getOttomanInstances = () => exports.__ottomanInstances; exports.getOttomanInstances = getOttomanInstances; const connect = (connectOptions) => __awaiter(void 0, void 0, void 0, function* () { if (!exports.__ottoman) { new Ottoman(); } exports.__ottoman && (yield exports.__ottoman.connect(connectOptions)); }); exports.connect = connect; const close = () => __awaiter(void 0, void 0, void 0, function* () { if (exports.__ottoman) { yield exports.__ottoman.close(); } }); exports.close = close; const start = () => exports.__ottoman === null || exports.__ottoman === void 0 ? void 0 : exports.__ottoman.start(); exports.start = start; const getModel = (name) => exports.__ottoman === null || exports.__ottoman === void 0 ? void 0 : exports.__ottoman.getModel(name); exports.getModel = getModel; const getCollection = (collectionName = constants_1.DEFAULT_COLLECTION, scopeName = constants_1.DEFAULT_SCOPE) => exports.__ottoman === null || exports.__ottoman === void 0 ? void 0 : exports.__ottoman.getCollection(collectionName, scopeName); exports.getCollection = getCollection; const model = (name, schema, options) => exports.__ottoman === null || exports.__ottoman === void 0 ? void 0 : exports.__ottoman.model(name, schema, options); exports.model = model; const searchQuery = (indexName, query, options) => exports.__ottoman === null || exports.__ottoman === void 0 ? void 0 : exports.__ottoman.searchQuery(indexName, query, options); exports.searchQuery = searchQuery; //# sourceMappingURL=ottoman.js.map