Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Serializer.register(type, options);
- **id** (optional): The key to use as the reference. Default = 'id'.
- **blacklist** (optional): An array of blacklisted attributes. Default = [].
- **whitelist** (optional): An array of whitelisted attributes. Default = [].
- **jsonapiObject** (optional): Enable/Disable [JSON API Object](http://jsonapi.org/format/#document-jsonapi-object). Default = true.
- **links** (optional): Describes the links inside data. 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) { ... }`
Expand Down Expand Up @@ -385,6 +386,7 @@ relationships: {
If your data contains one or multiple objects of different types, it's possible to define a configuration object instead of the type-string as the first argument of ```serialize``` and ```serializeAsync``` with these options:

- **type** (required): A *string* for the path to the key to use to determine type or a *function* deriving a type-string from each data-item.
- **jsonapiObject** (optional): Enable/Disable [JSON API Object](http://jsonapi.org/format/#document-jsonapi-object). Default = true.
- **topLevelMeta** (optional): Describes the top-level meta. It can be:
- An *object* (values can be string or function).
- A *function* with one argument `function(extraData) { ... }` or with two arguments `function(data, extraData) { ... }`
Expand Down
42 changes: 17 additions & 25 deletions lib/JSONAPISerializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ module.exports = class JSONAPISerializer {
unconvertCase: joi.string().valid('kebab-case', 'snake_case', 'camelCase'),
blacklistOnDeserialize: joi.array().items(joi.string()).single().default([]),
whitelistOnDeserialize: joi.array().items(joi.string()).single().default([]),
jsonapiObject: joi.boolean().default(true),
}).required();

const validated = joi.validate(options, optionsSchema);
Expand All @@ -77,6 +78,7 @@ module.exports = class JSONAPISerializer {
type: joi.alternatives([joi.func(), joi.string()]).required(),
topLevelLinks: joi.alternatives([joi.func(), joi.object()]).default({}),
topLevelMeta: joi.alternatives([joi.func(), joi.object()]).default({}),
jsonapiObject: joi.boolean().default(true),
}).required();

const validated = joi.validate(options, schema);
Expand Down Expand Up @@ -134,13 +136,12 @@ module.exports = class JSONAPISerializer {

const included = [];
let serializedData;
let topLevelOption;
let options;

if (_.isPlainObject(type)) { // Serialize data with the dynamic type
const typeOption = this.validateDynamicTypeOptions(type);
options = this.validateDynamicTypeOptions(type);
// Override top level data
topLevelOption = { topLevelMeta: typeOption.topLevelMeta, topLevelLinks: typeOption.topLevelLinks };
serializedData = this.serializeMixedData(typeOption, data, included, extraData);
serializedData = this.serializeMixedData(options, data, included, extraData);
} else { // Serialize data with the defined type
if (!this.schemas[type]) {
throw new Error(`No type registered for ${type}`);
Expand All @@ -150,18 +151,15 @@ module.exports = class JSONAPISerializer {
throw new Error(`No schema ${schema} registered for ${type}`);
}

const resourceOpts = this.schemas[type][schema];
topLevelOption = { topLevelMeta: resourceOpts.topLevelMeta, topLevelLinks: resourceOpts.topLevelLinks };
serializedData = this.serializeData(type, data, resourceOpts, included, extraData);
options = this.schemas[type][schema];
serializedData = this.serializeData(type, data, options, included, extraData);
}


return {
jsonapi: {
version: '1.0',
},
meta: this.processOptionsValues(data, extraData, topLevelOption.topLevelMeta, 'extraData'),
links: this.processOptionsValues(data, extraData, topLevelOption.topLevelLinks, 'extraData'),
jsonapi: options.jsonapiObject ? { version: '1.0' } : undefined,
meta: this.processOptionsValues(data, extraData, options.topLevelMeta, 'extraData'),
links: this.processOptionsValues(data, extraData, options.topLevelLinks, 'extraData'),
data: serializedData,
included: this.serializeIncluded(included),
};
Expand Down Expand Up @@ -194,15 +192,12 @@ module.exports = class JSONAPISerializer {
const included = [];
const isDataArray = Array.isArray(data);
const isDynamicType = _.isPlainObject(type);
let topLevelOption;
let serializedData;
let serializedIncludes;
let resourceOpts;
let options;

if (isDynamicType) {
type = this.validateDynamicTypeOptions(type);
// Override top level data
topLevelOption = { topLevelMeta: type.topLevelMeta, topLevelLinks: type.topLevelLinks };
options = this.validateDynamicTypeOptions(type);
} else {
if (!this.schemas[type]) {
throw new Error(`No type registered for ${type}`);
Expand All @@ -212,8 +207,7 @@ module.exports = class JSONAPISerializer {
throw new Error(`No schema ${schema} registered for ${type}`);
}

resourceOpts = this.schemas[type][schema];
topLevelOption = { topLevelMeta: resourceOpts.topLevelMeta, topLevelLinks: resourceOpts.topLevelLinks };
options = this.schemas[type][schema];
}

// Convert data into stream with serialization-transform. Single objects
Expand All @@ -225,7 +219,7 @@ module.exports = class JSONAPISerializer {
// Serialize a single item of the data-array.
const serializedItem = isDynamicType
? this.serializeMixedData(type, item, included, extraData)
: this.serializeData(type, item, resourceOpts, included, extraData);
: this.serializeData(type, item, options, included, extraData);

// If the serialized item is null, we won't push it to the stream,
// as pushing a null-value causes streams to end.
Expand All @@ -250,11 +244,9 @@ module.exports = class JSONAPISerializer {
.then((result) => {
serializedIncludes = result;
return {
jsonapi: {
version: '1.0',
},
meta: this.processOptionsValues(data, extraData, topLevelOption.topLevelMeta, 'extraData'),
links: this.processOptionsValues(data, extraData, topLevelOption.topLevelLinks, 'extraData'),
jsonapi: options.jsonapiObject ? { version: '1.0' } : undefined,
meta: this.processOptionsValues(data, extraData, options.topLevelMeta, 'extraData'),
links: this.processOptionsValues(data, extraData, options.topLevelLinks, 'extraData'),
// If the source data was an array, we just pass the serialized data array.
// Otherwise we try to take the first (and only) item of it or pass null.
data: isDataArray ? serializedData : (serializedData[0] || null),
Expand Down
2 changes: 1 addition & 1 deletion test/integration/examples.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ describe('Examples', function() {
var serializedData = Serializer.serialize('article', articlesData, {
count: 2
});
expect(serializedData).to.have.property('jsonapi').to.have.property('version');
expect(serializedData).to.have.property('jsonapi').to.have.property('version').to.eql('1.0');
expect(serializedData).to.have.property('meta').to.have.property('count').to.eql(2);
expect(serializedData).to.have.property('links').to.have.property('self').to.eql('/articles');
expect(serializedData).to.have.property('data');
Expand Down
33 changes: 33 additions & 0 deletions test/unit/JSONAPISerializer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,39 @@ describe('JSONAPISerializer', function() {
expect(serializedData.included).to.be.undefined;
done();
});

it('should serialize with option \'jsonapiObject\' disabled', function(done) {
const Serializer = new JSONAPISerializer();
Serializer.register('article', {
jsonapiObject: false
});

const data = {
id: '1',
title: 'JSON API paints my bikeshed!',
body: 'The shortest article. Ever.'
};

const serializedData = Serializer.serialize('article', data);
expect(serializedData).have.property('jsonapi').to.be.undefined;
done();
});

it('should serialize mixed data with option \'jsonapiObject\' disabled', function(done) {
const Serializer = new JSONAPISerializer();
Serializer.register('article');

const data = {
id: '1',
type: 'article',
title: 'JSON API paints my bikeshed!',
body: 'The shortest article. Ever.'
};

const serializedData = Serializer.serialize({ type: 'type', jsonapiObject: false }, data);
expect(serializedData).have.property('jsonapi').to.be.undefined;
done();
});
});

describe('serializeAsync', function() {
Expand Down