From 880226f19264d52c5022819a698c40f07b87870a Mon Sep 17 00:00:00 2001 From: Alec Gibson <12036746+alecgibson@users.noreply.github.com> Date: Fri, 25 Aug 2023 11:53:05 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Allow=20configuration=20of=20serial?= =?UTF-8?q?ized=20fields?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Part of the reason for originally forking this library was to allow us to specify extra fields in the `Delta` serialized JSON representation when the document is written to the database. We currently use this just for the `metadata` field, where we store auth information about the owner and book IDs. We're soon going to add `rich-text` migration to `reedsy-sharedb`, which requires us to record if a document has been migrated, which we'll store in a similar way. Rather than have to change this library every time we want to serialize a new field, this change instead adds module-level config, where consumers can declare the fields they want (de)serialized: ```js var json0 = require('@reedsy/rich-text') json0.config.serializedProperties.extra = true; ``` Any fields that aren't declared here will be ignored when (de)serializing. By default we enable `metadata` in order to keep this change backwards- compatible. --- lib/config.js | 5 +++ lib/delta-with-metadata.js | 8 +++-- lib/type.js | 13 +++++--- package.json | 5 +-- test/config.js | 66 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 lib/config.js create mode 100644 test/config.js diff --git a/lib/config.js b/lib/config.js new file mode 100644 index 0000000..a5dbae6 --- /dev/null +++ b/lib/config.js @@ -0,0 +1,5 @@ +module.exports = { + serializedProperties: { + metadata: true, + }, +}; diff --git a/lib/delta-with-metadata.js b/lib/delta-with-metadata.js index beaf6f8..99aaabb 100644 --- a/lib/delta-with-metadata.js +++ b/lib/delta-with-metadata.js @@ -1,3 +1,5 @@ +var config = require('./config'); + var DeltaWithMetadata = function (obj) { let ops = []; @@ -9,8 +11,10 @@ var DeltaWithMetadata = function (obj) { this.ops = ops; - if (obj && obj.metadata) { - this.metadata = obj.metadata; + if (!obj) return; + + for (var key in config.serializedProperties) { + if (config.serializedProperties[key]) this[key] = obj[key]; } }; diff --git a/lib/type.js b/lib/type.js index 8becf4c..9933a39 100644 --- a/lib/type.js +++ b/lib/type.js @@ -1,8 +1,10 @@ var Delta = require('@reedsy/quill-delta'); var DeltaWithMetadata = require('./delta-with-metadata'); +const config = require('./config'); module.exports = { Delta: Delta, + config: config, type: { name: 'rich-text', uri: 'https://github.com/reedsy/rich-text', @@ -53,10 +55,13 @@ module.exports = { }, serialize: function(delta) { - return { - ops: delta.ops, - metadata: delta.metadata, - }; + var serialized = {ops: delta.ops}; + + for (var key in config.serializedProperties) { + if (config.serializedProperties[key]) serialized[key] = delta[key]; + } + + return serialized; }, deserialize: function(ops) { diff --git a/package.json b/package.json index 625947a..fb484e2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@reedsy/rich-text", - "version": "4.1.0-reedsy.3.0.0", + "version": "4.1.0-reedsy.3.1.0", "description": "OT type for rich text", "author": "Jason Chen ", "homepage": "https://github.com/ottypes/rich-text", @@ -12,7 +12,8 @@ "chai": "^4.2.0", "lodash": "^4.17.15", "mocha": "^6.1.4", - "ot-fuzzer": "^1.3.1" + "ot-fuzzer": "^1.3.1", + "sinon": "^15.2.0" }, "engines": { "node": ">=0.10" diff --git a/test/config.js b/test/config.js new file mode 100644 index 0000000..b2fc1f3 --- /dev/null +++ b/test/config.js @@ -0,0 +1,66 @@ +var richText = require('../lib/type').type; +var expect = require('chai').expect; +var sinon = require('sinon'); +const config = require('../lib/config'); + +describe('config', function() { + describe('serialization', function() { + it('serializes metadata by default', function() { + var delta = richText.create(); + delta.metadata = {foo: '123'}; + expect(richText.serialize(delta)).to.eql({ + ops: [], + metadata: {foo: '123'}, + }); + }); + + it('deserializes metadata by default', function() { + var delta = richText.deserialize({ + ops: [], + metadata: {lorem: 'ipsum'}, + }); + + expect(delta.metadata).to.eql({lorem: 'ipsum'}); + }); + + it('does not serialize an unspecified prop', function() { + var delta = richText.create(); + delta.$doNotSerialize = {foo: '123'}; + expect(richText.serialize(delta)).to.eql({ + ops: [], + metadata: undefined, + }); + }); + + it('does not deserialize an unspecified prop', function () { + var delta = richText.deserialize({ + ops: [], + $doNotSerialize: {lorem: 'ipsum'}, + }); + + expect(delta).not.to.have.property('$doNotSerialize'); + }); + + it('can specify extra props to serialize using config', function() { + sinon.stub(config, 'serializedProperties').get(() => ({extra: true})); + + var delta = richText.create(); + delta.extra = {foo: '123'}; + expect(richText.serialize(delta)).to.eql({ + ops: [], + extra: {foo: '123'}, + }); + }); + + it('can specify extra props to deserialize using config', function () { + sinon.stub(config, 'serializedProperties').get(() => ({extra: true})); + + var delta = richText.deserialize({ + ops: [], + extra: {lorem: 'ipsum'}, + }); + + expect(delta.extra).to.eql({lorem: 'ipsum'}); + }); + }); +});