2014-09-20 11:49:04 -04:00
|
|
|
### @module omega-target/storage ###
|
|
|
|
Promise = require 'bluebird'
|
|
|
|
Log = require './log'
|
|
|
|
|
|
|
|
class Storage
|
2015-01-18 12:09:17 -05:00
|
|
|
###*
|
|
|
|
# Any operation that fails due to rate limiting should reject with an instance
|
|
|
|
# of RateLimitExceededError, when implemented in derived classes of Storage.
|
|
|
|
###
|
|
|
|
@RateLimitExceededError:
|
|
|
|
class RateLimitExceededError extends Error
|
|
|
|
constructor: -> super
|
|
|
|
|
|
|
|
###*
|
|
|
|
# Any operation that fails due to storage quota should reject with an instance
|
|
|
|
# of QuotaExceededError, when implemented in derived classes of Storage.
|
|
|
|
###
|
|
|
|
@QuotaExceededError:
|
|
|
|
class QuotaExceededError extends Error
|
|
|
|
constructor: -> super
|
|
|
|
|
|
|
|
###*
|
|
|
|
# A set of operations to be performed on a Storage.
|
|
|
|
# @typedef WriteOperations
|
|
|
|
# @type {object}
|
|
|
|
# @property {Object.<string, {}>} set - A map from keys to new values of the
|
|
|
|
# items to set
|
|
|
|
# @property {{}[]} remove - An array of keys to remove
|
|
|
|
###
|
|
|
|
|
|
|
|
###*
|
|
|
|
# Calculate the actual operations against storage that should be performed to
|
|
|
|
# replay the changes on a storage.
|
|
|
|
# @param {Object.<string, {}>} changes The changes to apply
|
|
|
|
# @param {?{}} args Extra arguments
|
|
|
|
# @param {Object.<string, {}>?} args.base The original items in the storage.
|
|
|
|
# @param {function(key, newVal, oldVal)} args.merge A function that merges
|
2015-02-11 02:29:47 -05:00
|
|
|
# the newVal and oldVal. oldVal is provided only if args.base is present.
|
|
|
|
# Otherwise it will be equal to newVal (i.e. merge(key, newVal, newVal)).
|
2015-01-18 12:09:17 -05:00
|
|
|
# @returns {WriteOperations} The operations that should be performed.
|
|
|
|
###
|
|
|
|
@operationsForChanges: (changes, {base, merge} = {}) ->
|
|
|
|
set = {}
|
|
|
|
remove = []
|
|
|
|
for key, newVal of changes
|
2015-02-11 02:29:47 -05:00
|
|
|
oldVal = if base? then base[key] else newVal
|
|
|
|
if merge
|
|
|
|
newVal = merge(key, newVal, oldVal)
|
|
|
|
continue if base? and newVal == oldVal
|
|
|
|
if typeof newVal == 'undefined'
|
|
|
|
if typeof oldVal != 'undefined' or not base?
|
2015-01-18 12:09:17 -05:00
|
|
|
remove.push(key)
|
|
|
|
else
|
2015-02-11 02:29:47 -05:00
|
|
|
set[key] = newVal
|
2015-01-18 12:09:17 -05:00
|
|
|
return {set: set, remove: remove}
|
|
|
|
|
2014-09-20 11:49:04 -04:00
|
|
|
###*
|
|
|
|
# Get the requested values by keys from the storage.
|
|
|
|
# @param {(string|string[]|null|Object.<string,{}>)} keys The keys to retrive,
|
|
|
|
# or null for all.
|
|
|
|
# @returns {Promise<(Object.<string, {}>)>} A map from keys to values
|
|
|
|
###
|
|
|
|
get: (keys) ->
|
|
|
|
Log.method('Storage#get', this, arguments)
|
2015-01-18 12:09:17 -05:00
|
|
|
return Promise.resolve({}) unless @_items
|
2014-09-20 11:49:04 -04:00
|
|
|
if not keys?
|
2015-01-18 12:09:17 -05:00
|
|
|
keys = @_items
|
2014-09-20 11:49:04 -04:00
|
|
|
map = {}
|
|
|
|
if typeof keys == 'string'
|
2015-01-18 12:09:17 -05:00
|
|
|
map[keys] = @_items[keys]
|
2014-09-20 11:49:04 -04:00
|
|
|
else if Array.isArray(keys)
|
|
|
|
for key in keys
|
2015-01-18 12:09:17 -05:00
|
|
|
map[key] = @_items[key]
|
2014-09-20 11:49:04 -04:00
|
|
|
else if typeof keys == 'object'
|
2015-01-18 12:09:17 -05:00
|
|
|
for key, value of keys
|
|
|
|
map[key] = @_items[key] ? value
|
2014-09-20 11:49:04 -04:00
|
|
|
Promise.resolve(map)
|
|
|
|
|
|
|
|
###*
|
|
|
|
# Set multiple values by keys in the storage.
|
|
|
|
# @param {(string|Object.<string,{}>)} items A map from key to value to set.
|
|
|
|
# @returns {Promise<(Object.<string, {}>)>} A map of key-value pairs just set.
|
|
|
|
###
|
|
|
|
set: (items) ->
|
|
|
|
Log.method('Storage#set', this, arguments)
|
2015-01-18 12:09:17 -05:00
|
|
|
@_items ?= {}
|
|
|
|
for key, value of items
|
|
|
|
@_items[key] = value
|
2014-09-20 11:49:04 -04:00
|
|
|
Promise.resolve(items)
|
|
|
|
|
|
|
|
###*
|
|
|
|
# Remove items by keys from the storage.
|
|
|
|
# @param {(string|string[]|null)} keys The keys to remove, or null for all.
|
|
|
|
# @returns {Promise} A promise that fulfills on successful removal.
|
|
|
|
###
|
|
|
|
remove: (keys) ->
|
|
|
|
Log.method('Storage#remove', this, arguments)
|
2015-01-18 12:09:17 -05:00
|
|
|
if @_items?
|
|
|
|
if not keys?
|
|
|
|
@_items = {}
|
|
|
|
else if Array.isArray(keys)
|
|
|
|
for key in keys
|
|
|
|
delete @_items[key]
|
|
|
|
else
|
|
|
|
delete @_items[keys]
|
2014-09-20 11:49:04 -04:00
|
|
|
Promise.resolve()
|
|
|
|
|
|
|
|
###*
|
|
|
|
# @callback watchCallback
|
|
|
|
# @param {Object.<string, {}>} map A map of key-value pairs just changed.
|
|
|
|
###
|
|
|
|
|
|
|
|
###*
|
|
|
|
# Watch for any changes to the storage.
|
|
|
|
# @param {(string|string[]|null)} keys The keys to watch, or null for all.
|
|
|
|
# @param {watchCallback} callback Called everytime something changes.
|
|
|
|
# @returns {function} Calling the returned function will stop watching.
|
|
|
|
###
|
|
|
|
watch: (keys, callback) ->
|
|
|
|
Log.method('Storage#watch', this, arguments)
|
|
|
|
return (-> null)
|
2015-01-18 12:09:17 -05:00
|
|
|
|
|
|
|
###*
|
|
|
|
# Apply WriteOperations to the storage.
|
|
|
|
# @param {WriteOperations|{changes: Object.<string,{}>}} operations The
|
|
|
|
# operations to apply, or the changes to be applied. If changes is provided,
|
|
|
|
# the operations are calculated by Storage.operationsForChanges, with extra
|
|
|
|
# fields passed through as the second argument.
|
|
|
|
# @returns {Promise} A promise that fulfills on operation success.
|
|
|
|
###
|
|
|
|
apply: (operations) ->
|
|
|
|
if 'changes' of operations
|
|
|
|
operations = Storage.operationsForChanges(operations.changes, operations)
|
|
|
|
@set(operations.set).then(=> @remove(operations.remove)).return(operations)
|
2014-09-20 11:49:04 -04:00
|
|
|
|
|
|
|
module.exports = Storage
|