Comparing version 0.19.2 to 0.20.0
@@ -0,1 +1,38 @@ | ||
## 0.20.0 (2016-12-20) | ||
### Features | ||
- Set request context when calling cloud code (#143) | ||
- Support unregister device (SkygearIO/skygear-server#245, SkygearIO/skygear-server#249) | ||
- Support union database for cloud code development | ||
- Export SkygearRequest and SkygearResponse | ||
- Add poolConnect to select the app's database schema | ||
- Implement server unknown type (SkygearIO/skygear-server#231) | ||
- Make the cache become LRU cache (#130, #128) | ||
### Bug Fixes | ||
- Fix binary static asset encoding | ||
The existing implementation cannot send binary static asset through | ||
a handler. The new static asset handler returns a buffer so that | ||
that the transport layer uses the buffer's base64 encoding to fix this problem. | ||
- Fix not able to create Record with `id` attr | ||
The constructor of Record prefers `id` to pass record ID, but it is not | ||
possible to use this attr as the update function will set the record ID | ||
to `id` property, which is readonly. | ||
Not having this change will result in TypeError: | ||
``` | ||
TypeError: Cannot set property id of #<Record> which has only a getter | ||
``` | ||
### Other Notes | ||
- Fix nodedev Dockerfile working directory | ||
## 0.19.2 (2016-11-19) | ||
@@ -21,2 +58,3 @@ | ||
## 0.19.1 (2016-11-12) | ||
@@ -32,2 +70,3 @@ | ||
## 0.19.0 (2016-11-10) | ||
@@ -44,2 +83,3 @@ | ||
## 0.18.0 (2016-10-28) | ||
@@ -61,2 +101,3 @@ | ||
## 0.17.0 (2016-09-15) | ||
@@ -63,0 +104,0 @@ |
@@ -23,3 +23,3 @@ 'use strict'; | ||
if (cmd === '--help') { | ||
process.stdout.write('\n Usage: skygear-node <file>\n\n file will default to index.js if not provided.\n\n skygear-node are configured by ENVVAR:\n - SKYGEAR_ADDRESS: Binds to this socket for skygear\n - SKYGEAR_ENDPOINT: Send to this addres for skygear handlers\n - API_KEY: API Key of the application [env var: API_KEY]\n - MASTER_KEY: Master Key of the application\n - APP_NAME: Application name of the skygear daemon\n - LOG_LEVEL: Log level\n - HTTP: Trigger http web server\n - HTTP_ADDR: Address where htp web server listen to\n - DEBUG: Enable debugging features\n - SERVE_STATIC_ASSETS: Enable to serve static asset from plugin process\n - PUBSUB_URL: The URL of the pubsub server, should start with ws://\n or wss:// and include the path\n '); | ||
process.stdout.write('\n Usage: skygear-node <file>\n\n file will default to index.js if not provided.\n\n skygear-node are configured by ENVVAR:\n - SKYGEAR_ADDRESS: Binds to this socket for skygear\n - SKYGEAR_ENDPOINT: Send to this address for skygear handlers\n - API_KEY: API Key of the application\n - MASTER_KEY: Master Key of the application\n - APP_NAME: Application name of the skygear daemon\n - LOG_LEVEL: Log level\n - HTTP: Trigger http web server\n - HTTP_ADDR: Address where htp web server listen to\n - DEBUG: Enable debugging features\n - SERVE_STATIC_ASSETS: Enable to serve static asset from plugin process\n - PUBSUB_URL: The URL of the pubsub server, should start with ws://\n or wss:// and include the path\n '); | ||
@@ -26,0 +26,0 @@ process.exit(); |
@@ -24,10 +24,6 @@ 'use strict'; | ||
var _lodash = require('lodash'); | ||
var _store = require('./store'); | ||
var _lodash2 = _interopRequireDefault(_lodash); | ||
var _store2 = _interopRequireDefault(_store); | ||
var _store2 = require('./store'); | ||
var _store3 = _interopRequireDefault(_store2); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -41,18 +37,11 @@ | ||
this._maxRetryCount = 1; | ||
this.prefix = prefix; | ||
this._keyStore = this.prefix + ':keys'; | ||
this.map = {}; | ||
this.keys = []; | ||
this.store = _store3.default; | ||
this.store.getItem(this._keyStore).then(function (jsonStr) { | ||
var ary = JSON.parse(jsonStr); | ||
this.keys = _lodash2.default.union(this.keys, ary); | ||
}.bind(this), function (err) { | ||
console.warn('Failed to get cached keys', this.prefix, err); | ||
}.bind(this)); | ||
this.store = _store2.default; | ||
} | ||
_createClass(Cache, [{ | ||
key: '_key', | ||
value: function _key(key) { | ||
key: '_applyNamespaceOnKey', | ||
value: function _applyNamespaceOnKey(key) { | ||
return this.prefix + ':' + key; | ||
@@ -63,9 +52,22 @@ } | ||
value: function set(key, value) { | ||
var prefixKey = this._key(key); | ||
return new Promise(function (resolve) { | ||
this.map[prefixKey] = value; | ||
this.keys = _lodash2.default.union(this.keys, [prefixKey]); | ||
this.store.setItem(this._keyStore, JSON.stringify(this.keys)); | ||
this.store.setItem(prefixKey, JSON.stringify(value)); | ||
resolve(); | ||
var namespacedKey = this._applyNamespaceOnKey(key); | ||
this.map[namespacedKey] = value; | ||
var stringifiedValue = JSON.stringify(value); | ||
return this._setWithRetry(namespacedKey, stringifiedValue); | ||
} | ||
}, { | ||
key: '_setWithRetry', | ||
value: function _setWithRetry(namespacedKey, stringifiedValue) { | ||
var attempt = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; | ||
return this.store.setPurgeableItem(namespacedKey, stringifiedValue).catch(function (error) { | ||
// base case | ||
if (attempt >= this._maxRetryCount) { | ||
return Promise.reject(error); | ||
} | ||
// recursive case | ||
// It seems that there is no easy way to | ||
// convert an asynchronous recursion into | ||
// iterative style with for-loop. | ||
return this._setWithRetry(namespacedKey, stringifiedValue, attempt + 1); | ||
}.bind(this)); | ||
@@ -76,31 +78,13 @@ } | ||
value: function get(key) { | ||
var prefixKey = this._key(key); | ||
return new Promise(function (resolve, reject) { | ||
var result = this.map[prefixKey]; | ||
if (result) { | ||
resolve(result); | ||
} else { | ||
this.store.getItem(prefixKey).then(function (jsonStr) { | ||
if (jsonStr) { | ||
var cachedJSON = JSON.parse(jsonStr); | ||
resolve(cachedJSON); | ||
} else { | ||
reject(); | ||
} | ||
}, function (err) { | ||
reject(err); | ||
}); | ||
var namespacedKey = this._applyNamespaceOnKey(key); | ||
if (this.map[namespacedKey]) { | ||
return Promise.resolve(this.map[namespacedKey]); | ||
} | ||
return this.store.getItem(namespacedKey).then(function (jsonStr) { | ||
if (jsonStr) { | ||
var cachedJSON = JSON.parse(jsonStr); | ||
return cachedJSON; | ||
} | ||
}.bind(this)); | ||
} | ||
}, { | ||
key: 'reset', | ||
value: function reset() { | ||
var _store = this.store; | ||
var removal = _lodash2.default.map(this.keys, function (key) { | ||
return _store.removeItem(key); | ||
return Promise.reject(); | ||
}); | ||
this.keys = []; | ||
removal.push(this.store.setItem(this._keyStore, JSON.stringify(this.keys))); | ||
return Promise.all(removal); | ||
} | ||
@@ -107,0 +91,0 @@ }]); |
@@ -61,3 +61,2 @@ 'use strict'; | ||
var data = _fs2.default.readFileSync(finalPath, { | ||
encoding: 'utf8', | ||
flag: 'r' | ||
@@ -64,0 +63,0 @@ }); |
@@ -6,3 +6,3 @@ 'use strict'; | ||
}); | ||
exports.SkygearError = exports.ErrorCodes = exports.CloudCodeContainer = exports.settings = exports.pool = exports.skyconfig = exports.BaseAuthProvider = undefined; | ||
exports.SkygearError = exports.ErrorCodes = exports.CloudCodeContainer = exports.settings = exports.SkygearResponse = exports.SkygearRequest = exports.poolConnect = exports.pool = exports.skyconfig = exports.BaseAuthProvider = undefined; | ||
@@ -38,2 +38,26 @@ var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /** | ||
var _pg = require('./pg'); | ||
Object.defineProperty(exports, 'poolConnect', { | ||
enumerable: true, | ||
get: function get() { | ||
return _pg.poolConnect; | ||
} | ||
}); | ||
var _common = require('./transport/common'); | ||
Object.defineProperty(exports, 'SkygearRequest', { | ||
enumerable: true, | ||
get: function get() { | ||
return _common.SkygearRequest; | ||
} | ||
}); | ||
Object.defineProperty(exports, 'SkygearResponse', { | ||
enumerable: true, | ||
get: function get() { | ||
return _common.SkygearResponse; | ||
} | ||
}); | ||
var _registry = require('./registry'); | ||
@@ -47,4 +71,2 @@ | ||
var _pg = require('./pg'); | ||
var _settings2 = require('./settings'); | ||
@@ -130,6 +152,10 @@ | ||
* @example | ||
* skygearCloud.handler('private', function(req) { | ||
* skygearCloud.handler('private', function(req, options) { | ||
* // cloud code handling the request | ||
* const { | ||
* context | ||
* } = options; | ||
* return { | ||
* status: 'ok' | ||
* status: 'ok', | ||
* user_id: context.user_id // only available if userRequired=true | ||
* }; | ||
@@ -142,3 +168,4 @@ * }, { | ||
* @param {string} path - The path of the handler to be mount. | ||
* @param {function(request:*): object} func - function to be registered. | ||
* @param {function(request:*, options:*): object} func - function to be | ||
* registered. | ||
* @param {object} [options] - options for setting method, userRequired and | ||
@@ -271,3 +298,3 @@ * keyRequired. | ||
* const skygearCloud = require('skygear/cloud');¬ | ||
* skygearCloud.beforeSave('note', function(record, original, pool) { | ||
* skygearCloud.beforeSave('note', function(record, original, pool, options) { | ||
* // cloud code handling the request | ||
@@ -280,3 +307,3 @@ * return; | ||
* @param {string} recordType - The type of the record. | ||
* @param {function(record: lib/record.js~Record, originalRecord: lib/record.js~Record, pool: pool): *} func - function to be registered. | ||
* @param {function(record: lib/record.js~Record, originalRecord: lib/record.js~Record, pool: pool, options: *): *} func - function to be registered. | ||
* @param {object} [options] - options for hook: async | ||
@@ -299,3 +326,3 @@ */ | ||
* const skygearCloud = require('skygear/cloud');¬ | ||
* skygearCloud.afterSave('note', function(record, original, pool) { | ||
* skygearCloud.afterSave('note', function(record, original, pool, options) { | ||
* // cloud code handling the request | ||
@@ -308,3 +335,3 @@ * return; | ||
* @param {string} recordType - The type of the record. | ||
* @param {function(record: lib/record.js~Record, originalRecord: lib/record.js~Record, pool: pool): *} func - function to be registered. | ||
* @param {function(record: lib/record.js~Record, originalRecord: lib/record.js~Record, pool: pool, options: *): *} func - function to be registered. | ||
* @param {object} [options] - options for hook: async | ||
@@ -327,3 +354,3 @@ */ | ||
* const skygearCloud = require('skygear/cloud');¬ | ||
* skygearCloud.beforeDelete('note', function(record, original, pool) { | ||
* skygearCloud.beforeDelete('note', function(record, original, pool, options) { | ||
* // cloud code handling the request | ||
@@ -336,3 +363,3 @@ * return; | ||
* @param {string} recordType - The type of the record. | ||
* @param {function(record: lib/record.js~Record, originalRecord: lib/record.js~Record, pool: pool): *} func - function to be registered. | ||
* @param {function(record: lib/record.js~Record, originalRecord: lib/record.js~Record, pool: pool, options: *): *} func - function to be registered. | ||
* @param {object} [options] - options for hook: async | ||
@@ -355,3 +382,3 @@ */ | ||
* const skygearCloud = require('skygear/cloud');¬ | ||
* skygearCloud.afterDelete('note', function(record, original, pool) { | ||
* skygearCloud.afterDelete('note', function(record, original, pool, options) { | ||
* // cloud code handling the request | ||
@@ -364,3 +391,3 @@ * return; | ||
* @param {string} recordType - The type of the record. | ||
* @param {function(record: lib/record.js~Record, originalRecord: lib/record.js~Record, pool: pool): *} func - function to be registered. | ||
* @param {function(record: lib/record.js~Record, originalRecord: lib/record.js~Record, pool: pool, options: *): *} func - function to be registered. | ||
* @param {object} [options] - options for hook: async | ||
@@ -367,0 +394,0 @@ */ |
@@ -7,2 +7,3 @@ 'use strict'; | ||
exports.pool = undefined; | ||
exports.poolConnect = poolConnect; | ||
@@ -59,2 +60,47 @@ var _settings = require('./settings'); | ||
*/ | ||
var pool = exports.pool = new _pg2.default.Pool(config); | ||
var pool = exports.pool = new _pg2.default.Pool(config); | ||
/** | ||
* Connect to a pg connection provided by pg pool. | ||
* | ||
* The pg connection is automatically set to use the application's database | ||
* schema. | ||
* | ||
* @param {function(err:*, client: *, done: function): *} callback - function | ||
* called after setting up the database connection | ||
* | ||
* @example | ||
* const skygearCloud = require('skygear/cloud'); | ||
* skygearCloud.poolConnect(function (err, client, done) { | ||
* // if err is undefined or null, make query using the client | ||
* client.query( | ||
* 'SELECT id FROM _user WHERE username = $1', | ||
* ['johndoe'], | ||
* function(queryErr, result) { | ||
* done(); | ||
* // use the query result here | ||
* } | ||
* ); | ||
* } | ||
*/ | ||
function poolConnect(callback) { | ||
pool.connect(function (err, client, done) { | ||
if (err !== null && err !== undefined) { | ||
console.error('Unable to connect to pg pool', err); | ||
callback(err, client, done); | ||
return; | ||
} | ||
var schemaName = 'app_' + _settings.settings.appName; | ||
var stmt = 'SET search_path TO ' + schemaName + ',public;'; | ||
client.query(stmt, function (queryErr) { | ||
if (queryErr !== null && queryErr !== undefined) { | ||
console.error('Unable to select "' + schemaName + '" schema', queryErr); | ||
callback(queryErr, client, done); | ||
return; | ||
} | ||
callback(null, client, done); | ||
}); | ||
}); | ||
} |
@@ -50,8 +50,14 @@ 'use strict'; | ||
/** | ||
* Encode 16-bit unicode strings to base64 string. | ||
* Encode 16-bit unicode strings or a buffer to base64 string. | ||
* | ||
* @see https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem | ||
*/ | ||
function b64EncodeUnicode(str) { | ||
return (0, _Base.btoa)(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) { | ||
function b64EncodeUnicode(data) { | ||
// If the data is a buffer, use its base64 encoding instead of using | ||
// our unicode to base64 encoding. | ||
if (data instanceof Buffer) { | ||
return data.toString('base64'); | ||
} | ||
return (0, _Base.btoa)(encodeURIComponent(data).replace(/%([0-9A-F]{2})/g, function (match, p1) { | ||
return String.fromCharCode('0x' + p1); | ||
@@ -211,3 +217,4 @@ })); | ||
var name = payload.name, | ||
param = payload.param; | ||
param = payload.param, | ||
context = payload.context; | ||
@@ -227,3 +234,4 @@ | ||
return this._promisify(func, incomingRecord, originalRecord, _pg.pool).then(function (_record) { | ||
var options = { context: context }; | ||
return this._promisify(func, incomingRecord, originalRecord, _pg.pool, options).then(function (_record) { | ||
var record = _record || incomingRecord; | ||
@@ -238,3 +246,8 @@ return { | ||
value: function opHandler(payload) { | ||
var func = this.registry.getFunc('op', payload.name); | ||
var name = payload.name, | ||
param = payload.param, | ||
context = payload.context; | ||
var func = this.registry.getFunc('op', name); | ||
if (!func) { | ||
@@ -244,3 +257,4 @@ return Promise.reject(new Error('Lambda function does not exist')); | ||
return this._promisify(func, payload.param).then(function (result) { | ||
var options = { context: context }; | ||
return this._promisify(func, param, options).then(function (result) { | ||
return { result: result }; | ||
@@ -287,6 +301,8 @@ }); | ||
value: function handlerHandler(payload) { | ||
var method = payload.param.method; | ||
var name = payload.name, | ||
param = payload.param, | ||
context = payload.context; | ||
var func = this.registry.getHandler(payload.name, method); | ||
var func = this.registry.getHandler(name, param.method); | ||
if (!func) { | ||
@@ -296,4 +312,5 @@ return Promise.reject(new Error('Handler not exist')); | ||
var req = new SkygearRequest(payload.param); | ||
return this._promisify(func, req).then(function (result) { | ||
var options = { context: context }; | ||
var req = new SkygearRequest(param); | ||
return this._promisify(func, req, options).then(function (result) { | ||
var headers = {}; | ||
@@ -300,0 +317,0 @@ var body = void 0; |
@@ -141,10 +141,3 @@ 'use strict'; | ||
value: function clearCache() { | ||
var clear = []; | ||
if (this._publicDB) { | ||
clear.push(this._publicDB.clearCache()); | ||
} | ||
if (this._privateDB) { | ||
clear.push(this._privateDB.clearCache()); | ||
} | ||
return Promise.all(clear); | ||
return store.clearPurgeableItems(); | ||
} | ||
@@ -230,4 +223,14 @@ }, { | ||
var container = this; | ||
this.clearCache(); | ||
return container.makeRequest('auth:logout', {}).then(function () { | ||
return this.unregisterDevice().then(function () { | ||
container.clearCache(); | ||
return container.makeRequest('auth:logout', {}); | ||
}, function (error) { | ||
if (error.code === _error.ErrorCodes.InvalidArgument && error.message === 'Missing device id') { | ||
container.clearCache(); | ||
return container.makeRequest('auth:logout', {}); | ||
} | ||
return Promise.reject(error); | ||
}).then(function () { | ||
return Promise.all([container._setAccessToken(null), container._setUser(null)]).then(function () { | ||
@@ -452,7 +455,7 @@ return null; | ||
// If the current deviceID is already null, will regards as server fail. | ||
var skyerr = null; | ||
var errorCode = null; | ||
if (error.error) { | ||
skyerr = error.error.code; | ||
errorCode = error.error.code; | ||
} | ||
if (self.deviceID && skyerr === 110) { | ||
if (self.deviceID && errorCode === _error.ErrorCodes.ResourceNotFound) { | ||
return self._setDeviceID(null).then(function () { | ||
@@ -467,2 +470,28 @@ return self.registerDevice(token, type); | ||
}, { | ||
key: 'unregisterDevice', | ||
value: function unregisterDevice() { | ||
if (!this.deviceID) { | ||
return Promise.reject(new _error.SkygearError('Missing device id', _error.ErrorCodes.InvalidArgument)); | ||
} | ||
var self = this; | ||
return self.makeRequest('device:unregister', { | ||
id: this.deviceID | ||
}).then(function () { | ||
// do nothing | ||
return; | ||
}, function (error) { | ||
var errorCode = null; | ||
if (error.error) { | ||
errorCode = error.error.code; | ||
} | ||
if (errorCode === _error.ErrorCodes.ResourceNotFound) { | ||
// regard it as success | ||
return self._setDeviceID(null); | ||
} else { | ||
return Promise.reject(error); | ||
} | ||
}); | ||
} | ||
}, { | ||
key: 'lambda', | ||
@@ -812,2 +841,7 @@ value: function lambda(name, data) { | ||
}, { | ||
key: 'Database', | ||
get: function get() { | ||
return _database2.default; | ||
} | ||
}, { | ||
key: 'relation', | ||
@@ -814,0 +848,0 @@ get: function get() { |
@@ -56,3 +56,3 @@ 'use strict'; | ||
if (dbID !== '_public' && dbID !== '_private') { | ||
if (dbID !== '_public' && dbID !== '_private' && dbID !== '_union') { | ||
throw new Error('Invalid database_id'); | ||
@@ -299,7 +299,2 @@ } | ||
}, { | ||
key: 'clearCache', | ||
value: function clearCache() { | ||
return this._cacheStore.reset(); | ||
} | ||
}, { | ||
key: 'cacheStore', | ||
@@ -306,0 +301,0 @@ get: function get() { |
@@ -124,2 +124,3 @@ 'use strict'; | ||
} | ||
delete attrs.id; // because `id` is a readonly property | ||
this._id = id; | ||
@@ -126,0 +127,0 @@ this._access = Record.defaultACL; |
@@ -33,2 +33,4 @@ 'use strict'; | ||
var PURGEABLE_KEYS_KEY = '_skygear_purgeable_keys_'; | ||
var SyncStorageDriver = function () { | ||
@@ -93,2 +95,58 @@ function SyncStorageDriver(syncImpl) { | ||
}, { | ||
key: 'multiGet', | ||
value: function multiGet(keys, callback) { | ||
return new Promise(function (resolve) { | ||
var output = []; | ||
for (var i = 0; i < keys.length; ++i) { | ||
var key = keys[i]; | ||
var value = this._syncImpl.getItem(key); | ||
output.push({ | ||
key: key, | ||
value: value | ||
}); | ||
} | ||
if (callback) { | ||
callback(null, output); | ||
} | ||
resolve(output); | ||
}.bind(this)); | ||
} | ||
}, { | ||
key: 'multiSet', | ||
value: function multiSet(keyValuePairs, callback) { | ||
return new Promise(function (resolve, reject) { | ||
try { | ||
for (var i = 0; i < keyValuePairs.length; ++i) { | ||
var pair = keyValuePairs[i]; | ||
var key = pair.key; | ||
var value = pair.value; | ||
this._syncImpl.setItem(key, value); | ||
} | ||
if (callback) { | ||
callback(null); | ||
} | ||
resolve(); | ||
} catch (e) { | ||
if (callback) { | ||
callback(e); | ||
} | ||
reject(e); | ||
} | ||
}.bind(this)); | ||
} | ||
}, { | ||
key: 'multiRemove', | ||
value: function multiRemove(keys, callback) { | ||
return new Promise(function (resolve) { | ||
for (var i = 0; i < keys.length; ++i) { | ||
var key = keys[i]; | ||
this._syncImpl.removeItem(key); | ||
} | ||
if (callback) { | ||
callback(null); | ||
} | ||
resolve(); | ||
}.bind(this)); | ||
} | ||
}, { | ||
key: 'key', | ||
@@ -163,2 +221,44 @@ value: function key(n, callback) { | ||
}, { | ||
key: 'multiGet', | ||
value: function multiGet(keys, callback) { | ||
return this._rnImpl.multiGet(keys).then(function (rnKeyValuePairs) { | ||
var output = []; | ||
for (var i = 0; i < rnKeyValuePairs.length; ++i) { | ||
var rnPair = rnKeyValuePairs[i]; | ||
var key = rnPair[0]; | ||
var value = rnPair[1]; | ||
output.push({ | ||
key: key, | ||
value: value | ||
}); | ||
} | ||
if (callback) { | ||
callback(null, output); | ||
} | ||
return output; | ||
}, function (errors) { | ||
if (callback) { | ||
callback(errors); | ||
} | ||
return Promise.reject(errors); | ||
}); | ||
} | ||
}, { | ||
key: 'multiSet', | ||
value: function multiSet(keyValuePairs, callback) { | ||
var rnKeyValuePairs = []; | ||
for (var i = 0; i < keyValuePairs.length; ++i) { | ||
var pair = keyValuePairs[i]; | ||
var key = pair.key; | ||
var value = pair.value; | ||
rnKeyValuePairs.push([key, value]); | ||
} | ||
return this._rnImpl.multiSet(rnKeyValuePairs, callback); | ||
} | ||
}, { | ||
key: 'multiRemove', | ||
value: function multiRemove(keys, callback) { | ||
return this._rnImpl.multiRemove(keys, callback); | ||
} | ||
}, { | ||
key: 'key', | ||
@@ -203,5 +303,68 @@ value: function key(n, callback) { | ||
this.keyWhiteList = keyWhiteList; | ||
this._purgeableKeys = []; | ||
this._driver.getItem(PURGEABLE_KEYS_KEY).then(function (value) { | ||
if (value) { | ||
try { | ||
var originalKeys = JSON.parse(value); | ||
var recentKeys = this._purgeableKeys; | ||
this._purgeableKeys = this._maintainLRUOrder(originalKeys, recentKeys); | ||
} catch (e) { | ||
// ignore | ||
} | ||
} | ||
}.bind(this)); | ||
} | ||
/* | ||
* @param originalKeys | ||
* @param recentKeys | ||
* @return newKeys with recentKeys come first, followed by deduped | ||
* originalKeys | ||
*/ | ||
_createClass(Store, [{ | ||
key: '_maintainLRUOrder', | ||
value: function _maintainLRUOrder(originalKeys, recentKeys) { | ||
var mapping = {}; | ||
for (var i = 0; i < recentKeys.length; ++i) { | ||
mapping[recentKeys[i]] = true; | ||
} | ||
var output = recentKeys.slice(); | ||
for (var _i = 0; _i < originalKeys.length; ++_i) { | ||
if (mapping[originalKeys[_i]]) { | ||
continue; | ||
} | ||
output.push(originalKeys[_i]); | ||
} | ||
return output; | ||
} | ||
/* | ||
* @param originalKeys | ||
* @param keysToRemove | ||
* @return newKeys without value contained in keysToRemove | ||
*/ | ||
}, { | ||
key: '_removeKeysInLRUOrder', | ||
value: function _removeKeysInLRUOrder(originalKeys, keysToRemove) { | ||
var mapping = {}; | ||
for (var i = 0; i < keysToRemove.length; ++i) { | ||
mapping[keysToRemove[i]] = true; | ||
} | ||
var output = []; | ||
for (var _i2 = 0; _i2 < originalKeys.length; ++_i2) { | ||
if (mapping[originalKeys[_i2]]) { | ||
continue; | ||
} | ||
output.push(originalKeys[_i2]); | ||
} | ||
return output; | ||
} | ||
}, { | ||
key: 'clear', | ||
@@ -222,5 +385,114 @@ value: function clear(callback) { | ||
} | ||
return this._driver.setItem(key, value, callback); | ||
return this._driver.setItem(key, value).then(function () { | ||
if (callback) { | ||
callback(null); | ||
} | ||
return Promise.resolve(); | ||
}, function (error) { | ||
return this._purge().then(function () { | ||
if (callback) { | ||
callback(error); | ||
} | ||
return Promise.reject(error); | ||
}, function () /* nestedError */{ | ||
if (callback) { | ||
callback(error); | ||
} | ||
return Promise.reject(error); | ||
}); | ||
}.bind(this)); | ||
} | ||
}, { | ||
key: 'setPurgeableItem', | ||
value: function setPurgeableItem(key, value, callback) { | ||
if (this.keyWhiteList && this.keyWhiteList.indexOf(key) < 0) { | ||
return Promise.reject(new Error('Saving key is not permitted')); | ||
} | ||
this._purgeableKeys = this._maintainLRUOrder(this._purgeableKeys, [key]); | ||
var keyValuePairs = [{ | ||
key: key, | ||
value: value | ||
}, { | ||
key: PURGEABLE_KEYS_KEY, | ||
value: JSON.stringify(this._purgeableKeys) | ||
}]; | ||
return this.multiSetTransactionally(keyValuePairs).then(function () { | ||
if (callback) { | ||
callback(null); | ||
} | ||
return Promise.resolve(); | ||
}, function (error) { | ||
return this._purge().then(function () { | ||
if (callback) { | ||
callback(error); | ||
} | ||
return Promise.reject(error); | ||
}, function () /* nestedError */{ | ||
if (callback) { | ||
callback(error); | ||
} | ||
return Promise.reject(error); | ||
}); | ||
}.bind(this)); | ||
} | ||
}, { | ||
key: '_selectKeysToPurge', | ||
value: function _selectKeysToPurge(keys) { | ||
var index = Math.floor(keys.length / 2); | ||
var keysToPurge = keys.slice(index); | ||
return keysToPurge; | ||
} | ||
}, { | ||
key: '_purge', | ||
value: function _purge() { | ||
var keysToPurge = this._selectKeysToPurge(this._purgeableKeys); | ||
if (keysToPurge.length <= 0) { | ||
return Promise.reject(new Error('no more keys to purge')); | ||
} | ||
this._purgeableKeys = this._removeKeysInLRUOrder(this._purgeableKeys, keysToPurge); | ||
return this._driver.multiRemove(keysToPurge).then(function () { | ||
return this._driver.setItem(PURGEABLE_KEYS_KEY, JSON.stringify(this._purgeableKeys)); | ||
}.bind(this)); | ||
} | ||
}, { | ||
key: 'multiSetTransactionally', | ||
value: function multiSetTransactionally(keyValuePairs, callback) { | ||
var keys = []; | ||
for (var i = 0; i < keyValuePairs.length; ++i) { | ||
var pair = keyValuePairs[i]; | ||
var key = pair.key; | ||
if (this.keyWhiteList && this.keyWhiteList.indexOf(key) < 0) { | ||
return Promise.reject(new Error('Saving key is not permitted')); | ||
} | ||
keys.push(key); | ||
} | ||
return this._driver.multiGet(keys).then(function (original) { | ||
return this._driver.multiSet(keyValuePairs).then(function () { | ||
if (callback) { | ||
callback(null); | ||
} | ||
return Promise.resolve(); | ||
}, function (e) { | ||
return this._driver.multiRemove(keys).then(function () { | ||
return this._driver.multiSet(original).then(function () { | ||
if (callback) { | ||
callback(e); | ||
} | ||
return Promise.reject(e); | ||
}); | ||
}.bind(this)); | ||
}.bind(this)); | ||
}.bind(this)); | ||
} | ||
}, { | ||
key: 'clearPurgeableItems', | ||
value: function clearPurgeableItems(callback) { | ||
var keys = this._purgeableKeys.slice(); | ||
this._purgeableKeys = []; | ||
return this._driver.multiRemove(keys, callback); | ||
} | ||
}, { | ||
key: 'removeItem', | ||
@@ -227,0 +499,0 @@ value: function removeItem(key, callback) { |
@@ -41,2 +41,27 @@ 'use strict'; | ||
return Sequence; | ||
}(); | ||
var UnknownValue = exports.UnknownValue = function () { | ||
function UnknownValue(underlyingType) { | ||
_classCallCheck(this, UnknownValue); | ||
this.underlyingType = underlyingType; | ||
} | ||
_createClass(UnknownValue, [{ | ||
key: 'toJSON', | ||
value: function toJSON() { | ||
return { | ||
$type: 'unknown', | ||
$underlying_type: this.underlyingType //eslint-disable-line camelcase | ||
}; | ||
} | ||
}], [{ | ||
key: 'fromJSON', | ||
value: function fromJSON(attrs) { | ||
return new UnknownValue(attrs.$underlying_type); | ||
} | ||
}]); | ||
return UnknownValue; | ||
}(); |
@@ -45,2 +45,4 @@ 'use strict'; | ||
var _type = require('./type'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -85,2 +87,4 @@ | ||
return new _reference2.default(attrs); | ||
case 'unknown': | ||
return _type.UnknownValue.fromJSON(attrs); | ||
default: | ||
@@ -87,0 +91,0 @@ return attrs; |
{ | ||
"name": "skygear", | ||
"version": "0.19.2", | ||
"version": "0.20.0", | ||
"description": "JS SDK of Skygear services", | ||
@@ -5,0 +5,0 @@ "homepage": "https://github.com/SkygearIO/skygear-SDK-JS", |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
2197832
25382
1
15