Skip to content

Commit 3aef662

Browse files
Call 'toJSON' if present for ID and String serialize (#1520)
Fixes #1518
1 parent 26c9874 commit 3aef662

File tree

2 files changed

+54
-27
lines changed

2 files changed

+54
-27
lines changed

src/type/__tests__/serialization-test.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,19 @@ describe('Type System: Scalar coercion', () => {
113113

114114
const stringableObjValue = {
115115
valueOf() {
116-
return 'something useful';
116+
return 'valueOf string';
117+
},
118+
toJSON() {
119+
return 'toJSON string';
117120
},
118121
};
119122
expect(GraphQLString.serialize(stringableObjValue)).to.equal(
120-
'something useful',
123+
'valueOf string',
124+
);
125+
126+
delete stringableObjValue.valueOf;
127+
expect(GraphQLString.serialize(stringableObjValue)).to.equal(
128+
'toJSON string',
121129
);
122130

123131
expect(() => GraphQLString.serialize(NaN)).to.throw(
@@ -165,13 +173,19 @@ describe('Type System: Scalar coercion', () => {
165173
expect(GraphQLID.serialize(0)).to.equal('0');
166174
expect(GraphQLID.serialize(-1)).to.equal('-1');
167175

168-
const objValue = {
176+
const serializableObjValue = {
169177
_id: 123,
170178
valueOf() {
171179
return this._id;
172180
},
181+
toJSON() {
182+
return `ID:${this._id}`;
183+
},
173184
};
174-
expect(GraphQLID.serialize(objValue)).to.equal('123');
185+
expect(GraphQLID.serialize(serializableObjValue)).to.equal('123');
186+
187+
delete serializableObjValue.valueOf;
188+
expect(GraphQLID.serialize(serializableObjValue)).to.equal('ID:123');
175189

176190
const badObjValue = {
177191
_id: false,

src/type/scalars.js

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -117,24 +117,39 @@ export const GraphQLFloat = new GraphQLScalarType({
117117
},
118118
});
119119

120-
function serializeString(value: mixed): string {
121-
// Support serializing objects with custom valueOf() functions - a common way
122-
// to represent an complex value which can be represented as a string
123-
// (ex: MongoDB id objects).
124-
const result =
125-
value && typeof value.valueOf === 'function' ? value.valueOf() : value;
120+
// Support serializing objects with custom valueOf() or toJSON() functions -
121+
// a common way to represent a complex value which can be represented as
122+
// a string (ex: MongoDB id objects).
123+
function serializeObject(value: mixed): mixed {
124+
if (typeof value === 'object' && value !== null) {
125+
if (typeof value.valueOf === 'function') {
126+
const valueOfResult = value.valueOf();
127+
if (typeof valueOfResult !== 'object') {
128+
return valueOfResult;
129+
}
130+
}
131+
if (typeof value.toJSON === 'function') {
132+
return value.toJSON();
133+
}
134+
}
135+
return value;
136+
}
137+
138+
function serializeString(rawValue: mixed): string {
139+
const value = serializeObject(rawValue);
140+
126141
// Serialize string, boolean and number values to a string, but do not
127142
// attempt to coerce object, function, symbol, or other types as strings.
128-
if (typeof result === 'string') {
129-
return result;
143+
if (typeof value === 'string') {
144+
return value;
130145
}
131-
if (typeof result === 'boolean') {
132-
return result ? 'true' : 'false';
146+
if (typeof value === 'boolean') {
147+
return value ? 'true' : 'false';
133148
}
134-
if (isFinite(result)) {
135-
return result.toString();
149+
if (isFinite(value)) {
150+
return value.toString();
136151
}
137-
throw new TypeError(`String cannot represent value: ${inspect(value)}`);
152+
throw new TypeError(`String cannot represent value: ${inspect(rawValue)}`);
138153
}
139154

140155
function coerceString(value: mixed): string {
@@ -190,18 +205,16 @@ export const GraphQLBoolean = new GraphQLScalarType({
190205
},
191206
});
192207

193-
function serializeID(value: mixed): string {
194-
// Support serializing objects with custom valueOf() functions - a common way
195-
// to represent an object identifier (ex. MongoDB).
196-
const result =
197-
value && typeof value.valueOf === 'function' ? value.valueOf() : value;
198-
if (typeof result === 'string') {
199-
return result;
208+
function serializeID(rawValue: mixed): string {
209+
const value = serializeObject(rawValue);
210+
211+
if (typeof value === 'string') {
212+
return value;
200213
}
201-
if (isInteger(result)) {
202-
return String(result);
214+
if (isInteger(value)) {
215+
return String(value);
203216
}
204-
throw new TypeError(`ID cannot represent value: ${inspect(value)}`);
217+
throw new TypeError(`ID cannot represent value: ${inspect(rawValue)}`);
205218
}
206219

207220
function coerceID(value: mixed): string {

0 commit comments

Comments
 (0)