Skip to content

Commit cefa80c

Browse files
committed
allow relationships to have an alternative key
closes #12
1 parent 153f0d2 commit cefa80c

File tree

3 files changed

+74
-2
lines changed

3 files changed

+74
-2
lines changed

Diff for: README.md

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Serializer.register(type, options);
3535
- **relationships** (optional): An object defining some relationships
3636
- relationship: The property in data to use as a relationship
3737
- **type**: The type to use for serializing the relationship (type need to be register)
38+
- **alternativeKey** (optional): An alternative key 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).
3839
- **schema** (optional): A custom schema for serializing the relationship. If no schema define, it use the default one.
3940
- **links** (optional): An *object* or a *function* that describes the links for the relationship. (If it is an object values can be string or function).
4041
- **convertCase** (optional): Case conversion for outputted data. Value can be : `kebab-case`, `snake_case`, `camelCase`

Diff for: lib/JSONAPISerializer.js

+18-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ module.exports = class JSONAPISerializer {
3737
links: joi.alternatives([joi.func(), joi.object()]).default({}),
3838
relationships: joi.object().pattern(/.+/, joi.object({
3939
type: joi.string().required(),
40+
alternativeKey: joi.string(),
4041
schema: joi.string(),
4142
links: joi.alternatives([joi.func(), joi.object()]).default({}),
4243
})).default({}),
@@ -183,7 +184,16 @@ module.exports = class JSONAPISerializer {
183184
data = _.pick(data, options.whitelist);
184185
}
185186

186-
let serializedAttributes = _.pick(data, _.difference(Object.keys(data), _.concat([options.id], Object.keys(options.relationships), options.blacklist)));
187+
// Support alternativeKey options for relationships
188+
const alternativeKeys = [];
189+
_.forOwn(options.relationships, (rOptions) => {
190+
if (rOptions.alternativeKey) {
191+
alternativeKeys.push(rOptions.alternativeKey);
192+
}
193+
});
194+
195+
// Remove unwanted keys (id, blacklist, relationships, alternativeKeys)
196+
let serializedAttributes = _.pick(data, _.difference(Object.keys(data), _.concat([options.id], Object.keys(options.relationships), alternativeKeys, options.blacklist)));
187197

188198
if (options.convertCase) {
189199
serializedAttributes = this._convertCase(serializedAttributes, options.convertCase);
@@ -209,9 +219,15 @@ module.exports = class JSONAPISerializer {
209219
_.forOwn(options.relationships, (rOptions, relationship) => {
210220
const schema = rOptions.schema || 'default';
211221

222+
// Support alternativeKey options for relationships
223+
let relationshipKey = relationship;
224+
if (!data[relationship] && rOptions.alternativeKey) {
225+
relationshipKey = rOptions.alternativeKey;
226+
}
227+
212228
const serializeRelationship = {
213229
links: this.processOptionsValues(data, rOptions.links),
214-
data: this.serializeRelationship(rOptions.type, data[relationship], this.schemas[options.relationships[relationship].type][schema], included),
230+
data: this.serializeRelationship(rOptions.type, data[relationshipKey], this.schemas[options.relationships[relationship].type][schema], included),
215231
};
216232

217233
relationship = (options.convertCase) ? this._convertCase(relationship, options.convertCase) : relationship;

Diff for: test/unit/JSONAPISerializer.test.js

+55
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,32 @@ describe('JSONAPISerializer', function() {
333333
expect(serializedRelationships).to.have.property('article-authors');
334334
done();
335335
});
336+
337+
it('should return relationships with alternativeKey option if relationship key not exist', function(done) {
338+
const included = [];
339+
const Serializer = new JSONAPISerializer();
340+
341+
Serializer.register('article', {
342+
relationships: {
343+
author: {
344+
type: 'people',
345+
alternativeKey: 'author_id'
346+
}
347+
}
348+
});
349+
Serializer.register('people');
350+
351+
const serializedRelationships = Serializer.serializeRelationships({
352+
id: '1',
353+
author_id: '1'
354+
}, Serializer.schemas.article.default, included);
355+
expect(serializedRelationships).to.have.property('author');
356+
expect(serializedRelationships.author).to.have.property('data');
357+
expect(serializedRelationships.author.data).to.have.property('type').to.eql('people');
358+
expect(serializedRelationships.author.data).to.have.property('id').to.be.a('string').to.eql('1');
359+
expect(serializedRelationships.author).to.have.property('links').to.be.undefined;
360+
done();
361+
});
336362
});
337363

338364
describe('serializeAttributes', function() {
@@ -455,6 +481,35 @@ describe('JSONAPISerializer', function() {
455481
expect(serializedAttributes.address).to.have.property('zipCode');
456482
done();
457483
});
484+
485+
it('should not return alternativeKey option on relationships', function(done) {
486+
const Serializer = new JSONAPISerializer();
487+
Serializer.register('people', {});
488+
Serializer.register('article', {
489+
relationships: {
490+
author: {
491+
type: 'people',
492+
alternativeKey: 'author_id'
493+
}
494+
}
495+
});
496+
497+
const data = {
498+
id: '1',
499+
title: 'Nice article',
500+
author_id: '1',
501+
author: {
502+
id: '1'
503+
},
504+
};
505+
506+
const serializedAttributes = Serializer.serializeAttributes(data, Serializer.schemas.article.default);
507+
expect(serializedAttributes).to.not.have.property('id'); // No identifier
508+
expect(serializedAttributes).to.not.have.property('author_id'); // No relationship alternativeKey
509+
expect(serializedAttributes).to.not.have.property('author'); // No relationship key
510+
expect(serializedAttributes).to.have.property('title');
511+
done();
512+
});
458513
});
459514

460515
describe('serializeIncluded', function() {

0 commit comments

Comments
 (0)