Skip to content

Commit

Permalink
Call 'toJSON' if present for ID and String serialize (#1520)
Browse files Browse the repository at this point in the history
Fixes #1518
  • Loading branch information
IvanGoncharov authored Oct 11, 2018
1 parent 26c9874 commit 3aef662
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 27 deletions.
22 changes: 18 additions & 4 deletions src/type/__tests__/serialization-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,19 @@ describe('Type System: Scalar coercion', () => {

const stringableObjValue = {
valueOf() {
return 'something useful';
return 'valueOf string';
},
toJSON() {
return 'toJSON string';
},
};
expect(GraphQLString.serialize(stringableObjValue)).to.equal(
'something useful',
'valueOf string',
);

delete stringableObjValue.valueOf;
expect(GraphQLString.serialize(stringableObjValue)).to.equal(
'toJSON string',
);

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

const objValue = {
const serializableObjValue = {
_id: 123,
valueOf() {
return this._id;
},
toJSON() {
return `ID:${this._id}`;
},
};
expect(GraphQLID.serialize(objValue)).to.equal('123');
expect(GraphQLID.serialize(serializableObjValue)).to.equal('123');

delete serializableObjValue.valueOf;
expect(GraphQLID.serialize(serializableObjValue)).to.equal('ID:123');

const badObjValue = {
_id: false,
Expand Down
59 changes: 36 additions & 23 deletions src/type/scalars.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,24 +117,39 @@ export const GraphQLFloat = new GraphQLScalarType({
},
});

function serializeString(value: mixed): string {
// Support serializing objects with custom valueOf() functions - a common way
// to represent an complex value which can be represented as a string
// (ex: MongoDB id objects).
const result =
value && typeof value.valueOf === 'function' ? value.valueOf() : value;
// Support serializing objects with custom valueOf() or toJSON() functions -
// a common way to represent a complex value which can be represented as
// a string (ex: MongoDB id objects).
function serializeObject(value: mixed): mixed {
if (typeof value === 'object' && value !== null) {
if (typeof value.valueOf === 'function') {
const valueOfResult = value.valueOf();
if (typeof valueOfResult !== 'object') {
return valueOfResult;
}
}
if (typeof value.toJSON === 'function') {
return value.toJSON();
}
}
return value;
}

function serializeString(rawValue: mixed): string {
const value = serializeObject(rawValue);

// Serialize string, boolean and number values to a string, but do not
// attempt to coerce object, function, symbol, or other types as strings.
if (typeof result === 'string') {
return result;
if (typeof value === 'string') {
return value;
}
if (typeof result === 'boolean') {
return result ? 'true' : 'false';
if (typeof value === 'boolean') {
return value ? 'true' : 'false';
}
if (isFinite(result)) {
return result.toString();
if (isFinite(value)) {
return value.toString();
}
throw new TypeError(`String cannot represent value: ${inspect(value)}`);
throw new TypeError(`String cannot represent value: ${inspect(rawValue)}`);
}

function coerceString(value: mixed): string {
Expand Down Expand Up @@ -190,18 +205,16 @@ export const GraphQLBoolean = new GraphQLScalarType({
},
});

function serializeID(value: mixed): string {
// Support serializing objects with custom valueOf() functions - a common way
// to represent an object identifier (ex. MongoDB).
const result =
value && typeof value.valueOf === 'function' ? value.valueOf() : value;
if (typeof result === 'string') {
return result;
function serializeID(rawValue: mixed): string {
const value = serializeObject(rawValue);

if (typeof value === 'string') {
return value;
}
if (isInteger(result)) {
return String(result);
if (isInteger(value)) {
return String(value);
}
throw new TypeError(`ID cannot represent value: ${inspect(value)}`);
throw new TypeError(`ID cannot represent value: ${inspect(rawValue)}`);
}

function coerceID(value: mixed): string {
Expand Down

0 comments on commit 3aef662

Please sign in to comment.