From b0679dc175f2d9cb1e50d7d14204377be5b7998b Mon Sep 17 00:00:00 2001 From: Daniel Lugo Date: Tue, 24 Nov 2020 17:07:13 -0400 Subject: [PATCH] allow overwrite of primitive values in gun put rpc --- services/gunDB/rpc/index.js | 41 +++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/services/gunDB/rpc/index.js b/services/gunDB/rpc/index.js index 09087533..97eebb2d 100644 --- a/services/gunDB/rpc/index.js +++ b/services/gunDB/rpc/index.js @@ -145,11 +145,44 @@ const put = async (rawPath, value) => { // eslint-disable-next-line no-useless-return return } else if (Schema.isObj(theValue)) { - const writes = mapValues(theValue, (v, k) => - put(rawPath + PATH_SEPARATOR + k, v) - ) + const currValue = await node.then() - await Bluebird.props(writes) + if (Schema.isObj(currValue)) { + const writes = mapValues(theValue, (v, k) => + put(rawPath + PATH_SEPARATOR + k, v) + ) + + await Bluebird.props(writes) + } else { + // if the value at path foo is null or another primitive, then + // foo.get('bar').put(baz) will NOT work, the write needs to happen like so: + // foo.put({ bar: baz }). Doing foo.put({}) also works but it won't replace + // the primitive value with an empty object but with the last object + // representation of that node. Doing foo.put({ anything: whatever }) will + // merge that new object with the previous representation too. Both of which + // can result in inconsistent states so please thread carefully. What I + // chose to do here was put without waiting for ack, and if the actual + // user-generated puts fail, roll back to the previous local state in order + // to accomplish some kind of atomicity. This bug will mostly affect maps + // instead of sets as deleted keys in a set should not be reused. + // Maps should conform to an schema to avoid inconsistent data. + try { + node.put({}) // changes from current primitive value to last known object value + + const writes = mapValues(theValue, (v, k) => + put(rawPath + PATH_SEPARATOR + k, v) + ) + + await Bluebird.props(writes) + } catch (e) { + if (typeof currValue !== 'undefined') { + // if write was somehow unsuccessful, revert to last known primitive value + node.put(currValue) + } + + throw e + } + } } /* is primitive */ else { await makePromise((res, rej) => { node.put(/** @type {ValidDataValue} */ (theValue), ack => {