Skip to content

Commit 273a9d2

Browse files
Fix bug where objects were not being parsed as json
1 parent db908b7 commit 273a9d2

File tree

4 files changed

+79
-19
lines changed

4 files changed

+79
-19
lines changed

Route.ts

Lines changed: 73 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {
22
IMountableItem, IConstructorRouteOptions, IRouteOptions,
3-
IOperationVariable, IResponse,
3+
IOperationVariableMap, IOperationVariable, IResponse,
44
} from '.';
55

66
import { IncomingHttpHeaders } from 'http';
@@ -42,12 +42,40 @@ function cleanPath(path: string): string {
4242
return `/${path}`;
4343
}
4444

45+
46+
// NOTE:
47+
// Consider moving the introspection of the graphql query into the routes so that
48+
// we know for certain which variables are INPUT_VARIABLES and which are enums / strings.
49+
//
50+
// This attempts to typecast all unknown types to JSON but the try catch deoptimizes the parsing of the JSON
51+
// and may affect performance.
52+
function attemptJSONParse(variable: string): any {
53+
try {
54+
return JSON.parse(variable);
55+
} catch (e) {
56+
return variable;
57+
}
58+
}
59+
60+
function typecastVariable(variable: string, variableDefinition: IOperationVariable): any {
61+
switch (variableDefinition.type) {
62+
case 'Int':
63+
return parseInt(variable, 10);
64+
case 'Boolean':
65+
return Boolean(variable);
66+
case 'String':
67+
return variable;
68+
default:
69+
return attemptJSONParse(variable);
70+
}
71+
}
72+
4573
export default class Route implements IMountableItem {
4674
public path!: string;
4775
public httpMethod: string = 'get';
4876

4977
public passThroughHeaders: string[] = [];
50-
public operationVariables!: IOperationVariable[];
78+
public operationVariables!: IOperationVariableMap;
5179
public operationName!: string;
5280

5381
// TODO:
@@ -128,16 +156,24 @@ export default class Route implements IMountableItem {
128156
return this;
129157
}
130158

131-
private getOperationVariables(operation: any): IOperationVariable[] {
132-
return operation.variableDefinitions.map(
133-
(node: any): IOperationVariable => ({
134-
name: node.variable.name.value,
135-
required: node.type.kind === 'NonNullType',
136-
type: translateVariableType(node),
137-
array: isVariableArray(node),
138-
defaultValue: (node.defaultValue || {}).value,
139-
})
159+
private getOperationVariables(operation: any): IOperationVariableMap {
160+
const variableMap: IOperationVariableMap = {};
161+
162+
operation.variableDefinitions.forEach(
163+
(node: any): void => {
164+
const variable: IOperationVariable = {
165+
name: node.variable.name.value,
166+
required: node.type.kind === 'NonNullType',
167+
type: translateVariableType(node),
168+
array: isVariableArray(node),
169+
defaultValue: (node.defaultValue || {}).value,
170+
};
171+
172+
variableMap[variable.name] = variable;
173+
}
140174
);
175+
176+
return variableMap;
141177
}
142178

143179
private setOperationName(operationName: string): void {
@@ -189,7 +225,7 @@ export default class Route implements IMountableItem {
189225
}
190226

191227
private get requiredVariables(): string[] {
192-
return this.operationVariables
228+
return Object.values(this.operationVariables)
193229
.filter(
194230
({ required, defaultValue }) => required && !defaultValue
195231
)
@@ -227,11 +263,33 @@ export default class Route implements IMountableItem {
227263
.filter(requiredVariable => !variablesAsKeys.includes(requiredVariable));
228264
}
229265

266+
// When an encoded value is passed in, it is decoded automatically but will always
267+
// be a string.
268+
//
269+
// This method will iterate through all variables, check their definition type from the spec
270+
// and typecast them
271+
private typecastVariables(variables: { [key: string]: string }): { [key: string]: any } {
272+
const parsedVariables: { [key: string]: any } = {};
273+
274+
Object.entries(variables).forEach(
275+
([variableName, value]) => {
276+
const variableDefinition = this.operationVariables[variableName];
277+
278+
parsedVariables[variableName] = typecastVariable(value, variableDefinition);
279+
}
280+
);
281+
282+
return parsedVariables;
283+
}
284+
230285
asExpressRoute() {
231286
return async (req: express.Request, res: express.Response) => {
232287
const { query, params, body } = req;
233288

234-
const providedVariables = { ...query, ...params, ...body };
289+
const parsedQueryVariables = this.typecastVariables(query);
290+
const parsedPathVariables = this.typecastVariables(params);
291+
292+
const providedVariables = { ...parsedQueryVariables, ...parsedPathVariables, ...body };
235293

236294
// Assemble variables from query, path and default values
237295
const assembledVariables = this.assembleVariables(providedVariables);
@@ -309,15 +367,15 @@ export default class Route implements IMountableItem {
309367

310368
const pathVariableNames = matches.map(match => match.substr(1));
311369

312-
return this.operationVariables.filter(
370+
return Object.values(this.operationVariables).filter(
313371
({ name }) => pathVariableNames.includes(name)
314372
);
315373
}
316374

317375
get nonPathVariables(): IOperationVariable[] {
318376
const pathVariableNames = this.pathVariables.map(({ name }) => name);
319377

320-
return this.operationVariables
378+
return Object.values(this.operationVariables)
321379
.filter(
322380
({ name }) => !pathVariableNames.includes(name)
323381
);

describeRouteVariables.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,7 @@ function buildIntrospectionQuery(variables: string[]): string {
5050
function getAllUsedVariables(routes: Route[]): string[] {
5151
const variables: string[] = ([] as string[]).concat(
5252
...routes.map(
53-
route => route.operationVariables.map(
54-
variable => variable.type
55-
)
53+
route => Object.values(route.operationVariables).map(variable => variable.type)
5654
)
5755
);
5856

index.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ export interface IRouteOptions {
4141
defaultVariables?: {};
4242
}
4343

44+
export interface IOperationVariableMap {
45+
[variableName: string]: IOperationVariable;
46+
}
47+
4448
export interface IOperationVariable {
4549
name: string;
4650
required: boolean;

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "graphql-rest-router",
3-
"version": "1.0.0-alpha.11",
3+
"version": "1.0.0-alpha.12",
44
"description": "",
55
"main": "index.js",
66
"types": "./index.d.ts",

0 commit comments

Comments
 (0)