Blame view
node_modules/loopback/common/models/key-value-model.js
7.1 KB
f7563de62
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
var g = require('strong-globalize')(); /** * Data model for key-value databases. * * @class KeyValueModel * @inherits {Model} */ module.exports = function(KeyValueModel) { /** * Return the value associated with a given key. * * @param {String} key Key to use when searching the database. * @options {Object} options * @callback {Function} callback * @param {Error} err Error object. * @param {Any} result Value associated with the given key. * @promise * * @header KeyValueModel.get(key, cb) */ KeyValueModel.get = function(key, options, callback) { throwNotAttached(this.modelName, 'get'); }; /** * Persist a value and associate it with the given key. * * @param {String} key Key to associate with the given value. * @param {Any} value Value to persist. * @options {Number|Object} options Optional settings for the key-value * pair. If a Number is provided, it is set as the TTL (time to live) in ms * (milliseconds) for the key-value pair. * @property {Number} ttl TTL for the key-value pair in ms. * @callback {Function} callback * @param {Error} err Error object. * @promise * * @header KeyValueModel.set(key, value, cb) */ KeyValueModel.set = function(key, value, options, callback) { throwNotAttached(this.modelName, 'set'); }; /** * Set the TTL (time to live) in ms (milliseconds) for a given key. TTL is the * remaining time before a key-value pair is discarded from the database. * * @param {String} key Key to use when searching the database. * @param {Number} ttl TTL in ms to set for the key. * @options {Object} options * @callback {Function} callback * @param {Error} err Error object. * @promise * * @header KeyValueModel.expire(key, ttl, cb) */ KeyValueModel.expire = function(key, ttl, options, callback) { throwNotAttached(this.modelName, 'expire'); }; /** * Return the TTL (time to live) for a given key. TTL is the remaining time * before a key-value pair is discarded from the database. * * @param {String} key Key to use when searching the database. * @options {Object} options * @callback {Function} callback * @param {Error} error * @param {Number} ttl Expiration time for the key-value pair. `undefined` if * TTL was not initially set. * @promise * * @header KeyValueModel.ttl(key, cb) */ KeyValueModel.ttl = function(key, options, callback) { throwNotAttached(this.modelName, 'ttl'); }; /** * Return all keys in the database. * * **WARNING**: This method is not suitable for large data sets as all * key-values pairs are loaded into memory at once. For large data sets, * use `iterateKeys()` instead. * * @param {Object} filter An optional filter object with the following * @param {String} filter.match Glob string used to filter returned * keys (i.e. `userid.*`). All connectors are required to support `*` and * `?`, but may also support additional special characters specific to the * database. * @param {Object} options * @callback {Function} callback * @promise * * @header KeyValueModel.keys(filter, cb) */ KeyValueModel.keys = function(filter, options, callback) { throwNotAttached(this.modelName, 'keys'); }; /** * Asynchronously iterate all keys in the database. Similar to `.keys()` but * instead allows for iteration over large data sets without having to load * everything into memory at once. * * Callback example: * ```js * // Given a model named `Color` with two keys `red` and `blue` * var iterator = Color.iterateKeys(); * it.next(function(err, key) { * // key contains `red` * it.next(function(err, key) { * // key contains `blue` * }); * }); * ``` * * Promise example: * ```js * // Given a model named `Color` with two keys `red` and `blue` * var iterator = Color.iterateKeys(); * Promise.resolve().then(function() { * return it.next(); * }) * .then(function(key) { * // key contains `red` * return it.next(); * }); * .then(function(key) { * // key contains `blue` * }); * ``` * * @param {Object} filter An optional filter object with the following * @param {String} filter.match Glob string to use to filter returned * keys (i.e. `userid.*`). All connectors are required to support `*` and * `?`. They may also support additional special characters that are * specific to the backing database. * @param {Object} options * @returns {AsyncIterator} An Object implementing `next(cb) -> Promise` * function that can be used to iterate all keys. * * @header KeyValueModel.iterateKeys(filter) */ KeyValueModel.iterateKeys = function(filter, options) { throwNotAttached(this.modelName, 'iterateKeys'); }; /*! * Set up remoting metadata for this model. * * **Notes**: * - The method is called automatically by `Model.extend` and/or * `app.registry.createModel` * - In general, base models use call this to ensure remote methods are * inherited correctly, see bug at * https://github.com/strongloop/loopback/issues/2350 */ KeyValueModel.setup = function() { KeyValueModel.base.setup.apply(this, arguments); this.remoteMethod('get', { accepts: { arg: 'key', type: 'string', required: true, http: { source: 'path' }, }, returns: { arg: 'value', type: 'any', root: true }, http: { path: '/:key', verb: 'get' }, rest: { after: convertNullToNotFoundError }, }); this.remoteMethod('set', { accepts: [ { arg: 'key', type: 'string', required: true, http: { source: 'path' }}, { arg: 'value', type: 'any', required: true, http: { source: 'body' }}, { arg: 'ttl', type: 'number', http: { source: 'query' }, description: 'time to live in milliseconds' }, ], http: { path: '/:key', verb: 'put' }, }); this.remoteMethod('expire', { accepts: [ { arg: 'key', type: 'string', required: true, http: { source: 'path' }}, { arg: 'ttl', type: 'number', required: true, http: { source: 'form' }}, ], http: { path: '/:key/expire', verb: 'put' }, }); this.remoteMethod('keys', { accepts: { arg: 'filter', type: 'object', required: false, http: { source: 'query' }, }, returns: { arg: 'keys', type: ['string'], root: true }, http: { path: '/keys', verb: 'get' }, }); }; }; function throwNotAttached(modelName, methodName) { throw new Error(g.f( 'Cannot call %s.%s(). ' + 'The %s method has not been setup. ' + 'The {{KeyValueModel}} has not been correctly attached ' + 'to a {{DataSource}}!', modelName, methodName, methodName)); } function convertNullToNotFoundError(ctx, cb) { if (ctx.result !== null) return cb(); var modelName = ctx.method.sharedClass.name; var id = ctx.getArgByName('id'); var msg = g.f('Unknown "%s" {{key}} "%s".', modelName, id); var error = new Error(msg); error.statusCode = error.status = 404; error.code = 'KEY_NOT_FOUND'; cb(error); } |