Skip to content

Commit

Permalink
Refactor to incorporate review feedback. Always require types.
Browse files Browse the repository at this point in the history
  • Loading branch information
leebyron committed May 13, 2021
1 parent 8bff599 commit ba2d451
Show file tree
Hide file tree
Showing 23 changed files with 712 additions and 487 deletions.
2 changes: 1 addition & 1 deletion src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ export {
TypeInfo,
visitWithTypeInfo,
// Converts a value to a const value by replacing variables.
replaceASTVariables,
replaceVariables,
// Create a GraphQL Literal AST from a JavaScript input value.
valueToLiteral,
// Create a JavaScript input value from a GraphQL Literal AST.
Expand Down
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ export {
TypeInfo,
visitWithTypeInfo,
// Converts a value to a const value by replacing variables.
replaceASTVariables,
replaceVariables,
// Create a GraphQL Literal AST from a JavaScript input value.
valueToLiteral,
// Create a JavaScript input value from a GraphQL Literal AST.
Expand Down
4 changes: 4 additions & 0 deletions src/jsutils/hasOwnProperty.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* Determines if a provided object has a given property name.
*/
export function hasOwnProperty(obj: unknown, prop: string): boolean;
6 changes: 6 additions & 0 deletions src/jsutils/hasOwnProperty.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* Determines if a provided object has a given property name.
*/
export function hasOwnProperty(obj: mixed, prop: string): boolean {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
8 changes: 8 additions & 0 deletions src/jsutils/isSignedInt32.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* As per the GraphQL Spec, Integers are only treated as valid when a valid
* 32-bit signed integer, providing the broadest support across platforms.
*
* n.b. JavaScript's integers are safe between -(2^53 - 1) and 2^53 - 1 because
* they are internally represented as IEEE 754 doubles.
*/
export function isSignedInt32(value: unknown): value is number;
18 changes: 18 additions & 0 deletions src/jsutils/isSignedInt32.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const MAX_INT32 = 2147483647;
const MIN_INT32 = -2147483648;

/**
* As per the GraphQL Spec, Integers are only treated as valid when a valid
* 32-bit signed integer, providing the broadest support across platforms.
*
* n.b. JavaScript's integers are safe between -(2^53 - 1) and 2^53 - 1 because
* they are internally represented as IEEE 754 doubles.
*/
export function isSignedInt32(value: mixed): boolean %checks {
return (
typeof value === 'number' &&
Number.isInteger(value) &&
value <= MAX_INT32 &&
value >= MIN_INT32
);
}
14 changes: 8 additions & 6 deletions src/type/definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -1381,12 +1381,14 @@ export class GraphQLEnumType /* <T> */ {
}

valueToLiteral(value: mixed): ?ConstValueNode {
if (typeof value === 'string') {
// https://spec.graphql.org/draft/#Name
if (/^[_a-zA-Z][_a-zA-Z0-9]*$/.test(value)) {
return { kind: Kind.ENUM, value };
}
return { kind: Kind.STRING, value };
if (typeof value === 'string' && this.getValue(value)) {
return { kind: Kind.ENUM, value };
}
}

literalToValue(valueNode: ConstValueNode): mixed {
if (valueNode.kind === Kind.ENUM && this.getValue(valueNode.value)) {
return valueNode.value;
}
}

Expand Down
79 changes: 61 additions & 18 deletions src/type/scalars.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
import { inspect } from '../jsutils/inspect';
import { isObjectLike } from '../jsutils/isObjectLike';
import { isSignedInt32 } from '../jsutils/isSignedInt32';

import type { ConstValueNode } from '../language/ast';
import { Kind } from '../language/kinds';
import { print } from '../language/printer';

import { GraphQLError } from '../error/GraphQLError';

import { defaultScalarLiteralToValue } from '../utilities/literalToValue';
import { defaultScalarValueToLiteral } from '../utilities/valueToLiteral';

import type { GraphQLNamedType } from './definition';
import { GraphQLScalarType } from './definition';

// As per the GraphQL Spec, Integers are only treated as valid when a valid
// 32-bit signed integer, providing the broadest support across platforms.
//
// n.b. JavaScript's integers are safe between -(2^53 - 1) and 2^53 - 1 because
// they are internally represented as IEEE 754 doubles.
const MAX_INT = 2147483647;
const MIN_INT = -2147483648;

function serializeInt(outputValue: mixed): number {
const coercedValue = serializeObject(outputValue);

Expand All @@ -35,7 +31,7 @@ function serializeInt(outputValue: mixed): number {
`Int cannot represent non-integer value: ${inspect(coercedValue)}`,
);
}
if (num > MAX_INT || num < MIN_INT) {
if (!isSignedInt32(num)) {
throw new GraphQLError(
'Int cannot represent non 32-bit signed integer value: ' +
inspect(coercedValue),
Expand All @@ -50,7 +46,7 @@ function coerceInt(inputValue: mixed): number {
`Int cannot represent non-integer value: ${inspect(inputValue)}`,
);
}
if (inputValue > MAX_INT || inputValue < MIN_INT) {
if (!isSignedInt32(inputValue)) {
throw new GraphQLError(
`Int cannot represent non 32-bit signed integer value: ${inputValue}`,
);
Expand All @@ -72,14 +68,27 @@ export const GraphQLInt: GraphQLScalarType = new GraphQLScalarType({
);
}
const num = parseInt(valueNode.value, 10);
if (num > MAX_INT || num < MIN_INT) {
if (!isSignedInt32(num)) {
throw new GraphQLError(
`Int cannot represent non 32-bit signed integer value: ${valueNode.value}`,
valueNode,
);
}
return num;
},
valueToLiteral(value) {
if (isSignedInt32(value)) {
return defaultScalarValueToLiteral(value);
}
},
literalToValue(valueNode) {
if (valueNode.kind === Kind.INT) {
const value = defaultScalarLiteralToValue(valueNode);
if (isSignedInt32(value)) {
return value;
}
}
},
});

function serializeFloat(outputValue: mixed): number {
Expand Down Expand Up @@ -126,6 +135,17 @@ export const GraphQLFloat: GraphQLScalarType = new GraphQLScalarType({
}
return parseFloat(valueNode.value);
},
valueToLiteral(value) {
const literal = defaultScalarValueToLiteral(value);
if (literal.kind === Kind.FLOAT || literal.kind === Kind.INT) {
return literal;
}
},
literalToValue(valueNode) {
if (valueNode.kind === Kind.FLOAT || valueNode.kind === Kind.INT) {
return defaultScalarLiteralToValue(valueNode);
}
},
});

// Support serializing objects with custom valueOf() or toJSON() functions -
Expand Down Expand Up @@ -190,6 +210,17 @@ export const GraphQLString: GraphQLScalarType = new GraphQLScalarType({
}
return valueNode.value;
},
valueToLiteral(value) {
const literal = defaultScalarValueToLiteral(value);
if (literal.kind === Kind.STRING) {
return literal;
}
},
literalToValue(valueNode) {
if (valueNode.kind === Kind.STRING) {
return defaultScalarLiteralToValue(valueNode);
}
},
});

function serializeBoolean(outputValue: mixed): boolean {
Expand Down Expand Up @@ -229,6 +260,17 @@ export const GraphQLBoolean: GraphQLScalarType = new GraphQLScalarType({
}
return valueNode.value;
},
valueToLiteral(value) {
const literal = defaultScalarValueToLiteral(value);
if (literal.kind === Kind.BOOLEAN) {
return literal;
}
},
literalToValue(valueNode) {
if (valueNode.kind === Kind.BOOLEAN) {
return defaultScalarLiteralToValue(valueNode);
}
},
});

function serializeID(outputValue: mixed): string {
Expand Down Expand Up @@ -270,17 +312,18 @@ export const GraphQLID: GraphQLScalarType = new GraphQLScalarType({
return valueNode.value;
},
valueToLiteral(value: mixed): ?ConstValueNode {
// ID types can use Int literals.
if (typeof value === 'string') {
if (/^-?(?:0|[1-9][0-9]*)$/.test(value)) {
return { kind: Kind.INT, value };
}
return { kind: Kind.STRING, value };
// ID types can use number values and Int literals.
const stringValue = Number.isInteger(value) ? String(value) : value;
if (typeof stringValue === 'string') {
// Will parse as an IntValue.
return /^-?(?:0|[1-9][0-9]*)$/.test(stringValue)
? { kind: Kind.INT, value: stringValue }
: { kind: Kind.STRING, value: stringValue, block: false };
}
},
literalToValue(valueNode: ConstValueNode): mixed {
// ID Int literals are represented as string values.
if (valueNode.kind === Kind.INT || valueNode.kind === Kind.STRING) {
if (valueNode.kind === Kind.STRING || valueNode.kind === Kind.INT) {
return valueNode.value;
}
},
Expand Down
Loading

0 comments on commit ba2d451

Please sign in to comment.