Skip to content

Commit 5f0c680

Browse files
alechirschdanivek
authored andcommitted
Added ability to override schema options, added tests, updated docs (#96)
added ability to override schema options, added tests, updated docs closes #96
1 parent 04f9a8a commit 5f0c680

File tree

3 files changed

+238
-18
lines changed

3 files changed

+238
-18
lines changed

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,23 @@ Serializer.serializeAsync('article', data, 'default', {count: 2}, true)
343343
});
344344
```
345345

346+
#### Override schema options
347+
348+
On each individual call to `serialize` or `serializeAsync`, there is an parameter to override the options of any registered type. For example on a call to serialize, if a whitelist was not defined on the registered schema options, a whitelist (or any other options) for that type can be provided. This parameter is an object, where the key are the registered type names, and the values are the objects to override the registered schema.
349+
350+
In the following example, only the attribute `name` will be serialized on the article, and if there is a relationship for `person`, it will be serialized with `camelCase` even if the registered schema has a different value.
351+
```
352+
const result = Serializer.serialize('article', data, 'default', {count: 2}, true), {
353+
article: {
354+
whitelist: ['name']
355+
},
356+
person: {
357+
convertCase: 'camelCase'
358+
}
359+
};
360+
```
361+
362+
346363
Some others examples are available in [tests folders](https://github.com/danivek/json-api-serializer/blob/master/test/)
347364

348365
### Deserialize

lib/JSONAPISerializer.js

Lines changed: 101 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,10 @@ module.exports = class JSONAPISerializer {
7474
* @param {string|object} [schema='default'] resource's schema name.
7575
* @param {object} [extraData] additional data that can be used in topLevelMeta options.
7676
* @param {boolean} [excludeData] boolean that can be set to exclude the `data` property in serialized data.
77+
* @param {object} [overrideSchemaOptions=] additional schema options, a map of types with options to override
7778
* @returns {object} serialized data.
7879
*/
79-
serialize(type, data, schema, extraData, excludeData) {
80+
serialize(type, data, schema, extraData, excludeData, overrideSchemaOptions = {}) {
8081
// Support optional arguments (schema)
8182
if (arguments.length === 3) {
8283
if (typeof schema === 'object') {
@@ -108,14 +109,33 @@ module.exports = class JSONAPISerializer {
108109
options = this.schemas[type][schema];
109110
}
110111

112+
const overrideType = isDynamicType ? type.type : type;
113+
if (overrideSchemaOptions[overrideType]) {
114+
// Merge default (registered) options and extra options into new options object
115+
options = { ...options, ...overrideSchemaOptions[overrideType] };
116+
}
117+
111118
let dataProperty;
112119

113120
if (excludeData) {
114121
dataProperty = undefined;
115122
} else if (isDynamicType) {
116-
dataProperty = this.serializeMixedResource(options, data, included, extraData);
123+
dataProperty = this.serializeMixedResource(
124+
options,
125+
data,
126+
included,
127+
extraData,
128+
overrideSchemaOptions
129+
);
117130
} else {
118-
dataProperty = this.serializeResource(type, data, options, included, extraData);
131+
dataProperty = this.serializeResource(
132+
type,
133+
data,
134+
options,
135+
included,
136+
extraData,
137+
overrideSchemaOptions
138+
);
119139
}
120140

121141
return {
@@ -138,9 +158,10 @@ module.exports = class JSONAPISerializer {
138158
* @param {string} [schema='default'] resource's schema name.
139159
* @param {object} [extraData] additional data that can be used in topLevelMeta options.
140160
* @param {boolean} [excludeData] boolean that can be set to exclude the `data` property in serialized data.
161+
* @param {object} [overrideSchemaOptions=] additional schema options, a map of types with options to override
141162
* @returns {Promise} resolves with serialized data.
142163
*/
143-
serializeAsync(type, data, schema, extraData, excludeData) {
164+
serializeAsync(type, data, schema, extraData, excludeData, overrideSchemaOptions = {}) {
144165
// Support optional arguments (schema)
145166
if (arguments.length === 3) {
146167
if (typeof schema === 'object') {
@@ -175,6 +196,12 @@ module.exports = class JSONAPISerializer {
175196
options = this.schemas[type][schema];
176197
}
177198

199+
const overrideType = isDynamicType ? type.type : type;
200+
if (overrideSchemaOptions[overrideType]) {
201+
// Merge default (registered) options and extra options into new options object
202+
options = { ...options, ...overrideSchemaOptions[overrideType] };
203+
}
204+
178205
return new Promise((resolve, reject) => {
179206
/**
180207
* Non-blocking serialization using the immediate queue.
@@ -193,8 +220,21 @@ module.exports = class JSONAPISerializer {
193220
try {
194221
// Serialize a single item of the data-array.
195222
const serializedItem = isDynamicType
196-
? that.serializeMixedResource(type, arrayData[i], included, extraData)
197-
: that.serializeResource(type, arrayData[i], options, included, extraData);
223+
? that.serializeMixedResource(
224+
type,
225+
arrayData[i],
226+
included,
227+
extraData,
228+
overrideSchemaOptions
229+
)
230+
: that.serializeResource(
231+
type,
232+
arrayData[i],
233+
options,
234+
included,
235+
extraData,
236+
overrideSchemaOptions
237+
);
198238

199239
if (serializedItem !== null) {
200240
serializedData.push(serializedItem);
@@ -525,23 +565,32 @@ module.exports = class JSONAPISerializer {
525565
* @param {object} options resource's configuration options.
526566
* @param {Map<string, object>} [included] Included resources.
527567
* @param {object} [extraData] additional data.
568+
* @param {object} [overrideSchemaOptions=] additional schema options, a map of types with options to override
528569
* @returns {object|object[]} serialized data.
529570
*/
530-
serializeResource(type, data, options, included, extraData) {
571+
serializeResource(type, data, options, included, extraData, overrideSchemaOptions = {}) {
531572
if (isEmpty(data)) {
532573
// Return [] or null
533574
return Array.isArray(data) ? data : null;
534575
}
535576

536577
if (Array.isArray(data)) {
537-
return data.map(d => this.serializeResource(type, d, options, included, extraData));
578+
return data.map(d =>
579+
this.serializeResource(type, d, options, included, extraData, overrideSchemaOptions)
580+
);
538581
}
539582

540583
return {
541584
type,
542585
id: data[options.id] ? data[options.id].toString() : undefined,
543586
attributes: this.serializeAttributes(data, options),
544-
relationships: this.serializeRelationships(data, options, included, extraData),
587+
relationships: this.serializeRelationships(
588+
data,
589+
options,
590+
included,
591+
extraData,
592+
overrideSchemaOptions
593+
),
545594
meta: this.processOptionsValues(data, extraData, options.meta),
546595
links: this.processOptionsValues(data, extraData, options.links)
547596
};
@@ -557,16 +606,19 @@ module.exports = class JSONAPISerializer {
557606
* @param {object|object[]} data input data.
558607
* @param {Map<string, object>} [included] Included resources.
559608
* @param {object} [extraData] additional data.
609+
* @param {object} [overrideSchemaOptions=] additional schema options, a map of types with options to override
560610
* @returns {object|object[]} serialized data.
561611
*/
562-
serializeMixedResource(typeOption, data, included, extraData) {
612+
serializeMixedResource(typeOption, data, included, extraData, overrideSchemaOptions = {}) {
563613
if (isEmpty(data)) {
564614
// Return [] or null
565615
return Array.isArray(data) ? data : null;
566616
}
567617

568618
if (Array.isArray(data)) {
569-
return data.map(d => this.serializeMixedResource(typeOption, d, included, extraData));
619+
return data.map(d =>
620+
this.serializeMixedResource(typeOption, d, included, extraData, overrideSchemaOptions)
621+
);
570622
}
571623

572624
// Resolve type from data (can be a string or a function deriving a type-string from each data-item)
@@ -581,7 +633,13 @@ module.exports = class JSONAPISerializer {
581633
throw new Error(`No type registered for ${type}`);
582634
}
583635

584-
return this.serializeResource(type, data, this.schemas[type].default, included, extraData);
636+
let options = this.schemas[type].default;
637+
if (overrideSchemaOptions[type]) {
638+
// Merge default (registered) options and extra options into new options object
639+
options = { ...options, ...overrideSchemaOptions[type] };
640+
}
641+
642+
return this.serializeResource(type, data, options, included, extraData, overrideSchemaOptions);
585643
}
586644

587645
/**
@@ -633,9 +691,10 @@ module.exports = class JSONAPISerializer {
633691
* @param {object} options resource's configuration options.
634692
* @param {Map<string, object>} [included] Included resources.
635693
* @param {object} [extraData] additional data.
694+
* @param {object} [overrideSchemaOptions=] additional schema options, a map of types with options to override
636695
* @returns {object} serialized relationships.
637696
*/
638-
serializeRelationships(data, options, included, extraData) {
697+
serializeRelationships(data, options, included, extraData, overrideSchemaOptions = {}) {
639698
const serializedRelationships = {};
640699

641700
Object.keys(options.relationships).forEach(relationship => {
@@ -656,7 +715,8 @@ module.exports = class JSONAPISerializer {
656715
get(data, relationshipKey),
657716
included,
658717
data,
659-
extraData
718+
extraData,
719+
overrideSchemaOptions
660720
)
661721
};
662722

@@ -689,9 +749,18 @@ module.exports = class JSONAPISerializer {
689749
* @param {Map<string, object>} [included] Included resources.
690750
* @param {object} [data] the entire resource's data.
691751
* @param {object} [extraData] additional data.
752+
* @param {object} [overrideSchemaOptions=] additional schema options, a map of types with options to override
692753
* @returns {object|object[]} serialized relationship data.
693754
*/
694-
serializeRelationship(rType, rSchema, rData, included, data, extraData) {
755+
serializeRelationship(
756+
rType,
757+
rSchema,
758+
rData,
759+
included,
760+
data,
761+
extraData,
762+
overrideSchemaOptions = {}
763+
) {
695764
included = included || new Map();
696765
const schema = rSchema || 'default';
697766

@@ -707,7 +776,15 @@ module.exports = class JSONAPISerializer {
707776

708777
if (Array.isArray(rData)) {
709778
return rData.map(d =>
710-
this.serializeRelationship(rType, schema, d, included, data, extraData)
779+
this.serializeRelationship(
780+
rType,
781+
schema,
782+
d,
783+
included,
784+
data,
785+
extraData,
786+
overrideSchemaOptions
787+
)
711788
);
712789
}
713790

@@ -726,7 +803,13 @@ module.exports = class JSONAPISerializer {
726803
throw new Error(`No schema "${schema}" registered for type "${type}"`);
727804
}
728805

729-
const rOptions = this.schemas[type][schema];
806+
let rOptions = this.schemas[type][schema];
807+
808+
if (overrideSchemaOptions[type]) {
809+
// Merge default (registered) options and extra options into new options object
810+
rOptions = { ...rOptions, ...overrideSchemaOptions[type] };
811+
}
812+
730813
const serializedRelationship = { type };
731814

732815
// Support for unpopulated relationships (an id, or array of ids)
@@ -738,7 +821,7 @@ module.exports = class JSONAPISerializer {
738821
if (!(Object.keys(rData).length === 1 && rData[rOptions.id])) {
739822
included.set(
740823
`${type}-${serializedRelationship.id}`,
741-
this.serializeResource(type, rData, rOptions, included, extraData)
824+
this.serializeResource(type, rData, rOptions, included, extraData, overrideSchemaOptions)
742825
);
743826
}
744827
}

0 commit comments

Comments
 (0)