Dycey
Dycey

Reputation: 4705

Using fast-json-patched for document versioning and timelining

I'm attempting to create a revisable document, with the edits stored as fast-json-patched objects.

The rough outline is :

const jsonpatch = require('fast-json-patch');

/**
 * Generate a doc, with all the state changes recorded as
 * diffed objects.
 * @param {object} docParams The parameters to generate the doc from
 * @returns {object} the doc
 */
function generateDoc(docParams) {
  const defaults = {
    docnumber: 'TS99999',
    edits: 1,
  };
  // create a complete set of properties to work from
  const { docnumber, edits } = {
    ...defaults,
    ...docParams,
  };
  // basic docs setup
  const doc = {
    parameters: { docnumber, edits },
    history: [],
    state: { docnumber, notes: [] },
  };

  // update the doc 'edits' times
  for (let edit = 0; edit < edits; edit++) {
    // clone to preserve the current state
    const currentState = JSON.parse(JSON.stringify(doc.state));
    // add at least one note, up to every edit
    const notesToAdd = Math.ceil(Math.random() * 5);
    for (let i = 0; i < notesToAdd; i++) {
      doc.state.notes.push('Note: ' + Math.ceil(Math.random() * 500));
    }
    doc.history.push(jsonpatch.compare(currentState, doc.state));
  }
  return doc;
}

/**
 * Set the current doc state to the state at the spercifier point in time
 * @param {object} doc The doc to update
 * @param {integer} edit The point in time to use
 * @returns {boolean} Was the doc set updated?
 */
function timetravel(doc, editPoint) {
  if (
    doc.parameters.currentedit === editPoint ||
    editPoint > doc.parameters.edits ||
    editPoint < 1
  ) {
    return false; // don't travel too far into the future or past!
  }
  const patchesToApply = doc.history.slice(0, editPoint);
  const patches = [].concat.apply([], patchesToApply);
  let newDoc = {};
  newDoc = jsonpatch.applyPatch(newDoc, patches).newDocument;
  doc.state = newDoc;
  doc.parameters.currentedit = editPoint;
  return true;
}

// Testing....
const doc = generateDoc({
  docnumber: 'TS99999',
  edits: 5,
});

console.log(doc);
timetravel(doc, 2);
console.log(doc);

Clearly my understanding of what should be happening is wrong, as I get the following error...

/Users/dd/Code/patchtesting/node_modules/fast-json-patch/commonjs/core.js:14
        obj[key] = this.value;

just at the jsonpatch.applyPatch line.

I've tried alternative approaches:

// seems to be one popular suggestion...
  const patchesToApply = doc.history.slice(0, editPoint);
  const patches = [].concat.apply([], patchesToApply);
  doc.state = patches.reduce(jsonpatch.applyReducer, {});
  doc.parameters.currentedit = editPoint;

or...

// Trying to see the effect of applying a single patch at a time...
  patches.forEach(patch => {
    console.log(patch);
    newDoc = jsonpatch.applyPatch(newDoc, [patch]).newDocument;
    console.log(newDoc);
  });

The patches that are generated make sense, I just can't seem to apply them :-(

Upvotes: 0

Views: 795

Answers (1)

Dycey
Dycey

Reputation: 4705

Sorry, it was a basic coding / fencepost-ish error:

...

// basic docs setup
const doc = {
  parameters: { docnumber, edits },
  history: [],
  state: { docnumber, notes: [] },
};

// ADDED: need to record the initial state
doc.history.push(jsonpatch.compare({}, doc.state))

// update the doc 'edits' times
...
;

Upvotes: 0

Related Questions