258 lines
9.7 KiB
JavaScript
258 lines
9.7 KiB
JavaScript
"use strict";
|
|
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 __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.Cache = void 0;
|
|
/**
|
|
* Storage cache
|
|
* @module multer-gridfs-storage/cache
|
|
*/
|
|
const events_1 = require("events");
|
|
const mongodb_uri_1 = __importDefault(require("mongodb-uri"));
|
|
const utils_1 = require("./utils");
|
|
/**
|
|
* Plugin cached connection handling class.
|
|
* @version 3.1.0
|
|
*/
|
|
class Cache {
|
|
constructor() {
|
|
this.store = new Map();
|
|
this.emitter = new events_1.EventEmitter();
|
|
this.emitter.setMaxListeners(0);
|
|
}
|
|
/**
|
|
* Handles creating a new connection from an url and caching if necessary
|
|
* @param {object} options - Options to initialize the cache
|
|
* @param {string} options.url - The url to cache
|
|
* @param {string} options.cacheName - The name of the cache to use
|
|
* @param {any} options.init - The connection options provided
|
|
**/
|
|
initialize(options) {
|
|
let { url, cacheName: name } = options;
|
|
// If the option is a falsey value or empty object use null as initial value
|
|
const init = utils_1.compare(options.init, null) ? null : options.init;
|
|
// If a cache under that name does not exist create one
|
|
if (!this.store.has(name)) {
|
|
this.store.set(name, new Map());
|
|
}
|
|
// Check if the url has been used for that cache before
|
|
let cached = this.store.get(name).get(url);
|
|
if (!this.store.get(name).has(url)) {
|
|
// If the url matches any equivalent url used before use that connection instead
|
|
const eqUrl = this.findUri(name, url);
|
|
if (!eqUrl) {
|
|
const store = new Map();
|
|
store.set(0, {
|
|
db: null,
|
|
client: null,
|
|
pending: true,
|
|
opening: false,
|
|
init,
|
|
});
|
|
this.store.get(name).set(url, store);
|
|
return {
|
|
url,
|
|
name,
|
|
index: 0,
|
|
};
|
|
}
|
|
url = eqUrl;
|
|
cached = this.store.get(name).get(url);
|
|
}
|
|
// Compare connection options to create more only if they are semantically different
|
|
for (const [index, value] of cached) {
|
|
if (utils_1.compare(value.init, options.init)) {
|
|
return {
|
|
url,
|
|
name,
|
|
index,
|
|
};
|
|
}
|
|
}
|
|
cached.set(cached.size, {
|
|
db: null,
|
|
client: null,
|
|
pending: true,
|
|
opening: false,
|
|
init,
|
|
});
|
|
return {
|
|
url,
|
|
name,
|
|
index: cached.size - 1,
|
|
};
|
|
}
|
|
/**
|
|
* Search the cache for a space stored under an equivalent url.
|
|
*
|
|
* Just swapping parameters can cause two url to be deemed different when in fact they are not.
|
|
* This method finds an url in the cache where another url could be stored even when they are not strictly equal
|
|
* @param cacheName The name of the cache to search
|
|
* @param url The mongodb url to compare
|
|
* @return The similar url already in the cache
|
|
*/
|
|
findUri(cacheName, url) {
|
|
for (const [storedUrl] of this.store.get(cacheName)) {
|
|
const parsedUri = mongodb_uri_1.default.parse(storedUrl);
|
|
const parsedCache = mongodb_uri_1.default.parse(url);
|
|
if (utils_1.compareUris(parsedUri, parsedCache)) {
|
|
return storedUrl;
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Returns true if the cache has an entry matching the given index
|
|
* @param cacheIndex The index to look for
|
|
* @return Returns if the cache was found
|
|
*/
|
|
has(cacheIndex) {
|
|
return Boolean(this.get(cacheIndex));
|
|
}
|
|
/**
|
|
* Returns the contents of the cache in a given index
|
|
* @param cacheIndex {object} The index to look for
|
|
* @return {object} The cache contents or null if was not found
|
|
*/
|
|
get(cacheIndex) {
|
|
const { name, url, index } = cacheIndex;
|
|
if (!this.store.has(name)) {
|
|
return null;
|
|
}
|
|
if (!this.store.get(name).has(url)) {
|
|
return null;
|
|
}
|
|
if (!this.store.get(name).get(url).has(index)) {
|
|
return null;
|
|
}
|
|
return this.store.get(name).get(url).get(index);
|
|
}
|
|
/**
|
|
* Sets the contents of the cache in a given index
|
|
* @param cacheIndex The index to look for
|
|
* @param value The value to set
|
|
*/
|
|
set(cacheIndex, value) {
|
|
const { name, url, index } = cacheIndex;
|
|
this.store.get(name).get(url).set(index, value);
|
|
}
|
|
/**
|
|
* Returns true if a given cache is resolving its associated connection
|
|
* @param cacheIndex {object} The index to look for
|
|
* @return Return true if the connection is not found yet
|
|
*/
|
|
isPending(cacheIndex) {
|
|
const cached = this.get(cacheIndex);
|
|
return Boolean(cached) && cached.pending;
|
|
}
|
|
/**
|
|
* Return true if a given cache started resolving a connection for itself
|
|
* @param cacheIndex {object} The index to look for
|
|
* @return Return true if no instances have started creating a connection for this cache
|
|
*/
|
|
isOpening(cacheIndex) {
|
|
const cached = this.get(cacheIndex);
|
|
return Boolean(cached === null || cached === void 0 ? void 0 : cached.opening);
|
|
}
|
|
/**
|
|
* Sets the database and client for a given cache and resolves all instances waiting for it
|
|
* @param cacheIndex {object} The index to look for
|
|
* @param db The database used to store files
|
|
* @param [client] The client used to open the connection or null if none is provided
|
|
*/
|
|
resolve(cacheIndex, db, client) {
|
|
const cached = this.get(cacheIndex);
|
|
cached.db = db;
|
|
cached.client = client;
|
|
cached.pending = false;
|
|
cached.opening = false;
|
|
this.emitter.emit('resolve', cacheIndex);
|
|
}
|
|
/**
|
|
* Rejects all instances waiting for this connections
|
|
* @param cacheIndex The index to look for
|
|
* @param err The error thrown by the driver
|
|
*/
|
|
reject(cacheIndex, error) {
|
|
const cached = this.get(cacheIndex);
|
|
cached.pending = false;
|
|
this.emitter.emit('reject', cacheIndex, error);
|
|
this.remove(cacheIndex);
|
|
}
|
|
/**
|
|
* Allows waiting for a connection associated to a given cache
|
|
* @param cacheIndex The index to look for
|
|
* @return A promise that will resolve when the connection for this cache is created
|
|
*/
|
|
waitFor(cacheIndex) {
|
|
return __awaiter(this, void 0, void 0, function* () {
|
|
if (!this.isPending(cacheIndex) && !this.isOpening(cacheIndex)) {
|
|
return this.get(cacheIndex);
|
|
}
|
|
return new Promise((resolve, reject) => {
|
|
const _resolve = (index) => {
|
|
if (utils_1.compare(cacheIndex, index)) {
|
|
this.emitter.removeListener('resolve', _resolve);
|
|
this.emitter.removeListener('reject', _reject);
|
|
resolve(this.get(cacheIndex));
|
|
}
|
|
};
|
|
const _reject = (index, error) => {
|
|
if (utils_1.compare(cacheIndex, index)) {
|
|
this.emitter.removeListener('resolve', _resolve);
|
|
this.emitter.removeListener('reject', _reject);
|
|
reject(error);
|
|
}
|
|
};
|
|
this.emitter.on('resolve', _resolve);
|
|
this.emitter.on('reject', _reject);
|
|
});
|
|
});
|
|
}
|
|
/**
|
|
* Gives the number of connections created by all cache instances
|
|
* @return {number} The number of created connections
|
|
*/
|
|
connections() {
|
|
let total = 0;
|
|
for (const urlStore of this.store.values()) {
|
|
for (const store of urlStore.values()) {
|
|
total += store.size;
|
|
}
|
|
}
|
|
return total;
|
|
}
|
|
/**
|
|
* Removes a cache entry.
|
|
*
|
|
* > If the cache hasn't resolved yet it will be rejected.
|
|
* @param cacheIndex The index to look for
|
|
*/
|
|
remove(cacheIndex) {
|
|
if (this.has(cacheIndex)) {
|
|
if (this.isPending(cacheIndex)) {
|
|
this.emitter.emit('reject', cacheIndex, new Error('The cache entry was deleted'));
|
|
}
|
|
const { name, url, index } = cacheIndex;
|
|
this.store.get(name).get(url).delete(index);
|
|
}
|
|
}
|
|
/**
|
|
* Removes all entries in the cache and all listeners
|
|
*/
|
|
clear() {
|
|
this.store = new Map();
|
|
this.emitter.removeAllListeners();
|
|
}
|
|
}
|
|
exports.Cache = Cache;
|
|
//# sourceMappingURL=cache.js.map
|