From cc9e83d57d346aece3c48a4b226b0a735c052fa9 Mon Sep 17 00:00:00 2001 From: danivek Date: Mon, 12 Oct 2020 10:45:19 +0200 Subject: [PATCH] feat: add data option on relationships --- README.md | 1 + lib/JSONAPISerializer.js | 7 ++++++- lib/validator.js | 3 +++ test/unit/JSONAPISerializer.test.js | 31 +++++++++++++++++++++++++++++ test/unit/validator.test.js | 15 ++++++++++++++ 5 files changed, 56 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ef78996..18b9f74 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ Serializer.register(type, options); * **type**: A _string_ or a _function_ `function(relationshipData, data) { ... }` for the type to use for serializing the relationship (type need to be register). * **alternativeKey** (optional): An alternative key (string or path) to use if relationship key not exist (example: 'author_id' as an alternative key for 'author' relationship). See [issue #12](https://github.com/danivek/json-api-serializer/issues/12). * **schema** (optional): A custom schema for serializing the relationship. If no schema define, it use the default one. + * **data** (optional): A _function_ `function(data) { ... }` that returns the data to serialize for the relationship. * **links** (optional): Describes the links for the relationship. It can be: * An _object_ (values can be string or function). * A _function_ with one argument `function(data) { ... }` or with two arguments `function(data, extraData) { ... }` diff --git a/lib/JSONAPISerializer.js b/lib/JSONAPISerializer.js index 0bf2221..bc1adf7 100644 --- a/lib/JSONAPISerializer.js +++ b/lib/JSONAPISerializer.js @@ -716,13 +716,17 @@ module.exports = class JSONAPISerializer { relationshipKey = relationshipOptions.alternativeKey; } + const dataFn = relationshipOptions.data + ? relationshipOptions.data + : (relationshipData) => relationshipData; + const serializeRelationship = { links: this.processOptionsValues(data, extraData, relationshipOptions.links), meta: this.processOptionsValues(data, extraData, relationshipOptions.meta), data: this.serializeRelationship( relationshipOptions.type, relationshipOptions.schema, - get(data, relationshipKey), + dataFn(get(data, relationshipKey)), included, data, extraData, @@ -996,6 +1000,7 @@ module.exports = class JSONAPISerializer { * @property {string|Function} type a string or a function for the type to use for serializing the relationship (type need to be register) * @property {string} [alternativeKey] an alternative key (string or path) to use if relationship key not exist (example: 'author_id' as an alternative key for 'author' relationship) * @property {string} [schema] a custom schema for serializing the relationship. If no schema define, it use the default one. + * @property {Function} [data] a function that returns the data to serialize for the relationship * @property {Function|object} [links] describes the links for the relationship * @property {Function|object} [meta] describes meta that contains non-standard meta-information about the relationship * @property {Function} [deserialize] describes the function which should be used to deserialize a related property which is not included in the JSON:API document diff --git a/lib/validator.js b/lib/validator.js index 663a211..8ea0d6b 100644 --- a/lib/validator.js +++ b/lib/validator.js @@ -102,6 +102,9 @@ function validateOptions(options) { if (relationships[key].deserialize && typeof relationships[key].deserialize !== 'function') throw new Error(`option 'deserialize' for relationship '${key}' must be a function`); + + if (relationships[key].data && typeof relationships[key].data !== 'function') + throw new Error(`option 'data' for relationship '${key}' must be a function`); }); return options; diff --git a/test/unit/JSONAPISerializer.test.js b/test/unit/JSONAPISerializer.test.js index 0f02348..507e0f8 100644 --- a/test/unit/JSONAPISerializer.test.js +++ b/test/unit/JSONAPISerializer.test.js @@ -644,6 +644,37 @@ describe('JSONAPISerializer', function() { expect(serializedRelationships.author).to.have.property('links').to.be.undefined; done(); }); + + it('should return relationships with data option', function(done) { + const Serializer = new JSONAPISerializer(); + + Serializer.register('article', { + relationships: { + comments: { + type: 'comment', + data: (data) => data.data + } + } + }); + Serializer.register('comment'); + + const serializedRelationships = Serializer.serializeRelationships({ + id: '1', + comments: { + total: 2, + data: [{id: '1', title: 'comment 1'}, {id: '2', title: 'comment 2'}] + } + }, Serializer.schemas.article.default); + console.log(JSON.stringify(serializedRelationships, null, 2)); + expect(serializedRelationships).to.have.property('comments'); + expect(serializedRelationships.comments).to.have.property('data'); + expect(serializedRelationships.comments.data).to.be.instanceof(Array).to.have.length(2); + expect(serializedRelationships.comments.data[0]).to.have.property('type').to.eql('comment'); + expect(serializedRelationships.comments.data[0]).to.have.property('id').to.be.a('string').to.eql('1'); + expect(serializedRelationships.comments.data[1]).to.have.property('id').to.be.a('string').to.eql('2'); + expect(serializedRelationships.comments).to.have.property('links').to.be.undefined; + done(); + }); }); describe('serializeAttributes', function() { diff --git a/test/unit/validator.test.js b/test/unit/validator.test.js index 60642f2..84741b4 100644 --- a/test/unit/validator.test.js +++ b/test/unit/validator.test.js @@ -291,5 +291,20 @@ describe('validator', function () { done(); }); + + it('incorrect data on relationship', (done) => { + expect(function () { + validator.validateOptions({ + relationships: { + test: { + type: 'test', + data: 'test', + }, + }, + }); + }).to.throw(Error, "option 'data' for relationship 'test' must be a function"); + + done(); + }); }); });