From 86f0df7fd5f25d6756557c54590d42b7987d01c8 Mon Sep 17 00:00:00 2001 From: yoavkarako Date: Thu, 27 Sep 2018 10:56:22 +0300 Subject: [PATCH] init tests + projection tests (#29) * init tests + projection tests * correct travis node version to supported functionality --- .npmignore | 3 +- .travis.yml | 4 +- index.ts | 2 +- package.json | 12 +- src/logger.ts | 17 +- src/mongoDbFilter.ts | 8 +- src/mongoDbProjection.ts | 41 +-- src/mongoDbUpdate.ts | 20 +- src/queryResolver.ts | 2 +- src/updateResolver.ts | 2 +- tests/specs/mongoDbProjection.spec.ts | 441 ++++++++++++++++++++++++++ tests/utils/fieldResolve.ts | 32 ++ tests/utils/types.ts | 70 ++++ 13 files changed, 601 insertions(+), 53 deletions(-) create mode 100644 tests/specs/mongoDbProjection.spec.ts create mode 100644 tests/utils/fieldResolve.ts create mode 100644 tests/utils/types.ts diff --git a/.npmignore b/.npmignore index aef8d13..26c9088 100644 --- a/.npmignore +++ b/.npmignore @@ -3,4 +3,5 @@ index.ts .gitignore .travis.yml CHANGELOG.md -tsconfig.json \ No newline at end of file +tsconfig.json +tests/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index b515633..66e4ccc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,10 @@ language: node_js node_js: - 6 - - 5 install: - npm install before_script: - npm install -g typescript script: - - npm run build \ No newline at end of file + - npm run build + - npm run test \ No newline at end of file diff --git a/index.ts b/index.ts index 109c439..fe522a8 100644 --- a/index.ts +++ b/index.ts @@ -5,7 +5,7 @@ import getMongoDbUpdate from './src/mongoDbUpdate'; import GraphQLPaginationType from './src/graphQLPaginationType'; import getGraphQLSortType from './src/graphQLSortType'; import getMongoDbSort from './src/mongoDbSort'; -import getMongoDbProjection from './src/mongoDbProjection'; +import { getMongoDbProjection } from './src/mongoDbProjection'; import { getMongoDbQueryResolver, getGraphQLQueryArgs, QueryOptions } from './src/queryResolver'; import { getMongoDbUpdateResolver, getGraphQLUpdateArgs, UpdateOptions } from './src/updateResolver'; import { setLogger } from './src/logger'; diff --git a/package.json b/package.json index b643133..97c03b1 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "types": "lib/index.d.ts", "scripts": { "build": "tsc -p .", - "publish-now": "npm run build && npm publish" + "publish-now": "npm run build && npm publish", + "test": "ts-mocha tests/**/*.spec.ts" }, "repository": { "type": "git", @@ -19,7 +20,14 @@ }, "homepage": "https://github.com/Soluto/graphql-to-mongodb#readme", "devDependencies": { - "@types/graphql": "^0.12.4" + "@types/chai": "^4.1.4", + "@types/graphql": "^0.12.4", + "@types/mocha": "^5.2.5", + "chai": "^4.1.2", + "graphql": "^0.13.2", + "mocha": "^5.2.0", + "ts-mocha": "^2.0.0", + "typescript": "^3.0.3" }, "peerDependencies": { "graphql": ">=0.10" diff --git a/src/logger.ts b/src/logger.ts index 72c15f2..6fa62bc 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -28,11 +28,14 @@ export function setLogger(loggerObject: Logger): void { logger = loggerObject || {}; } -export function logOnError(action: () => T): T { - try { - return action(); - } catch (exception) { - error('graphql-to-mongodb internal exception:', exception); - throw exception; - } +export function logOnError(func: T): T { + const wrappedFunction = (...args) => { + try { + return (func as any)(...args); + } catch (exception) { + error('graphql-to-mongodb internal exception:', exception); + throw exception; + } + }; + return wrappedFunction as any; } \ No newline at end of file diff --git a/src/mongoDbFilter.ts b/src/mongoDbFilter.ts index 402ddcf..e7199d4 100644 --- a/src/mongoDbFilter.ts +++ b/src/mongoDbFilter.ts @@ -15,12 +15,6 @@ const operatorsMongoDbKeys = { OPTIONS: '$options', }; -function getMongoDbFilterOuter(graphQLType: GraphQLObjectType, graphQLFilter: object = {}): object { - return logOnError(() => { - return getMongoDbFilter(graphQLType, graphQLFilter); - }); -} - function getMongoDbFilter(graphQLType: GraphQLObjectType, graphQLFilter: object = {}): object { if (!isType(graphQLType)) throw 'First arg of getMongoDbFilter must be the base graphqlType to be parsed' @@ -120,4 +114,4 @@ function parseMongoDbScalarFilter(graphQLFilter: object): object { return mongoDbScalarFilter; } -export default getMongoDbFilterOuter; \ No newline at end of file +export default logOnError(getMongoDbFilter); \ No newline at end of file diff --git a/src/mongoDbProjection.ts b/src/mongoDbProjection.ts index da166fc..a52e415 100644 --- a/src/mongoDbProjection.ts +++ b/src/mongoDbProjection.ts @@ -6,7 +6,7 @@ export interface MongoDbProjection { [key: string]: 1 }; -interface Field { +export interface Field { [key: string]: Field | 1 } @@ -18,20 +18,23 @@ interface FragmentDictionary { [key: string]: FieldGraph[] } -function getMongoDbProjection(info: GraphQLResolveInfo, graphQLType: GraphQLObjectType, ...excludedFields: string[]): MongoDbProjection { - return logOnError(() => { - if (!Object.keys(info).includes('fieldNodes')) throw 'First argument of "getMongoDbProjection" must be a GraphQLResolveInfo'; - if (!isType(graphQLType)) throw 'Second argument of "getMongoDbProjection" must be a GraphQLType'; +export const getMongoDbProjection = logOnError((info: GraphQLResolveInfo, graphQLType: GraphQLObjectType, ...excludedFields: string[]): MongoDbProjection => { + if (!Object.keys(info).includes('fieldNodes')) throw 'First argument of "getMongoDbProjection" must be a GraphQLResolveInfo'; + if (!isType(graphQLType)) throw 'Second argument of "getMongoDbProjection" must be a GraphQLType'; - const selections = flatten(info.fieldNodes.map(_ => _.selectionSet.selections)); - const simplifiedNodes = simplifyNodes({ selections: selections }, info); - const field = mergeNodes(simplifiedNodes); + const requestedFields = getRequestedFields(info); - const projection = getProjection(field, graphQLType, [], ...excludedFields); + const projection = getProjection(requestedFields, graphQLType, [], ...excludedFields); - const resolveFieldsDependencies = getResolveFieldsDependencies(field, graphQLType); - return mergeProjectionAndResolveDependencies(projection, resolveFieldsDependencies); - }); + const resolveFieldsDependencies = getResolveFieldsDependencies(requestedFields, graphQLType); + + return mergeProjectionAndResolveDependencies(projection, resolveFieldsDependencies); +}); + +export function getRequestedFields(info: GraphQLResolveInfo): Field { + const selections = flatten(info.fieldNodes.map(_ => _.selectionSet.selections)); + const simplifiedNodes = simplifyNodes({ selections: selections }, info); + return mergeNodes(simplifiedNodes); } function simplifyNodes(selectionSetNode: { selections: SelectionNode[] }, info: GraphQLResolveInfo, dictionary: FragmentDictionary = {}): FieldGraph[] { @@ -90,7 +93,7 @@ function mergeNodes(fieldGraphs: FieldGraph[]): Field { }, {}); } -function getProjection(fieldNode: Field, graphQLType: GraphQLObjectType, path: string[] = [], ...excludedFields: string[]): MongoDbProjection { +export function getProjection(fieldNode: Field, graphQLType: GraphQLObjectType, path: string[] = [], ...excludedFields: string[]): MongoDbProjection { const typeFields = getTypeFields(graphQLType)(); return Object.assign({}, ...Object.keys(fieldNode) @@ -109,7 +112,7 @@ function getProjection(fieldNode: Field, graphQLType: GraphQLObjectType, path: s })); } -function getResolveFieldsDependencies(fieldNode: Field, graphQLType: GraphQLObjectType): string[] { +export function getResolveFieldsDependencies(fieldNode: Field, graphQLType: GraphQLObjectType): string[] { const typeFields = getTypeFields(graphQLType)(); return Object.keys(fieldNode) @@ -134,7 +137,7 @@ function getResolveFieldsDependencies(fieldNode: Field, graphQLType: GraphQLObje }, []); } -function mergeProjectionAndResolveDependencies(projection: MongoDbProjection, resolveDependencies: string[]): MongoDbProjection { +export function mergeProjectionAndResolveDependencies(projection: MongoDbProjection, resolveDependencies: string[]): MongoDbProjection { const newProjection = { ...projection }; @@ -147,11 +150,11 @@ function mergeProjectionAndResolveDependencies(projection: MongoDbProjection, re continue; } - if (projectionKeys.some(key => new RegExp(`^${key}.`).test(dependency))) { + if (projectionKeys.some(key => new RegExp(`^${key}[.]`).test(dependency))) { continue; } - const regex = new RegExp(`^${dependency}.`) + const regex = new RegExp(`^${dependency}[.]`) projectionKeys .filter(key => regex.test(key)) @@ -161,6 +164,4 @@ function mergeProjectionAndResolveDependencies(projection: MongoDbProjection, re } return newProjection; -} - -export default getMongoDbProjection \ No newline at end of file +} \ No newline at end of file diff --git a/src/mongoDbUpdate.ts b/src/mongoDbUpdate.ts index 9a5d971..22c3bf2 100644 --- a/src/mongoDbUpdate.ts +++ b/src/mongoDbUpdate.ts @@ -13,16 +13,14 @@ export interface updateObj { } function getMongoDbUpdate(update: updateArg): updateObj { - return logOnError(() => { - return clear({ - update: { - $setOnInsert: update.setOnInsert, - $set: update.set ? flattenMongoDbSet(update.set) : undefined, - $inc: update.inc - }, - options: update.setOnInsert ? { upsert: true } : undefined - }, FICTIVE_INC); - }); + return clear({ + update: { + $setOnInsert: update.setOnInsert, + $set: update.set ? flattenMongoDbSet(update.set) : undefined, + $inc: update.inc + }, + options: update.setOnInsert ? { upsert: true } : undefined + }, FICTIVE_INC); } function flattenMongoDbSet(set: object, path: string[] = []): object { @@ -42,4 +40,4 @@ function flattenMongoDbSet(set: object, path: string[] = []): object { })); } -export default getMongoDbUpdate; \ No newline at end of file +export default logOnError(getMongoDbUpdate); \ No newline at end of file diff --git a/src/queryResolver.ts b/src/queryResolver.ts index 11dea64..c0199e7 100644 --- a/src/queryResolver.ts +++ b/src/queryResolver.ts @@ -1,5 +1,5 @@ import getMongoDbFilter from './mongoDbFilter'; -import getMongoDbProjection, { MongoDbProjection } from './mongoDbProjection'; +import { getMongoDbProjection, MongoDbProjection } from './mongoDbProjection'; import { getGraphQLFilterType } from './graphQLFilterType'; import getGraphQLSortType from './graphQLSortType'; import GraphQLPaginationType from './graphQLPaginationType'; diff --git a/src/updateResolver.ts b/src/updateResolver.ts index fcdbbae..85c80ff 100644 --- a/src/updateResolver.ts +++ b/src/updateResolver.ts @@ -3,7 +3,7 @@ import { getGraphQLUpdateType } from './graphQLMutationType'; import getMongoDbFilter from './mongoDbFilter'; import getMongoDbUpdate from './mongoDbUpdate'; import { GraphQLNonNull, isType, GraphQLResolveInfo, GraphQLFieldResolver, GraphQLObjectType } from 'graphql'; -import getMongoDbProjection, { MongoDbProjection } from './mongoDbProjection'; +import { getMongoDbProjection, MongoDbProjection } from './mongoDbProjection'; export interface UpdateCallback { ( diff --git a/tests/specs/mongoDbProjection.spec.ts b/tests/specs/mongoDbProjection.spec.ts new file mode 100644 index 0000000..0673174 --- /dev/null +++ b/tests/specs/mongoDbProjection.spec.ts @@ -0,0 +1,441 @@ +import { getMongoDbProjection, getRequestedFields, getProjection, getResolveFieldsDependencies, mergeProjectionAndResolveDependencies, Field, MongoDbProjection } from "../../src/mongoDbProjection"; +import { ObjectType } from "../utils/types"; +import fieldResolve from "../utils/fieldResolve"; +import { expect } from "chai"; + +describe("mongoDbProjection", () => { + const queries = { + simple: ` + query { + test { + stringScalar + intScalar + floatScalar + enumScalar + stringList + intList + floatList + enumList + } + }`, + nested: ` + query { + test { + nested { + stringScalar + intScalar + floatScalar + enumScalar + stringList + intList + floatList + enumList + } + } + }`, + fragments: ` + query { + test { + ...fieldsA + ...fieldsB + } + } + + fragment fieldsA on Object { + stringScalar + intScalar + floatScalar + enumScalar + } + + fragment fieldsB on Object { + stringScalar + intList + floatList + enumList + nested { + stringScalar + intScalar + floatScalar + enumScalar + } + }`, + resolvers: ` + query { + test { + resolveSpecificDependencies + } + }`, + full: ` + query { + test { + stringScalar + intScalar + nested { + floatScalar + enumScalar + } + nestedList { + enumList + } + resolveCommonDependencies + ...fieldsA + } + } + + fragment fieldsA on Object { + nestedList { + floatScalar + enumScalar + } + }` + }; + + describe("getMongoDbProjection", () => { + const tests: { description: string, query: string, expectedProjection: any }[] = [ + { + description: "Should create projection for a simple request", + query: queries.simple, + expectedProjection: { + "stringScalar": 1, + "intScalar": 1, + "floatScalar": 1, + "enumScalar": 1, + "stringList": 1, + "intList": 1, + "floatList": 1, + "enumList": 1, + } + }, { + description: "Should create projection for a nested request", + query: queries.nested, + expectedProjection: { + "nested.stringScalar": 1, + "nested.intScalar": 1, + "nested.floatScalar": 1, + "nested.enumScalar": 1, + "nested.stringList": 1, + "nested.intList": 1, + "nested.floatList": 1, + "nested.enumList": 1, + } + }, { + description: "Should create projection for a request with fragments", + query: queries.fragments, + expectedProjection: { + "stringScalar": 1, + "intScalar": 1, + "floatScalar": 1, + "enumScalar": 1, + "intList": 1, + "floatList": 1, + "enumList": 1, + "nested.stringScalar": 1, + "nested.intScalar": 1, + "nested.floatScalar": 1, + "nested.enumScalar": 1, + } + }, { + description: "Should create projection for a request with dependent resolvers", + query: queries.resolvers, + expectedProjection: { + "nested.stringScalar": 1, + "nested.intScalar": 1 + } + }, { + description: "Should create projection for a request", + query: queries.full, + expectedProjection: { + "stringScalar": 1, + "intScalar": 1, + "nested": 1, + "nestedList.floatScalar": 1, + "nestedList.enumScalar": 1, + "nestedList.enumList": 1, + } + } + ]; + + tests.forEach(test => it(test.description, () => { + // Arrange + const query = test.query; + const { info } = fieldResolve(ObjectType, query); + + // Act + const projection = getMongoDbProjection(info, ObjectType); + + // Assert + expect(test.expectedProjection).to.deep.equal(projection, "should produce a correct projection") + })); + }); + describe("getRequestedFields", () => { + const tests: { description: string, query: string, expecteFields: Field }[] = [ + { + description: "Should get fields of a simple request", + query: queries.simple, + expecteFields: { + stringScalar: 1, + intScalar: 1, + floatScalar: 1, + enumScalar: 1, + stringList: 1, + intList: 1, + floatList: 1, + enumList: 1, + } + }, { + description: "Should get fields of a nested request", + query: queries.nested, + expecteFields: { + nested: { + stringScalar: 1, + intScalar: 1, + floatScalar: 1, + enumScalar: 1, + stringList: 1, + intList: 1, + floatList: 1, + enumList: 1, + } + } + }, { + description: "Should get fields of a request with fragments", + query: queries.fragments, + expecteFields: { + stringScalar: 1, + intScalar: 1, + floatScalar: 1, + enumScalar: 1, + intList: 1, + floatList: 1, + enumList: 1, + nested: { + stringScalar: 1, + intScalar: 1, + floatScalar: 1, + enumScalar: 1, + } + } + }, { + description: "Should get fields of a request", + query: queries.full, + expecteFields: { + stringScalar: 1, + intScalar: 1, + nested: { + floatScalar: 1, + enumScalar: 1, + }, + nestedList: { + enumList: 1, + floatScalar: 1, + enumScalar: 1, + }, + resolveCommonDependencies: 1 + } + } + ]; + + tests.forEach(test => it(test.description, () => { + // Arrange + const query = test.query; + const { info } = fieldResolve(ObjectType, query); + + // Act + const fields = getRequestedFields(info); + + // Assert + expect(test.expecteFields).to.deep.equal(fields, "should derivce correct requested fields") + })); + }); + describe("getProjection", () => { + const tests: { description: string, fieldNode: Field, expectedProjection: MongoDbProjection }[] = [ + { + description: "Should get projection of a simple request", + fieldNode: { + stringScalar: 1, + enumScalar: 1, + intList: 1, + enumList: 1, + }, + expectedProjection: { + "stringScalar": 1, + "enumScalar": 1, + "intList": 1, + "enumList": 1 + } + }, + { + description: "Should get projection of a nested request", + fieldNode: { + nested: { + stringScalar: 1, + enumScalar: 1, + intList: 1, + enumList: 1, + }, + nestedList: { + stringScalar: 1, + enumScalar: 1, + intList: 1, + enumList: 1, + } + }, + expectedProjection: { + "nested.stringScalar": 1, + "nested.enumScalar": 1, + "nested.intList": 1, + "nested.enumList": 1, + "nestedList.stringScalar": 1, + "nestedList.enumScalar": 1, + "nestedList.intList": 1, + "nestedList.enumList": 1 + } + }, + { + description: "Should get projection of a request with resolvers", + fieldNode: { + intScalar: 1, + resolveSpecificDependencies: 1, + resolveCommonDependencies: 1, + resolveObject: 1 + }, + expectedProjection: { + "intScalar": 1 + } + }, + { + description: "Should get projection", + fieldNode: { + enumScalar: 1, + intList: 1, + nested: { + floatScalar: 1, + }, + resolveSpecificDependencies: 1, + }, + expectedProjection: { + "enumScalar": 1, + "intList": 1, + "nested.floatScalar": 1, + } + }, + ]; + + tests.forEach(test => it(test.description, () => { + // Act + const projection = getProjection(test.fieldNode, ObjectType); + + // Assert + expect(test.expectedProjection).to.deep.equal(projection, "should produce correct projection") + })); + }); + describe("getResolveFieldsDependencies", () => { + const tests: { description: string, fieldNode: Field, expectedDependencies: string[] }[] = [{ + description: "Should get resolve fields dependencies of scalars", + fieldNode: { + resolveSpecificDependencies: 1 + }, + expectedDependencies: ["nested.stringScalar", "nested.intScalar"] + }, { + description: "Should get resolve fields dependencies of objects", + fieldNode: { + resolveObject: 1 + }, + expectedDependencies: ["stringScalar"] + }, { + description: "Should get resolve fields dependencies of nested", + fieldNode: { + nested: { + resolveScalar: 1 + } + }, + expectedDependencies: ["nested.stringScalar"] + }, { + description: "Should get resolve fields dependencies", + fieldNode: { + resolveSpecificDependencies: 1, + resolveCommonDependencies: 1, + resolveObject: 1, + nested: { + resolveScalar: 1 + } + }, + expectedDependencies: ["nested.stringScalar", "nested.intScalar", "nested", "stringScalar", "nested.stringScalar"] + }]; + + tests.forEach(test => it(test.description, () => { + // Act + const dependencies = getResolveFieldsDependencies(test.fieldNode, ObjectType); + + // Assert + expect(test.expectedDependencies).to.deep.equal(dependencies, "Should extract correct resolver dependencies"); + })); + }); + describe("mergeProjectionAndResolveDependencies", () => { + const tests: { description: string, projection: MongoDbProjection, resolverDependencies: string[], expectedProjection: MongoDbProjection }[] = [{ + description: "Should merge and replace projection of shorther dependencies", + projection: { + "nested.stringScalar": 1 + }, + resolverDependencies: [ + "nested" + ], + expectedProjection: { + "nested": 1 + } + }, { + description: "Should ignore projection of existing dependencies", + projection: { + "stringScalar": 1 + }, + resolverDependencies: [ + "stringScalar" + ], + expectedProjection: { + "stringScalar": 1 + } + }, { + description: "Should ignore projection of longer dependencies", + projection: { + "nested": 1 + }, + resolverDependencies: [ + "nested.stringScalar" + ], + expectedProjection: { + "nested": 1 + } + }, { + description: "Should merge projection and dependencies", + projection: { + "stringScalar": 1, + "nested.floatScalar": 1, + "nestedList.intScalar": 1, + "someNested": 1 + }, + resolverDependencies: [ + "enumScalar", + "nested", + "nestedList.intList", + "someNested.inner" + ], + expectedProjection: { + "stringScalar": 1, + "enumScalar": 1, + "nested": 1, + "nestedList.intScalar": 1, + "nestedList.intList": 1, + "someNested": 1 + } + }] + + tests.forEach(test => it(test.description, () => { + // Act + const actualProjection = mergeProjectionAndResolveDependencies(test.projection, test.resolverDependencies); + + // Assert + expect(test.expectedProjection).to.deep.equal(actualProjection, "Should merge projection and dependencies correctly"); + })); + }); +}); \ No newline at end of file diff --git a/tests/utils/fieldResolve.ts b/tests/utils/fieldResolve.ts new file mode 100644 index 0000000..1cc5076 --- /dev/null +++ b/tests/utils/fieldResolve.ts @@ -0,0 +1,32 @@ +import { parse, execute, GraphQLSchema, GraphQLObjectType, GraphQLFieldConfigArgumentMap, GraphQLOutputType, GraphQLResolveInfo, Source } from "graphql" + +export interface ResolveArgs { + args: any + info: GraphQLResolveInfo +}; + +export default (type: GraphQLOutputType, query: string, args?: GraphQLFieldConfigArgumentMap, fieldName: string = "test"): ResolveArgs => { + let resolveArgs: ResolveArgs; + + const schema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: "RootQuery", + fields: () => ({ + [fieldName]: { + type, + args, + resolve: (obj, args, context, info) => { + resolveArgs = { args, info }; + } + } + }) + }) + }) + + const source = new Source(query, 'GraphQL request'); + const document = parse(source); + + execute(schema, document); + + return resolveArgs; +}; \ No newline at end of file diff --git a/tests/utils/types.ts b/tests/utils/types.ts new file mode 100644 index 0000000..976202c --- /dev/null +++ b/tests/utils/types.ts @@ -0,0 +1,70 @@ +import { GraphQLObjectType, GraphQLInt, GraphQLString, GraphQLList, GraphQLEnumType, GraphQLFloat } from "graphql"; + +export const CharactersEnum = new GraphQLEnumType({ + name: "Characters", + values: { + A: { value: "A" }, + B: { value: "B" }, + C: { value: "C" }, + } +}); + +export const NestedType = new GraphQLObjectType({ + name: "Nested", + fields: () => ({ + stringScalar: { type: GraphQLString }, + intScalar: { type: GraphQLInt }, + floatScalar: { type: GraphQLFloat }, + enumScalar: { type: CharactersEnum }, + + stringList: { type: new GraphQLList(GraphQLString) }, + intList: { type: new GraphQLList(GraphQLInt) }, + floatList: { type: new GraphQLList(GraphQLFloat) }, + enumList: { type: new GraphQLList(CharactersEnum) }, + + resolveScalar: { + type: GraphQLString, + resolve: obj => obj.stringScalar, + dependencies: ["stringScalar"] + }, + resolveObject: { + type: NestedType, + resolve: obj => ({ stringScalar: obj.stringScalar }), + dependencies: ["stringScalar"] + }, + }) +}); + +export const ObjectType = new GraphQLObjectType({ + name: "Object", + fields: () => ({ + stringScalar: { type: GraphQLString }, + intScalar: { type: GraphQLInt }, + floatScalar: { type: GraphQLFloat }, + enumScalar: { type: CharactersEnum }, + + stringList: { type: new GraphQLList(GraphQLString) }, + intList: { type: new GraphQLList(GraphQLInt) }, + floatList: { type: new GraphQLList(GraphQLFloat) }, + enumList: { type: new GraphQLList(CharactersEnum) }, + + nested: { type: NestedType }, + nestedList: { type: new GraphQLList(NestedType) }, + + resolveSpecificDependencies: { + type: GraphQLString, + resolve: obj => `${obj.nested.stringScalar} ${obj.nested.intScalar}`, + dependencies: ["nested.stringScalar", "nested.intScalar"] + }, + resolveCommonDependencies: { + type: GraphQLString, + resolve: obj => `${obj.nested.stringScalar} ${obj.nested.intScalar}`, + dependencies: ["nested"] + }, + resolveObject: { + type: NestedType, + resolve: obj => ({ stringScalar: obj.stringScalar }), + dependencies: ["stringScalar"] + }, + }) +}); \ No newline at end of file