Skip to content

Commit 3d2cebf

Browse files
committed
minor cleanup and updated weight function
1 parent 5fcd37b commit 3d2cebf

File tree

2 files changed

+84
-49
lines changed

2 files changed

+84
-49
lines changed

src/@types/buildTypeWeights.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
interface Fields {
2-
[index: string]: number | ((arg: number, type: Type) => number);
2+
[index: string]: number | ((args: ArgumentNode[]) => number);
33
}
44

55
interface Type {

src/analysis/buildTypeWeights.ts

Lines changed: 83 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
import {
2+
ArgumentNode,
23
GraphQLArgument,
3-
GraphQLEnumType,
44
GraphQLFieldMap,
5-
GraphQLInterfaceType,
6-
GraphQLList,
75
GraphQLNamedType,
8-
GraphQLNonNull,
96
GraphQLObjectType,
107
GraphQLOutputType,
11-
GraphQLScalarType,
12-
GraphQLUnionType,
8+
IntValueNode,
139
isCompositeType,
10+
isEnumType,
11+
isInterfaceType,
12+
isListType,
13+
isNonNullType,
14+
isObjectType,
15+
isScalarType,
16+
isUnionType,
17+
ValueNode,
1418
} from 'graphql';
1519
import { Maybe } from 'graphql/jsutils/Maybe';
20+
import { ObjMap } from 'graphql/jsutils/ObjMap';
1621
import { GraphQLSchema } from 'graphql/type/schema';
1722

1823
const KEYWORDS = ['first', 'last', 'limit'];
@@ -24,12 +29,17 @@ const KEYWORDS = ['first', 'last', 'limit'];
2429
* scalar: 0
2530
* connection: 2
2631
*/
32+
33+
// These variables exist to provide a default value for typescript when accessing a weight
34+
// since all props are optioal in TypeWeightConfig
2735
const DEFAULT_MUTATION_WEIGHT = 10;
2836
const DEFAULT_OBJECT_WEIGHT = 1;
2937
const DEFAULT_SCALAR_WEIGHT = 0;
3038
const DEFAULT_CONNECTION_WEIGHT = 2;
3139
const DEFAULT_QUERY_WEIGHT = 1;
3240

41+
// FIXME: What about Union, Enum and Interface defaults
42+
3343
export const defaultTypeWeightsConfig: TypeWeightConfig = {
3444
mutation: DEFAULT_MUTATION_WEIGHT,
3545
object: DEFAULT_OBJECT_WEIGHT,
@@ -43,8 +53,8 @@ export const defaultTypeWeightsConfig: TypeWeightConfig = {
4353
* back on shopifys settings. We can change this later.
4454
*
4555
* This function should
46-
* - TODO: iterate through the schema object and create the typeWeightObject as described in the tests
47-
* - TODO: validate that the typeWeightsConfig parameter has no negative values (throw an error if it does)
56+
* - iterate through the schema object and create the typeWeightObject as described in the tests
57+
* - validate that the typeWeightsConfig parameter has no negative values (throw an error if it does)
4858
*
4959
* @param schema
5060
* @param typeWeightsConfig Defaults to {mutation: 10, object: 1, field: 0, connection: 2}
@@ -53,14 +63,6 @@ function buildTypeWeightsFromSchema(
5363
schema: GraphQLSchema,
5464
typeWeightsConfig: TypeWeightConfig = defaultTypeWeightsConfig
5565
): TypeWeightObject {
56-
// Iterate each key in the schema object
57-
// this includes scalars, types, interfaces, unions, enums etc.
58-
// check the type of each add set the appropriate weight.
59-
// iterate through that types fields and set the appropriate weight
60-
// this is kind of only relevant for things like Query or Mutation
61-
// that have functions(?) as fields for which we should set the weight as a function
62-
// that take any required params.
63-
6466
if (!schema) throw new Error('Must provide schema');
6567

6668
// Merge the provided type weights with the default to account for missing values
@@ -78,50 +80,44 @@ function buildTypeWeightsFromSchema(
7880

7981
const result: TypeWeightObject = {};
8082

81-
// Iterate through __typeMap and set weights of all object types?
82-
83-
const typeMap = schema.getTypeMap();
83+
const typeMap: ObjMap<GraphQLNamedType> = schema.getTypeMap();
8484

85+
// Handle Object, Interface, Enum and Union types
8586
Object.keys(typeMap).forEach((type) => {
8687
const currentType: GraphQLNamedType = typeMap[type];
87-
// Limit to object types for now
88-
// Get all types that aren't Query or Mutation and don't start with __
88+
// Get all types that aren't Query or Mutation or a built in type that starts with '__'
8989
if (
9090
currentType.name !== 'Query' &&
9191
currentType.name !== 'Mutation' &&
9292
!currentType.name.startsWith('__')
9393
) {
94-
if (
95-
currentType instanceof GraphQLObjectType ||
96-
currentType instanceof GraphQLInterfaceType
97-
) {
94+
if (isObjectType(currentType) || isInterfaceType(currentType)) {
9895
// Add the type to the result
9996
result[type] = {
10097
fields: {},
10198
weight: typeWeights.object || DEFAULT_OBJECT_WEIGHT,
10299
};
103100

104101
const fields = currentType.getFields();
102+
105103
Object.keys(fields).forEach((field: string) => {
106104
const fieldType: GraphQLOutputType = fields[field].type;
107105
if (
108-
fieldType instanceof GraphQLScalarType ||
109-
(fieldType instanceof GraphQLNonNull &&
110-
fieldType.ofType instanceof GraphQLScalarType)
106+
isScalarType(fieldType) ||
107+
(isNonNullType(fieldType) && isScalarType(fieldType.ofType))
111108
) {
112109
result[type].fields[field] = typeWeights.scalar || DEFAULT_SCALAR_WEIGHT;
113110
}
114-
// FIXME: Do any other types need to be included?
115111
});
116-
} else if (currentType instanceof GraphQLEnumType) {
112+
} else if (isEnumType(currentType)) {
117113
result[currentType.name] = {
118114
fields: {},
119-
weight: 0,
115+
weight: typeWeights.scalar || DEFAULT_SCALAR_WEIGHT,
120116
};
121-
} else if (currentType instanceof GraphQLUnionType) {
117+
} else if (isUnionType(currentType)) {
122118
result[currentType.name] = {
123119
fields: {},
124-
weight: 1, // FIXME: Use the correct weight
120+
weight: typeWeights.object || DEFAULT_OBJECT_WEIGHT,
125121
};
126122
}
127123
}
@@ -133,40 +129,79 @@ function buildTypeWeightsFromSchema(
133129
if (queryType) {
134130
result.Query = {
135131
weight: typeWeights.query || DEFAULT_QUERY_WEIGHT,
136-
fields: {
137-
// This object gets populated with the query fields and associated weights.
138-
},
132+
// fields gets populated with the query fields and associated weights.
133+
fields: {},
139134
};
135+
140136
const queryFields: GraphQLFieldMap<any, any> = queryType.getFields();
137+
141138
Object.keys(queryFields).forEach((field) => {
139+
// this is the type the query resolves to
142140
const resolveType: GraphQLOutputType = queryFields[field].type;
143141

142+
// check if any of our keywords 'first', 'last', 'limit' exist in the arg list
144143
queryFields[field].args.forEach((arg: GraphQLArgument) => {
145-
// check if any of our keywords 'first', 'last', 'limit' exist in the arglist
146-
if (KEYWORDS.includes(arg.name) && resolveType instanceof GraphQLList) {
144+
// If query has an argument matching one of the limiting keywords and resolves to a list then the weight of the query
145+
// should be dependent on both the weight of the resolved type and the limiting argument.
146+
if (KEYWORDS.includes(arg.name) && isListType(resolveType)) {
147147
const defaultVal: number = <number>arg.defaultValue;
148-
// FIXME: How can we provide the complexity analysis algo with name of the argument to use?
148+
149+
// Get the type that comprises the list
149150
const listType = resolveType.ofType;
151+
152+
// Composite Types are Objects, Interfaces and Unions.
150153
if (isCompositeType(listType)) {
151-
result.Query.fields[field] = (multiplier: number = defaultVal) =>
152-
multiplier * result[listType.name].weight;
154+
// Set the field weight to a function that accepts
155+
// TODO: Accept ArgumentNode[] and look for the arg we need.
156+
// TODO: Test this function
157+
result.Query.fields[field] = (args: ArgumentNode[]): number => {
158+
// Function should receive object with arg, value as k, v pairs
159+
// function iterate on this object looking for a keyword then returns
160+
const limitArg: ArgumentNode | undefined = args.find(
161+
(cur) => cur.name.value === arg.name
162+
);
163+
164+
// const isVariable = (node: any): node is VariableNode => {
165+
// if (node as VariableNode) return true;
166+
// return false;
167+
// };
168+
169+
const isIntNode = (node: any): node is IntValueNode => {
170+
if (node as IntValueNode) return true;
171+
return false;
172+
};
173+
174+
if (limitArg) {
175+
const node: ValueNode = limitArg.value;
176+
177+
// FIXME: Is there a better way to check for the type here?
178+
if (isIntNode(node)) {
179+
const multiplier = Number(node.value || arg.defaultValue);
180+
181+
return result[listType.name].weight * multiplier;
182+
}
183+
}
184+
185+
// FIXME: The list is unbounded. Return the object weight
186+
return result[listType.name].weight;
187+
};
188+
} else {
189+
// TODO: determine the type of the list and use the appropriate weight
190+
// TODO: This should multiply as well
191+
result.Query.fields[field] = typeWeights.scalar || DEFAULT_SCALAR_WEIGHT;
153192
}
154193
}
155194
});
156195

157-
// if the field is a scalars set weight accordingly
196+
// if the field is a scalar set weight accordingly
158197
// FIXME: Enums shouldn't be here???
159-
if (
160-
resolveType instanceof GraphQLScalarType ||
161-
resolveType instanceof GraphQLEnumType
162-
) {
198+
if (isScalarType(resolveType) || isEnumType(resolveType)) {
163199
result.Query.fields[field] = typeWeights.scalar || DEFAULT_SCALAR_WEIGHT;
164200
}
165201
});
166202
}
167203

168-
// get the type of the field
169-
170204
return result;
171205
}
206+
172207
export default buildTypeWeightsFromSchema;

0 commit comments

Comments
 (0)