diff --git a/.changeset/brave-rats-march.md b/.changeset/brave-rats-march.md new file mode 100644 index 00000000000..a918b9e2515 --- /dev/null +++ b/.changeset/brave-rats-march.md @@ -0,0 +1,5 @@ +--- +'@graphql-tools/wrap': major +--- + +BREAKING - remove makeRemoteExecutableSchema diff --git a/.eslintrc.json b/.eslintrc.json index 12787662740..fd8fff382e9 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -61,7 +61,8 @@ "packages/load/tests/loaders/schema", "website", "scripts", - "packages/loaders/code-file/tests/test-files" + "packages/loaders/code-file/tests/test-files", + "packages/loaders/git/tests/test-files" ], "globals":{ "BigInt": true diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f4e0a414fe9..8e34139248e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -31,7 +31,7 @@ jobs: - name: Lint run: yarn lint build: - name: Build on ${{matrix.os}} GraphQL v${{matrix.graphql_version}} + name: Type Check on GraphQL v${{matrix.graphql_version}} runs-on: ubuntu-latest strategy: matrix: @@ -55,9 +55,9 @@ jobs: - name: Install Dependencies using Yarn run: yarn install --ignore-engines && git checkout yarn.lock - name: Build - run: yarn ts:transpile + run: yarn ts:check test: - name: Test, Node ${{matrix.node_version}} and GraphQL v${{matrix.graphql_version}} + name: Unit Test on Node ${{matrix.node_version}} and GraphQL v${{matrix.graphql_version}} runs-on: ubuntu-latest strategy: matrix: diff --git a/package.json b/package.json index c9c9ea3b3cf..b5552459df2 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "postinstall": "patch-package && husky install", "predeploy:website": "yarn build:api-docs", "deploy:website": "cd website && yarn deploy", - "ts:transpile": "concurrently \"tsc --project tsconfig.build.json\" \"tsc --project tsconfig.build.es5.json\"", + "ts:check": "tsc --noEmit --incremental", + "ts:transpile": "concurrently \"tsc --project tsconfig.build.json --incremental\" \"tsc --project tsconfig.build.es5.json --incremental\"", "clean-dist": "rimraf \"packages/**/dist\" && rimraf \"packages/**/dist-es5\" && rimraf \".bob\"", "build": "yarn ts:transpile && bob build", "build:api-docs": "node scripts/build-api-docs.js", diff --git a/packages/batch-delegate/src/batchDelegateToSchema.ts b/packages/batch-delegate/src/batchDelegateToSchema.ts index ee5057522b8..ba12dd310fb 100644 --- a/packages/batch-delegate/src/batchDelegateToSchema.ts +++ b/packages/batch-delegate/src/batchDelegateToSchema.ts @@ -2,7 +2,7 @@ import { BatchDelegateOptions } from './types'; import { getLoader } from './getLoader'; -export function batchDelegateToSchema(options: BatchDelegateOptions): any { +export function batchDelegateToSchema(options: BatchDelegateOptions): any { const key = options.key; if (key == null) { return null; diff --git a/packages/batch-delegate/tests/basic.example.test.ts b/packages/batch-delegate/tests/basic.example.test.ts index b05d07039b3..9f6ed75d6f3 100644 --- a/packages/batch-delegate/tests/basic.example.test.ts +++ b/packages/batch-delegate/tests/basic.example.test.ts @@ -89,7 +89,7 @@ describe('batch delegation within basic stitching example', () => { expect(numCalls).toEqual(1); expect(result.errors).toBeUndefined(); - expect(result.data!.trendingChirps[0].chirpedAtUser.email).not.toBe(null); + expect(result.data!['trendingChirps'][0].chirpedAtUser.email).not.toBe(null); }); test('works with key arrays', async () => { diff --git a/packages/batch-delegate/tests/typeMerging.example.test.ts b/packages/batch-delegate/tests/typeMerging.example.test.ts index 171afd9bdf0..e8e000341bc 100644 --- a/packages/batch-delegate/tests/typeMerging.example.test.ts +++ b/packages/batch-delegate/tests/typeMerging.example.test.ts @@ -84,7 +84,7 @@ describe('merging using type merging', () => { Query: { mostStockedProduct: () => inventory.find(i => i.upc === '3'), _products: (_root, { representations }) => { - return representations.map((rep: Record) => ({ ...rep, ...inventory.find(i => i.upc === rep.upc) })); + return representations.map((rep: Record) => ({ ...rep, ...inventory.find(i => i.upc === rep['upc']) })); }, }, }, diff --git a/packages/batch-delegate/tests/withTransforms.test.ts b/packages/batch-delegate/tests/withTransforms.test.ts index a325d146996..66f64086a8e 100644 --- a/packages/batch-delegate/tests/withTransforms.test.ts +++ b/packages/batch-delegate/tests/withTransforms.test.ts @@ -73,7 +73,7 @@ describe('works with complex transforms', () => { ] }), resultTransformer: (results, delegationContext) => { - const userIds = delegationContext.args.userIds; + const userIds = delegationContext.args['userIds']; const booksByUserIds = results.reduce( (acc: any, { userId, books }: { userId: string, books: any[] }) => { acc[userId] = books diff --git a/packages/batch-execute/src/createBatchingExecutor.ts b/packages/batch-execute/src/createBatchingExecutor.ts index 4a92f5ab6ba..7bb8a4201f0 100644 --- a/packages/batch-execute/src/createBatchingExecutor.ts +++ b/packages/batch-execute/src/createBatchingExecutor.ts @@ -51,20 +51,18 @@ function createLoadFn( } } - const executionResults: Array> = []; - execBatches.forEach(execBatch => { + const executionResults: Array> = execBatches.map(execBatch => { const mergedExecutionParams = mergeExecutionParams(execBatch, extensionsReducer); - executionResults.push(new ValueOrPromise(() => executor(mergedExecutionParams))); + return new ValueOrPromise(() => executor(mergedExecutionParams)); }); return ValueOrPromise.all(executionResults) - .then(resultBatches => { - let results: Array = []; - resultBatches.forEach((resultBatch, index) => { - results = [...results, ...splitResult(resultBatch!, execBatches[index].length)]; - }); - return results; - }) + .then(resultBatches => + resultBatches.reduce( + (results, resultBatch, index) => results.concat(splitResult(resultBatch, execBatches[index].length)), + new Array>>() + ) + ) .resolve(); }; } diff --git a/packages/batch-execute/src/mergeExecutionParams.ts b/packages/batch-execute/src/mergeExecutionParams.ts index 1dd6cbc0375..80e2fce52fc 100644 --- a/packages/batch-execute/src/mergeExecutionParams.ts +++ b/packages/batch-execute/src/mergeExecutionParams.ts @@ -68,22 +68,25 @@ export function mergeExecutionParams( let operation: Maybe; - execs.forEach((executionParams, index) => { + for (const index in execs) { + const executionParams = execs[index]; const prefixedExecutionParams = prefixExecutionParams(createPrefix(index), executionParams); - prefixedExecutionParams.document.definitions.forEach(def => { + for (const def of prefixedExecutionParams.document.definitions) { if (isOperationDefinition(def)) { operation = def.operation; mergedSelections.push(...def.selectionSet.selections); - mergedVariableDefinitions.push(...(def.variableDefinitions ?? [])); + if (def.variableDefinitions) { + mergedVariableDefinitions.push(...def.variableDefinitions); + } } if (isFragmentDefinition(def)) { mergedFragmentDefinitions.push(def); } - }); + } Object.assign(mergedVariables, prefixedExecutionParams.variables); mergedExtensions = extensionsReducer(mergedExtensions, executionParams); - }); + } if (operation == null) { throw new Error('Could not identify operation type. Did the document only include fragment definitions?'); diff --git a/packages/batch-execute/src/prefix.ts b/packages/batch-execute/src/prefix.ts index 4da94fc8e00..97460d521c8 100644 --- a/packages/batch-execute/src/prefix.ts +++ b/packages/batch-execute/src/prefix.ts @@ -1,6 +1,6 @@ // adapted from https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-source-graphql/src/batching/merge-queries.js -export function createPrefix(index: number): string { +export function createPrefix(index: string): string { return `graphqlTools${index}_`; } diff --git a/packages/batch-execute/src/splitResult.ts b/packages/batch-execute/src/splitResult.ts index 0cee17f31c6..1626a77186e 100644 --- a/packages/batch-execute/src/splitResult.ts +++ b/packages/batch-execute/src/splitResult.ts @@ -9,60 +9,41 @@ import { parseKey } from './prefix'; /** * Split and transform result of the query produced by the `merge` function */ -export function splitResult(mergedResult: ExecutionResult, numResults: number): Array { +export function splitResult({ data, errors }: ExecutionResult, numResults: number): Array { const splitResults: Array = []; for (let i = 0; i < numResults; i++) { splitResults.push({}); } - const data = mergedResult.data; if (data) { - Object.keys(data).forEach(prefixedKey => { + for (const prefixedKey in data) { const parsedKey = parseKey(prefixedKey); assertSome(parsedKey, "'parsedKey' should not be null."); const { index, originalKey } = parsedKey; const result = splitResults[index]; if (result == null) { - return; + continue; } if (result.data == null) { result.data = { [originalKey]: data[prefixedKey] }; } else { result.data[originalKey] = data[prefixedKey]; } - }); + } } - const errors = mergedResult.errors; if (errors) { - const newErrors: Record> = Object.create(null); - errors.forEach(error => { + for (const error of errors) { if (error.path) { const parsedKey = parseKey(error.path[0] as string); if (parsedKey) { const { index, originalKey } = parsedKey; const newError = relocatedError(error, [originalKey, ...error.path.slice(1)]); - if (!newErrors[index]) { - newErrors[index] = [newError]; - } else { - newErrors[index].push(newError); - } - return; + const errors = (splitResults[index].errors = (splitResults[index].errors || []) as GraphQLError[]); + errors.push(newError); } } - - splitResults.forEach((_splitResult, index) => { - if (!newErrors[index]) { - newErrors[index] = [error]; - } else { - newErrors[index].push(error); - } - }); - }); - - Object.keys(newErrors).forEach(index => { - splitResults[index].errors = newErrors[index]; - }); + } } return splitResults; diff --git a/packages/delegate/src/Transformer.ts b/packages/delegate/src/Transformer.ts index d8b0403edb2..376cb24015c 100644 --- a/packages/delegate/src/Transformer.ts +++ b/packages/delegate/src/Transformer.ts @@ -16,7 +16,9 @@ export class Transformer> { constructor(context: DelegationContext, binding: DelegationBinding = defaultDelegationBinding) { this.delegationContext = context; const delegationTransforms: Array = binding(this.delegationContext); - delegationTransforms.forEach(transform => this.addTransform(transform, {})); + for (const transform of delegationTransforms) { + this.addTransform(transform, {}); + } } private addTransform(transform: Transform, context = {}) { diff --git a/packages/delegate/src/createRequest.ts b/packages/delegate/src/createRequest.ts index 73f205e5c3c..0d1c9fb1cd6 100644 --- a/packages/delegate/src/createRequest.ts +++ b/packages/delegate/src/createRequest.ts @@ -10,7 +10,6 @@ import { typeFromAST, NamedTypeNode, GraphQLInputType, - GraphQLArgument, VariableDefinitionNode, SelectionSetNode, DefinitionNode, @@ -107,7 +106,7 @@ export function createRequest({ const variableDefinitionMap = Object.create(null); if (sourceSchema != null && variableDefinitions != null) { - variableDefinitions.forEach(def => { + for (const def of variableDefinitions) { const varName = def.variable.name.value; variableDefinitionMap[varName] = def; const varType = typeFromAST(sourceSchema, def.type as NamedTypeNode) as GraphQLInputType; @@ -115,7 +114,7 @@ export function createRequest({ if (serializedValue !== undefined) { newVariables[varName] = serializedValue; } - }); + } } if (sourceParentType != null && sourceFieldName != null) { @@ -130,7 +129,7 @@ export function createRequest({ const rootfieldNode: FieldNode = { kind: Kind.FIELD, - arguments: Object.keys(argumentNodeMap).map(argName => argumentNodeMap[argName]), + arguments: Object.values(argumentNodeMap), name: { kind: Kind.NAME, value: @@ -152,17 +151,17 @@ export function createRequest({ kind: Kind.OPERATION_DEFINITION, name: operationName, operation: targetOperation, - variableDefinitions: Object.keys(variableDefinitionMap).map(varName => variableDefinitionMap[varName]), + variableDefinitions: Object.values(variableDefinitionMap), selectionSet: { kind: Kind.SELECTION_SET, selections: [rootfieldNode], }, }; - let definitions: Array = [operationDefinition]; + const definitions: Array = [operationDefinition]; if (fragments != null) { - definitions = definitions.concat(Object.keys(fragments).map(fragmentName => fragments[fragmentName])); + definitions.push(...Object.values(fragments)); } const document: DocumentNode = { @@ -184,7 +183,7 @@ function updateArgumentsWithDefaults( variableValues: Record ): void { const sourceField = sourceParentType.getFields()[sourceFieldName]; - sourceField.args.forEach((argument: GraphQLArgument) => { + for (const argument of sourceField.args) { const argName = argument.name; const sourceArgType = argument.type; @@ -202,5 +201,5 @@ function updateArgumentsWithDefaults( ); } } - }); + } } diff --git a/packages/delegate/src/externalObjects.ts b/packages/delegate/src/externalObjects.ts index 81e2a9679e5..daee3d8035d 100644 --- a/packages/delegate/src/externalObjects.ts +++ b/packages/delegate/src/externalObjects.ts @@ -41,7 +41,8 @@ export function mergeExternalObjects( const results: Array = []; let errors: Array = []; - sources.forEach((source, index) => { + for (const index in sources) { + const source = sources[index]; if (source instanceof Error || source === null) { const selectionSet = selectionSets[index]; const fieldNodes = collectFields( @@ -56,7 +57,7 @@ export function mergeExternalObjects( Object.create(null) ); const nullResult = {}; - Object.keys(fieldNodes).forEach(responseKey => { + for (const responseKey in fieldNodes) { if (source instanceof GraphQLError) { nullResult[responseKey] = relocatedError(source, path.concat([responseKey])); } else if (source instanceof Error) { @@ -64,31 +65,24 @@ export function mergeExternalObjects( } else { nullResult[responseKey] = null; } - }); + } results.push(nullResult); } else { errors = errors.concat(source[UNPATHED_ERRORS_SYMBOL]); results.push(source); } - }); + } const combinedResult: ExternalObject = results.reduce(mergeDeep, target); - const newFieldSubschemaMap = target[FIELD_SUBSCHEMA_MAP_SYMBOL] ?? Object.create(null); - - results.forEach((source: ExternalObject) => { + const newFieldSubschemaMap = results.reduce((newFieldSubschemaMap, source) => { const objectSubschema = source[OBJECT_SUBSCHEMA_SYMBOL]; const fieldSubschemaMap = source[FIELD_SUBSCHEMA_MAP_SYMBOL]; - if (fieldSubschemaMap === undefined) { - Object.keys(source).forEach(responseKey => { - newFieldSubschemaMap[responseKey] = objectSubschema; - }); - } else { - Object.keys(source).forEach(responseKey => { - newFieldSubschemaMap[responseKey] = fieldSubschemaMap[responseKey] ?? objectSubschema; - }); + for (const responseKey in source) { + newFieldSubschemaMap[responseKey] = fieldSubschemaMap?.[responseKey] ?? objectSubschema; } - }); + return newFieldSubschemaMap; + }, target[FIELD_SUBSCHEMA_MAP_SYMBOL] ?? Object.create(null)); combinedResult[FIELD_SUBSCHEMA_MAP_SYMBOL] = newFieldSubschemaMap; combinedResult[OBJECT_SUBSCHEMA_SYMBOL] = target[OBJECT_SUBSCHEMA_SYMBOL]; diff --git a/packages/delegate/src/getFieldsNotInSubschema.ts b/packages/delegate/src/getFieldsNotInSubschema.ts index 6b81dad4db9..92b01eec8bc 100644 --- a/packages/delegate/src/getFieldsNotInSubschema.ts +++ b/packages/delegate/src/getFieldsNotInSubschema.ts @@ -17,7 +17,7 @@ function collectSubFields(info: GraphQLResolveInfo, typeName: string): Record { + for (const fieldNode of info.fieldNodes) { if (fieldNode.selectionSet) { subFieldNodes = collectFields( partialExecutionContext, @@ -27,13 +27,13 @@ function collectSubFields(info: GraphQLResolveInfo, typeName: string): Record = info.schema.extensions?.['stitchingInfo']; const selectionSetsByField = stitchingInfo?.selectionSetsByField; - Object.keys(subFieldNodes).forEach(responseName => { + for (const responseName in subFieldNodes) { const fieldName = subFieldNodes[responseName][0].name.value; const fieldSelectionSet = selectionSetsByField?.[typeName]?.[fieldName]; if (fieldSelectionSet != null) { @@ -45,7 +45,7 @@ function collectSubFields(info: GraphQLResolveInfo, typeName: string): Record = []; - Object.keys(subFieldNodes).forEach(responseName => { + for (const responseName in subFieldNodes) { const fieldName = subFieldNodes[responseName][0].name.value; if (!(fieldName in fields)) { fieldsNotInSchema = fieldsNotInSchema.concat(subFieldNodes[responseName]); } - }); + } return fieldsNotInSchema; }); diff --git a/packages/delegate/src/mergeFields.ts b/packages/delegate/src/mergeFields.ts index 8e711a6d058..e15ae559756 100644 --- a/packages/delegate/src/mergeFields.ts +++ b/packages/delegate/src/mergeFields.ts @@ -30,7 +30,7 @@ const sortSubschemasByProxiability = memoize4(function ( const proxiableSubschemas: Array = []; const nonProxiableSubschemas: Array = []; - targetSubschemas.forEach(t => { + for (const t of targetSubschemas) { const selectionSet = mergedTypeInfo.selectionSets.get(t); const fieldSelectionSets = mergedTypeInfo.fieldSelectionSets.get(t); if ( @@ -55,7 +55,7 @@ const sortSubschemasByProxiability = memoize4(function ( nonProxiableSubschemas.push(t); } } - }); + } return { proxiableSubschemas, @@ -77,9 +77,9 @@ const buildDelegationPlan = memoize3(function ( // 2. for each selection: const delegationMap: Map> = new Map(); - fieldNodes.forEach(fieldNode => { + for (const fieldNode of fieldNodes) { if (fieldNode.name.value === '__typename') { - return; + continue; } // 2a. use uniqueFields map to assign fields to subschema if one of possible subschemas @@ -88,7 +88,7 @@ const buildDelegationPlan = memoize3(function ( if (uniqueSubschema != null) { if (!proxiableSubschemas.includes(uniqueSubschema)) { unproxiableFieldNodes.push(fieldNode); - return; + continue; } const existingSubschema = delegationMap.get(uniqueSubschema); @@ -98,7 +98,7 @@ const buildDelegationPlan = memoize3(function ( delegationMap.set(uniqueSubschema, [fieldNode]); } - return; + continue; } // 2b. use nonUniqueFields to assign to a possible subschema, @@ -107,13 +107,13 @@ const buildDelegationPlan = memoize3(function ( let nonUniqueSubschemas: Array = nonUniqueFields[fieldNode.name.value]; if (nonUniqueSubschemas == null) { unproxiableFieldNodes.push(fieldNode); - return; + continue; } nonUniqueSubschemas = nonUniqueSubschemas.filter(s => proxiableSubschemas.includes(s)); if (!nonUniqueSubschemas.length) { unproxiableFieldNodes.push(fieldNode); - return; + continue; } const existingSubschema = nonUniqueSubschemas.find(s => delegationMap.has(s)); @@ -123,16 +123,16 @@ const buildDelegationPlan = memoize3(function ( } else { delegationMap.set(nonUniqueSubschemas[0], [fieldNode]); } - }); + } const finalDelegationMap: Map = new Map(); - delegationMap.forEach((selections, subschema) => { + for (const [subschema, selections] of delegationMap) { finalDelegationMap.set(subschema, { kind: Kind.SELECTION_SET, selections, }); - }); + } return { delegationMap: finalDelegationMap, @@ -177,14 +177,15 @@ export function mergeFields( } const resultMap: Map, SelectionSetNode> = new Map(); - delegationMap.forEach((selectionSet: SelectionSetNode, s: Subschema) => { - // TODO: Verify whether it is safe that resolver always exists. - const resolver = mergedTypeInfo.resolvers.get(s)!; - const valueOrPromise = new ValueOrPromise(() => resolver(object, context, info, s, selectionSet)).catch( - error => error - ); - resultMap.set(valueOrPromise, selectionSet); - }); + for (const [s, selectionSet] of delegationMap) { + const resolver = mergedTypeInfo.resolvers.get(s); + if (resolver) { + const valueOrPromise = new ValueOrPromise(() => resolver(object, context, info, s, selectionSet)).catch( + error => error + ); + resultMap.set(valueOrPromise, selectionSet); + } + } return ValueOrPromise.all(Array.from(resultMap.keys())) .then(results => diff --git a/packages/delegate/src/resolveExternalValue.ts b/packages/delegate/src/resolveExternalValue.ts index 6262c046490..66a2e768ee8 100644 --- a/packages/delegate/src/resolveExternalValue.ts +++ b/packages/delegate/src/resolveExternalValue.ts @@ -161,17 +161,17 @@ function resolveExternalListMember( } } -const reportedErrors: WeakMap = new Map(); +const reportedErrors = new WeakMap(); function reportUnpathedErrorsViaNull(unpathedErrors: Array) { if (unpathedErrors.length) { const unreportedErrors: Array = []; - unpathedErrors.forEach(error => { + for (const error of unpathedErrors) { if (!reportedErrors.has(error)) { unreportedErrors.push(error); reportedErrors.set(error, true); } - }); + } if (unreportedErrors.length) { if (unreportedErrors.length === 1) { diff --git a/packages/delegate/src/subschemaConfig.ts b/packages/delegate/src/subschemaConfig.ts index 2c9fd247f11..55c85c9d407 100644 --- a/packages/delegate/src/subschemaConfig.ts +++ b/packages/delegate/src/subschemaConfig.ts @@ -12,7 +12,7 @@ export function cloneSubschemaConfig(subschemaConfig: SubschemaConfig): Subschem if (newSubschemaConfig.merge != null) { newSubschemaConfig.merge = { ...subschemaConfig.merge }; - for (const typeName of Object.keys(newSubschemaConfig.merge)) { + for (const typeName in newSubschemaConfig.merge) { const mergedTypeConfig = (newSubschemaConfig.merge[typeName] = { ...(subschemaConfig.merge?.[typeName] ?? {}) }); if (mergedTypeConfig.entryPoints != null) { @@ -21,9 +21,9 @@ export function cloneSubschemaConfig(subschemaConfig: SubschemaConfig): Subschem if (mergedTypeConfig.fields != null) { const fields = (mergedTypeConfig.fields = { ...mergedTypeConfig.fields }); - Object.keys(fields).forEach(fieldName => { + for (const fieldName in fields) { fields[fieldName] = { ...fields[fieldName] }; - }); + } } } } diff --git a/packages/delegate/src/transforms/AddArgumentsAsVariables.ts b/packages/delegate/src/transforms/AddArgumentsAsVariables.ts index 33fb3881de1..05d96eeddef 100644 --- a/packages/delegate/src/transforms/AddArgumentsAsVariables.ts +++ b/packages/delegate/src/transforms/AddArgumentsAsVariables.ts @@ -2,7 +2,6 @@ import { ArgumentNode, DocumentNode, FragmentDefinitionNode, - GraphQLArgument, GraphQLField, GraphQLObjectType, GraphQLSchema, @@ -104,7 +103,7 @@ function addVariablesToRootField( newSelectionSet.push({ ...selection, - arguments: Object.keys(argumentNodeMap).map(argName => argumentNodeMap[argName]), + arguments: Object.values(argumentNodeMap), }); } else { newSelectionSet.push(selection); @@ -113,7 +112,7 @@ function addVariablesToRootField( return { ...operation, - variableDefinitions: Object.keys(variableDefinitionMap).map(varName => variableDefinitionMap[varName]), + variableDefinitions: Object.values(variableDefinitionMap), selectionSet: { kind: Kind.SELECTION_SET, selections: newSelectionSet, @@ -137,7 +136,7 @@ function updateArguments( variableValues: Record, newArgs: Record ): void { - targetField.args.forEach((argument: GraphQLArgument) => { + for (const argument of targetField.args) { const argName = argument.name; const argType = argument.type; @@ -151,5 +150,5 @@ function updateArguments( serializeInputValue(argType, newArgs[argName]) ); } - }); + } } diff --git a/packages/delegate/src/transforms/AddSelectionSets.ts b/packages/delegate/src/transforms/AddSelectionSets.ts index f663ad6651b..3ffad61e54a 100644 --- a/packages/delegate/src/transforms/AddSelectionSets.ts +++ b/packages/delegate/src/transforms/AddSelectionSets.ts @@ -50,7 +50,7 @@ function visitSelectionSet( } if (parentTypeName in selectionSetsByField) { - node.selections.forEach(selection => { + for (const selection of node.selections) { if (selection.kind === Kind.FIELD) { const name = selection.name.value; const selectionSet = selectionSetsByField[parentTypeName][name]; @@ -58,35 +58,35 @@ function visitSelectionSet( addSelectionsToMap(newSelections, selectionSet); } } - }); + } } if (parentTypeName in dynamicSelectionSetsByField) { - node.selections.forEach(selection => { + for (const selection of node.selections) { if (selection.kind === Kind.FIELD) { const name = selection.name.value; const dynamicSelectionSets = dynamicSelectionSetsByField[parentTypeName][name]; if (dynamicSelectionSets != null) { - dynamicSelectionSets.forEach(selectionSetFn => { + for (const selectionSetFn of dynamicSelectionSets) { const selectionSet = selectionSetFn(selection); if (selectionSet != null) { addSelectionsToMap(newSelections, selectionSet); } - }); + } } } - }); + } } return { ...node, - selections: Array.from(newSelections.values()), + selections: [...newSelections.values()], }; } } const addSelectionsToMap = memoize2(function (map: Map, selectionSet: SelectionSetNode): void { - selectionSet.selections.forEach(selection => { + for (const selection of selectionSet.selections) { map.set(print(selection), selection); - }); + } }); diff --git a/packages/delegate/src/transforms/CheckResultAndHandleErrors.ts b/packages/delegate/src/transforms/CheckResultAndHandleErrors.ts index 81552bafb8f..bb1de4a5cdc 100644 --- a/packages/delegate/src/transforms/CheckResultAndHandleErrors.ts +++ b/packages/delegate/src/transforms/CheckResultAndHandleErrors.ts @@ -84,38 +84,39 @@ export function mergeDataAndErrors( return { data, unpathedErrors: [] }; } - let unpathedErrors: Array = []; + const unpathedErrors: Array = []; - const errorMap: Record> = Object.create(null); - errors.forEach(error => { + const errorMap = new Map>(); + for (const error of errors) { const pathSegment = error.path?.[index]; if (pathSegment != null) { - const pathSegmentErrors = errorMap[pathSegment]; + let pathSegmentErrors = errorMap.get(pathSegment); if (pathSegmentErrors === undefined) { - errorMap[pathSegment] = [error]; + pathSegmentErrors = [error]; + errorMap.set(pathSegment, pathSegmentErrors); } else { pathSegmentErrors.push(error); } } else { unpathedErrors.push(error); } - }); + } - Object.keys(errorMap).forEach(pathSegment => { + for (const [pathSegment, pathSegmentErrors] of errorMap) { if (data[pathSegment] !== undefined) { const { data: newData, unpathedErrors: newErrors } = mergeDataAndErrors( data[pathSegment], - errorMap[pathSegment], + pathSegmentErrors, path, onLocatedError, index + 1 ); data[pathSegment] = newData; - unpathedErrors = unpathedErrors.concat(newErrors); + unpathedErrors.push(...newErrors); } else { - unpathedErrors = unpathedErrors.concat(errorMap[pathSegment]); + unpathedErrors.push(...pathSegmentErrors); } - }); + } return { data, unpathedErrors }; } diff --git a/packages/delegate/src/transforms/ExpandAbstractTypes.ts b/packages/delegate/src/transforms/ExpandAbstractTypes.ts index a2ba8d2abfa..f320397c209 100644 --- a/packages/delegate/src/transforms/ExpandAbstractTypes.ts +++ b/packages/delegate/src/transforms/ExpandAbstractTypes.ts @@ -50,7 +50,7 @@ function extractPossibleTypes(sourceSchema: GraphQLSchema, targetSchema: GraphQL const typeMap = sourceSchema.getTypeMap(); const possibleTypesMap: Record> = Object.create(null); const interfaceExtensionsMap: Record> = Object.create(null); - Object.keys(typeMap).forEach(typeName => { + for (const typeName in typeMap) { const type = typeMap[typeName]; if (isAbstractType(type)) { const targetType = targetSchema.getType(typeName); @@ -58,12 +58,14 @@ function extractPossibleTypes(sourceSchema: GraphQLSchema, targetSchema: GraphQL if (isInterfaceType(type) && isInterfaceType(targetType)) { const targetTypeFields = targetType.getFields(); const extensionFields: Record = Object.create(null); - Object.keys(type.getFields()).forEach((fieldName: string) => { + let isExtensionFieldsEmpty = true; + for (const fieldName in type.getFields()) { if (!targetTypeFields[fieldName]) { extensionFields[fieldName] = true; + isExtensionFieldsEmpty = false; } - }); - if (Object.keys(extensionFields).length) { + } + if (!isExtensionFieldsEmpty) { interfaceExtensionsMap[typeName] = extensionFields; } } @@ -75,21 +77,21 @@ function extractPossibleTypes(sourceSchema: GraphQLSchema, targetSchema: GraphQL .map(impl => impl.name); } } - }); + } return { possibleTypesMap, interfaceExtensionsMap }; } function flipMapping(mapping: Record>): Record> { const result: Record> = Object.create(null); - Object.keys(mapping).forEach(typeName => { + for (const typeName in mapping) { const toTypeNames = mapping[typeName]; - toTypeNames.forEach(toTypeName => { + for (const toTypeName of toTypeNames) { if (!(toTypeName in result)) { result[toTypeName] = []; } result[toTypeName].push(typeName); - }); - }); + } + } return result; } @@ -135,12 +137,12 @@ function expandAbstractTypes( const newFragments: Array = []; const fragmentReplacements: Record> = Object.create(null); - fragments.forEach((fragment: FragmentDefinitionNode) => { + for (const fragment of fragments) { newFragments.push(fragment); const possibleTypes = possibleTypesMap[fragment.typeCondition.name.value]; if (possibleTypes != null) { fragmentReplacements[fragment.name.value] = []; - possibleTypes.forEach(possibleTypeName => { + for (const possibleTypeName of possibleTypes) { const name = generateFragmentName(possibleTypeName); existingFragmentNames.push(name); const newFragment: FragmentDefinitionNode = { @@ -164,9 +166,9 @@ function expandAbstractTypes( fragmentName: name, typeName: possibleTypeName, }); - }); + } } - }); + } const newDocument = { ...document, @@ -184,12 +186,12 @@ function expandAbstractTypes( const parentType: GraphQLNamedType = getNamedType(maybeType); const interfaceExtension = interfaceExtensionsMap[parentType.name]; const interfaceExtensionFields = [] as Array; - node.selections.forEach((selection: SelectionNode) => { + for (const selection of node.selections) { if (selection.kind === Kind.INLINE_FRAGMENT) { if (selection.typeCondition != null) { const possibleTypes = possibleTypesMap[selection.typeCondition.name.value]; if (possibleTypes != null) { - possibleTypes.forEach(possibleType => { + for (const possibleType of possibleTypes) { const maybePossibleType = targetSchema.getType(possibleType); if ( maybePossibleType != null && @@ -197,13 +199,13 @@ function expandAbstractTypes( ) { addedSelections.push(generateInlineFragment(possibleType, selection.selectionSet)); } - }); + } } } } else if (selection.kind === Kind.FRAGMENT_SPREAD) { const fragmentName = selection.name.value; if (fragmentName in fragmentReplacements) { - fragmentReplacements[fragmentName].forEach(replacement => { + for (const replacement of fragmentReplacements[fragmentName]) { const typeName = replacement.typeName; const maybeReplacementType = targetSchema.getType(typeName); if (maybeReplacementType != null && implementsAbstractType(targetSchema, parentType, maybeType)) { @@ -215,7 +217,7 @@ function expandAbstractTypes( }, }); } - }); + } } } else if ( interfaceExtension != null && @@ -224,7 +226,7 @@ function expandAbstractTypes( ) { interfaceExtensionFields.push(selection); } - }); + } if (parentType.name in reversePossibleTypesMap) { addedSelections.push({ @@ -239,14 +241,14 @@ function expandAbstractTypes( if (interfaceExtensionFields.length) { const possibleTypes = possibleTypesMap[parentType.name]; if (possibleTypes != null) { - possibleTypes.forEach(possibleType => { + for (const possibleType of possibleTypes) { addedSelections.push( generateInlineFragment(possibleType, { kind: Kind.SELECTION_SET, selections: interfaceExtensionFields, }) ); - }); + } newSelections = newSelections.filter( (selection: SelectionNode) => diff --git a/packages/delegate/src/transforms/FilterToSchema.ts b/packages/delegate/src/transforms/FilterToSchema.ts index 553f3618903..3c703ed9141 100644 --- a/packages/delegate/src/transforms/FilterToSchema.ts +++ b/packages/delegate/src/transforms/FilterToSchema.ts @@ -71,7 +71,7 @@ function filterToSchema( let fragmentSet = Object.create(null); - operations.forEach((operation: OperationDefinitionNode) => { + for (const operation of operations) { let type: Maybe>; if (operation.operation === 'subscription') { type = targetSchema.getSubscriptionType(); @@ -112,7 +112,7 @@ function filterToSchema( variableDefinitions, selectionSet, }); - }); + } const newVariables = usedVariables.reduce((acc, variableName) => { const variableValue = variables[variableName]; @@ -278,13 +278,13 @@ function filterSelectionSet( function union(...arrays: Array>): Array { const cache: Record = Object.create(null); const result: Array = []; - arrays.forEach(array => { - array.forEach(item => { + for (const array of arrays) { + for (const item of array) { if (!(item in cache)) { cache[item] = true; result.push(item); } - }); - }); + } + } return result; } diff --git a/packages/delegate/src/transforms/VisitSelectionSets.ts b/packages/delegate/src/transforms/VisitSelectionSets.ts index 02c547e9e20..d0021378bc3 100644 --- a/packages/delegate/src/transforms/VisitSelectionSets.ts +++ b/packages/delegate/src/transforms/VisitSelectionSets.ts @@ -54,13 +54,13 @@ function visitSelectionSets( const operations: Array = []; const fragments: Record = Object.create(null); - document.definitions.forEach(def => { + for (const def of document.definitions) { if (def.kind === Kind.OPERATION_DEFINITION) { operations.push(def); } else if (def.kind === Kind.FRAGMENT_DEFINITION) { fragments[def.name.value] = def; } - }); + } const partialExecutionContext = { schema, @@ -87,14 +87,14 @@ function visitSelectionSets( ); const newSelections: Array = []; - Object.keys(fields).forEach(responseKey => { + for (const responseKey in fields) { const fieldNodes = fields[responseKey]; - fieldNodes.forEach(fieldNode => { + for (const fieldNode of fieldNodes) { const selectionSet = fieldNode.selectionSet; if (selectionSet == null) { newSelections.push(fieldNode); - return; + continue; } const newSelectionSet = visit( @@ -106,15 +106,15 @@ function visitSelectionSets( if (newSelectionSet === selectionSet) { newSelections.push(fieldNode); - return; + continue; } newSelections.push({ ...fieldNode, selectionSet: newSelectionSet, }); - }); - }); + } + } return { ...operation, @@ -125,7 +125,8 @@ function visitSelectionSets( }; }); - Object.values(fragments).forEach(fragment => { + for (const fragmentIndex in fragments) { + const fragment = fragments[fragmentIndex]; newDefinitions.push( visit( fragment, @@ -134,7 +135,7 @@ function visitSelectionSets( }) ) ); - }); + } return { ...document, diff --git a/packages/delegate/tests/delegateToSchema.test..ts b/packages/delegate/tests/delegateToSchema.test..ts index 4c51bf400c4..4f4c23ce2f0 100644 --- a/packages/delegate/tests/delegateToSchema.test..ts +++ b/packages/delegate/tests/delegateToSchema.test..ts @@ -54,7 +54,7 @@ describe('delegateToSchema', () => { ); assertSome(result.data) - expect(result.data.delegateToSchema).toEqual('test'); + expect(result.data['delegateToSchema']).toEqual('test'); }); test('should work even where there are default fields', async () => { @@ -101,7 +101,7 @@ describe('delegateToSchema', () => { ); assertSome(result.data) - expect(result.data.delegateToSchema).toEqual('test'); + expect(result.data['delegateToSchema']).toEqual('test'); }); test('should work even when there are variables', async () => { @@ -153,6 +153,6 @@ describe('delegateToSchema', () => { ); assertSome(result.data) - expect(result.data.delegateToSchema).toEqual('test'); + expect(result.data['delegateToSchema']).toEqual('test'); }); }); diff --git a/packages/delegate/tests/errors.test.ts b/packages/delegate/tests/errors.test.ts index 56833c27c62..a78982697a4 100644 --- a/packages/delegate/tests/errors.test.ts +++ b/packages/delegate/tests/errors.test.ts @@ -11,7 +11,7 @@ import { delegateToSchema, defaultMergedResolver } from '../src'; class ErrorWithExtensions extends GraphQLError { constructor(message: string, code: string) { - super(message, null, null, null, null, null, { code }); + super(message, null as any, null, null, null, null, { code }); } } @@ -38,7 +38,7 @@ describe('Errors', () => { fieldNodes: [], returnType: {} as any, parentType: {} as any, - path: {prev: undefined, key: "foo", typename: undefined }, + path: {prev: undefined, key: "foo", typename: undefined } as any, schema: {} as any, fragments: {}, rootValue: {}, @@ -100,9 +100,10 @@ describe('Errors', () => { expect(e.originalError).toBeDefined(); expect(e.originalError.errors).toBeDefined(); expect(e.originalError.errors).toHaveLength(result.errors.length); - result.errors.forEach((error, i) => { + for (const i in result.errors) { + const error = result.errors[i]; expect(e.originalError.errors[i]).toEqual(error); - }); + } } }); @@ -119,7 +120,7 @@ describe('Errors', () => { } `; - const unpathedError = locatedError(new Error('TestError'), undefined, ["_entities", 7, "name"]); + const unpathedError = locatedError(new Error('TestError'), undefined as any, ["_entities", 7, "name"]); const remoteSchema = makeExecutableSchema({ typeDefs, @@ -184,7 +185,7 @@ describe('Errors', () => { } `; - const unpathedError = locatedError(new Error('TestError'), undefined, ["_entities", 7, "name"]); + const unpathedError = locatedError(new Error('TestError'), undefined as any, ["_entities", 7, "name"]); const remoteSchema = makeExecutableSchema({ typeDefs, diff --git a/packages/links/tests/upload.test.ts b/packages/links/tests/upload.test.ts index f41b9eb424a..1b8b6d76066 100644 --- a/packages/links/tests/upload.test.ts +++ b/packages/links/tests/upload.test.ts @@ -25,7 +25,7 @@ function streamToString(stream: Readable) { function startServer(e: Express): Promise { return new Promise((resolve, reject) => { - e.listen(undefined, 'localhost', function (error: Error) { + e.listen(undefined as any, 'localhost', function (this: any, error: Error) { if (error) { reject(error); } else { diff --git a/packages/load-files/tests/file-scanner.spec.ts b/packages/load-files/tests/file-scanner.spec.ts index 4d7616709ef..da1e1454c60 100644 --- a/packages/load-files/tests/file-scanner.spec.ts +++ b/packages/load-files/tests/file-scanner.spec.ts @@ -19,7 +19,7 @@ function testSchemaDir({ path, expected, note, extensions, ignoreIndex }: TestDi }; }); - syncAndAsync.forEach(([type, loadFiles]) => { + for (const [type, loadFiles] of syncAndAsync) { describe(type, () => { it(`should return the correct schema results for path: ${path} (${note})`, async () => { const result = await loadFiles(path, options); @@ -33,7 +33,7 @@ function testSchemaDir({ path, expected, note, extensions, ignoreIndex }: TestDi })).toEqual(expected.map(stripWhitespaces)); }); }); - }) + } } @@ -55,7 +55,7 @@ function testResolversDir({ path, expected, note, extensions, compareValue, igno }; }); - syncAndAsync.forEach(([type, loadFiles]) => { + for (const [type, loadFiles] of syncAndAsync) { describe(type, () => { it(`should return the correct resolvers results for path: ${path} (${note})`, async () => { const result = await loadFiles(path, options); @@ -67,7 +67,7 @@ function testResolversDir({ path, expected, note, extensions, compareValue, igno } }); }) - }); + } } function stripWhitespaces(str: any): string { @@ -219,7 +219,7 @@ describe('file scanner', function() { note: 'extensions and ignored extensions works with a trailing dot', }); }); - syncAndAsync.forEach(([type, loadFiles]) => { + for (const [type, loadFiles] of syncAndAsync) { it(`${type}: should process custom extractExports properly`, async () => { const customQueryTypeName = 'root_query'; const customExtractExports = (fileExport: any) => { @@ -254,7 +254,7 @@ describe('file scanner', function() { expect(typeof resolvers.Query.foo).toBe('function'); expect(resolvers.Query.foo()).toBe('FOO'); }); - }) + } }); interface TestDirOptions { diff --git a/packages/load-files/tests/test-assets/11/1.spec.ts b/packages/load-files/tests/test-assets/11/1.spec.ts index d3cbba3204d..dcbb9a5413e 100644 --- a/packages/load-files/tests/test-assets/11/1.spec.ts +++ b/packages/load-files/tests/test-assets/11/1.spec.ts @@ -1,3 +1,3 @@ -function foo() {} +function bar(..._args: any[]) {} -foo('dummy', () => {}); +bar('dummy', () => {}); diff --git a/packages/load-files/tests/test-assets/11/2.test.ts b/packages/load-files/tests/test-assets/11/2.test.ts index d3cbba3204d..d23eadd8239 100644 --- a/packages/load-files/tests/test-assets/11/2.test.ts +++ b/packages/load-files/tests/test-assets/11/2.test.ts @@ -1,3 +1,3 @@ -function foo() {} +function foo(..._args: any[]) {} foo('dummy', () => {}); diff --git a/packages/load/src/filter-document-kind.ts b/packages/load/src/filter-document-kind.ts index aa49a3e155f..044a55404f4 100644 --- a/packages/load/src/filter-document-kind.ts +++ b/packages/load/src/filter-document-kind.ts @@ -17,11 +17,11 @@ export const filterKind = (content: DocumentNode | undefined, filterKinds: null } if (invalidDefinitions.length > 0) { - invalidDefinitions.forEach(d => { - if (env['DEBUG']) { - console.error(`Filtered document of kind ${d.kind} due to filter policy (${filterKinds.join(', ')})`); + if (env['DEBUG']) { + for (const d of invalidDefinitions) { + console.log(`Filtered document of kind ${d.kind} due to filter policy (${filterKinds.join(', ')})`); } - }); + } } return { diff --git a/packages/load/src/load-typedefs.ts b/packages/load/src/load-typedefs.ts index 255e86e179a..defb838b0af 100644 --- a/packages/load/src/load-typedefs.ts +++ b/packages/load/src/load-typedefs.ts @@ -86,7 +86,7 @@ export function loadTypedefsSync>( const validSources: Source[] = []; - sources.forEach(partialSource => { + for (const partialSource of sources) { parseSource({ partialSource, options, @@ -96,7 +96,7 @@ export function loadTypedefsSync>( validSources.push(source); }, }); - }); + } return prepareResult({ options, pointerOptionMap, validSources }); } diff --git a/packages/load/src/utils/queue.ts b/packages/load/src/utils/queue.ts index 89188845b71..1a396be83e7 100644 --- a/packages/load/src/utils/queue.ts +++ b/packages/load/src/utils/queue.ts @@ -22,7 +22,9 @@ export function useSyncQueue() { queue.push(fn); }, runAll() { - queue.forEach(fn => fn()); + for (const fn of queue) { + fn(); + } }, }; } diff --git a/packages/load/tests/loaders/documents/documents-from-glob.spec.ts b/packages/load/tests/loaders/documents/documents-from-glob.spec.ts index 39424514293..f7fe2ce3532 100644 --- a/packages/load/tests/loaders/documents/documents-from-glob.spec.ts +++ b/packages/load/tests/loaders/documents/documents-from-glob.spec.ts @@ -34,9 +34,10 @@ describe('documentsFromGlob', () => { const result = await load(glob, { loaders: [new CodeFileLoader()] }); - const operations = separateOperations(result[0].document); + const { document } = result[0]; + const operations = document && separateOperations(document); - expect(Object.keys(operations)).toHaveLength(2); + expect(operations && Object.keys(operations)).toHaveLength(2); }); test(`Should load GraphQL documents that match custom settings`, async () => { @@ -57,9 +58,10 @@ describe('documentsFromGlob', () => { ] }); - const operations = separateOperations(result[0].document); + const { document } = result[0]; + const operations = document && separateOperations(document); - expect(Object.keys(operations)).toHaveLength(1); + expect(operations && Object.keys(operations)).toHaveLength(1); }); test(`Should throw on syntax errors`, async () => { diff --git a/packages/load/tests/loaders/schema/integration.spec.ts b/packages/load/tests/loaders/schema/integration.spec.ts index aedfd07a2c8..b913ffae1c6 100644 --- a/packages/load/tests/loaders/schema/integration.spec.ts +++ b/packages/load/tests/loaders/schema/integration.spec.ts @@ -33,8 +33,8 @@ describe('loadSchema', () => { const schema = await load(schemaPath, { loaders: [new CodeFileLoader()] }); - expect(schema.getTypeMap().User).toBeDefined(); - expect(schema.getTypeMap().Query).toBeDefined(); + expect(schema.getTypeMap()['User']).toBeDefined(); + expect(schema.getTypeMap()['Query']).toBeDefined(); }); test('should work with graphql single file', async () => { @@ -43,7 +43,7 @@ describe('loadSchema', () => { loaders: [new GraphQLFileLoader()] }); - expect(schema.getTypeMap().User).toBeDefined(); + expect(schema.getTypeMap()['User']).toBeDefined(); }); test('import and merge Query types from few different files', async () => { diff --git a/packages/load/tests/loaders/schema/schema-from-export.spec.ts b/packages/load/tests/loaders/schema/schema-from-export.spec.ts index 0017c8ddf2f..2e8cce1aeaf 100644 --- a/packages/load/tests/loaders/schema/schema-from-export.spec.ts +++ b/packages/load/tests/loaders/schema/schema-from-export.spec.ts @@ -35,7 +35,7 @@ describe('Schema From Export', () => { expect(isSchema(result)).toBeTruthy(); const QueryType = result.getQueryType() assertNonMaybe(QueryType) - expect(QueryType.getFields().hello).toBeDefined(); + expect(QueryType.getFields()['hello']).toBeDefined(); }); test('should load the schema correctly from variable export', async () => { diff --git a/packages/load/tests/loaders/schema/schema-from-typedefs.spec.ts b/packages/load/tests/loaders/schema/schema-from-typedefs.spec.ts index b35d89ce1c9..b534df6b625 100644 --- a/packages/load/tests/loaders/schema/schema-from-typedefs.spec.ts +++ b/packages/load/tests/loaders/schema/schema-from-typedefs.spec.ts @@ -26,8 +26,8 @@ describe('schema from typedefs', () => { loaders: [new GraphQLFileLoader()] }); - expect(schema.getTypeMap().User).toBeDefined(); - expect(schema.getTypeMap().Query).toBeDefined(); + expect(schema.getTypeMap()['User']).toBeDefined(); + expect(schema.getTypeMap()['Query']).toBeDefined(); }); it('should ignore empty files when using glob expressions', async () => { @@ -78,8 +78,8 @@ describe('schema from typedefs', () => { loaders: [new CodeFileLoader()] }); - expect(schema.getTypeMap().User).toBeDefined(); - expect(schema.getTypeMap().Query).toBeDefined(); + expect(schema.getTypeMap()['User']).toBeDefined(); + expect(schema.getTypeMap()['Query']).toBeDefined(); }); it('should work without globs correctly', async () => { @@ -88,8 +88,8 @@ describe('schema from typedefs', () => { loaders: [new CodeFileLoader()] }); - expect(schema.getTypeMap().User).toBeDefined(); - expect(schema.getTypeMap().Query).toBeDefined(); + expect(schema.getTypeMap()['User']).toBeDefined(); + expect(schema.getTypeMap()['Query']).toBeDefined(); }); it('should work with import notations', async () => { @@ -98,8 +98,8 @@ describe('schema from typedefs', () => { loaders: [new GraphQLFileLoader()] }); - expect(schema.getTypeMap().User).toBeDefined(); - expect(schema.getTypeMap().Query).toBeDefined(); + expect(schema.getTypeMap()['User']).toBeDefined(); + expect(schema.getTypeMap()['Query']).toBeDefined(); }); it('should work with extensions (static graphql file)', async () => { @@ -149,9 +149,9 @@ describe('schema from typedefs', () => { includeSources: true, }); assertNonMaybe(schemaWithSources.extensions) - expect(schemaWithSources.extensions.sources).toBeDefined(); - expect(schemaWithSources.extensions.sources).toHaveLength(1); - expect(schemaWithSources.extensions.sources[0]).toMatchObject(expect.objectContaining({ + expect(schemaWithSources.extensions['sources']).toBeDefined(); + expect(schemaWithSources.extensions['sources']).toHaveLength(1); + expect(schemaWithSources.extensions['sources'][0]).toMatchObject(expect.objectContaining({ name: glob })) @@ -159,7 +159,7 @@ describe('schema from typedefs', () => { loaders: [new GraphQLFileLoader()] }); assertNonMaybe(schemaWithoutSources.extensions) - expect(schemaWithoutSources.extensions.sources).not.toBeDefined(); + expect(schemaWithoutSources.extensions['sources']).not.toBeDefined(); }); }) }); diff --git a/packages/loaders/code-file/src/index.ts b/packages/loaders/code-file/src/index.ts index cd88c9292d1..a1a7a7eb266 100644 --- a/packages/loaders/code-file/src/index.ts +++ b/packages/loaders/code-file/src/index.ts @@ -25,6 +25,7 @@ import { tryToLoadFromExport, tryToLoadFromExportSync } from './load-from-module import { isAbsolute, resolve } from 'path'; import { cwd, env } from 'process'; import { readFileSync, promises as fsPromises, existsSync } from 'fs'; +import { createRequire } from 'module'; const { readFile, access } = fsPromises; @@ -189,7 +190,10 @@ export class CodeFileLoader implements UniversalLoader { if (!options.noRequire) { try { if (options && options.require) { - asArray(options.require).forEach(m => require(m)); + const cwdRequire = createRequire(options.cwd || cwd()); + for (const m of asArray(options.require)) { + cwdRequire(m); + } } const loaded = tryToLoadFromExportSync(normalizedFilePath); diff --git a/packages/loaders/code-file/tests/load-from-code-file.spec.ts b/packages/loaders/code-file/tests/load-from-code-file.spec.ts index a18e5163819..06e273905a3 100644 --- a/packages/loaders/code-file/tests/load-from-code-file.spec.ts +++ b/packages/loaders/code-file/tests/load-from-code-file.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ import * as path from 'path'; import { CodeFileLoader } from '../src'; import { parse } from 'graphql'; @@ -11,7 +12,7 @@ describe('loadFromCodeFile', () => { noRequire: true, cwd: __dirname }); - const doc = loaded.document ? loaded.document : parse(loaded.rawSDL); + const doc = loaded?.document ? loaded?.document : parse(loaded?.rawSDL!); expect(doc).toBeFalsy(); } catch (e) { @@ -24,9 +25,9 @@ describe('loadFromCodeFile', () => { noRequire: true, cwd: __dirname }); - const doc = loaded.document ? loaded.document : parse(loaded.rawSDL); + const doc = loaded?.document ? loaded?.document : parse(loaded?.rawSDL!); - expect(doc.kind).toEqual('Document'); + expect(doc?.kind).toEqual('Document'); }); it('should consider options.cwd', async () => { @@ -34,9 +35,9 @@ describe('loadFromCodeFile', () => { cwd: path.resolve(__dirname, 'test-files'), noRequire: true, }); - const doc = loaded.document ? loaded.document : parse(loaded.rawSDL); + const doc = loaded?.document ? loaded?.document : parse(loaded?.rawSDL!); - expect(doc.kind).toEqual('Document'); + expect(doc?.kind).toEqual('Document'); }); it('should load a TypeScript file using decorator', async () => { @@ -44,18 +45,18 @@ describe('loadFromCodeFile', () => { noRequire: true, cwd: __dirname }); - const doc = loaded.document ? loaded.document : parse(loaded.rawSDL); + const doc = loaded?.document ? loaded?.document : parse(loaded?.rawSDL!); - expect(doc.kind).toEqual('Document'); + expect(doc?.kind).toEqual('Document'); }); it('should support string interpolation', async () => { const loaded = await loader.load('./test-files/string-interpolation.js', { cwd: __dirname }); - const doc = loaded.document ? loaded.document : parse(loaded.rawSDL); + const doc = loaded?.document ? loaded?.document : parse(loaded?.rawSDL!); - expect(doc.kind).toEqual('Document'); + expect(doc?.kind).toEqual('Document'); }); }); @@ -68,9 +69,9 @@ describe('loadFromCodeFileSync', () => { noRequire: true, cwd: __dirname }); - const doc = loaded.document ? loaded.document : parse(loaded.rawSDL); + const doc = loaded?.document ? loaded?.document : parse(loaded?.rawSDL!); - expect(doc.kind).toEqual('Document'); + expect(doc?.kind).toEqual('Document'); }).toThrowError('Syntax Error: Unexpected Name "InvalidGetUser"') }); @@ -79,9 +80,9 @@ describe('loadFromCodeFileSync', () => { noRequire: true, cwd: __dirname }); - const doc = loaded.document; + const doc = loaded?.document; - expect(doc.kind).toEqual('Document'); + expect(doc?.kind).toEqual('Document'); }); it('should consider options.cwd', () => { @@ -89,9 +90,9 @@ describe('loadFromCodeFileSync', () => { cwd: path.resolve(__dirname, 'test-files'), noRequire: true, }); - const doc = loaded.document; + const doc = loaded?.document; - expect(doc.kind).toEqual('Document'); + expect(doc?.kind).toEqual('Document'); }); it('should support string interpolation', () => { @@ -99,8 +100,8 @@ describe('loadFromCodeFileSync', () => { cwd: __dirname }); - const doc = loaded.document; + const doc = loaded?.document; - expect(doc.kind).toEqual('Document'); + expect(doc?.kind).toEqual('Document'); }); }); diff --git a/packages/loaders/prisma/src/prisma-yml/Environment.ts b/packages/loaders/prisma/src/prisma-yml/Environment.ts index 7bb138edfb3..e6bfb76c01e 100644 --- a/packages/loaders/prisma/src/prisma-yml/Environment.ts +++ b/packages/loaders/prisma/src/prisma-yml/Environment.ts @@ -113,8 +113,8 @@ export class Environment { // clean up all prisma-eu1 and prisma-us1 clusters if they already exist this.clusters = this._getClusters().filter(c => c.name !== 'prisma-eu1' && c.name !== 'prisma-us1'); - res.me.memberships.forEach((m: any) => { - m.workspace.clusters.forEach((cluster: any) => { + for (const m of res.me.memberships) { + for (const cluster of m.workspace.clusters) { const endpoint = cluster.connectInfo ? cluster.connectInfo.endpoint : cluster.customConnectionInfo @@ -132,8 +132,8 @@ export class Environment { m.workspace.slug ) ); - }); - }); + } + } } } catch (e) { debug(e); diff --git a/packages/loaders/prisma/src/prisma-yml/PrismaDefinition.ts b/packages/loaders/prisma/src/prisma-yml/PrismaDefinition.ts index 71d11941b47..4386064f280 100644 --- a/packages/loaders/prisma/src/prisma-yml/PrismaDefinition.ts +++ b/packages/loaders/prisma/src/prisma-yml/PrismaDefinition.ts @@ -255,7 +255,7 @@ and execute ${chalk.bold.green('prisma deploy')} again, to get that value auto-f : []; let allTypes = ''; - typesPaths.forEach(unresolvedTypesPath => { + for (const unresolvedTypesPath of typesPaths) { const typesPath = path.join(this.definitionDir!, unresolvedTypesPath!); try { fs.accessSync(typesPath); @@ -264,7 +264,7 @@ and execute ${chalk.bold.green('prisma deploy')} again, to get that value auto-f } catch { throw new Error(`The types definition file "${typesPath}" could not be found.`); } - }); + } return allTypes; } diff --git a/packages/loaders/prisma/src/prisma-yml/Variables.ts b/packages/loaders/prisma/src/prisma-yml/Variables.ts index 8af48d59272..6abb8d0bb5a 100644 --- a/packages/loaders/prisma/src/prisma-yml/Variables.ts +++ b/packages/loaders/prisma/src/prisma-yml/Variables.ts @@ -69,35 +69,38 @@ export class Variables { let warned = false; if (typeof property === 'string' && property.match(this.variableSyntax)) { - property.match(this.variableSyntax)!.forEach(matchedString => { - const variableString = matchedString - .replace(this.variableSyntax, (_, varName) => varName.trim()) - .replace(/\s/g, ''); - - let singleValueToPopulate: Promise | null = null; - if (variableString.match(this.overwriteSyntax)) { - singleValueToPopulate = this.overwrite(variableString); - } else { - singleValueToPopulate = this.getValueFromSource(variableString).then((valueToPopulate: any) => { - if (typeof valueToPopulate === 'object') { - return this.populateObject(valueToPopulate); - } - return valueToPopulate; - }); - } + const matchedStrings = property.match(this.variableSyntax); + if (matchedStrings) { + for (const matchedString of matchedStrings) { + const variableString = matchedString + .replace(this.variableSyntax, (_, varName) => varName.trim()) + .replace(/\s/g, ''); - singleValueToPopulate = singleValueToPopulate!.then(valueToPopulate => { - if (this.warnIfNotFound(variableString, valueToPopulate)) { - warned = true; + let singleValueToPopulate: Promise | null = null; + if (variableString.match(this.overwriteSyntax)) { + singleValueToPopulate = this.overwrite(variableString); + } else { + singleValueToPopulate = this.getValueFromSource(variableString).then((valueToPopulate: any) => { + if (typeof valueToPopulate === 'object') { + return this.populateObject(valueToPopulate); + } + return valueToPopulate; + }); } - return this.populateVariable(property, matchedString, valueToPopulate).then((newProperty: any) => { - property = newProperty; - return Promise.resolve(property); + + singleValueToPopulate = singleValueToPopulate!.then(valueToPopulate => { + if (this.warnIfNotFound(variableString, valueToPopulate)) { + warned = true; + } + return this.populateVariable(property, matchedString, valueToPopulate).then((newProperty: any) => { + property = newProperty; + return Promise.resolve(property); + }); }); - }); - allValuesToPopulate.push(singleValueToPopulate); - }); + allValuesToPopulate.push(singleValueToPopulate); + } + } return Promise.all(allValuesToPopulate).then(() => { if ((property as any) !== (this.json as any) && !warned) { return this.populateProperty(property); diff --git a/packages/loaders/url/src/index.ts b/packages/loaders/url/src/index.ts index 2fa8cda6872..e3172ab1dca 100644 --- a/packages/loaders/url/src/index.ts +++ b/packages/loaders/url/src/index.ts @@ -709,8 +709,7 @@ export class UrlLoader implements DocumentLoader { } function switchProtocols(pointer: string, protocolMap: Record): string { - const protocols: [string, string][] = Object.keys(protocolMap).map(source => [source, protocolMap[source]]); - return protocols.reduce( + return Object.entries(protocolMap).reduce( (prev, [source, target]) => prev.replace(`${source}://`, `${target}://`).replace(`${source}:\\`, `${target}:\\`), pointer ); diff --git a/packages/loaders/url/tests/url-loader.spec.ts b/packages/loaders/url/tests/url-loader.spec.ts index 1f6687c7bfb..ba0fad62a3e 100644 --- a/packages/loaders/url/tests/url-loader.spec.ts +++ b/packages/loaders/url/tests/url-loader.spec.ts @@ -156,7 +156,7 @@ input TestInput { assertNonMaybe(source.schema) expect(printSchemaWithDirectives(source.schema)).toBeSimilarGqlDoc(testTypeDefs); - expect(Array.isArray(headers.accept) ? headers.accept.join(',') : headers.accept).toContain(`application/json`); + expect(Array.isArray(headers['accept']) ? headers['accept'].join(',') : headers['accept']).toContain(`application/json`); expect(headers['content-type']).toContain(`application/json`); }); @@ -177,9 +177,9 @@ input TestInput { assertNonMaybe(source.schema) expect(printSchemaWithDirectives(source.schema)).toBeSimilarGqlDoc(testTypeDefs); - expect(Array.isArray(headers.accept) ? headers.accept.join(',') : headers.accept).toContain(`application/json`); + expect(Array.isArray(headers['accept']) ? headers['accept'].join(',') : headers['accept']).toContain(`application/json`); expect(headers['content-type']).toContain(`application/json`); - expect(headers.auth).toContain(`1`); + expect(headers['auth']).toContain(`1`); }); it('Should pass extra headers when they are specified as array', async () => { @@ -198,11 +198,11 @@ input TestInput { assertNonMaybe(source.schema) expect(printSchemaWithDirectives(source.schema)).toBeSimilarGqlDoc(testTypeDefs); - expect(Array.isArray(headers.accept) ? headers.accept.join(',') : headers.accept).toContain(`application/json`); + expect(Array.isArray(headers['accept']) ? headers['accept'].join(',') : headers['accept']).toContain(`application/json`); expect(headers['content-type']).toContain(`application/json`); - expect(headers.a).toContain(`1`); - expect(headers.b).toContain(`2`); - expect(headers.c).toContain(`3`); + expect(headers['a']).toContain(`1`); + expect(headers['b']).toContain(`2`); + expect(headers['c']).toContain(`3`); }); it('Should utilize extra introspection options', async () => { @@ -247,7 +247,7 @@ input TestInput { expect(result?.errors).toBeFalsy(); - expect(result?.data?.a).toBe(testVariableValue); + expect(result?.data?.['a']).toBe(testVariableValue); }); it('Should preserve "ws" and "http" in the middle of a pointer', async () => { @@ -488,8 +488,8 @@ input TestInput { expect(result.errors).toBeFalsy(); assertNonMaybe(result.data) - expect(result.data.uploadFile?.filename).toBe(fileName); - expect(result.data.uploadFile?.content).toBe(content); + expect(result.data['uploadFile']?.filename).toBe(fileName); + expect(result.data['uploadFile']?.content).toBe(content); }); }); }); diff --git a/packages/merge/src/merge-resolvers.ts b/packages/merge/src/merge-resolvers.ts index 4c66d5c7365..1f7b26b27fa 100644 --- a/packages/merge/src/merge-resolvers.ts +++ b/packages/merge/src/merge-resolvers.ts @@ -1,4 +1,4 @@ -import { IResolvers, mergeDeep } from '@graphql-tools/utils'; +import { IResolvers, Maybe, mergeDeep } from '@graphql-tools/utils'; /** * Additional options for merging resolvers @@ -37,7 +37,7 @@ export interface MergeResolversOptions { * ``` */ export function mergeResolvers( - resolversDefinitions: IResolvers | IResolvers[], + resolversDefinitions: Maybe> | Maybe>[]>, options?: MergeResolversOptions ): IResolvers { if (!resolversDefinitions || (Array.isArray(resolversDefinitions) && resolversDefinitions.length === 0)) { @@ -49,7 +49,7 @@ export function mergeResolvers( } if (resolversDefinitions.length === 1) { - return resolversDefinitions[0]; + return resolversDefinitions[0] || {}; } const resolvers = new Array>(); @@ -58,19 +58,21 @@ export function mergeResolvers( if (Array.isArray(resolversDefinition)) { resolversDefinition = mergeResolvers(resolversDefinition); } - if (typeof resolversDefinition === 'object') { + if (typeof resolversDefinition === 'object' && resolversDefinition) { resolvers.push(resolversDefinition); } } const result = resolvers.reduce(mergeDeep, {}); - options?.exclusions?.forEach(exclusion => { - const [typeName, fieldName] = exclusion.split('.'); - if (!fieldName || fieldName === '*') { - delete result[typeName]; - } else if (result[typeName]) { - delete result[typeName][fieldName]; + if (options?.exclusions) { + for (const exclusion of options.exclusions) { + const [typeName, fieldName] = exclusion.split('.'); + if (!fieldName || fieldName === '*') { + delete result[typeName]; + } else if (result[typeName]) { + delete result[typeName][fieldName]; + } } - }); + } return result; } diff --git a/packages/merge/src/typedefs-mergers/comments.ts b/packages/merge/src/typedefs-mergers/comments.ts index dade851edc9..fdd80c9d54f 100644 --- a/packages/merge/src/typedefs-mergers/comments.ts +++ b/packages/merge/src/typedefs-mergers/comments.ts @@ -2,7 +2,6 @@ import { getDescription, StringValueNode, FieldDefinitionNode, - InputValueDefinitionNode, ASTNode, NameNode, TypeNode, @@ -29,24 +28,26 @@ export function collectComment(node: NamedDefinitionNode): void { switch (node.kind) { case 'EnumTypeDefinition': - node.values?.forEach(value => { - pushComment(value, entityName, value.name.value); - }); + if (node.values) { + for (const value of node.values) { + pushComment(value, entityName, value.name.value); + } + } break; case 'ObjectTypeDefinition': case 'InputObjectTypeDefinition': case 'InterfaceTypeDefinition': if (node.fields) { - node.fields.forEach((field: FieldDefinitionNode | InputValueDefinitionNode) => { + for (const field of node.fields) { pushComment(field, entityName, field.name.value); if (isFieldDefinitionNode(field) && field.arguments) { - field.arguments.forEach(arg => { + for (const arg of field.arguments) { pushComment(arg, entityName, field.name.value, arg.name.value); - }); + } } - }); + } } break; } diff --git a/packages/merge/src/typedefs-mergers/merge-typedefs.ts b/packages/merge/src/typedefs-mergers/merge-typedefs.ts index 02057f10f37..9ccd5112993 100644 --- a/packages/merge/src/typedefs-mergers/merge-typedefs.ts +++ b/packages/merge/src/typedefs-mergers/merge-typedefs.ts @@ -124,7 +124,9 @@ function visitTypeSources( if (typeof typeSource === 'function') { visitTypeSources(typeSource(), options, allNodes, visitedTypeSources); } else if (Array.isArray(typeSource)) { - typeSource.forEach(type => visitTypeSources(type, options, allNodes, visitedTypeSources)); + for (const type of typeSource) { + visitTypeSources(type, options, allNodes, visitedTypeSources); + } } else if (isSchema(typeSource)) { const documentNode = getDocumentNodeFromSchema(typeSource, options); visitTypeSources(documentNode.definitions as DefinitionNode[], options, allNodes, visitedTypeSources); diff --git a/packages/merge/tests/merge-nodes.spec.ts b/packages/merge/tests/merge-nodes.spec.ts index 859851612f8..2ca7fbaa83a 100644 --- a/packages/merge/tests/merge-nodes.spec.ts +++ b/packages/merge/tests/merge-nodes.spec.ts @@ -1,6 +1,6 @@ import { mergeGraphQLNodes } from '../src'; -import { parse, InputObjectTypeDefinitionNode } from 'graphql'; -import { assertEnumTypeDefinitionNode, assertInputObjectTypeDefinitionNode, assertInterfaceTypeDefinitionNode, assertNamedTypeNode, assertObjectTypeDefinitionNode, assertScalarTypeDefinitionNode, assertUnionTypeDefinitionNode } from '../../testing/assertion'; +import { parse } from 'graphql'; +import { assertEnumTypeDefinitionNode, assertInputObjectTypeDefinitionNode, assertNamedTypeNode, assertObjectTypeDefinitionNode, assertScalarTypeDefinitionNode, assertUnionTypeDefinitionNode } from '../../testing/assertion'; import { assertSome } from '@graphql-tools/utils'; describe('Merge Nodes', () => { @@ -9,7 +9,7 @@ describe('Merge Nodes', () => { const type1 = parse(`type A { f1: String }`); const type2 = parse(`type A`); const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]); - const type = merged.A; + const type = merged['A']; assertObjectTypeDefinitionNode(type) assertSome(type.fields) expect(type.fields.length).toBe(1); @@ -22,7 +22,7 @@ describe('Merge Nodes', () => { const type1 = parse(`type A { f1: String }`); const type2 = parse(`type A { f2: Int }`); const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]); - const type = merged.A; + const type = merged['A']; assertObjectTypeDefinitionNode(type) assertSome(type.fields) @@ -39,7 +39,7 @@ describe('Merge Nodes', () => { const type1 = parse(`type A { f1: String }`); const type2 = parse(`type A { f1: String, f2: Int}`); const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]); - const type = merged.A; + const type = merged['A']; assertObjectTypeDefinitionNode(type) assertSome(type.fields) @@ -56,7 +56,7 @@ describe('Merge Nodes', () => { const type1 = parse(`interface Base { f1: String } type A implements Base { f1: String }`); const type2 = parse(`interface Base { f1: String } type A implements Base { f2: Int}`); const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]); - const type = merged.A; + const type = merged['A']; assertObjectTypeDefinitionNode(type) assertSome(type.interfaces) @@ -68,7 +68,7 @@ describe('Merge Nodes', () => { const type1 = parse(`interface Base { f1: String } type A implements Base { f1: String }`); const type2 = parse(`type A { f2: Int}`); const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]); - const type = merged.A; + const type = merged['A']; assertObjectTypeDefinitionNode(type) assertSome(type.interfaces) @@ -80,7 +80,7 @@ describe('Merge Nodes', () => { const type1 = parse(`type A @test { f1: String }`); const type2 = parse(`type A { f2: Int}`); const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]); - const type = merged.A; + const type = merged['A']; assertObjectTypeDefinitionNode(type) assertSome(type.directives) @@ -92,7 +92,7 @@ describe('Merge Nodes', () => { const type1 = parse(`type A @test { f1: String }`); const type2 = parse(`type A @other { f2: Int}`); const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]); - const type = merged.A; + const type = merged['A']; assertObjectTypeDefinitionNode(type) assertSome(type.directives) @@ -105,7 +105,7 @@ describe('Merge Nodes', () => { const type1 = parse(`type A @test { f1: String }`); const type2 = parse(`type A @test { f2: Int}`); const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]); - const type = merged.A; + const type = merged['A']; assertObjectTypeDefinitionNode(type) assertSome(type.directives) @@ -117,7 +117,7 @@ describe('Merge Nodes', () => { const type1 = parse(`type A @test { f1: String }`); const type2 = parse(`type A @test2 { f2: Int}`); const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]); - const type = merged.A; + const type = merged['A']; assertObjectTypeDefinitionNode(type) assertSome(type.directives) @@ -132,7 +132,7 @@ describe('Merge Nodes', () => { const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions], { reverseDirectives: true, }); - const type = merged.A; + const type = merged['A']; assertObjectTypeDefinitionNode(type) assertSome(type.directives) @@ -145,7 +145,7 @@ describe('Merge Nodes', () => { const type1 = parse(`interface Base1 { f1: String } type A implements Base1 { f1: String }`); const type2 = parse(`interface Base2 { f2: Int } type A implements Base2 { f2: Int}`); const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]); - const type = merged.A; + const type = merged['A']; assertObjectTypeDefinitionNode(type) assertSome(type.interfaces) @@ -168,7 +168,7 @@ describe('Merge Nodes', () => { const type1 = parse(`enum A { T }`); const type2 = parse(`enum A { S }`); const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]); - const result = merged.A + const result = merged['A'] assertEnumTypeDefinitionNode(result) assertSome(result.values) expect(result.values.length).toBe(2); @@ -180,7 +180,7 @@ describe('Merge Nodes', () => { const type1 = parse(`enum A { T }`); const type2 = parse(`enum A { T }`); const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]); - const result = merged.A; + const result = merged['A']; assertEnumTypeDefinitionNode(result) assertSome(result.values) @@ -192,7 +192,7 @@ describe('Merge Nodes', () => { const type1 = parse(`enum A @test { T }`); const type2 = parse(`enum A @test2 { T }`); const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]); - const result = merged.A; + const result = merged['A']; assertEnumTypeDefinitionNode(result) assertSome(result.directives) @@ -205,7 +205,7 @@ describe('Merge Nodes', () => { const type1 = parse(`enum A @test { T }`); const type2 = parse(`enum A { S }`); const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]); - const result = merged.A; + const result = merged['A']; assertEnumTypeDefinitionNode(result) assertSome(result.directives) @@ -219,7 +219,7 @@ describe('Merge Nodes', () => { const type1 = parse(`type A union C = A`); const type2 = parse(`type B union C = B`); const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]); - const result = merged.C; + const result = merged['C']; assertUnionTypeDefinitionNode(result) assertSome(result.types) @@ -234,7 +234,7 @@ describe('Merge Nodes', () => { const type1 = parse(`scalar A`); const type2 = parse(`scalar A`); const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]); - const result = merged.A; + const result = merged['A']; assertScalarTypeDefinitionNode(result) expect(result.name.value).toBe('A'); @@ -246,7 +246,7 @@ describe('Merge Nodes', () => { const type1 = parse(`input A { f1: String }`); const type2 = parse(`input A { f2: String }`); const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]); - const result = merged.A; + const result = merged['A']; assertInputObjectTypeDefinitionNode(result) assertSome(result.fields) @@ -259,7 +259,7 @@ describe('Merge Nodes', () => { const type1 = parse(`input A { f1: String }`); const type2 = parse(`input A { f1: String! }`); const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]); - const result = merged.A; + const result = merged['A']; assertInputObjectTypeDefinitionNode(result) assertSome(result.fields) @@ -274,7 +274,7 @@ describe('Merge Nodes', () => { const type1 = parse(`type Query { f1: String }`); const type2 = parse(`type Query { f2: String }`); const merged = mergeGraphQLNodes([...type1.definitions, ...type2.definitions]); - const type = merged.Query; + const type = merged['Query']; assertObjectTypeDefinitionNode(type) assertSome(type.fields) diff --git a/packages/merge/tests/merge-schemas.spec.ts b/packages/merge/tests/merge-schemas.spec.ts index 5b5b4de7c4a..03d5da68988 100644 --- a/packages/merge/tests/merge-schemas.spec.ts +++ b/packages/merge/tests/merge-schemas.spec.ts @@ -78,8 +78,8 @@ describe('Merge Schemas', () => { }); expect(errors).toBeFalsy(); assertSome(data) - expect(data.foo).toBe('FOO'); - expect(data.bar).toBe('BAR'); + expect(data['foo']).toBe('FOO'); + expect(data['bar']).toBe('BAR'); }); it('should merge two valid executable schemas async', async () => { const fooSchema = makeExecutableSchema({ @@ -119,8 +119,8 @@ describe('Merge Schemas', () => { }); expect(errors).toBeFalsy(); assertSome(data) - expect(data.foo).toBe('FOO'); - expect(data.bar).toBe('BAR'); + expect(data['foo']).toBe('FOO'); + expect(data['bar']).toBe('BAR'); }); it('should merge two valid executable schemas with extra resolvers', async () => { const fooSchema = makeExecutableSchema({ @@ -167,9 +167,9 @@ describe('Merge Schemas', () => { }); expect(errors).toBeFalsy(); assertSome(data) - expect(data.foo).toBe('FOO'); - expect(data.bar).toBe('BAR'); - expect(data.qux).toBe('QUX'); + expect(data['foo']).toBe('FOO'); + expect(data['bar']).toBe('BAR'); + expect(data['qux']).toBe('QUX'); }); it('should merge two valid executable schemas with extra typeDefs and resolvers', async () => { const fooSchema = makeExecutableSchema({ @@ -220,9 +220,9 @@ describe('Merge Schemas', () => { }); expect(errors).toBeFalsy(); assertSome(data) - expect(data.foo).toBe('FOO'); - expect(data.bar).toBe('BAR'); - expect(data.qux).toBe('QUX'); + expect(data['foo']).toBe('FOO'); + expect(data['bar']).toBe('BAR'); + expect(data['qux']).toBe('QUX'); }); it('should merge two valid schemas by keeping their directives to be used in extra typeDefs', async () => { const fooSchema = makeExecutableSchema({ @@ -274,9 +274,9 @@ describe('Merge Schemas', () => { }); expect(errors).toBeFalsy(); assertSome(data) - expect(data.foo).toBe('FOO'); - expect(data.bar).toBe('BAR'); - expect(data.qux).toBe('QUX'); + expect(data['foo']).toBe('FOO'); + expect(data['bar']).toBe('BAR'); + expect(data['qux']).toBe('QUX'); }); it('should merge valid schemas with interfaces correctly', async () => { const fooSchema = makeExecutableSchema({ @@ -345,10 +345,10 @@ describe('Merge Schemas', () => { }); expect(errors).toBeFalsy(); assertSome(data) - expect(data.bar.foo).toBe('foo'); - expect(data.bar.bar).toBe('bar'); - expect(data.qux.foo).toBe('foo'); - expect(data.qux.qux).toBe('qux'); + expect(data['bar'].foo).toBe('foo'); + expect(data['bar'].bar).toBe('bar'); + expect(data['qux'].foo).toBe('foo'); + expect(data['qux'].qux).toBe('qux'); }); it('should merge scalars (part of resolvers)', async () => { @@ -389,7 +389,7 @@ describe('Merge Schemas', () => { }); assertSome(dataA) - expect(dataA.a).toEqual(now.toISOString()); + expect(dataA['a']).toEqual(now.toISOString()); // merged schema const { data } = await graphql({ @@ -397,7 +397,7 @@ describe('Merge Schemas', () => { source: /* GraphQL */` { a } ` }); assertSome(data) - expect(data.a).toEqual(now.toISOString()); + expect(data['a']).toEqual(now.toISOString()); }); it.only('should not duplicate directives of scalars', () => { @@ -447,13 +447,13 @@ describe('Merge Schemas', () => { const QueryType = prev.getQueryType() assertSome(QueryType) const fields = QueryType.getFields() - assertSome(fields.test.astNode) - assertSome(fields.test.astNode.directives) - assertSome(fields.test.astNode.directives[0]) - assertSome(fields.test.astNode.directives[0].arguments) - assertListValueNode(fields.test.astNode.directives[0].arguments[0].value) + assertSome(fields['test'].astNode) + assertSome(fields['test'].astNode.directives) + assertSome(fields['test'].astNode.directives[0]) + assertSome(fields['test'].astNode.directives[0].arguments) + assertListValueNode(fields['test'].astNode.directives[0].arguments[0].value) - expect(fields.test.astNode.directives[0].arguments[0].value.values).toHaveLength(1); + expect(fields['test'].astNode.directives[0].arguments[0].value.values).toHaveLength(1); }); it('should merge schemas with custom scalars', () => { const GraphQLUUID = new GraphQLScalarType({ diff --git a/packages/merge/tests/merge-typedefs.spec.ts b/packages/merge/tests/merge-typedefs.spec.ts index bb52a8c0a8b..eb3df437285 100644 --- a/packages/merge/tests/merge-typedefs.spec.ts +++ b/packages/merge/tests/merge-typedefs.spec.ts @@ -7,7 +7,6 @@ import { stripWhitespaces } from './utils'; import gql from 'graphql-tag'; import { readFileSync } from 'fs'; import { join } from 'path'; -import { jest } from '@jest/globals'; import { assertSome } from '@graphql-tools/utils'; const introspectionSchema = JSON.parse(readFileSync(join(__dirname, './schema.json'), 'utf8')); @@ -657,7 +656,6 @@ describe('Merge TypeDefs', () => { const merged = mergeTypeDefs([ makeExecutableSchema({ typeDefs: ['type Query { f1: String }'], - allowUndefinedInResolve: true, }), 'type Query { f2: String }', ]); @@ -679,11 +677,9 @@ describe('Merge TypeDefs', () => { const merged = mergeTypeDefs([ makeExecutableSchema({ typeDefs: ['type RootQuery { f1: String }'], - allowUndefinedInResolve: true, }), makeExecutableSchema({ typeDefs: ['type RootQuery { f2: String }', 'schema { query: RootQuery }'], - allowUndefinedInResolve: true, }), ]); @@ -704,7 +700,6 @@ describe('Merge TypeDefs', () => { const merged = mergeTypeDefs([ makeExecutableSchema({ typeDefs: ['type Query { f1: String }'], - allowUndefinedInResolve: true, }), 'type Query { f2: String }', gql` @@ -732,7 +727,6 @@ describe('Merge TypeDefs', () => { const merged = mergeTypeDefs([ makeExecutableSchema({ typeDefs: ['type MyType { f1: String }'], - allowUndefinedInResolve: true, }), ]); @@ -747,11 +741,9 @@ describe('Merge TypeDefs', () => { const merged = mergeTypeDefs([ makeExecutableSchema({ typeDefs: ['type MyType { f1: String }'], - allowUndefinedInResolve: true, }), makeExecutableSchema({ typeDefs: ['type MyType { f2: String }'], - allowUndefinedInResolve: true, }), ]); diff --git a/packages/mock/src/MockStore.ts b/packages/mock/src/MockStore.ts index b21eac7e9b5..69e4ea10cf7 100644 --- a/packages/mock/src/MockStore.ts +++ b/packages/mock/src/MockStore.ts @@ -237,7 +237,7 @@ export class MockStore implements IMockStore { if (!isRecord(value)) { throw new Error('When no `fieldName` is provided, `value` should be a record.'); } - for (const fieldName of Object.keys(value)) { + for (const fieldName in value) { this.setImpl({ typeName, key, @@ -368,7 +368,7 @@ export class MockStore implements IMockStore { } const toInsert = { ...otherValues, ...values }; - for (const fieldName of Object.keys(toInsert)) { + for (const fieldName in toInsert) { if (fieldName === '$ref') continue; if (fieldName === '__typename') continue; this.set({ @@ -410,7 +410,7 @@ export class MockStore implements IMockStore { throw new Error(`Value returned by the mock for ${typeName} is not an object`); } - for (const otherFieldName of Object.keys(values)) { + for (const otherFieldName in values) { if (otherFieldName === fieldName) continue; if (typeof (values as any)[otherFieldName] === 'function') continue; onOtherFieldsGenerated && onOtherFieldsGenerated(otherFieldName, (values as any)[otherFieldName]); @@ -498,7 +498,7 @@ export class MockStore implements IMockStore { } const toInsert = {}; - for (const fieldName of Object.keys(values)) { + for (const fieldName in values) { if (fieldName === '__typename') continue; const fieldValue = (values as any)[fieldName]; toInsert[fieldName] = typeof fieldValue === 'function' ? fieldValue() : fieldValue; diff --git a/packages/mock/src/utils.ts b/packages/mock/src/utils.ts index e36d784446b..76edccb11ba 100644 --- a/packages/mock/src/utils.ts +++ b/packages/mock/src/utils.ts @@ -26,21 +26,21 @@ export function isObject(thing: any) { } export function copyOwnPropsIfNotPresent(target: Record, source: Record) { - Object.getOwnPropertyNames(source).forEach(prop => { + for (const prop of Object.getOwnPropertyNames(source)) { if (!Object.getOwnPropertyDescriptor(target, prop)) { const propertyDescriptor = Object.getOwnPropertyDescriptor(source, prop); Object.defineProperty(target, prop, propertyDescriptor == null ? {} : propertyDescriptor); } - }); + } } export function copyOwnProps(target: Record, ...sources: Array>) { - sources.forEach(source => { + for (const source of sources) { let chain = source; while (chain != null) { copyOwnPropsIfNotPresent(target, chain); chain = Object.getPrototypeOf(chain); } - }); + } return target; } diff --git a/packages/mock/tests/addMocksToSchema.spec.ts b/packages/mock/tests/addMocksToSchema.spec.ts index 0cba83b226d..24f46e010b7 100644 --- a/packages/mock/tests/addMocksToSchema.spec.ts +++ b/packages/mock/tests/addMocksToSchema.spec.ts @@ -267,8 +267,8 @@ describe('addMocksToSchema', () => { source: query }); - expect(data.foo.field1).toBe('text'); - expect(data.foo.field2).toBe(null); + expect(data?.['foo'].field1).toBe('text'); + expect(data?.['foo'].field2).toBe(null); }) it('should handle null fields correctly in nested fields', async () => { const schema = buildSchema(/* GraphQL */` @@ -309,8 +309,8 @@ describe('addMocksToSchema', () => { }); expect(errors).toBeFalsy(); - expect(data.foo.foo_field).toBe('text'); - expect(data.foo.boo).toBe(null); + expect(data?.['foo'].foo_field).toBe('text'); + expect(data?.['foo'].boo).toBe(null); }); it('handle objects without object prototype correctly', () => { const maybeRef = Object.create(null); diff --git a/packages/mock/tests/mocking-compatibility.spec.ts b/packages/mock/tests/mocking-compatibility.spec.ts index 4c2ffc15ee3..fd94b338287 100644 --- a/packages/mock/tests/mocking-compatibility.spec.ts +++ b/packages/mock/tests/mocking-compatibility.spec.ts @@ -120,13 +120,13 @@ describe('Mock retro-compatibility', () => { returnID }`; return graphql(jsSchema, testQuery).then((res) => { - expect(res.data.returnInt).toBeGreaterThanOrEqual(-1000); - expect(res.data.returnInt).toBeLessThanOrEqual(1000); - expect(res.data.returnFloat).toBeGreaterThanOrEqual(-1000); - expect(res.data.returnFloat).toBeLessThanOrEqual(1000); - expect(typeof res.data.returnBoolean).toBe('boolean'); - expect(typeof res.data.returnString).toBe('string'); - expect(typeof res.data.returnID).toBe('string'); + expect(res.data?.['returnInt']).toBeGreaterThanOrEqual(-1000); + expect(res.data?.['returnInt']).toBeLessThanOrEqual(1000); + expect(res.data?.['returnFloat']).toBeGreaterThanOrEqual(-1000); + expect(res.data?.['returnFloat']).toBeLessThanOrEqual(1000); + expect(typeof res.data?.['returnBoolean']).toBe('boolean'); + expect(typeof res.data?.['returnString']).toBe('string'); + expect(typeof res.data?.['returnID']).toBe('string'); }); }); @@ -290,7 +290,7 @@ describe('Mock retro-compatibility', () => { returnEnum }`; return graphql(jsSchema, testQuery).then((res) => { - expect(['A', 'B', 'C']).toContain(res.data.returnEnum); + expect(['A', 'B', 'C']).toContain(res.data?.['returnEnum']); }); }); @@ -304,7 +304,7 @@ describe('Mock retro-compatibility', () => { returnEnum }`; return graphql(jsSchema, testQuery).then((res) => { - expect('C').toBe(res.data.returnEnum); + expect('C').toBe(res.data?.['returnEnum']); }); }); @@ -333,13 +333,13 @@ describe('Mock retro-compatibility', () => { }`; return graphql(jsSchema, testQuery).then((res) => { // XXX this test is expected to fail once every 2^40 times ;-) - expect(res.data.returnBirdsAndBees).toContainEqual( + expect(res.data?.['returnBirdsAndBees']).toContainEqual( expect.objectContaining({ returnInt: 10, returnString: 'aha', }), ); - return expect(res.data.returnBirdsAndBees).toContainEqual( + return expect(res.data?.['returnBirdsAndBees']).toContainEqual( expect.objectContaining({ returnInt: 10, returnEnum: 'A', @@ -375,13 +375,13 @@ describe('Mock retro-compatibility', () => { } }`; return graphql(jsSchema, testQuery).then((res) => { - expect(res.data.returnFlying).toContainEqual( + expect(res.data?.['returnFlying']).toContainEqual( expect.objectContaining({ returnInt: 10, returnString: 'aha', }), ); - return expect(res.data.returnFlying).toContainEqual( + return expect(res.data?.['returnFlying']).toContainEqual( expect.objectContaining({ returnInt: 10, returnEnum: 'A', @@ -420,7 +420,7 @@ describe('Mock retro-compatibility', () => { }`; return graphql(jsSchema, testQuery).then((res) => { - expect(res.data.node).toEqual(null); + expect(res.data?.['node']).toEqual(null); }); }); @@ -448,7 +448,7 @@ describe('Mock retro-compatibility', () => { const res = await server.query(testQuery); - expect(res.data.node).toEqual({ + expect(res.data?.['node']).toEqual({ id: 'bee:hardcoded', returnAbility: { name: 'Hello World', @@ -479,7 +479,7 @@ describe('Mock retro-compatibility', () => { const __typename = ['Bird', 'Bee'].find( (r) => r.toLowerCase() === type, ); - return store.get(__typename, id); + return __typename && store.get(__typename, id); } } }); @@ -498,7 +498,7 @@ describe('Mock retro-compatibility', () => { return graphql(jsSchema, testQuery).then((res) => { expect(spy).toBe(1); // to make sure that Flying possible types are not randomly selected - expect(res.data.node).toMatchObject({ + expect(res.data?.['node']).toMatchObject({ id: 'bee:123456', returnSong: 'I believe i can fly', returnInt: 200, @@ -540,7 +540,7 @@ describe('Mock retro-compatibility', () => { return graphql(jsSchema, testQuery).then((res) => { expect(spy).toBe(1); - expect(res.data.node2).toMatchObject({ + expect(res.data?.['node2']).toMatchObject({ id: 'bee:hardcoded', returnEnum: 'A', }); @@ -569,7 +569,7 @@ describe('Mock retro-compatibility', () => { }`; const expected = 'Please return a __typename in "Flying"'; return graphql(jsSchema, testQuery).then((res) => { - expect(res.errors[0].originalError.message).toBe(expected); + expect(res.errors?.[0].originalError?.message).toBe(expected); }); }); @@ -582,7 +582,7 @@ describe('Mock retro-compatibility', () => { }`; const expected = 'No mock defined for type "MissingMockType"'; return graphql(jsSchema, testQuery).then((res) => { - expect(res.errors[0].originalError.message).toBe(expected); + expect(res.errors?.[0].originalError?.message).toBe(expected); }); }); @@ -595,7 +595,7 @@ describe('Mock retro-compatibility', () => { __parseLiteral: (val: string) => val, }, RootQuery: { - returnMockError: (): string => undefined, + returnMockError: () => undefined, }, }; jsSchema = addResolversToSchema(jsSchema, resolvers); @@ -611,7 +611,7 @@ describe('Mock retro-compatibility', () => { }`; const expected = 'No mock defined for type "MissingMockType"'; return graphql(jsSchema, testQuery).then((res) => { - expect(res.errors[0].originalError.message).toBe(expected); + expect(res.errors?.[0].originalError?.message).toBe(expected); }); }); @@ -655,7 +655,7 @@ describe('Mock retro-compatibility', () => { returnInt }`; return graphql(jsSchema, testQuery).then((res) => { - expect(res.data.returnInt).toBe(55); + expect(res.data?.['returnInt']).toBe(55); }); }); @@ -667,7 +667,7 @@ describe('Mock retro-compatibility', () => { returnFloat }`; return graphql(jsSchema, testQuery).then((res) => { - expect(res.data.returnFloat).toBe(55.5); + expect(res.data?.['returnFloat']).toBe(55.5); }); }); test('can mock a String', () => { @@ -678,7 +678,7 @@ describe('Mock retro-compatibility', () => { returnString }`; return graphql(jsSchema, testQuery).then((res) => { - expect(res.data.returnString).toBe('a string'); + expect(res.data?.['returnString']).toBe('a string'); }); }); test('can mock a Boolean', () => { @@ -689,7 +689,7 @@ describe('Mock retro-compatibility', () => { returnBoolean }`; return graphql(jsSchema, testQuery).then((res) => { - expect(res.data.returnBoolean).toBe(true); + expect(res.data?.['returnBoolean']).toBe(true); }); }); test('can mock an ID', () => { @@ -700,7 +700,7 @@ describe('Mock retro-compatibility', () => { returnID }`; return graphql(jsSchema, testQuery).then((res) => { - expect(res.data.returnID).toBe('ea5bdc19'); + expect(res.data?.['returnID']).toBe('ea5bdc19'); }); }); test('nullable type is nullable', () => { @@ -711,7 +711,7 @@ describe('Mock retro-compatibility', () => { returnNullableString }`; return graphql(jsSchema, testQuery).then((res) => { - expect(res.data.returnNullableString).toBe(null); + expect(res.data?.['returnNullableString']).toBe(null); }); }); test('can mock a nonNull type', () => { @@ -722,7 +722,7 @@ describe('Mock retro-compatibility', () => { returnNonNullString }`; return graphql(jsSchema, testQuery).then((res) => { - expect(res.data.returnNonNullString).toBe('nonnull'); + expect(res.data?.['returnNonNullString']).toBe('nonnull'); }); }); test('nonNull type is not nullable', () => { @@ -734,7 +734,7 @@ describe('Mock retro-compatibility', () => { }`; return graphql(jsSchema, testQuery).then((res) => { expect(res.data).toBe(null); - expect(res.errors.length).toBe(1); + expect(res.errors?.length).toBe(1); }); }); test('can mock object types', () => { @@ -1029,7 +1029,7 @@ describe('Mock retro-compatibility', () => { let jsSchema = buildSchema(shorthand); const resolvers = { RootQuery: { - returnString: (): string => null, // a) resolve of a string + returnString: () => null, // a) resolve of a string }, }; jsSchema = addResolversToSchema(jsSchema, resolvers); @@ -1053,7 +1053,7 @@ describe('Mock retro-compatibility', () => { returnInt: 666, // from the mock, see b) returnString: 'Hello World', // from mock default values. }, - returnString: null as string, // from the mock, see a) + returnString: null as unknown as string, // from the mock, see a) }; return graphql(jsSchema, testQuery, undefined, {}).then((res) => { expect(res.data).toEqual(expected); @@ -1064,7 +1064,7 @@ describe('Mock retro-compatibility', () => { let jsSchema = buildSchema(shorthand); const resolvers = { RootQuery: { - returnStringArgument: (_: void, a: Record) => a.s, + returnStringArgument: (_: void, a: Record) => a['s'], }, }; jsSchema = addMocksToSchema({ schema: jsSchema, resolvers }); @@ -1083,7 +1083,7 @@ describe('Mock retro-compatibility', () => { let jsSchema = buildSchema(shorthand); const resolvers = { RootMutation: { - returnStringArgument: (_: void, a: Record) => a.s, + returnStringArgument: (_: void, a: Record) => a['s'], }, }; jsSchema = addMocksToSchema({ schema: jsSchema, resolvers }); @@ -1127,9 +1127,9 @@ describe('Mock retro-compatibility', () => { returnListOfInt }`; return graphql(jsSchema, testQuery).then((res) => { - expect(res.data.returnListOfInt.length).toBeGreaterThanOrEqual(10); - expect(res.data.returnListOfInt.length).toBeLessThanOrEqual(20); - expect(res.data.returnListOfInt[0]).toBe(12); + expect(res.data?.['returnListOfInt'].length).toBeGreaterThanOrEqual(10); + expect(res.data?.['returnListOfInt'].length).toBeLessThanOrEqual(20); + expect(res.data?.['returnListOfInt'][0]).toBe(12); }); }); @@ -1366,9 +1366,9 @@ describe('Mock retro-compatibility', () => { `, }); - expect(result.data?.reviews?.length <= 4).toBeTruthy(); - expect(typeof result.data?.reviews[0]?.sentence).toBe('string'); - expect(typeof result.data?.reviews[0]?.user?.first_name).toBe('string'); + expect(result.data?.['reviews']?.length <= 4).toBeTruthy(); + expect(typeof result.data?.['reviews'][0]?.sentence).toBe('string'); + expect(typeof result.data?.['reviews'][0]?.user?.first_name).toBe('string'); }); it('resolves subscriptions only once', async () => { diff --git a/packages/mock/tests/store.spec.ts b/packages/mock/tests/store.spec.ts index 2d27062ffc1..869bd123359 100644 --- a/packages/mock/tests/store.spec.ts +++ b/packages/mock/tests/store.spec.ts @@ -250,7 +250,7 @@ describe('MockStore', () => { it('should be able to generate a list of Object type', () => { const store = createMockStore({ schema }); - const friends = store.get('User', '123', 'friends'); + const friends = store.get('User', '123', 'friends') as any[]; expect(friends).toBeInstanceOf(Array); expect(friends[0]).toHaveProperty('$ref'); diff --git a/packages/node-require/src/index.ts b/packages/node-require/src/index.ts index 37006940120..d238c3b50d5 100644 --- a/packages/node-require/src/index.ts +++ b/packages/node-require/src/index.ts @@ -23,9 +23,9 @@ function handleModule(m: NodeModule, filename: string) { } export function registerGraphQLExtensions(require: NodeRequire) { - VALID_EXTENSIONS.forEach(ext => { + for (const ext of VALID_EXTENSIONS) { require.extensions[`.${ext}`] = handleModule; - }); + } } registerGraphQLExtensions(require); diff --git a/packages/relay-operation-optimizer/tests/relay-optimizer.spec.ts b/packages/relay-operation-optimizer/tests/relay-optimizer.spec.ts index 62a9c03a058..44d04ae4e84 100644 --- a/packages/relay-operation-optimizer/tests/relay-optimizer.spec.ts +++ b/packages/relay-operation-optimizer/tests/relay-optimizer.spec.ts @@ -68,7 +68,7 @@ it('can inline @argumentDefinitions/@arguments annotated fragments', async () => const queryDoc = output.find(doc => doc.definitions[0].kind === 'OperationDefinition'); expect(queryDoc).toBeDefined(); - expect(print(queryDoc)).toBeSimilarGqlDoc(/* GraphQL */ ` + expect(print(queryDoc!)).toBeSimilarGqlDoc(/* GraphQL */ ` query user { users { id @@ -127,7 +127,7 @@ it('handles unions with interfaces the correct way', async () => { const queryDoc = output.find(doc => doc.definitions[0].kind === 'OperationDefinition'); expect(queryDoc).toBeDefined(); - expect(print(queryDoc)).toBeSimilarGqlDoc(/* GraphQL */ ` + expect(print(queryDoc!)).toBeSimilarGqlDoc(/* GraphQL */ ` query user { user { ... on User { diff --git a/packages/resolvers-composition/src/resolvers-composition.ts b/packages/resolvers-composition/src/resolvers-composition.ts index 6b8cb197885..f07bad3174d 100644 --- a/packages/resolvers-composition/src/resolvers-composition.ts +++ b/packages/resolvers-composition/src/resolvers-composition.ts @@ -45,9 +45,14 @@ function resolveRelevantMappings = Record< if (!resolvers) { return []; } - return Object.keys(resolvers).map(typeName => - resolveRelevantMappings(resolvers, `${typeName}.${fieldName}`, allMappings) - ).flat(); + const mappings: string[] = []; + for (const typeName in resolvers) { + const relevantMappings = resolveRelevantMappings(resolvers, `${typeName}.${fieldName}`, allMappings); + for (const relevantMapping of relevantMappings) { + mappings.push(relevantMapping); + } + } + return mappings; } if (fieldName === '*') { @@ -55,7 +60,16 @@ function resolveRelevantMappings = Record< if (!fieldMap) { return []; } - return Object.keys(fieldMap).map(field => resolveRelevantMappings(resolvers, `${typeName}.${field}`, allMappings)).flat().filter(mapItem => !allMappings[mapItem]); + const mappings: string[] = []; + for (const field in fieldMap) { + const relevantMappings = resolveRelevantMappings(resolvers, `${typeName}.${field}`, allMappings); + for (const relevantMapping of relevantMappings) { + if (!allMappings[relevantMapping]) { + mappings.push(relevantMapping); + } + } + } + return mappings; } else { const paths = []; @@ -83,9 +97,15 @@ function resolveRelevantMappings = Record< return []; } - return Object.keys(fieldMap).map(fieldName => - resolveRelevantMappings(resolvers, `${typeName}.${fieldName}`, allMappings) - ).flat(); + const mappings: string[] = []; + + for (const fieldName in fieldMap) { + const relevantMappings = resolveRelevantMappings(resolvers, `${typeName}.${fieldName}`, allMappings); + for (const relevantMapping of relevantMappings) { + mappings.push(relevantMapping); + } + } + return mappings; } return []; @@ -105,26 +125,26 @@ export function composeResolvers>( ): Resolvers { const mappingResult: { [path: string]: ((...args: any[]) => any)[] } = {}; - Object.keys(mapping).forEach((resolverPath: string) => { + for (const resolverPath in mapping) { const resolverPathMapping = mapping[resolverPath]; if (resolverPathMapping instanceof Array || typeof resolverPathMapping === 'function') { const composeFns = resolverPathMapping as ResolversComposition | ResolversComposition[]; const relevantFields = resolveRelevantMappings(resolvers, resolverPath, mapping); - relevantFields.forEach((path: string) => { + for (const path of relevantFields) { mappingResult[path] = asArray(composeFns); - }); + } } else if (resolverPathMapping) { - Object.keys(resolverPathMapping).forEach(fieldName => { + for (const fieldName in resolverPathMapping) { const composeFns = resolverPathMapping[fieldName]; const relevantFields = resolveRelevantMappings(resolvers, resolverPath + '.' + fieldName, mapping); for (const path of relevantFields) { mappingResult[path] = asArray(composeFns); } - }); + } } - }); + } for (const path in mappingResult) { const fns = chainFunctions([...asArray(mappingResult[path]), () => _.get(resolvers, path)]); diff --git a/packages/resolvers-composition/tests/resolvers-composition.spec.ts b/packages/resolvers-composition/tests/resolvers-composition.spec.ts index 123f7582100..8449371c3ab 100644 --- a/packages/resolvers-composition/tests/resolvers-composition.spec.ts +++ b/packages/resolvers-composition/tests/resolvers-composition.spec.ts @@ -2,7 +2,6 @@ import gql from 'graphql-tag'; import { composeResolvers, ResolversComposerMapping } from '../src'; import { makeExecutableSchema } from '@graphql-tools/schema'; import { execute, GraphQLScalarType, Kind } from 'graphql'; -import { IResolvers } from 'packages/graphql-tools/src'; function createAsyncIterator(array: T[]): AsyncIterator { let i = 0; @@ -53,7 +52,7 @@ describe('Resolvers composition', () => { `, }); expect(result.errors).toBeFalsy(); - expect(result.data!.foo).toBe('FOOFOO'); + expect(result.data!['foo']).toBe('FOOFOO'); }); it('should compose resolvers with resolve field', async () => { const getFoo = () => 'FOO'; @@ -91,7 +90,7 @@ describe('Resolvers composition', () => { `, }); expect(result.errors).toBeFalsy(); - expect(result.data!.foo).toBe('FOOFOO'); + expect(result.data!['foo']).toBe('FOOFOO'); }); it('should compose subscription resolvers', async () => { const array1 = [1, 2]; @@ -170,7 +169,7 @@ describe('Resolvers composition', () => { `, }); expect(result.errors).toBeFalsy(); - expect(result.data!.foo).toBe('FOOFOO'); + expect(result.data!['foo']).toBe('FOOFOO'); }); it('should be able to take nested composition objects for subscription resolvers', async () => { const array1 = [1, 2]; @@ -293,14 +292,14 @@ describe('Resolvers composition', () => { it('should handle nullish properties correctly', async () => { const getFoo = () => 'FOO'; - const resolvers: IResolvers = { + const resolvers = { Query: { foo: async () => getFoo(), bar: undefined, }, Mutation: undefined }; - const resolversComposition: ResolversComposerMapping = { + const resolversComposition: any = { 'Query.foo': (next: (arg0: any, arg1: any, arg2: any, arg3: any) => void) => async (root: any, args: any, context: any, info: any) => { const prevResult = await next(root, args, context, info); return getFoo() + prevResult; diff --git a/packages/schema/src/addResolversToSchema.ts b/packages/schema/src/addResolversToSchema.ts index 5364bacf13b..756f4b1f34b 100644 --- a/packages/schema/src/addResolversToSchema.ts +++ b/packages/schema/src/addResolversToSchema.ts @@ -61,92 +61,82 @@ export function addResolversToSchema( ? extendResolversFromInterfaces(schema, inputResolvers) : inputResolvers; - Object.getOwnPropertyNames(resolvers).forEach(typeName => { + for (const typeName in resolvers) { const resolverValue = resolvers[typeName]; const resolverType = typeof resolverValue; - if (typeName === '__schema') { - if (resolverType !== 'function') { - throw new Error( - `"${typeName}" defined in resolvers, but has invalid value "${ - resolverValue as unknown as string - }". A schema resolver's value must be of type object or function.` - ); - } - } else { - if (resolverType !== 'object') { - throw new Error( - `"${typeName}" defined in resolvers, but has invalid value "${ - resolverValue as unknown as string - }". The resolver's value must be of type object.` - ); - } + if (resolverType !== 'object') { + throw new Error( + `"${typeName}" defined in resolvers, but has invalid value "${ + resolverValue as unknown as string + }". The resolver's value must be of type object.` + ); + } - const type = schema.getType(typeName); + const type = schema.getType(typeName); - if (type == null) { - if (requireResolversToMatchSchema === 'ignore') { - return; + if (type == null) { + if (requireResolversToMatchSchema === 'ignore') { + break; + } + + throw new Error(`"${typeName}" defined in resolvers, but not in schema`); + } else if (isSpecifiedScalarType(type)) { + // allow -- without recommending -- overriding of specified scalar types + for (const fieldName in resolverValue) { + if (fieldName.startsWith('__')) { + type[fieldName.substring(2)] = resolverValue[fieldName]; + } else { + type[fieldName] = resolverValue[fieldName]; + } + } + } else if (isEnumType(type)) { + const values = type.getValues(); + + for (const fieldName in resolverValue) { + if ( + !fieldName.startsWith('__') && + !values.some(value => value.name === fieldName) && + requireResolversToMatchSchema && + requireResolversToMatchSchema !== 'ignore' + ) { + throw new Error(`${type.name}.${fieldName} was defined in resolvers, but not present within ${type.name}`); } + } + } else if (isUnionType(type)) { + for (const fieldName in resolverValue) { + if ( + !fieldName.startsWith('__') && + requireResolversToMatchSchema && + requireResolversToMatchSchema !== 'ignore' + ) { + throw new Error( + `${type.name}.${fieldName} was defined in resolvers, but ${type.name} is not an object or interface type` + ); + } + } + } else if (isObjectType(type) || isInterfaceType(type)) { + for (const fieldName in resolverValue) { + if (!fieldName.startsWith('__')) { + const fields = type.getFields(); + const field = fields[fieldName]; - throw new Error(`"${typeName}" defined in resolvers, but not in schema`); - } else if (isSpecifiedScalarType(type)) { - // allow -- without recommending -- overriding of specified scalar types - Object.getOwnPropertyNames(resolverValue).forEach(fieldName => { - if (fieldName.startsWith('__')) { - type[fieldName.substring(2)] = resolverValue[fieldName]; + if (field == null) { + // Field present in resolver but not in schema + if (requireResolversToMatchSchema && requireResolversToMatchSchema !== 'ignore') { + throw new Error(`${typeName}.${fieldName} defined in resolvers, but not in schema`); + } } else { - type[fieldName] = resolverValue[fieldName]; - } - }); - } else if (isEnumType(type)) { - const values = type.getValues(); - - Object.getOwnPropertyNames(resolverValue).forEach(fieldName => { - if ( - !fieldName.startsWith('__') && - !values.some(value => value.name === fieldName) && - requireResolversToMatchSchema && - requireResolversToMatchSchema !== 'ignore' - ) { - throw new Error(`${type.name}.${fieldName} was defined in resolvers, but not present within ${type.name}`); - } - }); - } else if (isUnionType(type)) { - Object.getOwnPropertyNames(resolverValue).forEach(fieldName => { - if ( - !fieldName.startsWith('__') && - requireResolversToMatchSchema && - requireResolversToMatchSchema !== 'ignore' - ) { - throw new Error( - `${type.name}.${fieldName} was defined in resolvers, but ${type.name} is not an object or interface type` - ); - } - }); - } else if (isObjectType(type) || isInterfaceType(type)) { - Object.getOwnPropertyNames(resolverValue).forEach(fieldName => { - if (!fieldName.startsWith('__')) { - const fields = type.getFields(); - const field = fields[fieldName]; - - if (field == null) { - // Field present in resolver but not in schema - if (requireResolversToMatchSchema && requireResolversToMatchSchema !== 'ignore') { - throw new Error(`${typeName}.${fieldName} defined in resolvers, but not in schema`); - } - } else { - // Field present in both the resolver and schema - const fieldResolve = resolverValue[fieldName]; - if (typeof fieldResolve !== 'function' && typeof fieldResolve !== 'object') { - throw new Error(`Resolver ${typeName}.${fieldName} must be object or function`); - } + // Field present in both the resolver and schema + const fieldResolve = resolverValue[fieldName]; + if (typeof fieldResolve !== 'function' && typeof fieldResolve !== 'object') { + throw new Error(`Resolver ${typeName}.${fieldName} must be object or function`); } } - }); + } } } - }); + } schema = updateResolversInPlace ? addResolversToExistingSchema(schema, resolvers, defaultFieldResolver) @@ -165,98 +155,96 @@ function addResolversToExistingSchema( defaultFieldResolver?: GraphQLFieldResolver ): GraphQLSchema { const typeMap = schema.getTypeMap(); - getAllPropertyNames(resolvers).forEach(typeName => { - if (typeName !== '__schema') { - const type = schema.getType(typeName); - const resolverValue = resolvers[typeName]; + for (const typeName in resolvers) { + const type = schema.getType(typeName); + const resolverValue = resolvers[typeName]; - if (isScalarType(type)) { - getAllPropertyNames(resolverValue).forEach(fieldName => { - if (fieldName.startsWith('__')) { - type[fieldName.substring(2)] = resolverValue[fieldName]; - } else if (fieldName === 'astNode' && type.astNode != null) { - type.astNode = { - ...type.astNode, - description: (resolverValue as GraphQLScalarType)?.astNode?.description ?? type.astNode.description, - directives: (type.astNode.directives ?? []).concat( - (resolverValue as GraphQLScalarType)?.astNode?.directives ?? [] - ), - }; - } else if (fieldName === 'extensionASTNodes' && type.extensionASTNodes != null) { - type.extensionASTNodes = type.extensionASTNodes.concat( - (resolverValue as GraphQLScalarType)?.extensionASTNodes ?? [] - ); - } else if ( - fieldName === 'extensions' && - type.extensions != null && - (resolverValue as GraphQLScalarType).extensions != null - ) { - type.extensions = Object.assign({}, type.extensions, (resolverValue as GraphQLScalarType).extensions); - } else { - type[fieldName] = resolverValue[fieldName]; - } - }); - } else if (isEnumType(type)) { - const config = type.toConfig(); - const enumValueConfigMap = config.values; + if (isScalarType(type)) { + for (const fieldName in resolverValue) { + if (fieldName.startsWith('__')) { + type[fieldName.substring(2)] = resolverValue[fieldName]; + } else if (fieldName === 'astNode' && type.astNode != null) { + type.astNode = { + ...type.astNode, + description: (resolverValue as GraphQLScalarType)?.astNode?.description ?? type.astNode.description, + directives: (type.astNode.directives ?? []).concat( + (resolverValue as GraphQLScalarType)?.astNode?.directives ?? [] + ), + }; + } else if (fieldName === 'extensionASTNodes' && type.extensionASTNodes != null) { + type.extensionASTNodes = type.extensionASTNodes.concat( + (resolverValue as GraphQLScalarType)?.extensionASTNodes ?? [] + ); + } else if ( + fieldName === 'extensions' && + type.extensions != null && + (resolverValue as GraphQLScalarType).extensions != null + ) { + type.extensions = Object.assign({}, type.extensions, (resolverValue as GraphQLScalarType).extensions); + } else { + type[fieldName] = resolverValue[fieldName]; + } + } + } else if (isEnumType(type)) { + const config = type.toConfig(); + const enumValueConfigMap = config.values; - getAllPropertyNames(resolverValue).forEach(fieldName => { - if (fieldName.startsWith('__')) { - config[fieldName.substring(2)] = resolverValue[fieldName]; - } else if (fieldName === 'astNode' && config.astNode != null) { - config.astNode = { - ...config.astNode, - description: (resolverValue as GraphQLScalarType)?.astNode?.description ?? config.astNode.description, - directives: (config.astNode.directives ?? []).concat( - (resolverValue as GraphQLEnumType)?.astNode?.directives ?? [] - ), - }; - } else if (fieldName === 'extensionASTNodes' && config.extensionASTNodes != null) { - config.extensionASTNodes = config.extensionASTNodes.concat( - (resolverValue as GraphQLEnumType)?.extensionASTNodes ?? [] - ); - } else if ( - fieldName === 'extensions' && - type.extensions != null && - (resolverValue as GraphQLEnumType).extensions != null - ) { - type.extensions = Object.assign({}, type.extensions, (resolverValue as GraphQLEnumType).extensions); - } else if (enumValueConfigMap[fieldName]) { - enumValueConfigMap[fieldName].value = resolverValue[fieldName]; - } - }); + for (const fieldName in resolverValue) { + if (fieldName.startsWith('__')) { + config[fieldName.substring(2)] = resolverValue[fieldName]; + } else if (fieldName === 'astNode' && config.astNode != null) { + config.astNode = { + ...config.astNode, + description: (resolverValue as GraphQLScalarType)?.astNode?.description ?? config.astNode.description, + directives: (config.astNode.directives ?? []).concat( + (resolverValue as GraphQLEnumType)?.astNode?.directives ?? [] + ), + }; + } else if (fieldName === 'extensionASTNodes' && config.extensionASTNodes != null) { + config.extensionASTNodes = config.extensionASTNodes.concat( + (resolverValue as GraphQLEnumType)?.extensionASTNodes ?? [] + ); + } else if ( + fieldName === 'extensions' && + type.extensions != null && + (resolverValue as GraphQLEnumType).extensions != null + ) { + type.extensions = Object.assign({}, type.extensions, (resolverValue as GraphQLEnumType).extensions); + } else if (enumValueConfigMap[fieldName]) { + enumValueConfigMap[fieldName].value = resolverValue[fieldName]; + } + } - typeMap[typeName] = new GraphQLEnumType(config); - } else if (isUnionType(type)) { - getAllPropertyNames(resolverValue).forEach(fieldName => { - if (fieldName.startsWith('__')) { - type[fieldName.substring(2)] = resolverValue[fieldName]; - } - }); - } else if (isObjectType(type) || isInterfaceType(type)) { - getAllPropertyNames(resolverValue).forEach(fieldName => { - if (fieldName.startsWith('__')) { - // this is for isTypeOf and resolveType and all the other stuff. - type[fieldName.substring(2)] = resolverValue[fieldName]; - return; - } + typeMap[typeName] = new GraphQLEnumType(config); + } else if (isUnionType(type)) { + for (const fieldName in resolverValue) { + if (fieldName.startsWith('__')) { + type[fieldName.substring(2)] = resolverValue[fieldName]; + } + } + } else if (isObjectType(type) || isInterfaceType(type)) { + for (const fieldName in resolverValue) { + if (fieldName.startsWith('__')) { + // this is for isTypeOf and resolveType and all the other stuff. + type[fieldName.substring(2)] = resolverValue[fieldName]; + break; + } - const fields = type.getFields(); - const field = fields[fieldName]; + const fields = type.getFields(); + const field = fields[fieldName]; - if (field != null) { - const fieldResolve = resolverValue[fieldName]; - if (typeof fieldResolve === 'function') { - // for convenience. Allows shorter syntax in resolver definition file - field.resolve = fieldResolve.bind(resolverValue); - } else { - setFieldProperties(field, fieldResolve); - } + if (field != null) { + const fieldResolve = resolverValue[fieldName]; + if (typeof fieldResolve === 'function') { + // for convenience. Allows shorter syntax in resolver definition file + field.resolve = fieldResolve.bind(resolverValue); + } else { + setFieldProperties(field, fieldResolve); } - }); + } } } - }); + } // serialize all default values prior to healing fields with new scalar/enum types. forEachDefaultValue(schema, serializeInputValue); @@ -286,7 +274,7 @@ function createNewSchemaWithResolvers( const config = type.toConfig(); const resolverValue = resolvers[type.name]; if (!isSpecifiedScalarType(type) && resolverValue != null) { - getAllPropertyNames(resolverValue).forEach(fieldName => { + for (const fieldName in resolverValue) { if (fieldName.startsWith('__')) { config[fieldName.substring(2)] = resolverValue[fieldName]; } else if (fieldName === 'astNode' && config.astNode != null) { @@ -310,7 +298,7 @@ function createNewSchemaWithResolvers( } else { config[fieldName] = resolverValue[fieldName]; } - }); + } return new GraphQLScalarType(config); } @@ -322,7 +310,7 @@ function createNewSchemaWithResolvers( const enumValueConfigMap = config.values; if (resolverValue != null) { - getAllPropertyNames(resolverValue).forEach(fieldName => { + for (const fieldName in resolverValue) { if (fieldName.startsWith('__')) { config[fieldName.substring(2)] = resolverValue[fieldName]; } else if (fieldName === 'astNode' && config.astNode != null) { @@ -346,7 +334,7 @@ function createNewSchemaWithResolvers( } else if (enumValueConfigMap[fieldName]) { enumValueConfigMap[fieldName].value = resolverValue[fieldName]; } - }); + } return new GraphQLEnumType(config); } @@ -356,11 +344,10 @@ function createNewSchemaWithResolvers( if (resolverValue != null) { const config = type.toConfig(); - getAllPropertyNames(resolverValue).forEach(fieldName => { - if (fieldName.startsWith('__')) { - config[fieldName.substring(2)] = resolverValue[fieldName]; - } - }); + + if (resolverValue['__resolveType']) { + config.resolveType = resolverValue['__resolveType']; + } return new GraphQLUnionType(config); } @@ -370,11 +357,9 @@ function createNewSchemaWithResolvers( if (resolverValue != null) { const config = type.toConfig(); - getAllPropertyNames(resolverValue).forEach(fieldName => { - if (fieldName.startsWith('__')) { - config[fieldName.substring(2)] = resolverValue[fieldName]; - } - }); + if (resolverValue['__isTypeOf']) { + config.isTypeOf = resolverValue['__isTypeOf']; + } return new GraphQLObjectType(config); } @@ -384,11 +369,9 @@ function createNewSchemaWithResolvers( if (resolverValue != null) { const config = type.toConfig(); - getAllPropertyNames(resolverValue).forEach(fieldName => { - if (fieldName.startsWith('__')) { - config[fieldName.substring(2)] = resolverValue[fieldName]; - } - }); + if (resolverValue['__resolveType']) { + config.resolveType = resolverValue['__resolveType']; + } return new GraphQLInterfaceType(config); } @@ -428,14 +411,7 @@ function setFieldProperties( field: GraphQLField | GraphQLFieldConfig, propertiesObj: Record ) { - Object.keys(propertiesObj).forEach(propertyName => { + for (const propertyName in propertiesObj) { field[propertyName] = propertiesObj[propertyName]; - }); -} - -function getAllPropertyNames(obj: any): string[] { - const prototype = Object.getPrototypeOf(obj); - let inherited = prototype ? getAllPropertyNames(prototype) : []; - inherited = inherited.filter(property => property !== 'constructor'); - return [...new Set(Object.getOwnPropertyNames(obj).concat(inherited))]; + } } diff --git a/packages/schema/src/extendResolversFromInterfaces.ts b/packages/schema/src/extendResolversFromInterfaces.ts index b9233ef60ce..e5c9b1abea0 100644 --- a/packages/schema/src/extendResolversFromInterfaces.ts +++ b/packages/schema/src/extendResolversFromInterfaces.ts @@ -3,28 +3,21 @@ import { GraphQLSchema } from 'graphql'; import { IResolvers, IObjectTypeResolver } from '@graphql-tools/utils'; export function extendResolversFromInterfaces(schema: GraphQLSchema, resolvers: IResolvers): IResolvers { - const typeNames = Object.keys({ - ...schema.getTypeMap(), - ...resolvers, - }); - const extendedResolvers = {}; - typeNames.forEach(typeName => { - const type = schema.getType(typeName); - if (type && 'getInterfaces' in type) { - const allInterfaceResolvers = type - .getInterfaces() - .map(iFace => resolvers[iFace.name]) - .filter(interfaceResolvers => interfaceResolvers != null); - + const typeMap = schema.getTypeMap(); + for (const typeName in typeMap) { + const type = typeMap[typeName]; + if ('getInterfaces' in type) { extendedResolvers[typeName] = {}; - allInterfaceResolvers.forEach(interfaceResolvers => { - Object.keys(interfaceResolvers).forEach(fieldName => { - if (fieldName === '__isTypeOf' || !fieldName.startsWith('__')) { - extendedResolvers[typeName][fieldName] = interfaceResolvers[fieldName]; + for (const iFace of type.getInterfaces()) { + if (resolvers[iFace.name]) { + for (const fieldName in resolvers[iFace.name]) { + if (fieldName === '__isTypeOf' || !fieldName.startsWith('__')) { + extendedResolvers[typeName][fieldName] = resolvers[iFace.name][fieldName]; + } } - }); - }); + } + } const typeResolvers = resolvers[typeName] as Record; extendedResolvers[typeName] = { @@ -37,7 +30,7 @@ export function extendResolversFromInterfaces(schema: GraphQLSchema, resolvers: extendedResolvers[typeName] = typeResolvers; } } - }); + } return extendedResolvers; } diff --git a/packages/schema/src/makeExecutableSchema.ts b/packages/schema/src/makeExecutableSchema.ts index 671afd9dff7..8dc6c0de675 100644 --- a/packages/schema/src/makeExecutableSchema.ts +++ b/packages/schema/src/makeExecutableSchema.ts @@ -1,4 +1,4 @@ -import { buildASTSchema } from 'graphql'; +import { buildASTSchema, buildSchema, GraphQLSchema } from 'graphql'; import { pruneSchema } from '@graphql-tools/utils'; import { addResolversToSchema } from './addResolversToSchema'; @@ -101,8 +101,18 @@ export function makeExecutableSchema({ schemaTransforms.push(pruneSchema); } - const mergedTypeDefs = mergeTypeDefs(typeDefs, parseOptions); - const schemaFromTypeDefs = buildASTSchema(mergedTypeDefs, parseOptions); + let schemaFromTypeDefs: GraphQLSchema; + + if (parseOptions?.commentDescriptions) { + const mergedTypeDefs = mergeTypeDefs(typeDefs, { + ...parseOptions, + commentDescriptions: true, + }); + schemaFromTypeDefs = buildSchema(mergedTypeDefs, parseOptions); + } else { + const mergedTypeDefs = mergeTypeDefs(typeDefs, parseOptions); + schemaFromTypeDefs = buildASTSchema(mergedTypeDefs, parseOptions); + } return schemaTransforms.reduce((schema, schemaTransform) => schemaTransform(schema), schemaFromTypeDefs); } diff --git a/packages/schema/tests/schemaGenerator.test.ts b/packages/schema/tests/schemaGenerator.test.ts index ae5aed0582d..ce83fdae67d 100644 --- a/packages/schema/tests/schemaGenerator.test.ts +++ b/packages/schema/tests/schemaGenerator.test.ts @@ -20,6 +20,7 @@ import { DocumentNode, GraphQLBoolean, graphqlSync, + GraphQLFieldResolver, } from 'graphql'; import { @@ -372,9 +373,9 @@ describe('generating schema from shorthand', () => { }, }, }); - const extensions = jsSchema.getQueryType()?.getFields().foo.extensions; + const extensions = jsSchema.getQueryType()?.getFields()['foo'].extensions; expect(extensions).toHaveProperty('verbose'); - expect(extensions!.verbose).toBe(true); + expect(extensions!['verbose']).toBe(true); }); test('properly deduplicates the array of type DefinitionNodes', () => { @@ -835,7 +836,7 @@ describe('generating schema from shorthand', () => { } `; const result = graphqlSync(jsSchema, testQuery); - expect(result.data!.foo.aField).toBe(false); + expect(result.data!['foo'].aField).toBe(false); jsSchema = addResolversToSchema({ schema: jsSchema, resolvers: { @@ -989,7 +990,7 @@ describe('generating schema from shorthand', () => { `; const resultPromise = graphql(jsSchema, testQuery); return resultPromise.then((result) => { - expect(result.data!.post.something).toEqual(testValue); + expect(result.data!['post'].something).toEqual(testValue); expect(result.errors).toEqual(undefined); }); }); @@ -1059,7 +1060,7 @@ describe('generating schema from shorthand', () => { `; const resultPromise = graphql(jsSchema, testQuery); return resultPromise.then((result) => { - expect(result.data!.post.something).toEqual(testDate.getTime()); + expect(result.data!['post'].something).toEqual(testDate.getTime()); expect(result.errors).toEqual(undefined); }); }); @@ -1161,9 +1162,9 @@ describe('generating schema from shorthand', () => { const resultPromise = graphql(jsSchema, testQuery); return resultPromise.then((result) => { - expect(result.data!.redColor).toEqual('RED'); - expect(result.data!.blueColor).toEqual('BLUE'); - expect(result.data!.numericEnum).toEqual('TEST'); + expect(result.data!['redColor']).toEqual('RED'); + expect(result.data!['blueColor']).toEqual('BLUE'); + expect(result.data!['numericEnum']).toEqual('TEST'); expect(result.errors).toEqual(undefined); }); }); @@ -1220,9 +1221,9 @@ describe('generating schema from shorthand', () => { const resultPromise = graphql(jsSchema, testQuery); return resultPromise.then((result) => { - expect(result.data!.red).toEqual(resolveFunctions.Color.RED); - expect(result.data!.blue).toEqual(resolveFunctions.Color.BLUE); - expect(result.data!.num).toEqual(resolveFunctions.NumericEnum.TEST); + expect(result.data!['red']).toEqual(resolveFunctions.Color.RED); + expect(result.data!['blue']).toEqual(resolveFunctions.Color.BLUE); + expect(result.data!['num']).toEqual(resolveFunctions.NumericEnum.TEST); expect(result.errors).toEqual(undefined); }); }); @@ -1266,7 +1267,7 @@ describe('generating schema from shorthand', () => { const resultPromise = graphql(jsSchema, testQuery); return resultPromise.then((result) => { - expect(result.data!.red).toEqual(resolveFunctions.Color.RED); + expect(result.data!['red']).toEqual(resolveFunctions.Color.RED); expect(result.errors).toEqual(undefined); }); }); @@ -1317,7 +1318,7 @@ describe('generating schema from shorthand', () => { const resultPromise = graphql(jsSchema, testQuery); return resultPromise.then((result) => { - expect(result.data!.red).toEqual('override'); + expect(result.data!['red']).toEqual('override'); expect(result.errors).toEqual(undefined); }); }); @@ -1365,7 +1366,7 @@ describe('generating schema from shorthand', () => { const resultPromise = graphql(jsSchema, testQuery); return resultPromise.then((result) => { - expect(result.data!.red).toEqual('#EA3232'); + expect(result.data!['red']).toEqual('#EA3232'); expect(result.errors).toEqual(undefined); }); }); @@ -2028,13 +2029,13 @@ describe('can specify lexical parser options', () => { const parsedQuery = parse(query, { experimentalFragmentVariables: true }); const hoist = (document: DocumentNode) => { - let variableDefs: Array = []; + const variableDefs: Array = []; - document.definitions.forEach((def) => { - if (def.kind === Kind.FRAGMENT_DEFINITION) { - variableDefs = variableDefs.concat(def.variableDefinitions!); + for (const def of document.definitions) { + if (def.kind === Kind.FRAGMENT_DEFINITION && def.variableDefinitions) { + variableDefs.push(...def.variableDefinitions); } - }); + } return { kind: Kind.DOCUMENT, diff --git a/packages/stitch/src/createMergedTypeResolver.ts b/packages/stitch/src/createMergedTypeResolver.ts index dfae35b8abf..3ceec4b3162 100644 --- a/packages/stitch/src/createMergedTypeResolver.ts +++ b/packages/stitch/src/createMergedTypeResolver.ts @@ -2,9 +2,9 @@ import { getNamedType, GraphQLOutputType, GraphQLList } from 'graphql'; import { delegateToSchema, MergedTypeResolver, MergedTypeResolverOptions } from '@graphql-tools/delegate'; import { batchDelegateToSchema } from '@graphql-tools/batch-delegate'; -export function createMergedTypeResolver( +export function createMergedTypeResolver( mergedTypeResolverOptions: MergedTypeResolverOptions -): MergedTypeResolver | undefined { +): MergedTypeResolver | undefined { const { fieldName, argsFromKeys, valuesFromResults, args } = mergedTypeResolverOptions; if (argsFromKeys != null) { diff --git a/packages/stitch/src/definitions.ts b/packages/stitch/src/definitions.ts index f11648113df..bf683125027 100644 --- a/packages/stitch/src/definitions.ts +++ b/packages/stitch/src/definitions.ts @@ -14,7 +14,7 @@ export function extractDefinitions(ast: DocumentNode) { const schemaDefs: SchemaDefinitionNode[] = []; const schemaExtensions: SchemaExtensionNode[] = []; const extensionDefs: TypeExtensionNode[] = []; - ast.definitions.forEach(def => { + for (const def of ast.definitions) { switch (def.kind) { case Kind.OBJECT_TYPE_DEFINITION: case Kind.INTERFACE_TYPE_DEFINITION: @@ -42,7 +42,7 @@ export function extractDefinitions(ast: DocumentNode) { extensionDefs.push(def); break; } - }); + } return { typeDefinitions, diff --git a/packages/stitch/src/mergeCandidates.ts b/packages/stitch/src/mergeCandidates.ts index be4aab4eaf7..cf8b7f30a24 100644 --- a/packages/stitch/src/mergeCandidates.ts +++ b/packages/stitch/src/mergeCandidates.ts @@ -92,15 +92,15 @@ function mergeObjectTypeCandidates>( const typeConfigs = candidates.map(candidate => (candidate.type as GraphQLObjectType).toConfig()); const interfaceMap = typeConfigs .map(typeConfig => typeConfig.interfaces) - .reduce((acc, interfaces) => { + .reduce>((acc, interfaces) => { if (interfaces != null) { - interfaces.forEach(iface => { + for (const iface of interfaces) { acc[iface.name] = iface; - }); + } } return acc; }, Object.create(null)); - const interfaces = Object.keys(interfaceMap).map(interfaceName => interfaceMap[interfaceName]); + const interfaces = Object.values(interfaceMap); const astNodes = pluck('astNode', candidates); const fieldAstNodes = canonicalFieldNamesForType(candidates) @@ -198,18 +198,18 @@ function mergeInterfaceTypeCandidates>( const description = mergeTypeDescriptions(candidates, typeMergingOptions); const fields = fieldConfigMapFromTypeCandidates(candidates, typeMergingOptions); - const typeConfigs = candidates.map(candidate => (candidate.type as GraphQLInterfaceType).toConfig()); + const typeConfigs = candidates.map(candidate => candidate.type.toConfig()); const interfaceMap = typeConfigs - .map(typeConfig => (typeConfig as unknown as { interfaces: Array }).interfaces) - .reduce((acc, interfaces) => { + .map(typeConfig => ('interfaces' in typeConfig ? typeConfig.interfaces : [])) + .reduce>((acc, interfaces) => { if (interfaces != null) { - interfaces.forEach(iface => { + for (const iface of interfaces) { acc[iface.name] = iface; - }); + } } return acc; }, Object.create(null)); - const interfaces = Object.keys(interfaceMap).map(interfaceName => interfaceMap[interfaceName]); + const interfaces = Object.values(interfaceMap); const astNodes = pluck('astNode', candidates); const fieldAstNodes = canonicalFieldNamesForType(candidates) @@ -256,14 +256,19 @@ function mergeUnionTypeCandidates>( candidates = orderedTypeCandidates(candidates, typeMergingOptions); const description = mergeTypeDescriptions(candidates, typeMergingOptions); - const typeConfigs = candidates.map(candidate => (candidate.type as GraphQLUnionType).toConfig()); - const typeMap = typeConfigs.reduce((acc, typeConfig) => { - typeConfig.types.forEach(type => { + const typeConfigs = candidates.map(candidate => { + if (!isUnionType(candidate.type)) { + throw new Error(`Expected ${candidate.type} to be a union type!`); + } + return candidate.type.toConfig(); + }); + const typeMap = typeConfigs.reduce>((acc, typeConfig) => { + for (const type of typeConfig.types) { acc[type.name] = type; - }); + } return acc; }, Object.create(null)); - const types = Object.keys(typeMap).map(typeName => typeMap[typeName]); + const types = Object.values(typeMap); const astNodes = pluck('astNode', candidates); const astNode = astNodes @@ -330,9 +335,9 @@ function enumValueConfigMapFromTypeCandidates( ): GraphQLEnumValueConfigMap { const enumValueConfigCandidatesMap: Record> = Object.create(null); - candidates.forEach(candidate => { + for (const candidate of candidates) { const valueMap = (candidate.type as GraphQLEnumType).toConfig().values; - Object.keys(valueMap).forEach(enumValue => { + for (const enumValue in valueMap) { const enumValueConfigCandidate = { enumValueConfig: valueMap[enumValue], enumValue, @@ -346,15 +351,15 @@ function enumValueConfigMapFromTypeCandidates( } else { enumValueConfigCandidatesMap[enumValue] = [enumValueConfigCandidate]; } - }); - }); + } + } const enumValueConfigMap = Object.create(null); - Object.keys(enumValueConfigCandidatesMap).forEach(enumValue => { + for (const enumValue in enumValueConfigCandidatesMap) { const enumValueConfigMerger = typeMergingOptions?.enumValueConfigMerger ?? defaultEnumValueConfigMerger; enumValueConfigMap[enumValue] = enumValueConfigMerger(enumValueConfigCandidatesMap[enumValue]); - }); + } return enumValueConfigMap; } @@ -455,10 +460,12 @@ function fieldConfigMapFromTypeCandidates>( ): GraphQLFieldConfigMap { const fieldConfigCandidatesMap: Record>> = Object.create(null); - candidates.forEach(candidate => { + for (const candidate of candidates) { const typeConfig = (candidate.type as GraphQLObjectType | GraphQLInterfaceType).toConfig(); const fieldConfigMap = typeConfig.fields; - Object.entries(fieldConfigMap).forEach(([fieldName, fieldConfig]) => { + for (const fieldName in fieldConfigMap) { + const fieldConfig = fieldConfigMap[fieldName]; + const fieldConfigCandidate = { fieldConfig, fieldName, @@ -472,14 +479,14 @@ function fieldConfigMapFromTypeCandidates>( } else { fieldConfigCandidatesMap[fieldName] = [fieldConfigCandidate]; } - }); - }); + } + } const fieldConfigMap = Object.create(null); - Object.keys(fieldConfigCandidatesMap).forEach(fieldName => { + for (const fieldName in fieldConfigCandidatesMap) { fieldConfigMap[fieldName] = mergeFieldConfigs(fieldConfigCandidatesMap[fieldName], typeMergingOptions); - }); + } return fieldConfigMap; } @@ -500,14 +507,14 @@ function defaultFieldConfigMerger>( const canonicalByField: Array> = []; const canonicalByType: Array> = []; - candidates.forEach(({ type, fieldName, fieldConfig, transformedSubschema }) => { - if (!isSubschemaConfig(transformedSubschema)) return; + for (const { type, fieldName, fieldConfig, transformedSubschema } of candidates) { + if (!isSubschemaConfig(transformedSubschema)) continue; if (transformedSubschema.merge?.[type.name]?.fields?.[fieldName]?.canonical) { canonicalByField.push(fieldConfig); } else if (transformedSubschema.merge?.[type.name]?.canonical) { canonicalByType.push(fieldConfig); } - }); + } if (canonicalByField.length > 1) { throw new Error(`Multiple canonical definitions for "${candidates[0].type.name}.${candidates[0].fieldName}"`); @@ -529,10 +536,11 @@ function inputFieldConfigMapFromTypeCandidates>( ); const fieldInclusionMap: Record = Object.create(null); - candidates.forEach(candidate => { + for (const candidate of candidates) { const typeConfig = (candidate.type as GraphQLInputObjectType).toConfig(); const inputFieldConfigMap = typeConfig.fields; - Object.entries(inputFieldConfigMap).forEach(([fieldName, inputFieldConfig]) => { + for (const fieldName in inputFieldConfigMap) { + const inputFieldConfig = inputFieldConfigMap[fieldName]; fieldInclusionMap[fieldName] = fieldInclusionMap[fieldName] || 0; fieldInclusionMap[fieldName] += 1; @@ -549,14 +557,14 @@ function inputFieldConfigMapFromTypeCandidates>( } else { inputFieldConfigCandidatesMap[fieldName] = [inputFieldConfigCandidate]; } - }); - }); + } + } validateInputObjectConsistency(fieldInclusionMap, candidates, typeMergingOptions); const inputFieldConfigMap = Object.create(null); - Object.keys(inputFieldConfigCandidatesMap).forEach(fieldName => { + for (const fieldName in inputFieldConfigCandidatesMap) { const inputFieldConfigMerger = typeMergingOptions?.inputFieldConfigMerger ?? defaultInputFieldConfigMerger; inputFieldConfigMap[fieldName] = inputFieldConfigMerger(inputFieldConfigCandidatesMap[fieldName]); validateInputFieldConsistency( @@ -564,7 +572,7 @@ function inputFieldConfigMapFromTypeCandidates>( inputFieldConfigCandidatesMap[fieldName], typeMergingOptions ); - }); + } return inputFieldConfigMap; } @@ -575,14 +583,14 @@ function defaultInputFieldConfigMerger>( const canonicalByField: Array = []; const canonicalByType: Array = []; - candidates.forEach(({ type, fieldName, inputFieldConfig, transformedSubschema }) => { - if (!isSubschemaConfig(transformedSubschema)) return; + for (const { type, fieldName, inputFieldConfig, transformedSubschema } of candidates) { + if (!isSubschemaConfig(transformedSubschema)) continue; if (transformedSubschema.merge?.[type.name]?.fields?.[fieldName]?.canonical) { canonicalByField.push(inputFieldConfig); } else if (transformedSubschema.merge?.[type.name]?.canonical) { canonicalByType.push(inputFieldConfig); } - }); + } if (canonicalByField.length > 1) { throw new Error(`Multiple canonical definitions for "${candidates[0].type.name}.${candidates[0].fieldName}"`); @@ -598,19 +606,18 @@ function defaultInputFieldConfigMerger>( function canonicalFieldNamesForType(candidates: Array>): Array { const canonicalFieldNames: Record = Object.create(null); - candidates.forEach(({ type, transformedSubschema }) => { - if (!isSubschemaConfig(transformedSubschema)) { - return; - } + for (const { type, transformedSubschema } of candidates) { + if (!isSubschemaConfig(transformedSubschema)) continue; const mergeConfig = transformedSubschema.merge?.[type.name]; if (mergeConfig != null && mergeConfig.fields != null && !mergeConfig.canonical) { - Object.entries(mergeConfig.fields).forEach(([fieldName, mergedFieldConfig]) => { + for (const fieldName in mergeConfig.fields) { + const mergedFieldConfig = mergeConfig.fields[fieldName]; if (mergedFieldConfig.canonical) { canonicalFieldNames[fieldName] = true; } - }); + } } - }); + } return Object.keys(canonicalFieldNames); } diff --git a/packages/stitch/src/mergeValidations.ts b/packages/stitch/src/mergeValidations.ts index c79a03bb1e7..4ed13197d4e 100644 --- a/packages/stitch/src/mergeValidations.ts +++ b/packages/stitch/src/mergeValidations.ts @@ -55,15 +55,16 @@ export function validateFieldConsistency>( } const argCandidatesMap: Record> = Object.create(null); - candidates.forEach(({ fieldConfig }) => { + for (const { fieldConfig } of candidates) { if (fieldConfig.args == null) { - return; + continue; } - Object.entries(fieldConfig.args).forEach(([argName, arg]) => { + for (const argName in fieldConfig.args) { + const arg = fieldConfig.args[argName]; argCandidatesMap[argName] = argCandidatesMap[argName] || []; argCandidatesMap[argName].push(arg); - }); - }); + } + } if (Object.values(argCandidatesMap).some(argCandidates => candidates.length !== argCandidates.length)) { validationMessage( @@ -73,10 +74,11 @@ export function validateFieldConsistency>( ); } - Object.entries(argCandidatesMap).forEach(([argName, argCandidates]) => { + for (const argName in argCandidatesMap) { if (finalFieldConfig.args == null) { - return; + continue; } + const argCandidates = argCandidatesMap[argName]; const argNamespace = `${fieldNamespace}.${argName}`; const finalArgConfig = finalFieldConfig.args[argName] || argCandidates[argCandidates.length - 1]; const finalArgType = getNamedType(finalArgConfig.type); @@ -104,7 +106,7 @@ export function validateFieldConsistency>( if (isEnumType(finalArgType)) { validateInputEnumConsistency(finalArgType, argCandidates, typeMergingOptions); } - }); + } } export function validateInputObjectConsistency>( @@ -112,7 +114,8 @@ export function validateInputObjectConsistency>( candidates: Array>, typeMergingOptions?: TypeMergingOptions ): void { - Object.entries(fieldInclusionMap).forEach(([fieldName, count]) => { + for (const fieldName in fieldInclusionMap) { + const count = fieldInclusionMap[fieldName]; if (candidates.length !== count) { const namespace = `${candidates[0].type.name}.${fieldName}`; validationMessage( @@ -121,7 +124,7 @@ export function validateInputObjectConsistency>( typeMergingOptions ); } - }); + } } export function validateInputFieldConsistency>( @@ -175,7 +178,7 @@ export function validateTypeConsistency>( const finalIsScalar = isScalarType(finalNamedType); const finalIsList = hasListType(finalElementConfig.type); - candidates.forEach(c => { + for (const c of candidates) { if (finalIsList !== hasListType(c.type)) { throw new Error( `Definitions of ${definitionType} "${settingNamespace}" implement inconsistent list types across subschemas and cannot be merged.` @@ -201,7 +204,7 @@ export function validateTypeConsistency>( ); } } - }); + } } function hasListType(type: GraphQLType): boolean { @@ -215,15 +218,15 @@ export function validateInputEnumConsistency>( ): void { const enumValueInclusionMap: Record = Object.create(null); - candidates.forEach(candidate => { + for (const candidate of candidates) { const enumType = getNamedType(candidate.type) as GraphQLEnumType; if (isEnumType(enumType)) { - enumType.getValues().forEach(({ value }) => { + for (const { value } of enumType.getValues()) { enumValueInclusionMap[value] = enumValueInclusionMap[value] || 0; enumValueInclusionMap[value] += 1; - }); + } } - }); + } if (Object.values(enumValueInclusionMap).some(count => candidates.length !== count)) { validationMessage( diff --git a/packages/stitch/src/stitchSchemas.ts b/packages/stitch/src/stitchSchemas.ts index c76c006c604..5bc564dc6ac 100644 --- a/packages/stitch/src/stitchSchemas.ts +++ b/packages/stitch/src/stitchSchemas.ts @@ -45,7 +45,7 @@ export function stitchSchemas>({ throw new Error('Expected `resolverValidationOptions` to be an object'); } - let transformedSubschemas: Array> = []; + const transformedSubschemas: Array> = []; const subschemaMap: Map< GraphQLSchema | SubschemaConfig, Subschema @@ -55,24 +55,29 @@ export function stitchSchemas>({ GraphQLSchema | SubschemaConfig > = new Map(); - subschemas.forEach(subschemaOrSubschemaArray => { + for (const subschemaOrSubschemaArray of subschemas) { if (Array.isArray(subschemaOrSubschemaArray)) { - subschemaOrSubschemaArray.forEach(s => { - transformedSubschemas = transformedSubschemas.concat( - applySubschemaConfigTransforms(subschemaConfigTransforms, s, subschemaMap, originalSubschemaMap) - ); - }); - } else { - transformedSubschemas = transformedSubschemas.concat( - applySubschemaConfigTransforms( + for (const s of subschemaOrSubschemaArray) { + for (const transformedSubschemaConfig of applySubschemaConfigTransforms( subschemaConfigTransforms, - subschemaOrSubschemaArray, + s, subschemaMap, originalSubschemaMap - ) - ); + )) { + transformedSubschemas.push(transformedSubschemaConfig); + } + } + } else { + for (const transformedSubschemaConfig of applySubschemaConfigTransforms( + subschemaConfigTransforms, + subschemaOrSubschemaArray, + subschemaMap, + originalSubschemaMap + )) { + transformedSubschemas.push(transformedSubschemaConfig); + } } - }); + } const extensions: Array = []; const directives: Array = []; @@ -100,9 +105,9 @@ export function stitchSchemas>({ mergeDirectives, }); - Object.keys(directiveMap).forEach(directiveName => { + for (const directiveName in directiveMap) { directives.push(directiveMap[directiveName]); - }); + } let stitchingInfo = createStitchingInfo(subschemaMap, typeCandidates, mergeTypes); @@ -127,11 +132,11 @@ export function stitchSchemas>({ extensions: null, }); - extensions.forEach(extension => { + for (const extension of extensions) { schema = extendSchema(schema, extension, { commentDescriptions: true, }); - }); + } // We allow passing in an array of resolver maps, in which case we merge them const resolverMap: IResolvers = mergeResolvers(resolvers); @@ -157,9 +162,9 @@ export function stitchSchemas>({ schema = addStitchingInfo(schema, stitchingInfo); - schemaTransforms.forEach(schemaTransform => { + for (const schemaTransform of schemaTransforms) { schema = schemaTransform(schema); - }); + } if (pruningOptions) { schema = pruneSchema(schema, pruningOptions); @@ -182,7 +187,7 @@ function applySubschemaConfigTransforms>( GraphQLSchema | SubschemaConfig > ): Array> { - let subschemaConfig: SubschemaConfig; + let subschemaConfig: SubschemaConfig; if (isSubschemaConfig(subschemaOrSubschemaConfig)) { subschemaConfig = subschemaOrSubschemaConfig; } else if (subschemaOrSubschemaConfig instanceof GraphQLSchema) { @@ -191,24 +196,13 @@ function applySubschemaConfigTransforms>( throw new TypeError('Received invalid input.'); } - let transformedSubschemaConfigs: Array> = [subschemaConfig]; - subschemaConfigTransforms - .concat(subschemaConfigTransformerPresets as Array>) - .forEach(subschemaConfigTransform => { - const mapped: Array | Array>> = - transformedSubschemaConfigs.map(ssConfig => subschemaConfigTransform(ssConfig)); - - transformedSubschemaConfigs = mapped.reduce( - (acc: Array>, configOrList) => { - if (Array.isArray(configOrList)) { - return acc.concat(configOrList); - } - acc.push(configOrList); - return acc; - }, - [] - ); - }); + const transformedSubschemaConfigs = subschemaConfigTransforms + .concat(subschemaConfigTransformerPresets) + .reduce( + (transformedSubschemaConfigs, subschemaConfigTransform) => + transformedSubschemaConfigs.map(ssConfig => subschemaConfigTransform(ssConfig)).flat(), + [subschemaConfig] + ); const transformedSubschemas = transformedSubschemaConfigs.map( ssConfig => new Subschema(ssConfig) @@ -218,7 +212,9 @@ function applySubschemaConfigTransforms>( subschemaMap.set(subschemaOrSubschemaConfig, baseSubschema); - transformedSubschemas.forEach(subschema => originalSubschemaMap.set(subschema, subschemaOrSubschemaConfig)); + for (const subschema of transformedSubschemas) { + originalSubschemaMap.set(subschema, subschemaOrSubschemaConfig); + } return transformedSubschemas; } diff --git a/packages/stitch/src/stitchingInfo.ts b/packages/stitch/src/stitchingInfo.ts index 255a074ad19..8f048d2b5dd 100644 --- a/packages/stitch/src/stitchingInfo.ts +++ b/packages/stitch/src/stitchingInfo.ts @@ -29,22 +29,23 @@ export function createStitchingInfo>( const mergedTypes = createMergedTypes(typeCandidates, mergeTypes); const selectionSetsByField: Record> = Object.create(null); - Object.entries(mergedTypes).forEach(([typeName, mergedTypeInfo]) => { + for (const typeName in mergedTypes) { + const mergedTypeInfo = mergedTypes[typeName]; if (mergedTypeInfo.selectionSets == null && mergedTypeInfo.fieldSelectionSets == null) { - return; + continue; } selectionSetsByField[typeName] = Object.create(null); - mergedTypeInfo.selectionSets.forEach((selectionSet, subschemaConfig) => { + for (const [subschemaConfig, selectionSet] of mergedTypeInfo.selectionSets) { const schema = subschemaConfig.transformedSchema; const type = schema.getType(typeName) as GraphQLObjectType; const fields = type.getFields(); - Object.keys(fields).forEach(fieldName => { + for (const fieldName in fields) { const field = fields[fieldName]; const fieldType = getNamedType(field.type); if (selectionSet && isLeafType(fieldType) && selectionSetContainsTopLevelField(selectionSet, fieldName)) { - return; + continue; } if (selectionSetsByField[typeName][fieldName] == null) { selectionSetsByField[typeName][fieldName] = { @@ -55,11 +56,11 @@ export function createStitchingInfo>( selectionSetsByField[typeName][fieldName].selections = selectionSetsByField[typeName][ fieldName ].selections.concat(selectionSet.selections); - }); - }); + } + } - mergedTypeInfo.fieldSelectionSets.forEach(selectionSetFieldMap => { - Object.keys(selectionSetFieldMap).forEach(fieldName => { + for (const [, selectionSetFieldMap] of mergedTypeInfo.fieldSelectionSets) { + for (const fieldName in selectionSetFieldMap) { if (selectionSetsByField[typeName][fieldName] == null) { selectionSetsByField[typeName][fieldName] = { kind: Kind.SELECTION_SET, @@ -69,9 +70,9 @@ export function createStitchingInfo>( selectionSetsByField[typeName][fieldName].selections = selectionSetsByField[typeName][ fieldName ].selections.concat(selectionSetFieldMap[fieldName].selections); - }); - }); - }); + } + } + } return { subschemaMap, @@ -88,7 +89,7 @@ function createMergedTypes>( ): Record> { const mergedTypes: Record> = Object.create(null); - Object.keys(typeCandidates).forEach(typeName => { + for (const typeName in typeCandidates) { if ( typeCandidates[typeName].length > 1 && (isObjectType(typeCandidates[typeName][0].type) || isInterfaceType(typeCandidates[typeName][0].type)) @@ -114,11 +115,11 @@ function createMergedTypes>( const fieldSelectionSets: Map, Record> = new Map(); const resolvers: Map, MergedTypeResolver> = new Map(); - typeCandidates[typeName].forEach(typeCandidate => { + for (const typeCandidate of typeCandidates[typeName]) { const subschema = typeCandidate.transformedSubschema; if (subschema == null) { - return; + continue; } typeMaps.set(subschema, subschema.transformedSchema.getTypeMap()); @@ -126,7 +127,7 @@ function createMergedTypes>( const mergedTypeConfig = subschema?.merge?.[typeName]; if (mergedTypeConfig == null) { - return; + continue; } if (mergedTypeConfig.selectionSet) { @@ -150,7 +151,7 @@ function createMergedTypes>( const resolver = mergedTypeConfig.resolve ?? createMergedTypeResolver(mergedTypeConfig); if (resolver == null) { - return; + continue; } const keyFn = mergedTypeConfig.key; @@ -169,18 +170,18 @@ function createMergedTypes>( const type = subschema.transformedSchema.getType(typeName) as GraphQLObjectType | GraphQLInterfaceType; const fieldMap = type.getFields(); const selectionSet = selectionSets.get(subschema); - Object.keys(fieldMap).forEach(fieldName => { + for (const fieldName in fieldMap) { const field = fieldMap[fieldName]; const fieldType = getNamedType(field.type); if (selectionSet && isLeafType(fieldType) && selectionSetContainsTopLevelField(selectionSet, fieldName)) { - return; + continue; } if (!(fieldName in supportedBySubschemas)) { supportedBySubschemas[fieldName] = []; } supportedBySubschemas[fieldName].push(subschema); - }); - }); + } + } const sourceSubschemas = typeCandidates[typeName] .map(typeCandidate => typeCandidate?.transformedSubschema) @@ -189,12 +190,12 @@ function createMergedTypes>( Subschema, Array> > = new Map(); - sourceSubschemas.forEach(subschema => { + for (const subschema of sourceSubschemas) { const filteredSubschemas = targetSubschemas.filter(s => s !== subschema); if (filteredSubschemas.length) { targetSubschemasBySubschema.set(subschema, filteredSubschemas); } - }); + } mergedTypes[typeName] = { typeName, @@ -207,16 +208,16 @@ function createMergedTypes>( resolvers, }; - Object.keys(supportedBySubschemas).forEach(fieldName => { + for (const fieldName in supportedBySubschemas) { if (supportedBySubschemas[fieldName].length === 1) { mergedTypes[typeName].uniqueFields[fieldName] = supportedBySubschemas[fieldName][0]; } else { mergedTypes[typeName].nonUniqueFields[fieldName] = supportedBySubschemas[fieldName]; } - }); + } } } - }); + } return mergedTypes; } @@ -227,21 +228,22 @@ export function completeStitchingInfo>( schema: GraphQLSchema ): StitchingInfo { const selectionSetsByType = Object.create(null); - [schema.getQueryType(), schema.getMutationType()].forEach(rootType => { + const rootTypes = [schema.getQueryType(), schema.getMutationType()]; + for (const rootType of rootTypes) { if (rootType) { selectionSetsByType[rootType.name] = parseSelectionSet('{ __typename }', { noLocation: true }); } - }); + } const selectionSetsByField = stitchingInfo.selectionSetsByField; const dynamicSelectionSetsByField = Object.create(null); - Object.keys(resolvers).forEach(typeName => { + for (const typeName in resolvers) { const type = resolvers[typeName]; if (isScalarType(type)) { - return; + continue; } - Object.keys(type).forEach(fieldName => { + for (const fieldName in type) { const field = type[fieldName] as IFieldResolverOptions; if (field.selectionSet) { if (typeof field.selectionSet === 'function') { @@ -271,20 +273,20 @@ export function completeStitchingInfo>( ].selections.concat(selectionSet.selections); } } - }); - }); + } + } - Object.keys(selectionSetsByField).forEach(typeName => { + for (const typeName in selectionSetsByField) { const typeSelectionSets = selectionSetsByField[typeName]; - Object.keys(typeSelectionSets).forEach(fieldName => { + for (const fieldName in typeSelectionSets) { const consolidatedSelections: Map = new Map(); const selectionSet = typeSelectionSets[fieldName]; - selectionSet.selections.forEach(selection => { + for (const selection of selectionSet.selections) { consolidatedSelections.set(print(selection), selection); - }); + } selectionSet.selections = Array.from(consolidatedSelections.values()); - }); - }); + } + } stitchingInfo.selectionSetsByType = selectionSetsByType; stitchingInfo.selectionSetsByField = selectionSetsByField; diff --git a/packages/stitch/src/subschemaConfigTransforms/isolateComputedFieldsTransformer.ts b/packages/stitch/src/subschemaConfigTransforms/isolateComputedFieldsTransformer.ts index 0090fc714e3..1b128d87a9d 100644 --- a/packages/stitch/src/subschemaConfigTransforms/isolateComputedFieldsTransformer.ts +++ b/packages/stitch/src/subschemaConfigTransforms/isolateComputedFieldsTransformer.ts @@ -1,4 +1,4 @@ -import { GraphQLObjectType, GraphQLInterfaceType, isObjectType, isInterfaceType } from 'graphql'; +import { GraphQLObjectType, isObjectType, isInterfaceType } from 'graphql'; import { SubschemaConfig, MergedTypeConfig, MergedFieldConfig } from '@graphql-tools/delegate'; @@ -14,12 +14,15 @@ export function isolateComputedFieldsTransformer(subschemaConfig: SubschemaConfi const baseSchemaTypes: Record = Object.create(null); const isolatedSchemaTypes: Record = Object.create(null); - Object.entries(subschemaConfig.merge).forEach(([typeName, mergedTypeConfig]) => { + for (const typeName in subschemaConfig.merge) { + const mergedTypeConfig = subschemaConfig.merge[typeName]; + baseSchemaTypes[typeName] = mergedTypeConfig; if (mergedTypeConfig.computedFields) { const mergeConfigFields = mergedTypeConfig.fields ?? Object.create(null); - Object.entries(mergedTypeConfig.computedFields).forEach(([fieldName, mergedFieldConfig]) => { + for (const fieldName in mergedTypeConfig.computedFields) { + const mergedFieldConfig = mergedTypeConfig.computedFields[fieldName]; console.warn( `The "computedFields" setting is deprecated. Update your @graphql-tools/stitching-directives package, and/or update static merged type config to "${typeName}.fields.${fieldName} = { selectionSet: '${mergedFieldConfig.selectionSet}', computed: true }"` ); @@ -28,7 +31,7 @@ export function isolateComputedFieldsTransformer(subschemaConfig: SubschemaConfi ...mergedFieldConfig, computed: true, }; - }); + } delete mergedTypeConfig.computedFields; mergedTypeConfig.fields = mergeConfigFields; } @@ -37,7 +40,8 @@ export function isolateComputedFieldsTransformer(subschemaConfig: SubschemaConfi const baseFields: Record = Object.create(null); const isolatedFields: Record = Object.create(null); - Object.entries(mergedTypeConfig.fields).forEach(([fieldName, mergedFieldConfig]) => { + for (const fieldName in mergedTypeConfig.fields) { + const mergedFieldConfig = mergedTypeConfig.fields[fieldName]; if (mergedFieldConfig.computed && mergedFieldConfig.selectionSet) { isolatedFields[fieldName] = mergedFieldConfig; } else if (mergedFieldConfig.computed) { @@ -45,7 +49,7 @@ export function isolateComputedFieldsTransformer(subschemaConfig: SubschemaConfi } else { baseFields[fieldName] = mergedFieldConfig; } - }); + } const isolatedFieldCount = Object.keys(isolatedFields).length; const objectType = subschemaConfig.schema.getType(typeName) as GraphQLObjectType; @@ -62,7 +66,7 @@ export function isolateComputedFieldsTransformer(subschemaConfig: SubschemaConfi }; } } - }); + } if (Object.keys(isolatedSchemaTypes).length) { return [ @@ -96,16 +100,16 @@ function filterBaseSubschema( ); const filteredFields: Record> = {}; - Object.keys(filteredSchema.getTypeMap()).forEach(typeName => { + for (const typeName in filteredSchema.getTypeMap()) { const type = filteredSchema.getType(typeName); if (isObjectType(type) || isInterfaceType(type)) { filteredFields[typeName] = { __typename: true }; const fieldMap = type.getFields(); - Object.keys(fieldMap).forEach(fieldName => { + for (const fieldName in fieldMap) { filteredFields[typeName][fieldName] = true; - }); + } } - }); + } const filteredSubschema = { ...subschemaConfig, @@ -125,11 +129,11 @@ function filterBaseSubschema( const remainingTypes = filteredSchema.getTypeMap(); const mergeConfig = filteredSubschema.merge; if (mergeConfig) { - Object.keys(mergeConfig).forEach(mergeType => { + for (const mergeType in mergeConfig) { if (!remainingTypes[mergeType]) { delete mergeConfig[mergeType]; } - }); + } if (!Object.keys(mergeConfig).length) { delete filteredSubschema.merge; @@ -146,28 +150,35 @@ type IsolatedSubschemaInput = Exclude & { function filterIsolatedSubschema(subschemaConfig: IsolatedSubschemaInput): SubschemaConfig { const rootFields: Record = {}; - Object.values(subschemaConfig.merge).forEach(mergedTypeConfig => { + for (const typeName in subschemaConfig.merge) { + const mergedTypeConfig = subschemaConfig.merge[typeName]; const entryPoints = mergedTypeConfig.entryPoints ?? [mergedTypeConfig]; - entryPoints.forEach(entryPoint => { + for (const entryPoint of entryPoints) { if (entryPoint.fieldName != null) { rootFields[entryPoint.fieldName] = true; } - }); - }); + } + } const interfaceFields: Record> = {}; - Object.keys(subschemaConfig.merge).forEach(typeName => { - (subschemaConfig.schema.getType(typeName) as GraphQLObjectType).getInterfaces().forEach(int => { - Object.keys((subschemaConfig.schema.getType(int.name) as GraphQLInterfaceType).getFields()).forEach( - intFieldName => { - if (subschemaConfig.merge[typeName].fields?.[intFieldName]) { - interfaceFields[int.name] = interfaceFields[int.name] || {}; - interfaceFields[int.name][intFieldName] = true; - } + for (const typeName in subschemaConfig.merge) { + const type = subschemaConfig.schema.getType(typeName); + if (!type || !('getInterfaces' in type)) { + throw new Error(`${typeName} expected to have 'getInterfaces' method`); + } + for (const int of type.getInterfaces()) { + const intType = subschemaConfig.schema.getType(int.name); + if (!intType || !('getFields' in intType)) { + throw new Error(`${int.name} expected to have 'getFields' method`); + } + for (const intFieldName in intType.getFields()) { + if (subschemaConfig.merge[typeName].fields?.[intFieldName]) { + interfaceFields[int.name] = interfaceFields[int.name] || {}; + interfaceFields[int.name][intFieldName] = true; } - ); - }); - }); + } + } + } const filteredSchema = pruneSchema( filterSchema({ @@ -179,16 +190,16 @@ function filterIsolatedSubschema(subschemaConfig: IsolatedSubschemaInput): Subsc ); const filteredFields: Record> = {}; - Object.keys(filteredSchema.getTypeMap()).forEach(typeName => { + for (const typeName in filteredSchema.getTypeMap()) { const type = filteredSchema.getType(typeName); if (isObjectType(type) || isInterfaceType(type)) { filteredFields[typeName] = { __typename: true }; const fieldMap = type.getFields(); - Object.keys(fieldMap).forEach(fieldName => { + for (const fieldName in fieldMap) { filteredFields[typeName][fieldName] = true; - }); + } } - }); + } return { ...subschemaConfig, diff --git a/packages/stitch/src/subschemaConfigTransforms/splitMergedTypeEntryPointsTransformer.ts b/packages/stitch/src/subschemaConfigTransforms/splitMergedTypeEntryPointsTransformer.ts index d4499f47e83..493772bbfc9 100644 --- a/packages/stitch/src/subschemaConfigTransforms/splitMergedTypeEntryPointsTransformer.ts +++ b/packages/stitch/src/subschemaConfigTransforms/splitMergedTypeEntryPointsTransformer.ts @@ -13,15 +13,15 @@ export function splitMergedTypeEntryPointsTransformer(subschemaConfig: Subschema for (let i = 0; i < maxEntryPoints; i += 1) { const subschemaPermutation = cloneSubschemaConfig(subschemaConfig); - const mergedTypesCopy: Record> = - subschemaPermutation.merge ?? Object.create(null); + const mergedTypesCopy: Record> = subschemaPermutation.merge ?? + Object.create(null); let currentMerge = mergedTypesCopy; if (i > 0) { subschemaPermutation.merge = currentMerge = Object.create(null); } - Object.keys(mergedTypesCopy).forEach(typeName => { + for (const typeName in mergedTypesCopy) { const mergedTypeConfig = mergedTypesCopy[typeName]; const mergedTypeEntryPoint = mergedTypeConfig?.entryPoints?.[i]; @@ -38,15 +38,16 @@ export function splitMergedTypeEntryPointsTransformer(subschemaConfig: Subschema if (i > 0) { delete mergedTypeConfig.canonical; if (mergedTypeConfig.fields != null) { - Object.values(mergedTypeConfig.fields).forEach(mergedFieldConfig => { + for (const mergedFieldName in mergedTypeConfig.fields) { + const mergedFieldConfig = mergedTypeConfig.fields[mergedFieldName]; delete mergedFieldConfig.canonical; - }); + } } } currentMerge[typeName] = mergedTypeConfig; } - }); + } subschemaPermutations.push(subschemaPermutation); } diff --git a/packages/stitch/src/typeCandidates.ts b/packages/stitch/src/typeCandidates.ts index 8eac67e0bd1..7d356cdc535 100644 --- a/packages/stitch/src/typeCandidates.ts +++ b/packages/stitch/src/typeCandidates.ts @@ -8,6 +8,7 @@ import { SchemaExtensionNode, isSpecifiedScalarType, GraphQLSchema, + isDirective, } from 'graphql'; import { wrapSchema } from '@graphql-tools/wrap'; @@ -72,7 +73,7 @@ export function buildTypeCandidates>({ setOperationTypeNames(schemaDefs, operationTypeNames); - subschemas.forEach(subschema => { + for (const subschema of subschemas) { const schema = wrapSchema(subschema); const operationTypes = { @@ -81,7 +82,7 @@ export function buildTypeCandidates>({ subscription: schema.getSubscriptionType(), }; - Object.keys(operationTypes).forEach(operationType => { + for (const operationType in operationTypes) { if (operationTypes[operationType] != null) { addTypeCandidate(typeCandidates, operationTypeNames[operationType], { type: operationTypes[operationType], @@ -89,16 +90,16 @@ export function buildTypeCandidates>({ transformedSubschema: subschema, }); } - }); + } if (mergeDirectives === true) { - schema.getDirectives().forEach(directive => { + for (const directive of schema.getDirectives()) { directiveMap[directive.name] = directive; - }); + } } const originalTypeMap = schema.getTypeMap(); - Object.keys(originalTypeMap).forEach(typeName => { + for (const typeName in originalTypeMap) { const type: GraphQLNamedType = originalTypeMap[typeName]; if ( isNamedType(type) && @@ -113,21 +114,27 @@ export function buildTypeCandidates>({ transformedSubschema: subschema, }); } - }); - }); + } + } if (document != null && extraction != null) { - extraction.typeDefinitions.forEach(def => { - const type = typeFromAST(def) as GraphQLNamedType; + for (const def of extraction.typeDefinitions) { + const type = typeFromAST(def); + if (!isNamedType(type)) { + throw new Error(`Expected to get named typed but got ${JSON.stringify(def)}`); + } if (type != null) { addTypeCandidate(typeCandidates, type.name, { type }); } - }); + } - extraction.directiveDefs.forEach(def => { - const directive = typeFromAST(def) as GraphQLDirective; + for (const def of extraction.directiveDefs) { + const directive = typeFromAST(def); + if (!isDirective(directive)) { + throw new Error(`Expected to get directive type but got ${JSON.stringify(def)}`); + } directiveMap[directive.name] = directive; - }); + } if (extraction.extensionDefs.length > 0) { extensions.push({ @@ -137,7 +144,9 @@ export function buildTypeCandidates>({ } } - types.forEach(type => addTypeCandidate(typeCandidates, type.name, { type })); + for (const type of types) { + addTypeCandidate(typeCandidates, type.name, { type }); + } return typeCandidates; } @@ -157,13 +166,13 @@ function setOperationTypeNames( allNodes.unshift(schemaDef); } - allNodes.forEach(node => { + for (const node of allNodes) { if (node.operationTypes != null) { - node.operationTypes.forEach(operationType => { + for (const operationType of node.operationTypes) { operationTypeNames[operationType.operation] = operationType.type.name.value; - }); + } } - }); + } } function addTypeCandidate>( @@ -196,7 +205,7 @@ export function buildTypes>({ }): { typeMap: TypeMap; directives: Array } { const typeMap: TypeMap = Object.create(null); - Object.keys(typeCandidates).forEach(typeName => { + for (const typeName in typeCandidates) { if ( typeName === operationTypeNames['query'] || typeName === operationTypeNames['mutation'] || @@ -214,7 +223,7 @@ export function buildTypes>({ : (cands: Array>) => cands[cands.length - 1]; typeMap[typeName] = candidateSelector(typeCandidates[typeName]).type; } - }); + } return rewireTypes(typeMap, directives); } diff --git a/packages/stitch/src/typeFromAST.ts b/packages/stitch/src/typeFromAST.ts index 0f17a8d830e..f72355dc1cd 100644 --- a/packages/stitch/src/typeFromAST.ts +++ b/packages/stitch/src/typeFromAST.ts @@ -165,11 +165,11 @@ function makeValues(nodes: ReadonlyArray): GraphQLFiel function makeDirective(node: DirectiveDefinitionNode): GraphQLDirective { const locations: Array = []; - node.locations.forEach(location => { + for (const location of node.locations) { if (location.value in DirectiveLocation) { locations.push(location.value as DirectiveLocationEnum); } - }); + } return new GraphQLDirective({ name: node.name.value, description: node.description != null ? node.description.value : null, diff --git a/packages/stitch/tests/alternateStitchSchemas.test.ts b/packages/stitch/tests/alternateStitchSchemas.test.ts index e957cdb841e..28621405339 100644 --- a/packages/stitch/tests/alternateStitchSchemas.test.ts +++ b/packages/stitch/tests/alternateStitchSchemas.test.ts @@ -441,11 +441,11 @@ describe('optional arguments', () => { const originalResult = await graphql(schema, query); assertSome(originalResult.data) - expect(originalResult.data.test).toEqual(true); + expect(originalResult.data['test']).toEqual(true); const stitchedResult = await graphql(stitchedSchema, query); assertSome(stitchedResult.data) - expect(stitchedResult.data.test).toEqual(true); + expect(stitchedResult.data['test']).toEqual(true); }); it('work with schema stitching when using variables', async () => { @@ -457,11 +457,11 @@ describe('optional arguments', () => { const originalResult = await graphql(schema, query); assertSome(originalResult.data) - expect(originalResult.data.test).toEqual(true); + expect(originalResult.data['test']).toEqual(true); const stitchedResult = await graphql(stitchedSchema, query); assertSome(stitchedResult.data) - expect(stitchedResult.data.test).toEqual(true); + expect(stitchedResult.data['test']).toEqual(true); }); // See https://github.com/graphql/graphql-js/issues/2533 @@ -480,7 +480,7 @@ describe('optional arguments', () => { { arg: undefined }, ); assertSome(originalResult.data) - expect(originalResult.data.test).toEqual(false); + expect(originalResult.data['test']).toEqual(false); const stitchedResult = await graphql( stitchedSchema, @@ -490,7 +490,7 @@ describe('optional arguments', () => { { arg: undefined }, ); assertSome(stitchedResult.data) - expect(stitchedResult.data.test).toEqual(false); + expect(stitchedResult.data['test']).toEqual(false); }); }); @@ -515,7 +515,7 @@ describe('default values', () => { args: { ...fieldConfig.args, input: { - ...fieldConfig.args.input, + ...fieldConfig.args['input'], defaultValue: { test: 'test' } } } @@ -1516,7 +1516,7 @@ describe('schema transformation with wrapping of object fields', () => { const query = '{ wrapped { user { dummy } } }'; const result = await graphql(stitchedSchema, query); assertSome(result.data) - expect(result.data.wrapped.user.dummy).not.toEqual(null); + expect(result.data['wrapped'].user.dummy).not.toEqual(null); }); }); }); @@ -1640,7 +1640,7 @@ describe('stitchSchemas', () => { const query = '{ test { field } }'; const response = await graphql(stitchedSchema, query); assertSome(response.data) - expect(response.data.test).toBe(null); + expect(response.data['test']).toBe(null); expect(response.errors).toBeUndefined(); }); @@ -1682,7 +1682,7 @@ type Query { } `.trim(), ); - expect(response.data?.getInput).toBe('test'); + expect(response.data?.['getInput']).toBe('test'); }); test('can override scalars with new internal values', async () => { @@ -1722,7 +1722,7 @@ type Query { const query = '{ getTestScalar }'; const response = await graphql(stitchedSchema, query); - expect(response.data?.getTestScalar).toBe('test'); + expect(response.data?.['getTestScalar']).toBe('test'); }); test('can override scalars with new internal values when using default input types', async () => { @@ -1762,7 +1762,7 @@ type Query { const query = '{ getTestScalar }'; const response = await graphql(stitchedSchema, query); - expect(response.data?.getTestScalar).toBe('test'); + expect(response.data?.['getTestScalar']).toBe('test'); }); test('can use @include directives', async () => { @@ -1810,7 +1810,7 @@ type Query { } `; const response = await graphql(stitchedSchema, query); - expect(response.data?.get2.subfield).toBe('test'); + expect(response.data?.['get2'].subfield).toBe('test'); }); test('can use functions in subfields', async () => { @@ -1838,7 +1838,7 @@ type Query { const query = '{ wrappingObject { functionField } }'; const response = await graphql(stitchedSchema, query); - expect(response.data?.wrappingObject.functionField).toBe(8); + expect(response.data?.['wrappingObject'].functionField).toBe(8); }); }); @@ -1902,7 +1902,7 @@ describe('onTypeConflict', () => { mergeTypes: false, }); const result1 = await graphql(stitchedSchema, '{ test2 { fieldC } }'); - expect(result1.data?.test2.fieldC).toBe('C'); + expect(result1.data?.['test2'].fieldC).toBe('C'); const result2 = await graphql(stitchedSchema, '{ test2 { fieldB } }'); expect(result2.data).toBeUndefined(); }); @@ -1914,7 +1914,7 @@ describe('onTypeConflict', () => { onTypeConflict: (_left, right) => right, }); const result1 = await graphql(stitchedSchema, '{ test2 { fieldC } }'); - expect(result1.data?.test2.fieldC).toBe('C'); + expect(result1.data?.['test2'].fieldC).toBe('C'); const result2 = await graphql(stitchedSchema, '{ test2 { fieldB } }'); expect(result2.data).toBeUndefined(); }); @@ -1926,7 +1926,7 @@ describe('onTypeConflict', () => { onTypeConflict: (left) => left, }); const result1 = await graphql(stitchedSchema, '{ test1 { fieldB } }'); - expect(result1.data?.test1.fieldB).toBe('B'); + expect(result1.data?.['test1'].fieldB).toBe('B'); const result2 = await graphql(stitchedSchema, '{ test1 { fieldC } }'); expect(result2.data).toBeUndefined(); }); diff --git a/packages/stitch/tests/dataloader.test.ts b/packages/stitch/tests/dataloader.test.ts index 2c5389ea2ae..4dcacf27d7e 100644 --- a/packages/stitch/tests/dataloader.test.ts +++ b/packages/stitch/tests/dataloader.test.ts @@ -60,7 +60,7 @@ describe('dataloader', () => { user: { selectionSet: '{ userId }', resolve(task, _args, context, info) { - return context.usersLoader.load({ id: task.userId, info }); + return context['usersLoader'].load({ id: task.userId, info }); }, }, }, diff --git a/packages/stitch/tests/example.test.ts b/packages/stitch/tests/example.test.ts index 6757bd0af7b..9e20c76a278 100644 --- a/packages/stitch/tests/example.test.ts +++ b/packages/stitch/tests/example.test.ts @@ -109,9 +109,9 @@ describe('basic stitching example', () => { expect(result.errors).toBeUndefined(); assertSome(result.data) - expect(result.data.userById.chirps[1].id).not.toBe(null); - expect(result.data.userById.chirps[1].text).not.toBe(null); - expect(result.data.userById.chirps[1].author.email).not.toBe(null); + expect(result.data['userById'].chirps[1].id).not.toBe(null); + expect(result.data['userById'].chirps[1].text).not.toBe(null); + expect(result.data['userById'].chirps[1].author.email).not.toBe(null); }); }); @@ -232,9 +232,9 @@ describe('stitching to interfaces', () => { expect(resultWithFragments.errors).toBeUndefined(); assertSome(resultWithFragments.data) - expect(resultWithFragments.data.node.chirps[1].id).not.toBe(null); - expect(resultWithFragments.data.node.chirps[1].text).not.toBe(null); - expect(resultWithFragments.data.node.chirps[1].author.email).not.toBe(null); + expect(resultWithFragments.data['node'].chirps[1].id).not.toBe(null); + expect(resultWithFragments.data['node'].chirps[1].text).not.toBe(null); + expect(resultWithFragments.data['node'].chirps[1].author.email).not.toBe(null); }); test('it works without fragments', async () => { @@ -257,9 +257,9 @@ describe('stitching to interfaces', () => { expect(resultWithoutFragments.errors).toBeUndefined(); assertSome(resultWithoutFragments.data) - expect(resultWithoutFragments.data.node.chirps[1].id).not.toBe(null); - expect(resultWithoutFragments.data.node.chirps[1].text).not.toBe(null); - expect(resultWithoutFragments.data.node.chirps[1].author.email).not.toBe(null); + expect(resultWithoutFragments.data['node'].chirps[1].id).not.toBe(null); + expect(resultWithoutFragments.data['node'].chirps[1].text).not.toBe(null); + expect(resultWithoutFragments.data['node'].chirps[1].author.email).not.toBe(null); }); }); diff --git a/packages/stitch/tests/extendedInterface.test.ts b/packages/stitch/tests/extendedInterface.test.ts index 45c5205b3b3..4e855c11433 100644 --- a/packages/stitch/tests/extendedInterface.test.ts +++ b/packages/stitch/tests/extendedInterface.test.ts @@ -47,7 +47,7 @@ describe('extended interfaces', () => { } `); assertSome(data) - expect(data.slot).toEqual({ id: '23', name: 'The Item' }); + expect(data['slot']).toEqual({ id: '23', name: 'The Item' }); }); test('merges types behind gateway interface extension', async () => { @@ -63,7 +63,7 @@ describe('extended interfaces', () => { `, resolvers: { Query: { - itemById(obj, args, context, info) { + itemById(_obj, args, _context, _info) { return { id: args.id, name: `Item ${args.id}` }; } } @@ -84,7 +84,7 @@ describe('extended interfaces', () => { `, resolvers: { Query: { - placementById(obj, args, context, info) { + placementById(_obj, args, _context, _info) { return { __typename: 'Item', id: args.id }; } } @@ -121,6 +121,6 @@ describe('extended interfaces', () => { } `); assertSome(result.data) - expect(result.data.placement).toEqual({ id: '23', name: 'Item 23' }); + expect(result.data['placement']).toEqual({ id: '23', name: 'Item 23' }); }); }); diff --git a/packages/stitch/tests/fixtures/schemas.ts b/packages/stitch/tests/fixtures/schemas.ts index 140d5d9b11e..a6840a8b890 100644 --- a/packages/stitch/tests/fixtures/schemas.ts +++ b/packages/stitch/tests/fixtures/schemas.ts @@ -190,10 +190,6 @@ export const sampleData: { }, }; -function values(o: { [s: string]: T }): Array { - return Object.keys(o).map((k) => o[k]); -} - function coerceString(value: any): string { if (Array.isArray(value)) { throw new TypeError( @@ -227,9 +223,9 @@ function parseLiteral(ast: ValueNode): any { return parseFloat(ast.value); case Kind.OBJECT: { const value = Object.create(null); - ast.fields.forEach((field) => { + for (const field of ast.fields) { value[field.name.value] = parseLiteral(field.value); - }); + } return value; } @@ -351,7 +347,7 @@ const propertyResolvers: IResolvers = { }, properties(_root, { limit }) { - const list = values(sampleData.Property); + const list = Object.values(sampleData.Property); return limit ? list.slice(0, limit) : list; }, @@ -466,7 +462,7 @@ const productTypeDefs = ` const productResolvers: IResolvers = { Query: { products(_root) { - const list = values(sampleData.Product); + const list = Object.values(sampleData.Product); return list; }, }, @@ -552,7 +548,7 @@ const bookingResolvers: IResolvers = { return sampleData.Booking[id]; }, bookingsByPropertyId(_parent, { propertyId, limit }) { - const list = values(sampleData.Booking).filter( + const list = Object.values(sampleData.Booking).filter( (booking: Booking) => booking.propertyId === propertyId, ); return limit ? list.slice(0, limit) : list; @@ -561,11 +557,11 @@ const bookingResolvers: IResolvers = { return sampleData.Customer[id]; }, bookings(_parent, { limit }) { - const list = values(sampleData.Booking); + const list = Object.values(sampleData.Booking); return limit ? list.slice(0, limit) : list; }, customers(_parent, { limit }) { - const list = values(sampleData.Customer); + const list = Object.values(sampleData.Customer); return limit ? list.slice(0, limit) : list; }, }, @@ -602,13 +598,13 @@ const bookingResolvers: IResolvers = { Customer: { bookings(parent: Customer, { limit }) { - const list = values(sampleData.Booking).filter( + const list = Object.values(sampleData.Booking).filter( (booking: Booking) => booking.customerId === parent.id, ); return limit ? list.slice(0, limit) : list; }, vehicle(parent: Customer) { - return sampleData.Vehicle[parent.vehicleId]; + return parent.vehicleId && sampleData.Vehicle[parent.vehicleId]; }, error() { throw new Error('Customer.error error'); @@ -717,7 +713,7 @@ function makeSubscriberFromSchema(schema: GraphQLSchema) { if (isPromise(result)) { return result.then(asyncIterator => { assertAsyncIterable(asyncIterator) - return mapAsyncIterator(asyncIterator, (originalResult: ExecutionResult) => JSON.parse(JSON.stringify(originalResult))) + return mapAsyncIterator(asyncIterator, (originalResult: ExecutionResult) => JSON.parse(JSON.stringify(originalResult))) }); } return JSON.parse(JSON.stringify(result)); diff --git a/packages/stitch/tests/isolateComputedFieldsTransformer.test.ts b/packages/stitch/tests/isolateComputedFieldsTransformer.test.ts index e8ffeb88df8..d8c3a043c73 100644 --- a/packages/stitch/tests/isolateComputedFieldsTransformer.test.ts +++ b/packages/stitch/tests/isolateComputedFieldsTransformer.test.ts @@ -75,13 +75,13 @@ describe('isolateComputedFieldsTransformer', () => { expect(computedSubschema.transformedSchema.getType('ProductRepresentation')).toBeDefined(); assertSome(baseSubschema.merge) - expect(baseSubschema.merge.Product.canonical).toEqual(true); - expect(baseSubschema.merge.Product.fields).toEqual({ + expect(baseSubschema.merge['Product'].canonical).toEqual(true); + expect(baseSubschema.merge['Product'].fields).toEqual({ deliveryService: { canonical: true }, }); assertSome(computedSubschema.merge) - expect(computedSubschema.merge.Product.canonical).toBeUndefined(); - expect(computedSubschema.merge.Product.fields).toEqual({ + expect(computedSubschema.merge['Product'].canonical).toBeUndefined(); + expect(computedSubschema.merge['Product'].fields).toEqual({ shippingEstimate: { selectionSet: '{ price }', computed: true, canonical: true }, }); }); @@ -198,16 +198,16 @@ describe('isolateComputedFieldsTransformer', () => { expect(Object.keys((baseSubschema.transformedSchema.getType('Product') as GraphQLObjectType).getFields())).toEqual(['base']); expect(Object.keys((baseSubschema.transformedSchema.getType('Storefront') as GraphQLObjectType).getFields())).toEqual(['base']); assertSome(baseSubschema.merge) - expect(baseSubschema.merge.Storefront.fields).toEqual({}); + expect(baseSubschema.merge['Storefront'].fields).toEqual({}); expect(Object.keys((computedSubschema.transformedSchema.getType('Query') as GraphQLObjectType).getFields())).toEqual(['storefront', '_products']); expect(Object.keys((computedSubschema.transformedSchema.getType('Product') as GraphQLObjectType).getFields())).toEqual(['computeMe']); expect(Object.keys((computedSubschema.transformedSchema.getType('Storefront') as GraphQLObjectType).getFields())).toEqual(['computeMe']); assertSome(computedSubschema.merge) - expect(computedSubschema.merge.Storefront.fields).toEqual({ + expect(computedSubschema.merge['Storefront'].fields).toEqual({ computeMe: { selectionSet: '{ availableProductIds }', computed: true }, }); - expect(computedSubschema.merge.Product.fields).toEqual({ + expect(computedSubschema.merge['Product'].fields).toEqual({ computeMe: { selectionSet: '{ price }', computed: true }, }); }); diff --git a/packages/stitch/tests/mergeAbstractTypes.test.ts b/packages/stitch/tests/mergeAbstractTypes.test.ts index 51628593d52..a8f25bd8365 100644 --- a/packages/stitch/tests/mergeAbstractTypes.test.ts +++ b/packages/stitch/tests/mergeAbstractTypes.test.ts @@ -86,7 +86,7 @@ describe('Abstract type merge', () => { } `); assertSome(data) - expect(data.post.leadArt).toEqual({ + expect(data['post'].leadArt).toEqual({ __typename: 'Image', url: '/path/to/23', id: '23', @@ -185,7 +185,7 @@ describe('Merged associations', () => { `); assertSome(data) - expect(data.slots).toEqual([{ + expect(data['slots']).toEqual([{ id: '55', network: { domain: 'network56.com' } }]); diff --git a/packages/stitch/tests/mergeComputedFields.test.ts b/packages/stitch/tests/mergeComputedFields.test.ts index 0016e87135d..c63c718b97d 100644 --- a/packages/stitch/tests/mergeComputedFields.test.ts +++ b/packages/stitch/tests/mergeComputedFields.test.ts @@ -110,7 +110,7 @@ describe('merge computed fields via config', () => { `); assertSome(data) - expect(data.product).toEqual({ + expect(data['product']).toEqual({ id: '77', price: 77.99, weight: 77, @@ -135,7 +135,7 @@ describe('merge computed fields via config', () => { `); assertSome(data) - expect(data.storefront.availableProducts).toEqual([ + expect(data['storefront'].availableProducts).toEqual([ { id: '23', price: 23.99, @@ -188,7 +188,7 @@ describe('merge computed fields via config', () => { `); assertSome(data) - expect(data.product).toEqual({ + expect(data['product']).toEqual({ id: '77', price: 77.99, weight: 77, @@ -276,7 +276,7 @@ describe('merge computed fields via SDL (Apollo Federation-style directive annot `); assertSome(data) - expect(data.storefront.availableProducts).toEqual([ + expect(data['storefront'].availableProducts).toEqual([ { id: '23', price: 23.99, diff --git a/packages/stitch/tests/mergeConflicts.test.ts b/packages/stitch/tests/mergeConflicts.test.ts index b99d122a3b7..99a83a549ba 100644 --- a/packages/stitch/tests/mergeConflicts.test.ts +++ b/packages/stitch/tests/mergeConflicts.test.ts @@ -98,8 +98,8 @@ describe('merge conflict handlers', () => { expect(Listing.description).toEqual('A type'); expect(IListing.description).toEqual('An interface'); expect(ListingInput.description).toEqual('An input'); - expect(Listing.getFields().id.description).toEqual('type identifier'); - expect(IListing.getFields().id.description).toEqual('interface identifier'); - expect(ListingInput.getFields().id.description).toEqual('input identifier'); + expect(Listing.getFields()['id'].description).toEqual('type identifier'); + expect(IListing.getFields()['id'].description).toEqual('interface identifier'); + expect(ListingInput.getFields()['id'].description).toEqual('input identifier'); }); }); diff --git a/packages/stitch/tests/mergeDefinitions.test.ts b/packages/stitch/tests/mergeDefinitions.test.ts index 3b4f2def57c..17af8555698 100644 --- a/packages/stitch/tests/mergeDefinitions.test.ts +++ b/packages/stitch/tests/mergeDefinitions.test.ts @@ -4,8 +4,6 @@ import { getDirectives } from '@graphql-tools/utils'; import { stitchingDirectives } from '@graphql-tools/stitching-directives'; import { GraphQLObjectType, - GraphQLInputObjectType, - GraphQLEnumType, graphql, } from 'graphql'; import { assertGraphQLEnumType, assertGraphQLInputObjectType, assertGraphQLInterfaceType, assertGraphQLObjectType, assertGraphQLScalerType, assertGraphQLUnionType } from '../../testing/assertion'; @@ -252,20 +250,20 @@ describe('merge canonical types', () => { const enumType = gatewaySchema.getType('ProductEnum'); assertGraphQLEnumType(enumType) - expect(queryType.getFields().field1.description).toEqual('first'); - expect(queryType.getFields().field2.description).toEqual('second'); + expect(queryType.getFields()['field1'].description).toEqual('first'); + expect(queryType.getFields()['field2'].description).toEqual('second'); - expect(objectType.getFields().id.description).toEqual('first'); - expect(interfaceType.getFields().id.description).toEqual('first'); - expect(inputType.getFields().id.description).toEqual('first'); + expect(objectType.getFields()['id'].description).toEqual('first'); + expect(interfaceType.getFields()['id'].description).toEqual('first'); + expect(inputType.getFields()['id'].description).toEqual('first'); - expect(objectType.getFields().url.description).toEqual('second'); - expect(interfaceType.getFields().url.description).toEqual('second'); - expect(inputType.getFields().url.description).toEqual('second'); + expect(objectType.getFields()['url'].description).toEqual('second'); + expect(interfaceType.getFields()['url'].description).toEqual('second'); + expect(inputType.getFields()['url'].description).toEqual('second'); - expect(enumType.toConfig().values.YES.description).toEqual('first'); - expect(enumType.toConfig().values.NO.description).toEqual('first'); - expect(enumType.toConfig().values.MAYBE.description).toEqual('second'); + expect(enumType.toConfig().values['YES'].description).toEqual('first'); + expect(enumType.toConfig().values['NO'].description).toEqual('first'); + expect(enumType.toConfig().values['MAYBE'].description).toEqual('second'); }); it('merges prioritized ASTs', () => { @@ -284,35 +282,35 @@ describe('merge canonical types', () => { const scalarType = gatewaySchema.getType('ProductScalar'); assertGraphQLScalerType(scalarType) - expect(getDirectives(firstSchema, queryType.toConfig()).mydir.value).toEqual('first'); - expect(getDirectives(firstSchema, objectType.toConfig()).mydir.value).toEqual('first'); - expect(getDirectives(firstSchema, interfaceType.toConfig()).mydir.value).toEqual('first'); - expect(getDirectives(firstSchema, inputType.toConfig()).mydir.value).toEqual('first'); - expect(getDirectives(firstSchema, enumType.toConfig()).mydir.value).toEqual('first'); - expect(getDirectives(firstSchema, unionType.toConfig()).mydir.value).toEqual('first'); - expect(getDirectives(firstSchema, scalarType.toConfig()).mydir.value).toEqual('first'); - - expect(getDirectives(firstSchema, queryType.getFields().field1).mydir.value).toEqual('first'); - expect(getDirectives(firstSchema, queryType.getFields().field2).mydir.value).toEqual('second'); - expect(getDirectives(firstSchema, objectType.getFields().id).mydir.value).toEqual('first'); - expect(getDirectives(firstSchema, objectType.getFields().url).mydir.value).toEqual('second'); - expect(getDirectives(firstSchema, interfaceType.getFields().id).mydir.value).toEqual('first'); - expect(getDirectives(firstSchema, interfaceType.getFields().url).mydir.value).toEqual('second'); - expect(getDirectives(firstSchema, inputType.getFields().id).mydir.value).toEqual('first'); - expect(getDirectives(firstSchema, inputType.getFields().url).mydir.value).toEqual('second'); + expect(getDirectives(firstSchema, queryType.toConfig())['mydir'].value).toEqual('first'); + expect(getDirectives(firstSchema, objectType.toConfig())['mydir'].value).toEqual('first'); + expect(getDirectives(firstSchema, interfaceType.toConfig())['mydir'].value).toEqual('first'); + expect(getDirectives(firstSchema, inputType.toConfig())['mydir'].value).toEqual('first'); + expect(getDirectives(firstSchema, enumType.toConfig())['mydir'].value).toEqual('first'); + expect(getDirectives(firstSchema, unionType.toConfig())['mydir'].value).toEqual('first'); + expect(getDirectives(firstSchema, scalarType.toConfig())['mydir'].value).toEqual('first'); + + expect(getDirectives(firstSchema, queryType.getFields()['field1'])['mydir'].value).toEqual('first'); + expect(getDirectives(firstSchema, queryType.getFields()['field2'])['mydir'].value).toEqual('second'); + expect(getDirectives(firstSchema, objectType.getFields()['id'])['mydir'].value).toEqual('first'); + expect(getDirectives(firstSchema, objectType.getFields()['url'])['mydir'].value).toEqual('second'); + expect(getDirectives(firstSchema, interfaceType.getFields()['id'])['mydir'].value).toEqual('first'); + expect(getDirectives(firstSchema, interfaceType.getFields()['url'])['mydir'].value).toEqual('second'); + expect(getDirectives(firstSchema, inputType.getFields()['id'])['mydir'].value).toEqual('first'); + expect(getDirectives(firstSchema, inputType.getFields()['url'])['mydir'].value).toEqual('second'); expect(enumType.toConfig().astNode?.values?.map(v => v.description?.value)).toEqual(['first', 'first', 'second']); - expect(enumType.toConfig().values.YES.astNode?.description?.value).toEqual('first'); - expect(enumType.toConfig().values.NO.astNode?.description?.value).toEqual('first'); - expect(enumType.toConfig().values.MAYBE.astNode?.description?.value).toEqual('second'); + expect(enumType.toConfig().values['YES'].astNode?.description?.value).toEqual('first'); + expect(enumType.toConfig().values['NO'].astNode?.description?.value).toEqual('first'); + expect(enumType.toConfig().values['MAYBE'].astNode?.description?.value).toEqual('second'); }); it('merges prioritized deprecations', () => { const objectType = gatewaySchema.getType('Product') as GraphQLObjectType; - expect(objectType.getFields().id.deprecationReason).toEqual('first'); - expect(objectType.getFields().url.deprecationReason).toEqual('second'); - expect(getDirectives(firstSchema, objectType.getFields().id).deprecated.reason).toEqual('first'); - expect(getDirectives(firstSchema, objectType.getFields().url).deprecated.reason).toEqual('second'); + expect(objectType.getFields()['id'].deprecationReason).toEqual('first'); + expect(objectType.getFields()['url'].deprecationReason).toEqual('second'); + expect(getDirectives(firstSchema, objectType.getFields()['id'])['deprecated'].reason).toEqual('first'); + expect(getDirectives(firstSchema, objectType.getFields()['url'])['deprecated'].reason).toEqual('second'); }); it('promotes canonical root field definitions', async () => { @@ -398,9 +396,9 @@ describe('merge @canonical directives', () => { expect(objectType.description).toEqual('first'); expect(inputType.description).toEqual('first'); expect(enumType.description).toEqual('first'); - expect(queryType.getFields().product.description).toEqual('first'); - expect(objectType.getFields().id.description).toEqual('first'); - expect(objectType.getFields().name.description).toEqual('second'); - expect(inputType.getFields().value.description).toEqual('second'); + expect(queryType.getFields()['product'].description).toEqual('first'); + expect(objectType.getFields()['id'].description).toEqual('first'); + expect(objectType.getFields()['name'].description).toEqual('second'); + expect(inputType.getFields()['value'].description).toEqual('second'); }); }); diff --git a/packages/stitch/tests/mergeInterfaces.test.ts b/packages/stitch/tests/mergeInterfaces.test.ts index 04d7a9e1593..5555c525c51 100644 --- a/packages/stitch/tests/mergeInterfaces.test.ts +++ b/packages/stitch/tests/mergeInterfaces.test.ts @@ -79,7 +79,7 @@ describe('merged interfaces via concrete type', () => { `); assertSome(result.data) - expect(result.data.placement).toEqual({ id: '23', index: 23, name: 'Item 23' }); + expect(result.data['placement']).toEqual({ id: '23', index: 23, name: 'Item 23' }); }); test('works without selection set key', async () => { @@ -93,7 +93,7 @@ describe('merged interfaces via concrete type', () => { `); assertSome(result.data) - expect(result.data.placement).toEqual({ index: 23, name: 'Item 23' }); + expect(result.data['placement']).toEqual({ index: 23, name: 'Item 23' }); }); }); @@ -174,7 +174,7 @@ describe('merged interfaces via abstract type', () => { assertSome(result.data) - expect(result.data.placement).toEqual({ id: '23', index: 23, name: 'Item 23' }); + expect(result.data['placement']).toEqual({ id: '23', index: 23, name: 'Item 23' }); }); test('works without selection set key', async () => { @@ -187,6 +187,6 @@ describe('merged interfaces via abstract type', () => { } `); assertSome(result.data) - expect(result.data.placement).toEqual({ index: 23, name: 'Item 23' }); + expect(result.data['placement']).toEqual({ index: 23, name: 'Item 23' }); }); }); diff --git a/packages/stitch/tests/mergeMultipleEntryPoints.test.ts b/packages/stitch/tests/mergeMultipleEntryPoints.test.ts index 410d29928a0..b57e3e0dc98 100644 --- a/packages/stitch/tests/mergeMultipleEntryPoints.test.ts +++ b/packages/stitch/tests/mergeMultipleEntryPoints.test.ts @@ -126,7 +126,7 @@ describe('merge on multiple keys', () => { } `); assertSome(data) - expect(data.productsByKey).toEqual(result); + expect(data['productsByKey']).toEqual(result); }); test('works from upc -> join -> id', async () => { @@ -142,7 +142,7 @@ describe('merge on multiple keys', () => { } `); assertSome(data) - expect(data.productsByUpc).toEqual(result); + expect(data['productsByUpc']).toEqual(result); }); test('works from id -> join -> upc', async () => { @@ -158,6 +158,6 @@ describe('merge on multiple keys', () => { } `); assertSome(data) - expect(data.productsById).toEqual(result); + expect(data['productsById']).toEqual(result); }); }); diff --git a/packages/stitch/tests/selectionSets.test.ts b/packages/stitch/tests/selectionSets.test.ts index 665342bc219..efa300c5974 100644 --- a/packages/stitch/tests/selectionSets.test.ts +++ b/packages/stitch/tests/selectionSets.test.ts @@ -20,7 +20,7 @@ describe('delegateToSchema ', () => { properties: Record, name: string, ): Property | undefined { - for (const key of Object.keys(properties)) { + for (const key in properties) { const property = properties[key]; if (property.location.name === name) { return property; @@ -96,7 +96,7 @@ describe('delegateToSchema ', () => { bookingById: { property: { location: { - coordinates: sampleData.Property.p1.location.coordinates, + coordinates: sampleData.Property['p1'].location.coordinates, }, }, }, @@ -214,7 +214,7 @@ describe('delegateToSchema ', () => { ); assertSome(data) - expect(data.posts).toEqual(expectedData); + expect(data['posts']).toEqual(expectedData); }); it('should resolve with a fragment', async () => { @@ -239,7 +239,7 @@ describe('delegateToSchema ', () => { `, ); assertSome(data) - expect(data.posts).toEqual(expectedData); + expect(data['posts']).toEqual(expectedData); }); it('should resolve with deep fragment', async () => { @@ -264,7 +264,7 @@ describe('delegateToSchema ', () => { `, ); assertSome(data) - expect(data.posts).toEqual(expectedData); + expect(data['posts']).toEqual(expectedData); }); it('should resolve with nested fragments', async () => { @@ -293,7 +293,7 @@ describe('delegateToSchema ', () => { `, ) assertSome(data) - expect(data.posts).toEqual(expectedData); + expect(data['posts']).toEqual(expectedData); }); }); }); diff --git a/packages/stitch/tests/splitMergedTypeEntryPointsTransformer.test.ts b/packages/stitch/tests/splitMergedTypeEntryPointsTransformer.test.ts index bda4d300c5f..16f52ac6dba 100644 --- a/packages/stitch/tests/splitMergedTypeEntryPointsTransformer.test.ts +++ b/packages/stitch/tests/splitMergedTypeEntryPointsTransformer.test.ts @@ -20,7 +20,7 @@ describe('splitMergedTypeEntryPointsTransformer', () => { expect(results.length).toEqual(1); assertSome(results[0].merge) - expect(results[0].merge.Product).toEqual({ + expect(results[0].merge['Product']).toEqual({ selectionSet: '{ yep }', fieldName: 'yep', }); @@ -82,12 +82,12 @@ describe('splitMergedTypeEntryPointsTransformer', () => { expect(results.length).toEqual(2); assertSome(results[0].merge) - expect(results[0].merge.Product).toEqual({ + expect(results[0].merge['Product']).toEqual({ selectionSet: '{ id }', fieldName: 'productById', }); assertSome(results[1].merge) - expect(results[1].merge.Product).toEqual({ + expect(results[1].merge['Product']).toEqual({ selectionSet: '{ upc }', fieldName: 'productByUpc', }); diff --git a/packages/stitch/tests/stitchSchemas.test.ts b/packages/stitch/tests/stitchSchemas.test.ts index 2037cc14ecb..91fec6016e9 100644 --- a/packages/stitch/tests/stitchSchemas.test.ts +++ b/packages/stitch/tests/stitchSchemas.test.ts @@ -325,7 +325,7 @@ const schemaDirectiveTypeDefs = ` } `; -testCombinations.forEach((combination) => { +for (const combination of testCombinations) { describe('merging ' + combination.name, () => { let stitchedSchema: GraphQLSchema; let propertySchema: GraphQLSchema | SubschemaConfig; @@ -2223,8 +2223,8 @@ fragment BookingFragment on Booking { expect(originalResult.errors).toBeUndefined(); expect(originalResult.data).toBeDefined(); assertSome(originalResult.data) - expect(originalResult.data.persona.transactions.items.length).toBe(2); - expect(originalResult.data.persona.transactions.items[1].debt).toBeDefined(); + expect(originalResult.data['persona'].transactions.items.length).toBe(2); + expect(originalResult.data['persona'].transactions.items[1].debt).toBeDefined(); const mergedSchema = stitchSchemas({ subschemas: [remoteSchema], @@ -2323,8 +2323,8 @@ fragment BookingFragment on Booking { }, transformResult: (originalResult: ExecutionResult) => { assertSome(originalResult.data) - originalResult.data.persona = { - page: originalResult.data.persona.transactions.items, + originalResult.data['persona'] = { + page: originalResult.data['persona'].transactions.items, }; return originalResult; }, @@ -2366,8 +2366,8 @@ fragment BookingFragment on Booking { expect(result.errors).toBeUndefined(); assertSome(result.data) - expect(result.data.flattenedTransactions.page.length).toBe(2); - expect(result.data.flattenedTransactions.page[1].debt).toBeDefined(); + expect(result.data['flattenedTransactions'].page.length).toBe(2); + expect(result.data['flattenedTransactions'].page[1].debt).toBeDefined(); }); test('aliases', async () => { @@ -2679,13 +2679,13 @@ fragment BookingFragment on Booking { const stitchedResult = await graphql(stitchedSchema, propertyQuery, undefined, {}); - [propertyResult, stitchedResult].forEach((result) => { + for (const result of [propertyResult, stitchedResult]) { assertSome(result.errors) expect(result.errors.length > 0).toBe(true); const error = result.errors[0]; assertSome(error.extensions) - expect(error.extensions.code).toBe('SOME_CUSTOM_CODE'); - }); + expect(error.extensions['code']).toBe('SOME_CUSTOM_CODE'); + } }, ); }); @@ -2720,19 +2720,19 @@ fragment BookingFragment on Booking { test('should parse descriptions on new fields', () => { const Query = stitchedSchema.getQueryType(); assertSome(Query) - expect(Query.getFields().linkTest.description).toBe( + expect(Query.getFields()['linkTest'].description).toBe( 'A new field on the root query.', ); const Booking = stitchedSchema.getType('Booking') as GraphQLObjectType; - expect(Booking.getFields().property.description).toBe( + expect(Booking.getFields()['property'].description).toBe( 'The property of the booking.', ); const Property = stitchedSchema.getType( 'Property', ) as GraphQLObjectType; - const bookingsField = Property.getFields().bookings; + const bookingsField = Property.getFields()['bookings']; expect(bookingsField.description).toBe('A list of bookings.'); expect(bookingsField.args[0].description).toBe( 'The maximum number of bookings to retrieve.', @@ -3119,7 +3119,7 @@ fragment BookingFragment on Booking { const result = await graphql(schema, '{ book { cat: category } }'); assertSome(result.data) - expect(result.data.book.cat).toBe('Test'); + expect(result.data['book'].cat).toBe('Test'); }); }); @@ -3252,7 +3252,7 @@ assertSome(result.data) const result = await graphql(schema, '{ book { cat: category } }'); assertSome(result.data) - expect(result.data.book.cat).toBe('Test'); + expect(result.data['book'].cat).toBe('Test'); }); }); @@ -3510,4 +3510,4 @@ assertSome(result.data) }); }); }); -}); +} diff --git a/packages/stitch/tests/stitchingFromSubschemas.test.ts b/packages/stitch/tests/stitchingFromSubschemas.test.ts deleted file mode 100644 index a30a2e0afe9..00000000000 --- a/packages/stitch/tests/stitchingFromSubschemas.test.ts +++ /dev/null @@ -1,148 +0,0 @@ -// The below is meant to be an alternative canonical schema stitching example -// which intermingles local (mocked) resolvers and stitched schemas and does -// not require use of the fragment field, because it follows best practices of -// always returning the necessary object fields: -// https://medium.com/paypal-engineering/graphql-resolvers-best-practices-cd36fdbcef55 - -// This is achieved at the considerable cost of moving all of the delegation -// logic from the gateway to each subschema so that each subschema imports all -// the required types and performs all delegation. - -// The fragment field is still necessary when working with a remote schema -// where this is not possible. - -import { graphql, GraphQLSchema } from 'graphql'; - -import { delegateToSchema } from '@graphql-tools/delegate'; -import { addMocksToSchema } from '@graphql-tools/mock'; -import { assertSome } from '@graphql-tools/utils'; - -import { stitchSchemas } from '../src/stitchSchemas'; - -const chirpTypeDefs = ` - type Chirp { - id: ID! - text: String - authorId: ID! - author: User - } -`; - -const authorTypeDefs = ` - type User { - id: ID! - email: String - chirps: [Chirp] - } -`; - -const schemas: Record = {}; -const getSchema = (name: string) => schemas[name]; - -let chirpSchema = stitchSchemas({ - typeDefs: [ - chirpTypeDefs, - authorTypeDefs, - ` - type Query { - chirpById(id: ID!): Chirp - chirpsByAuthorId(authorId: ID!): [Chirp] - } - `, - ], - resolvers: { - Chirp: { - author: (chirp, _args, context, info) => - delegateToSchema({ - schema: getSchema('authorSchema'), - operation: 'query', - fieldName: 'userById', - args: { - id: chirp.authorId, - }, - context, - info, - }), - }, - }, -}); - -chirpSchema = addMocksToSchema({ - schema: chirpSchema, - mocks: { - Chirp: () => ({ - authorId: '1', - }), - }, - preserveResolvers: true, -}); - -let authorSchema = stitchSchemas({ - typeDefs: [ - chirpTypeDefs, - authorTypeDefs, - ` - type Query { - userById(id: ID!): User - } - `, - ], - resolvers: { - User: { - chirps: (user, _args, context, info) => - delegateToSchema({ - schema: getSchema('chirpSchema'), - operation: 'query', - fieldName: 'chirpsByAuthorId', - args: { - authorId: user.id, - }, - context, - info, - }), - }, - }, -}); - -authorSchema = addMocksToSchema({ - schema: authorSchema, - mocks: { - User: () => ({ - id: '1', - }), - }, - preserveResolvers: true, -}); - -schemas.chirpSchema = chirpSchema; -schemas.authorSchema = authorSchema; - -const stitchedSchema = stitchSchemas({ - subschemas:Object.keys(schemas).map((schemaName) => schemas[schemaName]), -}); - -describe('merging without specifying fragments', () => { - test.skip('works', async () => { - const query = ` - query { - userById(id: 5) { - chirps { - id - textAlias: text - author { - email - } - } - } - } - `; - - const result = await graphql(stitchedSchema, query); - - expect(result.errors).toBeUndefined(); - assertSome(result.data) - expect(result.data.userById.chirps[1].id).not.toBe(null); - expect(result.data.userById.chirps[1].text).not.toBe(null); - expect(result.data.userById.chirps[1].author.email).not.toBe(null); - }); -}); diff --git a/packages/stitch/tests/typeMerging.test.ts b/packages/stitch/tests/typeMerging.test.ts index 1b6db5ce120..284c1f8b0c0 100644 --- a/packages/stitch/tests/typeMerging.test.ts +++ b/packages/stitch/tests/typeMerging.test.ts @@ -110,10 +110,10 @@ describe('merging using type merging', () => { expect(result.errors).toBeUndefined(); assertSome(result.data) - expect(result.data.userById.__typename).toBe('User'); - expect(result.data.userById.chirps[1].id).not.toBe(null); - expect(result.data.userById.chirps[1].text).not.toBe(null); - expect(result.data.userById.chirps[1].author.email).not.toBe(null); + expect(result.data['userById'].__typename).toBe('User'); + expect(result.data['userById'].chirps[1].id).not.toBe(null); + expect(result.data['userById'].chirps[1].text).not.toBe(null); + expect(result.data['userById'].chirps[1].author.email).not.toBe(null); }); test("handle top level failures on subschema queries", async() => { @@ -426,7 +426,7 @@ describe('Merged associations', () => { } `); assertSome(data) - expect(data.posts).toEqual([{ + expect(data['posts']).toEqual([{ title: 'Post 55', network: { domain: 'network57.com' }, sections: ['News'] @@ -531,10 +531,10 @@ describe('merging using type merging when renaming', () => { expect(result.errors).toBeUndefined(); assertSome(result.data) - expect(result.data.User_userById.__typename).toBe('Gateway_User'); - expect(result.data.User_userById.chirps[1].id).not.toBe(null); - expect(result.data.User_userById.chirps[1].text).not.toBe(null); - expect(result.data.User_userById.chirps[1].author.email).not.toBe(null); + expect(result.data['User_userById'].__typename).toBe('Gateway_User'); + expect(result.data['User_userById'].chirps[1].id).not.toBe(null); + expect(result.data['User_userById'].chirps[1].text).not.toBe(null); + expect(result.data['User_userById'].chirps[1].author.email).not.toBe(null); }); }); @@ -622,7 +622,7 @@ describe('external object annotation with batchDelegateToSchema', () => { `, ) assertSome(data) - expect(data.posts).toEqual([ + expect(data['posts']).toEqual([ { network: { id: '57', domains: [{ id: '60', name: 'network57.com' }] }, }, diff --git a/packages/stitch/tests/typeMergingWithDirectives.test.ts b/packages/stitch/tests/typeMergingWithDirectives.test.ts index 389ef1d8246..de32835db5d 100644 --- a/packages/stitch/tests/typeMergingWithDirectives.test.ts +++ b/packages/stitch/tests/typeMergingWithDirectives.test.ts @@ -56,7 +56,7 @@ describe('merging using type merging with directives', () => { resolvers: { Query: { me: () => users[0], - _users: (_root, { keys }) => keys.map((key: Record) => users.find(u => u.id === key.id)), + _users: (_root, { keys }) => keys.map((key: Record) => users.find(u => u.id === key['id'])), }, }, schemaTransforms: [stitchingDirectivesValidator], @@ -122,7 +122,7 @@ describe('merging using type merging with directives', () => { Query: { mostStockedProduct: () => inventory.find(i => i.upc === '3'), _products: (_root, { keys }) => { - return keys.map((key: Record) => ({ ...key, ...inventory.find(i => i.upc === key.upc) })); + return keys.map((key: Record) => ({ ...key, ...inventory.find(i => i.upc === key['upc']) })); }, }, }, diff --git a/packages/stitch/tests/typeMergingWithExtensions.test.ts b/packages/stitch/tests/typeMergingWithExtensions.test.ts index e47b7247b3a..a84c33d130d 100644 --- a/packages/stitch/tests/typeMergingWithExtensions.test.ts +++ b/packages/stitch/tests/typeMergingWithExtensions.test.ts @@ -35,7 +35,7 @@ describe('merging using type merging', () => { accountsSchemaTypes._Key = new GraphQLScalarType({ name: '_Key', - }); + } as any); accountsSchemaTypes.Query = new GraphQLObjectType({ name: 'Query', fields: () => ({ @@ -50,7 +50,7 @@ describe('merging using type merging', () => { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(accountsSchemaTypes._Key))) }, }, - resolve: (_root, { keys }) => keys.map((key: Record) => users.find(u => u.id === key.id)), + resolve: (_root, { keys }) => keys.map((key: Record) => users.find(u => u.id === key['id'])), extensions: { directives: { merge: {}, @@ -138,7 +138,7 @@ describe('merging using type merging', () => { keys: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(inventorySchemaTypes.ProductKey))) } }, resolve: (_root, { keys }) => { - return keys.map((key: Record) => ({ ...key, ...inventory.find(i => i.upc === key.upc) })); + return keys.map((key: Record) => ({ ...key, ...inventory.find(i => i.upc === key['upc']) })); }, extensions: { directives: { @@ -204,7 +204,7 @@ describe('merging using type merging', () => { defaultValue: 2, }, }, - resolve: (_root, args) => products.slice(0, args.first), + resolve: (_root, args) => products.slice(0, args['first']), }, _productsByUpc: { type: new GraphQLList(productsSchemaTypes.Product), diff --git a/packages/stitch/tests/typeMergingWithInterfaces.test.ts b/packages/stitch/tests/typeMergingWithInterfaces.test.ts index d575cd20dc5..5b0fa9511a2 100644 --- a/packages/stitch/tests/typeMergingWithInterfaces.test.ts +++ b/packages/stitch/tests/typeMergingWithInterfaces.test.ts @@ -58,7 +58,7 @@ describe('merging using type merging', () => { Query: { me: () => users[0], _entities: (_root, { keys }) => { - return keys.map((key: Record) => ({ ...key, ...users.find(u => u.id === key.id) })); + return keys.map((key: Record) => ({ ...key, ...users.find(u => u.id === key['id']) })); }, }, }, @@ -122,7 +122,7 @@ describe('merging using type merging', () => { Query: { mostStockedProduct: () => inventory.find(i => i.upc === '3'), _entities: (_root, { keys }) => { - return keys.map((key: Record) => ({ ...key, ...inventory.find(i => i.upc === key.upc) })); + return keys.map((key: Record) => ({ ...key, ...inventory.find(i => i.upc === key['upc']) })); }, }, }, @@ -187,7 +187,7 @@ describe('merging using type merging', () => { Query: { topProducts: (_root, args) => products.slice(0, args.first), _entities: (_root, { keys }) => { - return keys.map((key: Record) => ({ ...key, ...products.find(product => product.upc === key.upc) })); + return keys.map((key: Record) => ({ ...key, ...products.find(product => product.upc === key['upc']) })); } } }, @@ -281,8 +281,8 @@ describe('merging using type merging', () => { Query: { _entities: (_root, { keys }) => { return keys.map((key: Record) => { - if (key.__typename === 'Review') { - return ({ ...key, ...reviews.find(review => review.id === key.id) }); + if (key['__typename'] === 'Review') { + return ({ ...key, ...reviews.find(review => review.id === key['id']) }); } return { ...key }; diff --git a/packages/stitching-directives/src/getSourcePaths.ts b/packages/stitching-directives/src/getSourcePaths.ts index b361130d959..c5dbf7b6f9d 100644 --- a/packages/stitching-directives/src/getSourcePaths.ts +++ b/packages/stitching-directives/src/getSourcePaths.ts @@ -9,22 +9,24 @@ export function getSourcePaths( selectionSet?: SelectionSetNode ): Array> { const sourcePaths: Array> = []; - mappingInstructions.forEach(mappingInstruction => { + for (const mappingInstruction of mappingInstructions) { const { sourcePath } = mappingInstruction; if (sourcePath.length) { sourcePaths.push(sourcePath); - return; + continue; } if (selectionSet == null) { - return; + continue; } const paths = pathsFromSelectionSet(selectionSet); - paths.forEach(path => sourcePaths.push(path)); + for (const path of paths) { + sourcePaths.push(path); + } sourcePaths.push([TypeNameMetaFieldDef.name]); - }); + } return sourcePaths; } diff --git a/packages/stitching-directives/src/parseMergeArgsExpr.ts b/packages/stitching-directives/src/parseMergeArgsExpr.ts index c3cfd73c3ab..ae1fcb23f27 100644 --- a/packages/stitching-directives/src/parseMergeArgsExpr.ts +++ b/packages/stitching-directives/src/parseMergeArgsExpr.ts @@ -9,10 +9,7 @@ import { getSourcePaths } from './getSourcePaths'; type VariablePaths = Record>; -export function parseMergeArgsExpr( - mergeArgsExpr: string, - selectionSet?: SelectionSetNode, -): ParsedMergeArgsExpr { +export function parseMergeArgsExpr(mergeArgsExpr: string, selectionSet?: SelectionSetNode): ParsedMergeArgsExpr { const { mergeArgsExpr: newMergeArgsExpr, expansionExpressions } = preparseMergeArgsExpr(mergeArgsExpr); const inputValue = parseValue(`{ ${newMergeArgsExpr} }`, { noLocation: true }); @@ -32,15 +29,15 @@ export function parseMergeArgsExpr( } const expansionRegEx = new RegExp(`^${EXPANSION_PREFIX}[0-9]+$`); - Object.keys(variablePaths).forEach(variableName => { + for (const variableName in variablePaths) { if (!variableName.match(expansionRegEx)) { throw new Error('Expansions cannot be mixed with single key declarations.'); } - }); + } const expansions: Array = []; const sourcePaths: Array> = []; - Object.keys(expansionExpressions).forEach(variableName => { + for (const variableName in expansionExpressions) { const str = expansionExpressions[variableName]; const valuePath = variablePaths[variableName]; const { inputValue: expansionInputValue, variablePaths: expansionVariablePaths } = extractVariables( @@ -57,12 +54,13 @@ export function parseMergeArgsExpr( sourcePaths.push(...getSourcePaths(mappingInstructions, selectionSet)); + assertNotWithinList(valuePath); expansions.push({ - valuePath: assertNotWithinList(valuePath), + valuePath, value, mappingInstructions, }); - }); + } const usedProperties = propertyTreeFromPaths(sourcePaths); @@ -71,23 +69,24 @@ export function parseMergeArgsExpr( function getMappingInstructions(variablePaths: VariablePaths): Array { const mappingInstructions: Array = []; - Object.entries(variablePaths).forEach(([keyPath, valuePath]) => { + for (const keyPath in variablePaths) { + const valuePath = variablePaths[keyPath]; const splitKeyPath = keyPath.split(KEY_DELIMITER).slice(1); + assertNotWithinList(valuePath); mappingInstructions.push({ - destinationPath: assertNotWithinList(valuePath), + destinationPath: valuePath, sourcePath: splitKeyPath, }); - }); + } return mappingInstructions; } -function assertNotWithinList(path: Array): Array { - path.forEach(pathSegment => { +function assertNotWithinList(path: Array): asserts path is string[] { + for (const pathSegment of path) { if (typeof pathSegment === 'number') { throw new Error('Insertions cannot be made into a list.'); } - }); - return path as Array; + } } diff --git a/packages/stitching-directives/src/pathsFromSelectionSet.ts b/packages/stitching-directives/src/pathsFromSelectionSet.ts index bca1586562f..9d452734e49 100644 --- a/packages/stitching-directives/src/pathsFromSelectionSet.ts +++ b/packages/stitching-directives/src/pathsFromSelectionSet.ts @@ -1,11 +1,13 @@ import { Kind, SelectionNode, SelectionSetNode } from 'graphql'; export function pathsFromSelectionSet(selectionSet: SelectionSetNode, path: Array = []): Array> { - let paths: Array> = []; - selectionSet.selections.forEach(selection => { - const addition = pathsFromSelection(selection, path) ?? []; - paths = [...paths, ...addition]; - }); + const paths: Array> = []; + for (const selection of selectionSet.selections) { + const additions = pathsFromSelection(selection, path) ?? []; + for (const addition of additions) { + paths.push(addition); + } + } return paths; } diff --git a/packages/stitching-directives/src/properties.ts b/packages/stitching-directives/src/properties.ts index b0166b3e6e0..deca3896bef 100644 --- a/packages/stitching-directives/src/properties.ts +++ b/packages/stitching-directives/src/properties.ts @@ -44,25 +44,27 @@ export function getProperties(object: Record, propertyTree: Propert const newObject = Object.create(null); - Object.entries(propertyTree).forEach(([key, subKey]) => { + for (const key in propertyTree) { + const subKey = propertyTree[key]; + if (subKey == null) { newObject[key] = object[key]; - return; + continue; } const prop = object[key]; newObject[key] = deepMap(prop, item => getProperties(item, subKey)); - }); + } return newObject; } export function propertyTreeFromPaths(paths: Array>): PropertyTree { const propertyTree = Object.create(null); - paths.forEach(path => { + for (const path of paths) { addProperty(propertyTree, path, null); - }); + } return propertyTree; } diff --git a/packages/stitching-directives/src/stitchingDirectivesTransformer.ts b/packages/stitching-directives/src/stitchingDirectivesTransformer.ts index 78cb22311c4..27691311b2b 100644 --- a/packages/stitching-directives/src/stitchingDirectivesTransformer.ts +++ b/packages/stitching-directives/src/stitchingDirectivesTransformer.ts @@ -194,7 +194,8 @@ export function stitchingDirectivesTransformer( }); if (subschemaConfig.merge) { - Object.entries(subschemaConfig.merge).forEach(([typeName, mergedTypeConfig]) => { + for (const typeName in subschemaConfig.merge) { + const mergedTypeConfig = subschemaConfig.merge[typeName]; if (mergedTypeConfig.selectionSet) { const selectionSet = parseSelectionSet(mergedTypeConfig.selectionSet, { noLocation: true }); if (selectionSet) { @@ -206,8 +207,9 @@ export function stitchingDirectivesTransformer( } } if (mergedTypeConfig.fields) { - Object.entries(mergedTypeConfig.fields).forEach(([fieldName, fieldConfig]) => { - if (!fieldConfig.selectionSet) return; + for (const fieldName in mergedTypeConfig.fields) { + const fieldConfig = mergedTypeConfig.fields[fieldName]; + if (!fieldConfig.selectionSet) continue; const selectionSet = parseSelectionSet(fieldConfig.selectionSet, { noLocation: true }); if (selectionSet) { @@ -223,30 +225,27 @@ export function stitchingDirectivesTransformer( computedFieldSelectionSets[typeName][fieldName] = selectionSet; } } - }); + } } - }); + } } const allSelectionSetsByType: Record> = Object.create(null); - Object.entries(selectionSetsByType).forEach(([typeName, selectionSet]) => { - if (allSelectionSetsByType[typeName] == null) { - allSelectionSetsByType[typeName] = [selectionSet]; - } else { + for (const typeName in selectionSetsByType) { + allSelectionSetsByType[typeName] = allSelectionSetsByType[typeName] || []; + const selectionSet = selectionSetsByType[typeName]; + allSelectionSetsByType[typeName].push(selectionSet); + } + + for (const typeName in computedFieldSelectionSets) { + const selectionSets = computedFieldSelectionSets[typeName]; + for (const i in selectionSets) { + allSelectionSetsByType[typeName] = allSelectionSetsByType[typeName] || []; + const selectionSet = selectionSets[i]; allSelectionSetsByType[typeName].push(selectionSet); } - }); - - Object.entries(computedFieldSelectionSets).forEach(([typeName, selectionSets]) => { - Object.values(selectionSets).forEach(selectionSet => { - if (allSelectionSetsByType[typeName] == null) { - allSelectionSetsByType[typeName] = [selectionSet]; - } else { - allSelectionSetsByType[typeName].push(selectionSet); - } - }); - }); + } mapSchema(schema, { [MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName) => { @@ -272,9 +271,9 @@ export function stitchingDirectivesTransformer( const lastArgName = argNames.pop(); mergeArgsExpr = returnsList ? `${lastArgName}: [[${keyExpr}]]` : `${lastArgName}: ${keyExpr}`; - argNames.reverse().forEach(argName => { + for (const argName of argNames.reverse()) { mergeArgsExpr = `${argName}: { ${mergeArgsExpr} }`; - }); + } } const typeNames: Array = directiveArgumentMap.types; @@ -307,9 +306,10 @@ export function stitchingDirectivesTransformer( }, }); - Object.entries(selectionSetsByType).forEach(([typeName, selectionSet]) => { - const mergeConfig: Record> = - newSubschemaConfig.merge ?? Object.create(null); + for (const typeName in selectionSetsByType) { + const selectionSet = selectionSetsByType[typeName]; + const mergeConfig: Record> = newSubschemaConfig.merge ?? + Object.create(null); newSubschemaConfig.merge = mergeConfig; if (mergeConfig[typeName] == null) { @@ -319,11 +319,12 @@ export function stitchingDirectivesTransformer( const mergeTypeConfig = mergeConfig[typeName]; mergeTypeConfig.selectionSet = print(selectionSet); - }); + } - Object.entries(computedFieldSelectionSets).forEach(([typeName, selectionSets]) => { - const mergeConfig: Record> = - newSubschemaConfig.merge ?? Object.create(null); + for (const typeName in computedFieldSelectionSets) { + const selectionSets = computedFieldSelectionSets[typeName]; + const mergeConfig: Record> = newSubschemaConfig.merge ?? + Object.create(null); newSubschemaConfig.merge = mergeConfig; if (mergeConfig[typeName] == null) { @@ -334,18 +335,21 @@ export function stitchingDirectivesTransformer( const mergeTypeConfigFields: Record = mergeTypeConfig.fields ?? Object.create(null); mergeTypeConfig.fields = mergeTypeConfigFields; - Object.entries(selectionSets).forEach(([fieldName, selectionSet]) => { + for (const fieldName in selectionSets) { + const selectionSet = selectionSets[fieldName]; const fieldConfig: MergedFieldConfig = mergeTypeConfigFields[fieldName] ?? Object.create(null); mergeTypeConfigFields[fieldName] = fieldConfig; fieldConfig.selectionSet = print(selectionSet); fieldConfig.computed = true; - }); - }); + } + } - Object.entries(mergedTypesResolversInfo).forEach(([typeName, mergedTypeResolverInfo]) => { - const mergeConfig: Record> = - newSubschemaConfig.merge ?? Object.create(null); + for (const typeName in mergedTypesResolversInfo) { + const mergedTypeResolverInfo = mergedTypesResolversInfo[typeName]; + + const mergeConfig: Record> = newSubschemaConfig.merge ?? + Object.create(null); newSubschemaConfig.merge = mergeConfig; if (newSubschemaConfig.merge[typeName] == null) { @@ -362,11 +366,12 @@ export function stitchingDirectivesTransformer( } else { mergeTypeConfig.args = generateArgsFn(mergedTypeResolverInfo); } - }); + } - Object.entries(canonicalTypesInfo).forEach(([typeName, canonicalTypeInfo]) => { - const mergeConfig: Record> = - newSubschemaConfig.merge ?? Object.create(null); + for (const typeName in canonicalTypesInfo) { + const canonicalTypeInfo = canonicalTypesInfo[typeName]; + const mergeConfig: Record> = newSubschemaConfig.merge ?? + Object.create(null); newSubschemaConfig.merge = mergeConfig; if (newSubschemaConfig.merge[typeName] == null) { @@ -382,14 +387,14 @@ export function stitchingDirectivesTransformer( if (canonicalTypeInfo.fields) { const mergeTypeConfigFields: Record = mergeTypeConfig.fields ?? Object.create(null); mergeTypeConfig.fields = mergeTypeConfigFields; - Object.keys(canonicalTypeInfo.fields).forEach(fieldName => { + for (const fieldName in canonicalTypeInfo.fields) { if (mergeTypeConfigFields[fieldName] == null) { mergeTypeConfigFields[fieldName] = Object.create(null); } mergeTypeConfigFields[fieldName].canonical = true; - }); + } } - }); + } return newSubschemaConfig; }; @@ -402,17 +407,17 @@ function forEachConcreteType( fn: (typeName: string) => void ) { if (isInterfaceType(type)) { - getImplementingTypes(type.name, schema).forEach(typeName => { + for (const typeName of getImplementingTypes(type.name, schema)) { if (typeNames == null || typeNames.includes(typeName)) { fn(typeName); } - }); + } } else if (isUnionType(type)) { - type.getTypes().forEach(({ name: typeName }) => { + for (const { name: typeName } of type.getTypes()) { if (typeNames == null || typeNames.includes(typeName)) { fn(typeName); } - }); + } } else if (isObjectType(type)) { fn(type.name); } @@ -428,23 +433,24 @@ function generateArgsFromKeysFn( const { expansions, args } = mergedTypeResolverInfo; return (keys: ReadonlyArray): Record => { const newArgs = mergeDeep({}, args); - expansions?.forEach(expansion => { - const mappingInstructions = expansion.mappingInstructions; - const expanded: Array = []; - keys.forEach(key => { - let newValue = mergeDeep({}, expansion.valuePath); - mappingInstructions.forEach(mappingInstruction => { - const { destinationPath, sourcePath } = mappingInstruction; - if (destinationPath.length) { - addProperty(newValue, destinationPath, getProperty(key, sourcePath)); - } else { - newValue = getProperty(key, sourcePath); + if (expansions) { + for (const expansion of expansions) { + const mappingInstructions = expansion.mappingInstructions; + const expanded: Array = []; + for (const key of keys) { + let newValue = mergeDeep({}, expansion.valuePath); + for (const { destinationPath, sourcePath } of mappingInstructions) { + if (destinationPath.length) { + addProperty(newValue, destinationPath, getProperty(key, sourcePath)); + } else { + newValue = getProperty(key, sourcePath); + } } - }); - expanded.push(newValue); - }); - addProperty(newArgs, expansion.valuePath, expanded); - }); + expanded.push(newValue); + } + addProperty(newArgs, expansion.valuePath, expanded); + } + } return newArgs; }; } @@ -455,17 +461,19 @@ function generateArgsFn(mergedTypeResolverInfo: MergedTypeResolverInfo): (origin return (originalResult: any): Record => { const newArgs = mergeDeep({}, args); const filteredResult = getProperties(originalResult, usedProperties); - mappingInstructions?.forEach(mappingInstruction => { - const { destinationPath, sourcePath } = mappingInstruction; - addProperty(newArgs, destinationPath, getProperty(filteredResult, sourcePath)); - }); + if (mappingInstructions) { + for (const mappingInstruction of mappingInstructions) { + const { destinationPath, sourcePath } = mappingInstruction; + addProperty(newArgs, destinationPath, getProperty(filteredResult, sourcePath)); + } + } return newArgs; }; } function buildKeyExpr(key: Array): string { let mergedObject = {}; - key.forEach(keyDef => { + for (const keyDef of key) { let [aliasOrKeyPath, keyPath] = keyDef.split(':'); let aliasPath: string; if (keyPath == null) { @@ -478,11 +486,11 @@ function buildKeyExpr(key: Array): string { assertSome(lastAliasPart); let object: Record = { [lastAliasPart]: `$key.${keyPath}` }; - aliasParts.reverse().forEach(aliasPart => { + for (const aliasPart of aliasParts.reverse()) { object = { [aliasPart]: object }; - }); + } mergedObject = mergeDeep(mergedObject, object); - }); + } return JSON.stringify(mergedObject).replace(/"/g, ''); } @@ -490,12 +498,12 @@ function buildKeyExpr(key: Array): string { function mergeSelectionSets(...selectionSets: Array): SelectionSetNode { const normalizedSelections: Record = Object.create(null); - selectionSets.forEach(selectionSet => { - selectionSet.selections.forEach(selection => { + for (const selectionSet of selectionSets) { + for (const selection of selectionSet.selections) { const normalizedSelection = print(selection); normalizedSelections[normalizedSelection] = selection; - }); - }); + } + } const newSelectionSet = { kind: Kind.SELECTION_SET, @@ -512,17 +520,17 @@ function forEachConcreteTypeName( fn: (typeName: string) => void ): void { if (isInterfaceType(returnType)) { - getImplementingTypes(returnType.name, schema).forEach(typeName => { + for (const typeName of getImplementingTypes(returnType.name, schema)) { if (typeNames == null || typeNames.includes(typeName)) { fn(typeName); } - }); + } } else if (isUnionType(returnType)) { - returnType.getTypes().forEach(type => { + for (const type of returnType.getTypes()) { if (typeNames == null || typeNames.includes(type.name)) { fn(type.name); } - }); + } } else if (isObjectType(returnType) && (typeNames == null || typeNames.includes(returnType.name))) { fn(returnType.name); } diff --git a/packages/stitching-directives/src/stitchingDirectivesValidator.ts b/packages/stitching-directives/src/stitchingDirectivesValidator.ts index bda6241d88b..92ab3ddd9b5 100644 --- a/packages/stitching-directives/src/stitchingDirectivesValidator.ts +++ b/packages/stitching-directives/src/stitchingDirectivesValidator.ts @@ -108,7 +108,7 @@ export function stitchingDirectivesValidator( throw new Error('Cannot use @merge directive with both `keyField` and `key` arguments.'); } - key.forEach(keyDef => { + for (const keyDef of key) { let [aliasOrKeyPath, keyPath] = keyDef.split(':'); let aliasPath: string; if (keyPath == null) { @@ -129,7 +129,7 @@ export function stitchingDirectivesValidator( ); // TODO: ideally we should check that the arg exists within the resolver } - }); + } } const additionalArgs = directiveArgumentMap.additionalArgs; @@ -156,13 +156,13 @@ export function stitchingDirectivesValidator( ? getImplementingTypes(returnType.name, schema).map(typeName => schema.getType(typeName)) : returnType.getTypes(); const implementingTypeNames = implementingTypes.filter(isSome).map(type => type.name); - typeNames.forEach(typeName => { + for (const typeName of typeNames) { if (!implementingTypeNames.includes(typeName)) { throw new Error( `Types argument can only include only type names that implement the field return type's abstract type.` ); } - }); + } } } diff --git a/packages/stitching-directives/tests/stitchingDirectivesTransformer.test.ts b/packages/stitching-directives/tests/stitchingDirectivesTransformer.test.ts index c15e5de5dfb..29bf970afbd 100644 --- a/packages/stitching-directives/tests/stitchingDirectivesTransformer.test.ts +++ b/packages/stitching-directives/tests/stitchingDirectivesTransformer.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ import { print } from 'graphql'; import { makeExecutableSchema } from '@graphql-tools/schema'; @@ -31,8 +32,8 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - expect(transformedSubschemaConfig.merge.User.selectionSet).toEqual(print(parseSelectionSet('{ id }'))); - expect(transformedSubschemaConfig.merge.User.fieldName).toEqual('_user'); + expect(transformedSubschemaConfig.merge?.['User'].selectionSet).toEqual(print(parseSelectionSet('{ id }'))); + expect(transformedSubschemaConfig.merge?.['User'].fieldName).toEqual('_user'); }); test('adds type selection sets when returns union', () => { @@ -60,8 +61,8 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - expect(transformedSubschemaConfig.merge.User.selectionSet).toEqual(print(parseSelectionSet('{ id }'))); - expect(transformedSubschemaConfig.merge.User.fieldName).toEqual('_entity'); + expect(transformedSubschemaConfig.merge?.['User'].selectionSet).toEqual(print(parseSelectionSet('{ id }'))); + expect(transformedSubschemaConfig.merge?.['User'].fieldName).toEqual('_entity'); }); test('adds type selection sets when returns interface', () => { @@ -91,8 +92,8 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - expect(transformedSubschemaConfig.merge.User.selectionSet).toEqual(print(parseSelectionSet('{ id }'))); - expect(transformedSubschemaConfig.merge.User.fieldName).toEqual('_entity'); + expect(transformedSubschemaConfig.merge?.['User'].selectionSet).toEqual(print(parseSelectionSet('{ id }'))); + expect(transformedSubschemaConfig.merge?.['User'].fieldName).toEqual('_entity'); }); test('adds type selection sets when returns list', () => { @@ -117,7 +118,7 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - const argsFn = transformedSubschemaConfig.merge.User.args; + const argsFn = transformedSubschemaConfig.merge?.['User'].args!; const originalResult = { relations: [ @@ -168,7 +169,7 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - const argsFn = transformedSubschemaConfig.merge.User.args; + const argsFn = transformedSubschemaConfig.merge?.['User'].args!; const originalResult = { relationSets: [ @@ -233,7 +234,7 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - const argsFn = transformedSubschemaConfig.merge.User.args; + const argsFn = transformedSubschemaConfig.merge?.['User'].args!; const originalResult: { nestedField: null } = { nestedField: null @@ -271,9 +272,9 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - expect(transformedSubschemaConfig.merge.User.fields.name.selectionSet).toEqual(print(parseSelectionSet('{ id }'))); - expect(transformedSubschemaConfig.merge.User.fields.name.computed).toEqual(true); - expect(transformedSubschemaConfig.merge.User.fieldName).toEqual('_user'); + expect(transformedSubschemaConfig.merge?.['User']?.fields?.['name']?.selectionSet).toEqual(print(parseSelectionSet('{ id }'))); + expect(transformedSubschemaConfig.merge?.['User']?.fields?.['name']?.computed).toEqual(true); + expect(transformedSubschemaConfig.merge?.['User'].fieldName).toEqual('_user'); }); test('adds args function when used without arguments', () => { @@ -299,7 +300,7 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - const argsFn = transformedSubschemaConfig.merge.User.args; + const argsFn = transformedSubschemaConfig.merge?.['User'].args!; const originalResult = { id: '5', @@ -338,7 +339,7 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - const argsFn = transformedSubschemaConfig.merge.User.args; + const argsFn = transformedSubschemaConfig.merge?.['User'].args!; const originalResult = { id: '5', @@ -377,7 +378,7 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - const argsFn = transformedSubschemaConfig.merge.User.args; + const argsFn = transformedSubschemaConfig.merge?.['User'].args!; const originalResult = { id: '5', @@ -416,7 +417,7 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - const argsFn = transformedSubschemaConfig.merge.User.args; + const argsFn = transformedSubschemaConfig.merge?.['User'].args!; const originalResult = { id: '5', @@ -459,7 +460,7 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - const argsFn = transformedSubschemaConfig.merge.User.args; + const argsFn = transformedSubschemaConfig.merge?.['User'].args!; const originalResult = { id: '5', @@ -501,7 +502,7 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - const argsFn = transformedSubschemaConfig.merge.User.args; + const argsFn = transformedSubschemaConfig.merge?.['User'].args!; const originalResult = { id: '5', @@ -539,9 +540,9 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - expect(transformedSubschemaConfig.merge.User.selectionSet).toEqual(`{\n id\n}`); + expect(transformedSubschemaConfig.merge?.['User'].selectionSet).toEqual(`{\n id\n}`); - const argsFn = transformedSubschemaConfig.merge.User.args; + const argsFn = transformedSubschemaConfig.merge?.['User'].args!; const originalResult = { id: '5', @@ -578,7 +579,7 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - const argsFn = transformedSubschemaConfig.merge.User.args; + const argsFn = transformedSubschemaConfig.merge?.['User'].args!; const originalResult = { id: '5', @@ -624,8 +625,8 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - const keyFn = transformedSubschemaConfig.merge.User.key; - const argsFromKeysFn = transformedSubschemaConfig.merge.User.argsFromKeys; + const keyFn = transformedSubschemaConfig.merge?.['User'].key!; + const argsFromKeysFn = transformedSubschemaConfig.merge?.['User'].argsFromKeys!; const originalResult = { id: '5', @@ -670,8 +671,8 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - const keyFn = transformedSubschemaConfig.merge.User.key; - const argsFromKeysFn = transformedSubschemaConfig.merge.User.argsFromKeys; + const keyFn = transformedSubschemaConfig.merge?.['User'].key!; + const argsFromKeysFn = transformedSubschemaConfig.merge?.['User'].argsFromKeys!; const originalResult = { __typename: 'User', @@ -721,8 +722,8 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - const keyFn = transformedSubschemaConfig.merge.User.key; - const argsFromKeysFn = transformedSubschemaConfig.merge.User.argsFromKeys; + const keyFn = transformedSubschemaConfig.merge?.['User'].key!; + const argsFromKeysFn = transformedSubschemaConfig.merge?.['User'].argsFromKeys!; const originalResult = { __typename: 'User', @@ -768,8 +769,8 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - const keyFn = transformedSubschemaConfig.merge.User.key; - const argsFromKeysFn = transformedSubschemaConfig.merge.User.argsFromKeys; + const keyFn = transformedSubschemaConfig.merge?.['User'].key!; + const argsFromKeysFn = transformedSubschemaConfig.merge?.['User'].argsFromKeys!; const originalResult = { id: '5', @@ -812,8 +813,8 @@ describe('type merging directives', () => { const transformedSubschemaConfig = stitchingDirectivesTransformer(subschemaConfig); - const keyFn = transformedSubschemaConfig.merge.User.key; - const argsFromKeysFn = transformedSubschemaConfig.merge.User.argsFromKeys; + const keyFn = transformedSubschemaConfig.merge?.['User'].key!; + const argsFromKeysFn = transformedSubschemaConfig.merge?.['User'].argsFromKeys!; const originalResult = { id: '5', diff --git a/packages/utils/src/addTypes.ts b/packages/utils/src/addTypes.ts index 849b5f52681..ae4859c3c2d 100644 --- a/packages/utils/src/addTypes.ts +++ b/packages/utils/src/addTypes.ts @@ -33,6 +33,7 @@ import { GraphQLDirective, isNamedType, isDirective, + isObjectType, } from 'graphql'; import { rewireTypes } from './rewire'; @@ -50,35 +51,44 @@ export function addTypes( const config = schema.toConfig(); - const originalTypeMap = {}; - config.types.forEach(type => { + const originalTypeMap: Record = {}; + for (const type of config.types) { originalTypeMap[type.name] = type; - }); + } - const originalDirectiveMap = {}; - config.directives.forEach(directive => { + const originalDirectiveMap: Record = {}; + for (const directive of config.directives) { originalDirectiveMap[directive.name] = directive; - }); + } - newTypesOrDirectives.forEach(newTypeOrDirective => { + for (const newTypeOrDirective of newTypesOrDirectives) { if (isNamedType(newTypeOrDirective)) { originalTypeMap[newTypeOrDirective.name] = newTypeOrDirective; } else if (isDirective(newTypeOrDirective)) { originalDirectiveMap[newTypeOrDirective.name] = newTypeOrDirective; } - }); + } - const { typeMap, directives } = rewireTypes( - originalTypeMap, - Object.keys(originalDirectiveMap).map(directiveName => originalDirectiveMap[directiveName]) - ); + const { typeMap, directives } = rewireTypes(originalTypeMap, Object.values(originalDirectiveMap)); return new GraphQLSchema({ ...config, - query: queryTypeName ? (typeMap[queryTypeName] as GraphQLObjectType) : undefined, - mutation: mutationTypeName ? (typeMap[mutationTypeName] as GraphQLObjectType) : undefined, - subscription: subscriptionTypeName != null ? (typeMap[subscriptionTypeName] as GraphQLObjectType) : undefined, - types: Object.keys(typeMap).map(typeName => typeMap[typeName]), + query: getObjectTypeFromTypeMap(typeMap, queryTypeName), + mutation: getObjectTypeFromTypeMap(typeMap, mutationTypeName), + subscription: getObjectTypeFromTypeMap(typeMap, subscriptionTypeName), + types: Object.values(typeMap), directives, }); } + +export function getObjectTypeFromTypeMap( + typeMap: Record, + typeName?: string +): GraphQLObjectType | undefined { + if (typeName) { + const maybeObjectType = typeMap[typeName]; + if (isObjectType(maybeObjectType)) { + return maybeObjectType; + } + } +} diff --git a/packages/utils/src/astFromValueUntyped.ts b/packages/utils/src/astFromValueUntyped.ts index 2719adb05ab..a7ed8213702 100644 --- a/packages/utils/src/astFromValueUntyped.ts +++ b/packages/utils/src/astFromValueUntyped.ts @@ -30,18 +30,19 @@ export function astFromValueUntyped(value: any): ValueNode | null { // the value is not an array, convert the value using the list's item type. if (Array.isArray(value)) { const valuesNodes: Array = []; - value.forEach(item => { + for (const item of value) { const itemNode = astFromValueUntyped(item); if (itemNode != null) { valuesNodes.push(itemNode); } - }); + } return { kind: Kind.LIST, values: valuesNodes }; } if (typeof value === 'object') { const fieldNodes: Array = []; - Object.entries(value).forEach(([fieldName, fieldValue]) => { + for (const fieldName in value) { + const fieldValue = value[fieldName]; const ast = astFromValueUntyped(fieldValue); if (ast) { fieldNodes.push({ @@ -50,7 +51,7 @@ export function astFromValueUntyped(value: any): ValueNode | null { value: ast, }); } - }); + } return { kind: Kind.OBJECT, fields: fieldNodes }; } diff --git a/packages/utils/src/build-operation-for-field.ts b/packages/utils/src/build-operation-for-field.ts index 745c2bfabc6..87652876f51 100644 --- a/packages/utils/src/build-operation-for-field.ts +++ b/packages/utils/src/build-operation-for-field.ts @@ -129,12 +129,12 @@ function buildOperationAndCollectVariables({ const operationName = `${fieldName}_${kind}`; if (field.args) { - field.args.forEach(arg => { + for (const arg of field.args) { const argName = arg.name; if (!argNames || argNames.includes(argName)) { addOperationVariable(resolveVariable(arg, argName)); } - }); + } } return { diff --git a/packages/utils/src/fields.ts b/packages/utils/src/fields.ts index 15a16eaca10..e33180753e6 100644 --- a/packages/utils/src/fields.ts +++ b/packages/utils/src/fields.ts @@ -24,12 +24,13 @@ export function appendObjectFields( const originalFieldConfigMap = config.fields; const newFieldConfigMap = {}; - Object.keys(originalFieldConfigMap).forEach(fieldName => { + + for (const fieldName in originalFieldConfigMap) { newFieldConfigMap[fieldName] = originalFieldConfigMap[fieldName]; - }); - Object.keys(additionalFields).forEach(fieldName => { + } + for (const fieldName in additionalFields) { newFieldConfigMap[fieldName] = additionalFields[fieldName]; - }); + } return correctASTNodes( new GraphQLObjectType({ @@ -55,14 +56,14 @@ export function removeObjectFields( const originalFieldConfigMap = config.fields; const newFieldConfigMap = {}; - Object.keys(originalFieldConfigMap).forEach(fieldName => { + for (const fieldName in originalFieldConfigMap) { const originalFieldConfig = originalFieldConfigMap[fieldName]; if (testFn(fieldName, originalFieldConfig)) { removedFields[fieldName] = originalFieldConfig; } else { newFieldConfigMap[fieldName] = originalFieldConfig; } - }); + } return correctASTNodes( new GraphQLObjectType({ @@ -89,12 +90,12 @@ export function selectObjectFields( const config = type.toConfig(); const originalFieldConfigMap = config.fields; - Object.keys(originalFieldConfigMap).forEach(fieldName => { + for (const fieldName in originalFieldConfigMap) { const originalFieldConfig = originalFieldConfigMap[fieldName]; if (testFn(fieldName, originalFieldConfig)) { selectedFields[fieldName] = originalFieldConfig; } - }); + } } return undefined; @@ -118,19 +119,19 @@ export function modifyObjectFields( const originalFieldConfigMap = config.fields; const newFieldConfigMap = {}; - Object.keys(originalFieldConfigMap).forEach(fieldName => { + for (const fieldName in originalFieldConfigMap) { const originalFieldConfig = originalFieldConfigMap[fieldName]; if (testFn(fieldName, originalFieldConfig)) { removedFields[fieldName] = originalFieldConfig; } else { newFieldConfigMap[fieldName] = originalFieldConfig; } - }); + } - Object.keys(newFields).forEach(fieldName => { + for (const fieldName in newFields) { const fieldConfig = newFields[fieldName]; newFieldConfigMap[fieldName] = fieldConfig; - }); + } return correctASTNodes( new GraphQLObjectType({ diff --git a/packages/utils/src/filterSchema.ts b/packages/utils/src/filterSchema.ts index 5648d5ee15a..74e04b06232 100644 --- a/packages/utils/src/filterSchema.ts +++ b/packages/utils/src/filterSchema.ts @@ -81,17 +81,18 @@ function filterRootFields( ): GraphQLObjectType { if (rootFieldFilter || argumentFilter) { const config = type.toConfig(); - Object.entries(config.fields).forEach(([fieldName, field]) => { + for (const fieldName in config.fields) { + const field = config.fields[fieldName]; if (rootFieldFilter && !rootFieldFilter(operation, fieldName, config.fields[fieldName])) { delete config.fields[fieldName]; } else if (argumentFilter && field.args) { - for (const argName of Object.keys(field.args)) { + for (const argName in field.args) { if (!argumentFilter(operation, fieldName, argName, field.args[argName])) { delete field.args[argName]; } } } - }); + } return new GraphQLObjectType(config); } return type; @@ -105,17 +106,18 @@ function filterElementFields( ): ElementType | undefined { if (fieldFilter || argumentFilter) { const config = type.toConfig(); - Object.entries(config.fields).forEach(([fieldName, field]) => { + for (const fieldName in config.fields) { + const field = config.fields[fieldName]; if (fieldFilter && !fieldFilter(type.name, fieldName, config.fields[fieldName])) { delete config.fields[fieldName]; } else if (argumentFilter && 'args' in field) { - for (const argName of Object.keys(field.args)) { + for (const argName in field.args) { if (!argumentFilter(type.name, fieldName, argName, field.args[argName])) { delete field.args[argName]; } } } - }); + } return new ElementConstructor(config); } } diff --git a/packages/utils/src/forEachDefaultValue.ts b/packages/utils/src/forEachDefaultValue.ts index 4d15403c186..4ff70bc5a4c 100644 --- a/packages/utils/src/forEachDefaultValue.ts +++ b/packages/utils/src/forEachDefaultValue.ts @@ -4,26 +4,26 @@ import { IDefaultValueIteratorFn } from './Interfaces'; export function forEachDefaultValue(schema: GraphQLSchema, fn: IDefaultValueIteratorFn): void { const typeMap = schema.getTypeMap(); - Object.keys(typeMap).forEach(typeName => { + for (const typeName in typeMap) { const type = typeMap[typeName]; if (!getNamedType(type).name.startsWith('__')) { if (isObjectType(type)) { const fields = type.getFields(); - Object.keys(fields).forEach(fieldName => { + for (const fieldName in fields) { const field = fields[fieldName]; - field.args.forEach(arg => { + for (const arg of field.args) { arg.defaultValue = fn(arg.type, arg.defaultValue); - }); - }); + } + } } else if (isInputObjectType(type)) { const fields = type.getFields(); - Object.keys(fields).forEach(fieldName => { + for (const fieldName in fields) { const field = fields[fieldName]; field.defaultValue = fn(field.type, field.defaultValue); - }); + } } } - }); + } } diff --git a/packages/utils/src/forEachField.ts b/packages/utils/src/forEachField.ts index f6795c8d2c5..a59a9a969cb 100644 --- a/packages/utils/src/forEachField.ts +++ b/packages/utils/src/forEachField.ts @@ -4,16 +4,16 @@ import { IFieldIteratorFn } from './Interfaces'; export function forEachField(schema: GraphQLSchema, fn: IFieldIteratorFn): void { const typeMap = schema.getTypeMap(); - Object.keys(typeMap).forEach(typeName => { + for (const typeName in typeMap) { const type = typeMap[typeName]; // TODO: maybe have an option to include these? if (!getNamedType(type).name.startsWith('__') && isObjectType(type)) { const fields = type.getFields(); - Object.keys(fields).forEach(fieldName => { + for (const fieldName in fields) { const field = fields[fieldName]; fn(field, typeName, fieldName); - }); + } } - }); + } } diff --git a/packages/utils/src/get-directives.ts b/packages/utils/src/get-directives.ts index a8c21af9b6d..d0afe8fb287 100644 --- a/packages/utils/src/get-directives.ts +++ b/packages/utils/src/get-directives.ts @@ -96,9 +96,9 @@ export function getDirectives( const result: DirectiveUseMap = {}; - astNodes.forEach(astNode => { + for (const astNode of astNodes) { if (astNode.directives) { - astNode.directives.forEach(directiveNode => { + for (const directiveNode of astNode.directives) { const schemaDirective = schemaDirectiveMap[directiveNode.name.value]; if (schemaDirective) { if (schemaDirective.isRepeatable) { @@ -108,9 +108,9 @@ export function getDirectives( result[schemaDirective.name] = getArgumentValues(schemaDirective, directiveNode); } } - }); + } } - }); + } return result; } diff --git a/packages/utils/src/getResolversFromSchema.ts b/packages/utils/src/getResolversFromSchema.ts index 2f0a1746e5d..deeb1814836 100644 --- a/packages/utils/src/getResolversFromSchema.ts +++ b/packages/utils/src/getResolversFromSchema.ts @@ -16,7 +16,7 @@ export function getResolversFromSchema(schema: GraphQLSchema): IResolvers { const typeMap = schema.getTypeMap(); - Object.keys(typeMap).forEach(typeName => { + for (const typeName in typeMap) { if (!typeName.startsWith('__')) { const type = typeMap[typeName]; @@ -30,9 +30,9 @@ export function getResolversFromSchema(schema: GraphQLSchema): IResolvers { resolvers[typeName] = {}; const values = type.getValues(); - values.forEach(value => { + for (const value of values) { resolvers[typeName][value.name] = value.value; - }); + } } else if (isInterfaceType(type)) { if (type.resolveType != null) { resolvers[typeName] = { @@ -53,20 +53,24 @@ export function getResolversFromSchema(schema: GraphQLSchema): IResolvers { } const fields = type.getFields(); - Object.keys(fields).forEach(fieldName => { + for (const fieldName in fields) { const field = fields[fieldName]; if (field.subscribe != null) { resolvers[typeName][fieldName] = resolvers[typeName][fieldName] || {}; resolvers[typeName][fieldName].subscribe = field.subscribe; } - if (field.resolve != null && field.resolve?.name !== 'defaultFieldResolver' && field.resolve?.name !== 'defaultMergedResolver') { + if ( + field.resolve != null && + field.resolve?.name !== 'defaultFieldResolver' && + field.resolve?.name !== 'defaultMergedResolver' + ) { resolvers[typeName][fieldName] = resolvers[typeName][fieldName] || {}; resolvers[typeName][fieldName].resolve = field.resolve; } - }); + } } } - }); + } return resolvers; } diff --git a/packages/utils/src/heal.ts b/packages/utils/src/heal.ts index 73a00cbf224..f0dd92fc8b3 100644 --- a/packages/utils/src/heal.ts +++ b/packages/utils/src/heal.ts @@ -66,14 +66,15 @@ export function healTypes( // schema.getTypeMap() have changed, the keys of the type map need to // be updated accordingly. - Object.entries(originalTypeMap).forEach(([typeName, namedType]) => { + for (const typeName in originalTypeMap) { + const namedType = originalTypeMap[typeName]; if (namedType == null || typeName.startsWith('__')) { - return; + continue; } const actualName = namedType.name; if (actualName.startsWith('__')) { - return; + continue; } if (actualName in actualNamedTypeMap) { @@ -85,31 +86,33 @@ export function healTypes( // Note: we are deliberately leaving namedType in the schema by its // original name (which might be different from actualName), so that // references by that name can be healed. - }); + } // Now add back every named type by its actual name. - Object.entries(actualNamedTypeMap).forEach(([typeName, namedType]) => { + for (const typeName in actualNamedTypeMap) { + const namedType = actualNamedTypeMap[typeName]; originalTypeMap[typeName] = namedType; - }); + } // Directive declaration argument types can refer to named types. - directives.forEach((decl: GraphQLDirective) => { + for (const decl of directives) { decl.args = decl.args.filter(arg => { arg.type = healType(arg.type) as GraphQLInputType; return arg.type !== null; }); - }); + } - Object.entries(originalTypeMap).forEach(([typeName, namedType]) => { + for (const typeName in originalTypeMap) { + const namedType = originalTypeMap[typeName]; // Heal all named types, except for dangling references, kept only to redirect. if (!typeName.startsWith('__') && typeName in actualNamedTypeMap) { if (namedType != null) { healNamedType(namedType); } } - }); + } - for (const typeName of Object.keys(originalTypeMap)) { + for (const typeName in originalTypeMap) { if (!typeName.startsWith('__') && !(typeName in actualNamedTypeMap)) { delete originalTypeMap[typeName]; } diff --git a/packages/utils/src/mapSchema.ts b/packages/utils/src/mapSchema.ts index a561b6890f3..d80b8e1284b 100644 --- a/packages/utils/src/mapSchema.ts +++ b/packages/utils/src/mapSchema.ts @@ -29,6 +29,7 @@ import { Kind, EnumValueDefinitionNode, } from 'graphql'; +import { getObjectTypeFromTypeMap } from './addTypes'; import { SchemaMapper, @@ -47,16 +48,30 @@ import { rewireTypes } from './rewire'; import { serializeInputValue, parseInputValue } from './transformInputValue'; export function mapSchema(schema: GraphQLSchema, schemaMapper: SchemaMapper = {}): GraphQLSchema { - const originalTypeMap = schema.getTypeMap(); - - let newTypeMap = mapDefaultValues(originalTypeMap, schema, serializeInputValue); - newTypeMap = mapTypes(newTypeMap, schema, schemaMapper, type => isLeafType(type)); - newTypeMap = mapEnumValues(newTypeMap, schema, schemaMapper); - newTypeMap = mapDefaultValues(newTypeMap, schema, parseInputValue); - - newTypeMap = mapTypes(newTypeMap, schema, schemaMapper, type => !isLeafType(type)); - newTypeMap = mapFields(newTypeMap, schema, schemaMapper); - newTypeMap = mapArguments(newTypeMap, schema, schemaMapper); + const newTypeMap = mapArguments( + mapFields( + mapTypes( + mapDefaultValues( + mapEnumValues( + mapTypes(mapDefaultValues(schema.getTypeMap(), schema, serializeInputValue), schema, schemaMapper, type => + isLeafType(type) + ), + schema, + schemaMapper + ), + schema, + parseInputValue + ), + schema, + schemaMapper, + type => !isLeafType(type) + ), + schema, + schemaMapper + ), + schema, + schemaMapper + ); const originalDirectives = schema.getDirectives(); const newDirectives = mapDirectives(originalDirectives, schema, schemaMapper); @@ -65,29 +80,18 @@ export function mapSchema(schema: GraphQLSchema, schemaMapper: SchemaMapper = {} const mutationType = schema.getMutationType(); const subscriptionType = schema.getSubscriptionType(); - const newQueryTypeName = - queryType != null ? (newTypeMap[queryType.name] != null ? newTypeMap[queryType.name].name : undefined) : undefined; - const newMutationTypeName = - mutationType != null - ? newTypeMap[mutationType.name] != null - ? newTypeMap[mutationType.name].name - : undefined - : undefined; - const newSubscriptionTypeName = - subscriptionType != null - ? newTypeMap[subscriptionType.name] != null - ? newTypeMap[subscriptionType.name].name - : undefined - : undefined; + const newQueryTypeName = queryType?.name && newTypeMap?.[queryType?.name]?.name; + const newMutationTypeName = mutationType?.name && newTypeMap?.[mutationType?.name]?.name; + const newSubscriptionTypeName = subscriptionType?.name && newTypeMap?.[subscriptionType?.name]?.name; const { typeMap, directives } = rewireTypes(newTypeMap, newDirectives); return new GraphQLSchema({ ...schema.toConfig(), - query: newQueryTypeName ? (typeMap[newQueryTypeName] as GraphQLObjectType) : undefined, - mutation: newMutationTypeName ? (typeMap[newMutationTypeName] as GraphQLObjectType) : undefined, - subscription: newSubscriptionTypeName != null ? (typeMap[newSubscriptionTypeName] as GraphQLObjectType) : undefined, - types: Object.keys(typeMap).map(typeName => typeMap[typeName]), + query: getObjectTypeFromTypeMap(typeMap, newQueryTypeName), + mutation: getObjectTypeFromTypeMap(typeMap, newMutationTypeName), + subscription: getObjectTypeFromTypeMap(typeMap, newSubscriptionTypeName), + types: Object.values(typeMap), directives, }); } @@ -100,32 +104,32 @@ function mapTypes( ): TypeMap { const newTypeMap = {}; - Object.keys(originalTypeMap).forEach(typeName => { + for (const typeName in originalTypeMap) { if (!typeName.startsWith('__')) { const originalType = originalTypeMap[typeName]; if (originalType == null || !testFn(originalType)) { newTypeMap[typeName] = originalType; - return; + continue; } const typeMapper = getTypeMapper(schema, schemaMapper, typeName); if (typeMapper == null) { newTypeMap[typeName] = originalType; - return; + continue; } const maybeNewType = typeMapper(originalType, schema); if (maybeNewType === undefined) { newTypeMap[typeName] = originalType; - return; + continue; } newTypeMap[typeName] = maybeNewType; } - }); + } return newTypeMap; } @@ -144,7 +148,7 @@ function mapEnumValues(originalTypeMap: TypeMap, schema: GraphQLSchema, schemaMa const config = type.toConfig(); const originalEnumValueConfigMap = config.values; const newEnumValueConfigMap = {}; - Object.keys(originalEnumValueConfigMap).forEach(externalValue => { + for (const externalValue in originalEnumValueConfigMap) { const originalEnumValueConfig = originalEnumValueConfigMap[externalValue]; const mappedEnumValue = enumValueMapper(originalEnumValueConfig, type.name, schema, externalValue); if (mappedEnumValue === undefined) { @@ -156,7 +160,7 @@ function mapEnumValues(originalTypeMap: TypeMap, schema: GraphQLSchema, schemaMa } else if (mappedEnumValue !== null) { newEnumValueConfigMap[externalValue] = mappedEnumValue; } - }); + } return correctASTNodes( new GraphQLEnumType({ ...config, @@ -221,26 +225,26 @@ function getNewType(newTypeMap: TypeMap, type: T): T | nu function mapFields(originalTypeMap: TypeMap, schema: GraphQLSchema, schemaMapper: SchemaMapper): TypeMap { const newTypeMap = {}; - Object.keys(originalTypeMap).forEach(typeName => { + for (const typeName in originalTypeMap) { if (!typeName.startsWith('__')) { const originalType = originalTypeMap[typeName]; if (!isObjectType(originalType) && !isInterfaceType(originalType) && !isInputObjectType(originalType)) { newTypeMap[typeName] = originalType; - return; + continue; } const fieldMapper = getFieldMapper(schema, schemaMapper, typeName); if (fieldMapper == null) { newTypeMap[typeName] = originalType; - return; + continue; } const config = originalType.toConfig(); const originalFieldConfigMap = config.fields; const newFieldConfigMap = {}; - Object.keys(originalFieldConfigMap).forEach(fieldName => { + for (const fieldName in originalFieldConfigMap) { const originalFieldConfig = originalFieldConfigMap[fieldName]; const mappedField = fieldMapper(originalFieldConfig, fieldName, typeName, schema); if (mappedField === undefined) { @@ -260,7 +264,7 @@ function mapFields(originalTypeMap: TypeMap, schema: GraphQLSchema, schemaMapper } else if (mappedField !== null) { newFieldConfigMap[fieldName] = mappedField; } - }); + } if (isObjectType(originalType)) { newTypeMap[typeName] = correctASTNodes( @@ -285,7 +289,7 @@ function mapFields(originalTypeMap: TypeMap, schema: GraphQLSchema, schemaMapper ); } } - }); + } return newTypeMap; } @@ -293,44 +297,44 @@ function mapFields(originalTypeMap: TypeMap, schema: GraphQLSchema, schemaMapper function mapArguments(originalTypeMap: TypeMap, schema: GraphQLSchema, schemaMapper: SchemaMapper): TypeMap { const newTypeMap = {}; - Object.keys(originalTypeMap).forEach(typeName => { + for (const typeName in originalTypeMap) { if (!typeName.startsWith('__')) { const originalType = originalTypeMap[typeName]; if (!isObjectType(originalType) && !isInterfaceType(originalType)) { newTypeMap[typeName] = originalType; - return; + continue; } const argumentMapper = getArgumentMapper(schemaMapper); if (argumentMapper == null) { newTypeMap[typeName] = originalType; - return; + continue; } const config = originalType.toConfig(); const originalFieldConfigMap = config.fields; const newFieldConfigMap = {}; - Object.keys(originalFieldConfigMap).forEach(fieldName => { + for (const fieldName in originalFieldConfigMap) { const originalFieldConfig = originalFieldConfigMap[fieldName]; const originalArgumentConfigMap = originalFieldConfig.args; if (originalArgumentConfigMap == null) { newFieldConfigMap[fieldName] = originalFieldConfig; - return; + continue; } const argumentNames = Object.keys(originalArgumentConfigMap); if (!argumentNames.length) { newFieldConfigMap[fieldName] = originalFieldConfig; - return; + continue; } const newArgumentConfigMap = {}; - argumentNames.forEach(argumentName => { + for (const argumentName of argumentNames) { const originalArgumentConfig = originalArgumentConfigMap[argumentName]; const mappedArgument = argumentMapper(originalArgumentConfig, fieldName, typeName, schema); @@ -343,12 +347,13 @@ function mapArguments(originalTypeMap: TypeMap, schema: GraphQLSchema, schemaMap } else if (mappedArgument !== null) { newArgumentConfigMap[argumentName] = mappedArgument; } - }); + } + newFieldConfigMap[fieldName] = { ...originalFieldConfig, args: newArgumentConfigMap, }; - }); + } if (isObjectType(originalType)) { newTypeMap[typeName] = new GraphQLObjectType({ @@ -367,7 +372,7 @@ function mapArguments(originalTypeMap: TypeMap, schema: GraphQLSchema, schemaMap }); } } - }); + } return newTypeMap; } @@ -384,14 +389,14 @@ function mapDirectives( const newDirectives: Array = []; - originalDirectives.forEach(directive => { + for (const directive of originalDirectives) { const mappedDirective = directiveMapper(directive, schema); if (mappedDirective === undefined) { newDirectives.push(directive); } else if (mappedDirective !== null) { newDirectives.push(mappedDirective); } - }); + } return newDirectives; } @@ -507,11 +512,14 @@ export function correctASTNodes(type: GraphQLNamedType): GraphQLNamedType { const config = (type as GraphQLObjectType).toConfig(); if (config.astNode != null) { const fields: Array = []; - Object.values(config.fields).forEach(fieldConfig => { + for (const fieldName in config.fields) { + const fieldConfig = config.fields[fieldName]; + if (fieldConfig.astNode != null) { fields.push(fieldConfig.astNode); } - }); + } + config.astNode = { ...config.astNode, kind: Kind.OBJECT_TYPE_DEFINITION, @@ -532,11 +540,13 @@ export function correctASTNodes(type: GraphQLNamedType): GraphQLNamedType { const config = (type as GraphQLInterfaceType).toConfig(); if (config.astNode != null) { const fields: Array = []; - Object.values(config.fields).forEach(fieldConfig => { + for (const fieldName in config.fields) { + const fieldConfig = config.fields[fieldName]; + if (fieldConfig.astNode != null) { fields.push(fieldConfig.astNode); } - }); + } config.astNode = { ...config.astNode, kind: Kind.INTERFACE_TYPE_DEFINITION, @@ -557,11 +567,13 @@ export function correctASTNodes(type: GraphQLNamedType): GraphQLNamedType { const config = (type as GraphQLInputObjectType).toConfig(); if (config.astNode != null) { const fields: Array = []; - Object.values(config.fields).forEach(fieldConfig => { + for (const fieldName in config.fields) { + const fieldConfig = config.fields[fieldName]; + if (fieldConfig.astNode != null) { fields.push(fieldConfig.astNode); } - }); + } config.astNode = { ...config.astNode, kind: Kind.INPUT_OBJECT_TYPE_DEFINITION, @@ -582,11 +594,12 @@ export function correctASTNodes(type: GraphQLNamedType): GraphQLNamedType { const config = (type as GraphQLEnumType).toConfig(); if (config.astNode != null) { const values: Array = []; - Object.values(config.values).forEach(enumValueConfig => { + for (const enumKey in config.values) { + const enumValueConfig = config.values[enumKey]; if (enumValueConfig.astNode != null) { values.push(enumValueConfig.astNode); } - }); + } config.astNode = { ...config.astNode, values, diff --git a/packages/utils/src/mergeDeep.ts b/packages/utils/src/mergeDeep.ts index 780bdd4815f..79a7b3a241e 100644 --- a/packages/utils/src/mergeDeep.ts +++ b/packages/utils/src/mergeDeep.ts @@ -10,7 +10,7 @@ export function mergeDeep( ...sources: S ): T & UnboxIntersection>> & any { if (isScalarType(target)) { - return target as any; + return target; } const output = {}; Object.setPrototypeOf(output, Object.create(Object.getPrototypeOf(target))); @@ -19,12 +19,12 @@ export function mergeDeep( const outputPrototype = Object.getPrototypeOf(output); const sourcePrototype = Object.getPrototypeOf(source); if (sourcePrototype) { - Object.getOwnPropertyNames(sourcePrototype).forEach(key => { + for (const key of Object.getOwnPropertyNames(sourcePrototype)) { const descriptor = Object.getOwnPropertyDescriptor(sourcePrototype, key); if (isSome(descriptor)) { Object.defineProperty(outputPrototype, key, descriptor); } - }); + } } for (const key in source) { diff --git a/packages/utils/src/observableToAsyncIterable.ts b/packages/utils/src/observableToAsyncIterable.ts index 3fbe7322853..62c256c7559 100644 --- a/packages/utils/src/observableToAsyncIterable.ts +++ b/packages/utils/src/observableToAsyncIterable.ts @@ -72,7 +72,9 @@ export function observableToAsyncIterable(observable: Observable): AsyncIt if (listening) { listening = false; subscription.unsubscribe(); - pullQueue.forEach(resolve => resolve({ value: undefined, done: true })); + for (const resolve of pullQueue) { + resolve({ value: undefined, done: true }); + } pullQueue.length = 0; pushQueue.length = 0; } diff --git a/packages/utils/src/print-schema-with-directives.ts b/packages/utils/src/print-schema-with-directives.ts index 4e1da45b0b2..d89f09e1958 100644 --- a/packages/utils/src/print-schema-with-directives.ts +++ b/packages/utils/src/print-schema-with-directives.ts @@ -127,21 +127,23 @@ export function astFromSchema( subscription: undefined, }; - let nodes: Array = []; + const nodes: Array = []; if (schema.astNode != null) { nodes.push(schema.astNode); } if (schema.extensionASTNodes != null) { - nodes = nodes.concat(schema.extensionASTNodes); + for (const extensionASTNode of schema.extensionASTNodes) { + nodes.push(extensionASTNode); + } } - nodes.forEach(node => { + for (const node of nodes) { if (node.operationTypes) { - node.operationTypes.forEach(operationTypeDefinitionNode => { + for (const operationTypeDefinitionNode of node.operationTypes) { operationTypeMap[operationTypeDefinitionNode.operation] = operationTypeDefinitionNode; - }); + } } - }); + } const rootTypeMap: Record> = { query: schema.getQueryType(), @@ -149,7 +151,7 @@ export function astFromSchema( subscription: schema.getSubscriptionType(), }; - Object.keys(operationTypeMap).forEach(operationTypeNode => { + for (const operationTypeNode in operationTypeMap) { if (rootTypeMap[operationTypeNode] != null) { if (operationTypeMap[operationTypeNode] != null) { operationTypeMap[operationTypeNode].type = astFromType(rootTypeMap[operationTypeNode]); @@ -161,7 +163,7 @@ export function astFromSchema( }; } } - }); + } const operationTypes = Object.values(operationTypeMap).filter(isSome); @@ -598,7 +600,7 @@ export function makeDirectiveNode( const directiveArguments: Array = []; if (directive != null) { - directive.args.forEach(arg => { + for (const arg of directive.args) { const argName = arg.name; const argValue = args[argName]; if (argValue !== undefined) { @@ -614,9 +616,10 @@ export function makeDirectiveNode( }); } } - }); + } } else { - Object.entries(args).forEach(([argName, argValue]) => { + for (const argName in args) { + const argValue = args[argName]; const value = astFromValueUntyped(argValue); if (value) { directiveArguments.push({ @@ -628,7 +631,7 @@ export function makeDirectiveNode( value, }); } - }); + } } return { @@ -646,15 +649,16 @@ export function makeDirectiveNodes( directiveValues: Record ): Array { const directiveNodes: Array = []; - Object.entries(directiveValues).forEach(([directiveName, arrayOrSingleValue]) => { + for (const directiveName in directiveValues) { + const arrayOrSingleValue = directiveValues[directiveName]; const directive = schema?.getDirective(directiveName); if (Array.isArray(arrayOrSingleValue)) { - arrayOrSingleValue.forEach(value => { + for (const value of arrayOrSingleValue) { directiveNodes.push(makeDirectiveNode(directiveName, value, directive)); - }); + } } else { directiveNodes.push(makeDirectiveNode(directiveName, arrayOrSingleValue, directive)); } - }); + } return directiveNodes; } diff --git a/packages/utils/src/prune.ts b/packages/utils/src/prune.ts index 5d046383054..7be287cf9a8 100644 --- a/packages/utils/src/prune.ts +++ b/packages/utils/src/prune.ts @@ -46,18 +46,18 @@ export function pruneSchema(schema: GraphQLSchema, options: PruneSchemaOptions = implementations: Object.create(null), }; - Object.keys(schema.getTypeMap()).forEach(typeName => { + for (const typeName in schema.getTypeMap()) { const type = schema.getType(typeName); if (type && 'getInterfaces' in type) { - type.getInterfaces().forEach(iface => { + for (const iface of type.getInterfaces()) { const implementations = getImplementations(pruningContext, iface); if (implementations == null) { pruningContext.implementations[iface.name] = Object.create(null); } pruningContext.implementations[iface.name][type.name] = true; - }); + } } - }); + } visitTypes(pruningContext, schema); @@ -115,35 +115,36 @@ function visitOutputType( if (isObjectType(type) || isInterfaceType(type)) { const fields = type.getFields(); - Object.keys(fields).forEach(fieldName => { + for (const fieldName in fields) { const field = fields[fieldName]; const namedType = getNamedType(field.type) as NamedOutputType; visitOutputType(visitedTypes, pruningContext, namedType); - const args = field.args; - args.forEach(arg => { + for (const arg of field.args) { const type = getNamedType(arg.type) as NamedInputType; visitInputType(visitedTypes, pruningContext, type); - }); - }); + } + } if (isInterfaceType(type)) { const implementations = getImplementations(pruningContext, type); if (implementations) { - Object.keys(implementations).forEach(typeName => { + for (const typeName in implementations) { visitOutputType(visitedTypes, pruningContext, pruningContext.schema.getType(typeName) as NamedOutputType); - }); + } } } if ('getInterfaces' in type) { - type.getInterfaces().forEach(type => { - visitOutputType(visitedTypes, pruningContext, type); - }); + for (const iFace of type.getInterfaces()) { + visitOutputType(visitedTypes, pruningContext, iFace); + } } } else if (isUnionType(type)) { const types = type.getTypes(); - types.forEach(type => visitOutputType(visitedTypes, pruningContext, type)); + for (const type of types) { + visitOutputType(visitedTypes, pruningContext, type); + } } } @@ -171,31 +172,33 @@ function visitInputType( if (isInputObjectType(type)) { const fields = type.getFields(); - Object.keys(fields).forEach(fieldName => { + for (const fieldName in fields) { const field = fields[fieldName]; const namedType = getNamedType(field.type) as NamedInputType; visitInputType(visitedTypes, pruningContext, namedType); - }); + } } } function visitTypes(pruningContext: PruningContext, schema: GraphQLSchema): void { - Object.keys(schema.getTypeMap()).forEach(typeName => { + for (const typeName in schema.getTypeMap()) { if (!typeName.startsWith('__')) { pruningContext.unusedTypes[typeName] = true; } - }); + } const visitedTypes: Record = Object.create(null); const rootTypes = [schema.getQueryType(), schema.getMutationType(), schema.getSubscriptionType()].filter(isSome); - rootTypes.forEach(rootType => visitOutputType(visitedTypes, pruningContext, rootType)); + for (const rootType of rootTypes) { + visitOutputType(visitedTypes, pruningContext, rootType); + } - schema.getDirectives().forEach(directive => { - directive.args.forEach(arg => { + for (const directive of schema.getDirectives()) { + for (const arg of directive.args) { const type = getNamedType(arg.type) as NamedInputType; visitInputType(visitedTypes, pruningContext, type); - }); - }); + } + } } diff --git a/packages/utils/src/rewire.ts b/packages/utils/src/rewire.ts index 4e257714569..45e6639d688 100644 --- a/packages/utils/src/rewire.ts +++ b/packages/utils/src/rewire.ts @@ -37,21 +37,21 @@ export function rewireTypes( directives: Array; } { const referenceTypeMap = Object.create(null); - Object.keys(originalTypeMap).forEach(typeName => { + for (const typeName in originalTypeMap) { referenceTypeMap[typeName] = originalTypeMap[typeName]; - }); + } const newTypeMap: TypeMap = Object.create(null); - Object.keys(referenceTypeMap).forEach(typeName => { + for (const typeName in referenceTypeMap) { const namedType = referenceTypeMap[typeName]; if (namedType == null || typeName.startsWith('__')) { - return; + continue; } const newName = namedType.name; if (newName.startsWith('__')) { - return; + continue; } if (newTypeMap[newName] != null) { @@ -59,11 +59,11 @@ export function rewireTypes( } newTypeMap[newName] = namedType; - }); + } - Object.keys(newTypeMap).forEach(typeName => { + for (const typeName in newTypeMap) { newTypeMap[typeName] = rewireNamedType(newTypeMap[typeName]); - }); + } const newDirectives = directives.map(directive => rewireDirective(directive)); @@ -83,14 +83,14 @@ export function rewireTypes( function rewireArgs(args: GraphQLFieldConfigArgumentMap): GraphQLFieldConfigArgumentMap { const rewiredArgs = {}; - Object.keys(args).forEach(argName => { + for (const argName in args) { const arg = args[argName]; const rewiredArgType = rewireType(arg.type); if (rewiredArgType != null) { arg.type = rewiredArgType; rewiredArgs[argName] = arg; } - }); + } return rewiredArgs; } @@ -144,7 +144,7 @@ export function rewireTypes( function rewireFields(fields: GraphQLFieldConfigMap): GraphQLFieldConfigMap { const rewiredFields = {}; - Object.keys(fields).forEach(fieldName => { + for (const fieldName in fields) { const field = fields[fieldName]; const rewiredFieldType = rewireType(field.type); if (rewiredFieldType != null && field.args) { @@ -152,31 +152,31 @@ export function rewireTypes( field.args = rewireArgs(field.args); rewiredFields[fieldName] = field; } - }); + } return rewiredFields; } function rewireInputFields(fields: GraphQLInputFieldConfigMap): GraphQLInputFieldConfigMap { const rewiredFields = {}; - Object.keys(fields).forEach(fieldName => { + for (const fieldName in fields) { const field = fields[fieldName]; const rewiredFieldType = rewireType(field.type); if (rewiredFieldType != null) { field.type = rewiredFieldType; rewiredFields[fieldName] = field; } - }); + } return rewiredFields; } function rewireNamedTypes(namedTypes: Array): Array { const rewiredTypes: Array = []; - namedTypes.forEach(namedType => { + for (const namedType of namedTypes) { const rewiredType = rewireType(namedType); if (rewiredType != null) { rewiredTypes.push(rewiredType); } - }); + } return rewiredTypes; } diff --git a/packages/utils/src/stub.ts b/packages/utils/src/stub.ts index 1d6cbdb2c13..de72bbfa995 100644 --- a/packages/utils/src/stub.ts +++ b/packages/utils/src/stub.ts @@ -8,9 +8,6 @@ import { GraphQLFloat, GraphQLBoolean, GraphQLID, - isObjectType, - isInterfaceType, - isInputObjectType, TypeNode, Kind, GraphQLType, @@ -64,10 +61,13 @@ export function createStub(node: TypeNode, type: any): any { } export function isNamedStub(type: GraphQLNamedType): boolean { - if (isObjectType(type) || isInterfaceType(type) || isInputObjectType(type)) { + if ('getFields' in type) { const fields = type.getFields(); - const fieldNames = Object.keys(fields); - return fieldNames.length === 1 && fields[fieldNames[0]].name === '_fake'; + // eslint-disable-next-line no-unreachable-loop + for (const fieldName in fields) { + const field = fields[fieldName]; + return field.name === '_fake'; + } } return false; diff --git a/packages/utils/src/transformInputValue.ts b/packages/utils/src/transformInputValue.ts index db95d48d71c..530a0a7be19 100644 --- a/packages/utils/src/transformInputValue.ts +++ b/packages/utils/src/transformInputValue.ts @@ -23,7 +23,7 @@ export function transformInputValue( } else if (isInputObjectType(nullableType)) { const fields = nullableType.getFields(); const newValue = {}; - Object.keys(value).forEach(key => { + for (const key in value) { const field = fields[key]; if (field != null) { newValue[key] = transformInputValue( @@ -33,7 +33,7 @@ export function transformInputValue( inputObjectValueTransformer ); } - }); + } return inputObjectValueTransformer != null ? inputObjectValueTransformer(nullableType, newValue) : newValue; } diff --git a/packages/utils/src/validate-documents.ts b/packages/utils/src/validate-documents.ts index 9cf306dba02..9ec3a978f5f 100644 --- a/packages/utils/src/validate-documents.ts +++ b/packages/utils/src/validate-documents.ts @@ -7,6 +7,9 @@ import { FragmentDefinitionNode, ValidationContext, ASTVisitor, + DefinitionNode, + concatAST, + DocumentNode, } from 'graphql'; import { Source } from './loaders'; import { AggregateError } from './AggregateError'; @@ -24,41 +27,42 @@ export async function validateGraphQlDocuments( effectiveRules?: ValidationRule[] ): Promise> { effectiveRules = effectiveRules || createDefaultRules(); - const allFragments: FragmentDefinitionNode[] = []; + const allFragmentMap = new Map(); + const documentFileObjectsToValidate: { + location?: string; + document: DocumentNode; + }[] = []; - documentFiles.forEach(documentFile => { + for (const documentFile of documentFiles) { if (documentFile.document) { + const definitionsToValidate: DefinitionNode[] = []; for (const definitionNode of documentFile.document.definitions) { if (definitionNode.kind === Kind.FRAGMENT_DEFINITION) { - allFragments.push(definitionNode); + allFragmentMap.set(definitionNode.name.value, definitionNode); + } else { + definitionsToValidate.push(definitionNode); } } + documentFileObjectsToValidate.push({ + location: documentFile.location, + document: { + kind: Kind.DOCUMENT, + definitions: definitionsToValidate, + }, + }); } - }); + } const allErrors: LoadDocumentError[] = []; + const allFragmentsDocument: DocumentNode = { + kind: Kind.DOCUMENT, + definitions: [...allFragmentMap.values()], + }; + await Promise.all( - documentFiles.map(async documentFile => { - const documentToValidate = { - kind: Kind.DOCUMENT, - definitions: [...allFragments, ...(documentFile.document?.definitions ?? [])].filter( - (definition, index, list) => { - if (definition.kind === Kind.FRAGMENT_DEFINITION) { - const firstIndex = list.findIndex( - def => def.kind === Kind.FRAGMENT_DEFINITION && def.name.value === definition.name.value - ); - const isDuplicated = firstIndex !== index; - - if (isDuplicated) { - return false; - } - } - - return true; - } - ), - }; + documentFileObjectsToValidate.map(async documentFile => { + const documentToValidate = concatAST([allFragmentsDocument, documentFile.document]); const errors = validate(schema, documentToValidate, effectiveRules); @@ -84,26 +88,25 @@ export function checkValidationErrors(loadDocumentErrors: ReadonlyArray (error.stack += `\n at ${loadDocumentError.filePath}:${location.line}:${location.column}`) - ); + if (graphQLError.locations) { + for (const location of graphQLError.locations) { + error.stack += `\n at ${loadDocumentError.filePath}:${location.line}:${location.column}`; + } + } errors.push(error); } } - throw new AggregateError(errors, 'Validation failed'); + throw new AggregateError(errors, `GraphQL Document Validation failed with ${loadDocumentErrors.length} errors`); } } function createDefaultRules() { const ignored = ['NoUnusedFragmentsRule', 'NoUnusedVariablesRule', 'KnownDirectivesRule']; + const v4ignored = ignored.map(rule => rule.replace(/Rule$/, '')); - // GraphQL v14 has no Rule suffix in function names - // Adding `*Rule` makes validation backwards compatible - ignored.forEach(rule => { - ignored.push(rule.replace(/Rule$/, '')); - }); - - return specifiedRules.filter((f: (...args: any[]) => any) => !ignored.includes(f.name)); + return specifiedRules.filter( + (f: (...args: any[]) => any) => !ignored.includes(f.name) && !v4ignored.includes(f.name) + ); } diff --git a/packages/utils/src/visitResult.ts b/packages/utils/src/visitResult.ts index 3fc168b71ee..f71725fd41f 100644 --- a/packages/utils/src/visitResult.ts +++ b/packages/utils/src/visitResult.ts @@ -57,10 +57,10 @@ export function visitData(data: any, enter?: ValueVisitor, leave?: ValueVisitor) const newData = enter != null ? enter(data) : data; if (newData != null) { - Object.keys(newData).forEach(key => { + for (const key in newData) { const value = newData[key]; newData[key] = visitData(value, enter, leave); - }); + } } return leave != null ? leave(newData) : newData; @@ -195,10 +195,12 @@ function visitObjectValue( if (errors != null) { sortedErrors = sortErrorsByPathSegment(errors, pathIndex); errorMap = sortedErrors.errorMap; - sortedErrors.unpathedErrors.forEach(error => errorInfo.unpathedErrors.add(error)); + for (const error of sortedErrors.unpathedErrors) { + errorInfo.unpathedErrors.add(error); + } } - Object.keys(fieldNodeMap).forEach(responseKey => { + for (const responseKey in fieldNodeMap) { const subFieldNodes = fieldNodeMap[responseKey]; const fieldName = subFieldNodes[0].name.value; const fieldType = fieldName === '__typename' ? TypeNameMetaFieldDef.type : fieldMap[fieldName].type; @@ -226,7 +228,7 @@ function visitObjectValue( ); updateObject(newObject, responseKey, newValue, typeVisitorMap, fieldName); - }); + } const oldTypename = newObject.__typename; if (oldTypename != null) { @@ -353,11 +355,11 @@ function visitFieldValue( function sortErrorsByPathSegment(errors: ReadonlyArray, pathIndex: number): SortedErrors { const errorMap = Object.create(null); const unpathedErrors: Set = new Set(); - errors.forEach(error => { + for (const error of errors) { const pathSegment = error.path?.[pathIndex]; if (pathSegment == null) { unpathedErrors.add(error); - return; + continue; } if (pathSegment in errorMap) { @@ -365,7 +367,7 @@ function sortErrorsByPathSegment(errors: ReadonlyArray, pathIndex: } else { errorMap[pathSegment] = [error]; } - }); + } return { errorMap, @@ -380,7 +382,7 @@ function addPathSegmentInfo( errors: ReadonlyArray = [], errorInfo: ErrorInfo ) { - errors.forEach(error => { + for (const error of errors) { const segmentInfo = { type, fieldName, @@ -392,7 +394,7 @@ function addPathSegmentInfo( } else { pathSegmentsInfo.push(segmentInfo); } - }); + } } function collectSubFields( @@ -403,11 +405,11 @@ function collectSubFields( let subFieldNodes: Record> = Object.create(null); const visitedFragmentNames = Object.create(null); - fieldNodes.forEach(fieldNode => { + for (const fieldNode of fieldNodes) { if (fieldNode.selectionSet) { subFieldNodes = collectFields(exeContext, type, fieldNode.selectionSet, subFieldNodes, visitedFragmentNames); } - }); + } return subFieldNodes; } diff --git a/packages/utils/tests/get-directives.spec.ts b/packages/utils/tests/get-directives.spec.ts index 0af6716fc25..19da8413343 100644 --- a/packages/utils/tests/get-directives.spec.ts +++ b/packages/utils/tests/get-directives.spec.ts @@ -1,6 +1,7 @@ import { makeExecutableSchema } from '@graphql-tools/schema'; import { getDirectives } from '../src'; import { assertGraphQLObjectType } from '../../testing/assertion'; +import { GraphQLSchema } from 'graphql'; describe('getDirectives', () => { it('should return the correct directives map when no directives specified', () => { @@ -10,7 +11,7 @@ describe('getDirectives', () => { } `; const schema = makeExecutableSchema({ typeDefs, resolvers: {} }) as GraphQLSchema; - const directivesMap = getDirectives(schema, schema.getQueryType()); + const directivesMap = getDirectives(schema, schema.getQueryType()!); expect(directivesMap).toEqual({}); }); @@ -23,7 +24,7 @@ describe('getDirectives', () => { `; const schema = makeExecutableSchema({ typeDefs, resolvers: {} }) as GraphQLSchema; - const directivesMap = getDirectives(schema, schema.getQueryType().getFields().test); + const directivesMap = getDirectives(schema, schema.getQueryType()!.getFields()['test']); expect(directivesMap).toEqual({ deprecated: { reason: 'No longer supported', @@ -41,7 +42,7 @@ describe('getDirectives', () => { `; const schema = makeExecutableSchema({ typeDefs, resolvers: {} }) as GraphQLSchema; - const directivesMap = getDirectives(schema, schema.getQueryType().getFields().test); + const directivesMap = getDirectives(schema, schema.getQueryType()!.getFields()['test']); expect(directivesMap).toEqual({ mydir: {}, }); @@ -57,7 +58,7 @@ describe('getDirectives', () => { `; const schema = makeExecutableSchema({ typeDefs, resolvers: {} }) as GraphQLSchema; - const directivesMap = getDirectives(schema, schema.getQueryType().getFields().test); + const directivesMap = getDirectives(schema, schema.getQueryType()!.getFields()['test']); expect(directivesMap).toEqual({ mydir: { f1: 'test', @@ -75,7 +76,7 @@ describe('getDirectives', () => { `; const schema = makeExecutableSchema({ typeDefs, resolvers: {} }) as GraphQLSchema; - const directivesMap = getDirectives(schema, schema.getQueryType().getFields().test); + const directivesMap = getDirectives(schema, schema.getQueryType()!.getFields()['test']); expect(directivesMap).toEqual({ mydir: {}, }); diff --git a/packages/utils/tests/mapSchema.test.ts b/packages/utils/tests/mapSchema.test.ts index 6054485db3c..e2622760e3e 100644 --- a/packages/utils/tests/mapSchema.test.ts +++ b/packages/utils/tests/mapSchema.test.ts @@ -29,7 +29,7 @@ describe('mapSchema', () => { const newSchema = mapSchema(schema, { [MapperKind.QUERY]: (type) => { const queryConfig = type.toConfig(); - queryConfig.fields.version.resolve = () => 1; + queryConfig.fields['version'].resolve = () => 1; return new GraphQLObjectType(queryConfig); }, }); @@ -37,7 +37,7 @@ describe('mapSchema', () => { expect(newSchema).toBeInstanceOf(GraphQLSchema); const result = graphqlSync(newSchema, '{ version }'); - expect(result.data?.version).toBe(1); + expect(result.data?.['version']).toBe(1); }); test('can change the root query name', () => { diff --git a/packages/utils/tests/print-schema-with-directives.spec.ts b/packages/utils/tests/print-schema-with-directives.spec.ts index 5eb8ec2a9c6..4108123390b 100644 --- a/packages/utils/tests/print-schema-with-directives.spec.ts +++ b/packages/utils/tests/print-schema-with-directives.spec.ts @@ -224,7 +224,7 @@ describe('printSchemaWithDirectives', () => { name: 'dummy', locations: ['QUERY'], })] - }); + } as any); const output = printSchemaWithDirectives(schema); diff --git a/packages/utils/tests/relocatedError.test.ts b/packages/utils/tests/relocatedError.test.ts index e5d5d427a02..9f4d3ab5204 100644 --- a/packages/utils/tests/relocatedError.test.ts +++ b/packages/utils/tests/relocatedError.test.ts @@ -4,11 +4,11 @@ import { relocatedError } from '../src/errors'; describe('Errors', () => { describe('relocatedError', () => { test('should adjust the path of a GraphqlError', () => { - const originalError = new GraphQLError('test', null, null, null, [ + const originalError = new GraphQLError('test', null as any, null, null, [ 'test', ]); const newError = relocatedError(originalError, ['test', 1]); - const expectedError = new GraphQLError('test', null, null, null, [ + const expectedError = new GraphQLError('test', null as any, null, null, [ 'test', 1, ]); diff --git a/packages/utils/tests/schemaTransforms.test.ts b/packages/utils/tests/schemaTransforms.test.ts index c0b18263499..451314b4a82 100644 --- a/packages/utils/tests/schemaTransforms.test.ts +++ b/packages/utils/tests/schemaTransforms.test.ts @@ -122,12 +122,12 @@ describe('@directives', () => { return schema => mapSchema(schema, { [MapperKind.OBJECT_TYPE]: type => { const directives = getDirectives(schema, type); - Object.keys(directives).forEach(directiveName => { + for (const directiveName in directives) { if (directiveNames.includes(directiveName)) { expect(type.name).toBe(schema.getQueryType()?.name); visited.add(type); } - }); + } return undefined; } }) @@ -149,11 +149,11 @@ describe('@directives', () => { function recordSchemaDirectiveUses(directiveNames: Array): (schema: GraphQLSchema) => GraphQLSchema { return schema => { const directives = getDirectives(schema, schema); - Object.keys(directives).forEach(directiveName => { + for (const directiveName in directives) { if (directiveNames.includes(directiveName)) { visited.push(schema); } - }); + } return schema; } } @@ -269,7 +269,7 @@ describe('@directives', () => { schemaTransforms: [deprecatedDirectiveTransformer], }); - expect((schema.getType('ExampleType') as GraphQLObjectType).getFields().oldField.deprecationReason).toBe('Use \`newField\`.') + expect((schema.getType('ExampleType') as GraphQLObjectType).getFields()['oldField'].deprecationReason).toBe('Use \`newField\`.') }); test('can be used to implement the @date example', () => { @@ -552,10 +552,10 @@ describe('@directives', () => { execWithRole('ADMIN') .then(checkErrors(0)) .then((data) => { - expect(data?.users.length).toBe(1); - expect(data?.users[0].banned).toBe(true); - expect(data?.users[0].canPost).toBe(false); - expect(data?.users[0].name).toBe('Ben'); + expect(data?.['users'].length).toBe(1); + expect(data?.['users'][0].banned).toBe(true); + expect(data?.['users'][0].canPost).toBe(false); + expect(data?.['users'][0].name).toBe('Ben'); }), ]); }); @@ -614,9 +614,9 @@ describe('@directives', () => { function wrapType | GraphQLInputFieldConfig>(fieldConfig: F, directiveArgumentMap: Record): void { if (isNonNullType(fieldConfig.type) && isScalarType(fieldConfig.type.ofType)) { - fieldConfig.type = getLimitedLengthType(fieldConfig.type.ofType, directiveArgumentMap.max); + fieldConfig.type = getLimitedLengthType(fieldConfig.type.ofType, directiveArgumentMap['max']); } else if (isScalarType(fieldConfig.type)) { - fieldConfig.type = getLimitedLengthType(fieldConfig.type, directiveArgumentMap.max); + fieldConfig.type = getLimitedLengthType(fieldConfig.type, directiveArgumentMap['max']); } else { throw new Error(`Not a scalar type: ${fieldConfig.type.toString()}`); } @@ -729,9 +729,9 @@ describe('@directives', () => { resolve(object: any) { const hash = createHash('sha1'); hash.update(type.name); - from.forEach((fieldName: string) => { + for (const fieldName of from ){ hash.update(String(object[fieldName])); - }); + } return hash.digest('hex'); }, }; @@ -805,7 +805,7 @@ describe('@directives', () => { ).then((result) => { const { data } = result; - expect(data?.people).toEqual([ + expect(data?.['people']).toEqual([ { uid: '580a207c8e94f03b93a2b01217c3cc218490571a', personID: 1, @@ -813,7 +813,7 @@ describe('@directives', () => { }, ]); - expect(data?.locations).toEqual([ + expect(data?.['locations']).toEqual([ { uid: 'c31b71e6e23a7ae527f94341da333590dd7cba96', locationID: 1, @@ -844,7 +844,7 @@ describe('@directives', () => { }); const Query = schema.getType('Query') as GraphQLObjectType; - const peopleType = Query.getFields().people.type; + const peopleType = Query.getFields()['people'].type; if (isListType(peopleType)) { expect(peopleType.ofType).toBe(schema.getType('Human')); } else { @@ -852,7 +852,7 @@ describe('@directives', () => { } const Mutation = schema.getType('Mutation') as GraphQLObjectType; - const addPersonResultType = Mutation.getFields().addPerson.type; + const addPersonResultType = Mutation.getFields()['addPerson'].type; expect(addPersonResultType).toBe( schema.getType('Human') as GraphQLOutputType, ); @@ -1023,16 +1023,16 @@ describe('@directives', () => { const Human = schema.getType('Human') as GraphQLObjectType; expect(Human.name).toBe('Human'); - expect(Human.getFields().heightInInches.type).toBe(GraphQLInt); + expect(Human.getFields()['heightInInches'].type).toBe(GraphQLInt); const Person = schema.getType('Person') as GraphQLObjectType; expect(Person.name).toBe('Person'); - expect(Person.getFields().born.type).toBe( + expect(Person.getFields()['born'].type).toBe( schema.getType('Date') as GraphQLScalarType, ); const Query = schema.getType('Query') as GraphQLObjectType; - const peopleType = Query.getFields().people.type as GraphQLList< + const peopleType = Query.getFields()['people'].type as GraphQLList< GraphQLObjectType >; expect(peopleType.ofType).toBe(Human); diff --git a/packages/webpack-loader/src/index.ts b/packages/webpack-loader/src/index.ts index 1bc6fc5e103..da45e5aa978 100644 --- a/packages/webpack-loader/src/index.ts +++ b/packages/webpack-loader/src/index.ts @@ -64,10 +64,10 @@ export default function graphqlLoader(this: LoaderContext, source: stri let stringifiedDoc = JSON.stringify(doc); if (options.replaceKinds) { - Object.keys(Kind).forEach(identifier => { + for (const identifier in Kind) { const value = Kind[identifier as keyof typeof Kind]; stringifiedDoc = stringifiedDoc.replace(new RegExp(`"kind":"${value}"`, 'g'), `"kind": Kind.${identifier}`); - }); + } } const headerCode = ` diff --git a/packages/wrap/src/generateProxyingResolvers.ts b/packages/wrap/src/generateProxyingResolvers.ts index c71dccb2705..e394509003d 100644 --- a/packages/wrap/src/generateProxyingResolvers.ts +++ b/packages/wrap/src/generateProxyingResolvers.ts @@ -27,15 +27,15 @@ export function generateProxyingResolvers( }; const resolvers = {}; - // @ts-expect-error: Object.keys typings suck. - Object.keys(operationTypes).forEach((operation: OperationTypeNode) => { + for (const operationAsString in operationTypes) { + const operation = operationAsString as OperationTypeNode; const rootType = operationTypes[operation]; if (rootType != null) { const typeName = rootType.name; const fields = rootType.getFields(); resolvers[typeName] = {}; - Object.keys(fields).forEach(fieldName => { + for (const fieldName in fields) { const proxyingResolver = createProxyingResolver({ subschemaConfig, transformedSchema, @@ -56,9 +56,9 @@ export function generateProxyingResolvers( resolve: finalResolver, }; } - }); + } } - }); + } return resolvers; } diff --git a/packages/wrap/src/index.ts b/packages/wrap/src/index.ts index 7be4ebcc7bf..317d012e34c 100644 --- a/packages/wrap/src/index.ts +++ b/packages/wrap/src/index.ts @@ -3,6 +3,5 @@ export { defaultCreateProxyingResolver, generateProxyingResolvers } from './gene export * from './transforms/index'; -export { makeRemoteExecutableSchema, defaultCreateRemoteResolver } from './makeRemoteExecutableSchema'; export * from './types'; export * from './introspect'; diff --git a/packages/wrap/src/makeRemoteExecutableSchema.ts b/packages/wrap/src/makeRemoteExecutableSchema.ts deleted file mode 100644 index 7fcf10bd59f..00000000000 --- a/packages/wrap/src/makeRemoteExecutableSchema.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { buildSchema, GraphQLFieldResolver, GraphQLSchema } from 'graphql'; - -import { IMakeRemoteExecutableSchemaOptions } from './types'; -import { delegateToSchema } from '@graphql-tools/delegate'; - -import { wrapSchema } from './wrapSchema'; -import { Executor, Subscriber } from '@graphql-tools/utils'; - -export function makeRemoteExecutableSchema({ - schema: schemaOrTypeDefs, - executor, - subscriber, - createResolver = defaultCreateRemoteResolver, - buildSchemaOptions, -}: IMakeRemoteExecutableSchemaOptions): GraphQLSchema { - const targetSchema = - typeof schemaOrTypeDefs === 'string' ? buildSchema(schemaOrTypeDefs, buildSchemaOptions) : schemaOrTypeDefs; - - return wrapSchema({ - schema: targetSchema, - createProxyingResolver: () => createResolver(executor, subscriber), - }); -} - -export function defaultCreateRemoteResolver( - executor: Executor, - subscriber?: Subscriber | undefined -): GraphQLFieldResolver { - return (_parent, _args, context, info) => - delegateToSchema({ - schema: { schema: info.schema, executor, subscriber }, - context, - info, - }); -} diff --git a/packages/wrap/src/transforms/HoistField.ts b/packages/wrap/src/transforms/HoistField.ts index b9463510028..4c686613016 100644 --- a/packages/wrap/src/transforms/HoistField.ts +++ b/packages/wrap/src/transforms/HoistField.ts @@ -80,12 +80,12 @@ export default class HoistField implements Transform { const argsMap: Record = Object.create(null); const innerType: GraphQLObjectType = this.pathToField.reduce((acc, pathSegment, index) => { const field = acc.getFields()[pathSegment]; - field.args.forEach(arg => { + for (const arg of field.args) { if (this.argFilters[index](arg)) { argsMap[arg.name] = arg; this.argLevels[arg.name] = index; } - }); + } return getNullableType(field.type) as GraphQLObjectType; }, originalWrappingSchema.getType(this.typeName) as GraphQLObjectType); diff --git a/packages/wrap/src/transforms/MapLeafValues.ts b/packages/wrap/src/transforms/MapLeafValues.ts index 028736d6605..cbd4c0141ea 100644 --- a/packages/wrap/src/transforms/MapLeafValues.ts +++ b/packages/wrap/src/transforms/MapLeafValues.ts @@ -8,7 +8,6 @@ import { FragmentDefinitionNode, VariableDefinitionNode, ArgumentNode, - GraphQLArgument, FieldNode, valueFromAST, isLeafType, @@ -62,14 +61,14 @@ export default class MapLeafValues implements Transform { + for (const typeName in typeMap) { const type = typeMap[typeName]; if (!typeName.startsWith('__')) { if (isLeafType(type)) { this.resultVisitorMap[typeName] = (value: any) => this.outputValueTransformer(typeName, value); } } - }); + } this.typeInfo = new TypeInfo(originalWrappingSchema); return originalWrappingSchema; } @@ -142,7 +141,7 @@ export default class MapLeafValues implements Transform variableDefinitionMap[varName]), + variableDefinitions: Object.values(variableDefinitionMap), }; }); } @@ -165,7 +164,7 @@ export default class MapLeafValues implements Transform { + for (const argument of targetField.args) { const argName = argument.name; const argType = argument.type; @@ -188,11 +187,11 @@ export default class MapLeafValues implements Transform argumentNodeMap[argName]), + arguments: Object.values(argumentNodeMap), }; } } diff --git a/packages/wrap/src/transforms/TransformCompositeFields.ts b/packages/wrap/src/transforms/TransformCompositeFields.ts index c8feffe87e2..127fa9db5e9 100644 --- a/packages/wrap/src/transforms/TransformCompositeFields.ts +++ b/packages/wrap/src/transforms/TransformCompositeFields.ts @@ -79,11 +79,11 @@ export default class TransformCompositeFields> im ): Request { const document = originalRequest.document; const fragments = Object.create(null); - document.definitions.forEach(def => { + for (const def of document.definitions) { if (def.kind === Kind.FRAGMENT_DEFINITION) { fragments[def.name.value] = def; } - }); + } return { ...originalRequest, document: this.transformDocument(document, fragments, transformationContext), @@ -135,10 +135,10 @@ export default class TransformCompositeFields> im const parentTypeName = parentType.name; let newSelections: Array = []; - node.selections.forEach(selection => { + for (const selection of node.selections) { if (selection.kind !== Kind.FIELD) { newSelections.push(selection); - return; + continue; } const newName = selection.name.value; @@ -172,25 +172,25 @@ export default class TransformCompositeFields> im } if (transformedSelection == null) { - return; + continue; } else if (Array.isArray(transformedSelection)) { newSelections = newSelections.concat(transformedSelection); - return; + continue; } else if (transformedSelection.kind !== Kind.FIELD) { newSelections.push(transformedSelection); - return; + continue; } const typeMapping = this.mapping[parentTypeName]; if (typeMapping == null) { newSelections.push(transformedSelection); - return; + continue; } const oldName = this.mapping[parentTypeName][newName]; if (oldName == null) { newSelections.push(transformedSelection); - return; + continue; } newSelections.push({ @@ -204,7 +204,7 @@ export default class TransformCompositeFields> im value: transformedSelection.alias?.value ?? newName, }, }); - }); + } return { ...node, diff --git a/packages/wrap/src/transforms/TransformInputObjectFields.ts b/packages/wrap/src/transforms/TransformInputObjectFields.ts index 9adeb7cda0d..8f15bc67dd2 100644 --- a/packages/wrap/src/transforms/TransformInputObjectFields.ts +++ b/packages/wrap/src/transforms/TransformInputObjectFields.ts @@ -8,11 +8,10 @@ import { Kind, FragmentDefinitionNode, GraphQLInputObjectType, - GraphQLInputType, ObjectValueNode, ObjectFieldNode, OperationDefinitionNode, - NamedTypeNode, + isInputType, } from 'graphql'; import { Maybe, Request, MapperKind, mapSchema, transformInputValue, assertSome } from '@graphql-tools/utils'; @@ -79,24 +78,27 @@ export default class TransformInputObjectFields implements Transform { const operations: Array = []; - originalRequest.document.definitions.forEach(def => { - if ((def as OperationDefinitionNode).kind === Kind.OPERATION_DEFINITION) { - operations.push(def as OperationDefinitionNode); - } else { - fragments[(def as FragmentDefinitionNode).name.value] = def; + for (const def of originalRequest.document.definitions) { + if (def.kind === Kind.OPERATION_DEFINITION) { + operations.push(def); + } else if (def.kind === Kind.FRAGMENT_DEFINITION) { + fragments[def.name.value] = def; } - }); + } - operations.forEach(def => { + for (const def of operations) { const variableDefs = def.variableDefinitions; if (variableDefs != null) { - variableDefs.forEach(variableDef => { + for (const variableDef of variableDefs) { const varName = variableDef.variable.name.value; + if (variableDef.type.kind !== Kind.NAMED_TYPE) { + throw new Error(`Expected ${variableDef.type} to be a named type`); + } // requirement for 'as NamedTypeNode' appears to be a bug within types, as function should take any TypeNode - const varType = typeFromAST( - delegationContext.transformedSchema, - variableDef.type as NamedTypeNode - ) as GraphQLInputType; + const varType = typeFromAST(delegationContext.transformedSchema, variableDef.type); + if (!isInputType(varType)) { + throw new Error(`Expected ${varType} to be an input type`); + } variableValues[varName] = transformInputValue( varType, variableValues[varName], @@ -104,7 +106,7 @@ export default class TransformInputObjectFields implements Transform { (type, originalValue) => { const newValue = Object.create(null); const fields = type.getFields(); - Object.keys(originalValue).forEach(key => { + for (const key in originalValue) { const field = fields[key]; if (field != null) { const newFieldName = this.mapping[type.name]?.[field.name]; @@ -114,19 +116,17 @@ export default class TransformInputObjectFields implements Transform { newValue[field.name] = originalValue[field.name]; } } - }); + } return newValue; } ); - }); + } } - }); + } - originalRequest.document.definitions - .filter(def => def.kind === Kind.FRAGMENT_DEFINITION) - .forEach(def => { - fragments[(def as FragmentDefinitionNode).name.value] = def; - }); + for (const def of originalRequest.document.definitions.filter(def => def.kind === Kind.FRAGMENT_DEFINITION)) { + fragments[(def as FragmentDefinitionNode).name.value] = def; + } const document = this.transformDocument( originalRequest.document, this.mapping, @@ -162,7 +162,7 @@ export default class TransformInputObjectFields implements Transform { const parentTypeName = parentType.name; const newInputFields: Array = []; - node.fields.forEach(inputField => { + for (const inputField of node.fields) { const newName = inputField.name.value; const transformedInputField = @@ -171,17 +171,17 @@ export default class TransformInputObjectFields implements Transform { : inputField; if (Array.isArray(transformedInputField)) { - transformedInputField.forEach(individualTransformedInputField => { + for (const individualTransformedInputField of transformedInputField) { const typeMapping = mapping[parentTypeName]; if (typeMapping == null) { newInputFields.push(individualTransformedInputField); - return; + continue; } const oldName = typeMapping[newName]; if (oldName == null) { newInputFields.push(individualTransformedInputField); - return; + continue; } newInputFields.push({ @@ -191,20 +191,20 @@ export default class TransformInputObjectFields implements Transform { value: oldName, }, }); - }); - return; + } + continue; } const typeMapping = mapping[parentTypeName]; if (typeMapping == null) { newInputFields.push(transformedInputField); - return; + continue; } const oldName = typeMapping[newName]; if (oldName == null) { newInputFields.push(transformedInputField); - return; + continue; } newInputFields.push({ @@ -214,7 +214,7 @@ export default class TransformInputObjectFields implements Transform { value: oldName, }, }); - }); + } const newNode = { ...node, diff --git a/packages/wrap/src/transforms/WrapFields.ts b/packages/wrap/src/transforms/WrapFields.ts index 817e692fea8..2ae45201e1c 100644 --- a/packages/wrap/src/transforms/WrapFields.ts +++ b/packages/wrap/src/transforms/WrapFields.ts @@ -89,14 +89,14 @@ export default class WrapFields implements Transform = Object.create(null); - Object.keys(targetFieldConfigMap).forEach(fieldName => { + for (const fieldName in targetFieldConfigMap) { const field = targetFieldConfigMap[fieldName]; const newField: GraphQLFieldConfig = { ...field, resolve: defaultMergedResolver, }; newTargetFieldConfigMap[fieldName] = newField; - }); + } let wrapIndex = this.numWraps - 1; let wrappingTypeName = this.wrappingTypeNames[wrapIndex]; @@ -181,7 +181,7 @@ function collectFields( visitedFragmentNames = {} ): Array { if (selectionSet != null) { - selectionSet.selections.forEach(selection => { + for (const selection of selectionSet.selections) { switch (selection.kind) { case Kind.FIELD: fields.push(selection); @@ -201,7 +201,7 @@ function collectFields( // unreachable break; } - }); + } } return fields; @@ -242,7 +242,7 @@ function hoistFieldNodes({ if (index < path.length) { const pathSegment = path[index]; - collectFields(fieldNode.selectionSet, fragments).forEach((possibleFieldNode: FieldNode) => { + for (const possibleFieldNode of collectFields(fieldNode.selectionSet, fragments)) { if (possibleFieldNode.name.value === pathSegment) { const newWrappingPath = wrappingPath.concat([alias]); @@ -259,9 +259,9 @@ function hoistFieldNodes({ }) ); } - }); + } } else { - collectFields(fieldNode.selectionSet, fragments).forEach((possibleFieldNode: FieldNode) => { + for (const possibleFieldNode of collectFields(fieldNode.selectionSet, fragments)) { if (!fieldNames || fieldNames.includes(possibleFieldNode.name.value)) { const nextIndex = transformationContext.nextIndex; transformationContext.nextIndex++; @@ -272,7 +272,7 @@ function hoistFieldNodes({ }; newFieldNodes.push(aliasFieldNode(possibleFieldNode, indexingAlias)); } - }); + } } return newFieldNodes; @@ -285,22 +285,22 @@ export function dehoistValue(originalValue: any, context: WrapFieldsTransformati const newValue = Object.create(null); - Object.keys(originalValue).forEach(alias => { + for (const alias in originalValue) { let obj = newValue; const path = context.paths[alias]; if (path == null) { newValue[alias] = originalValue[alias]; - return; + continue; } const pathToField = path.pathToField; const fieldAlias = path.alias; - pathToField.forEach(key => { + for (const key of pathToField) { obj = obj[key] = obj[key] || Object.create(null); - }); + } obj[fieldAlias] = originalValue[alias]; - }); + } return newValue; } @@ -320,20 +320,20 @@ function dehoistErrors( } let newPath: Array = []; - originalPath.forEach(pathSegment => { + for (const pathSegment of originalPath) { if (typeof pathSegment !== 'string') { newPath.push(pathSegment); - return; + continue; } const path = context.paths[pathSegment]; if (path == null) { newPath.push(pathSegment); - return; + continue; } newPath = newPath.concat(path.pathToField, [path.alias]); - }); + } return relocatedError(error, newPath); }); diff --git a/packages/wrap/src/types.ts b/packages/wrap/src/types.ts index 3eb9c7ef05e..2ff018fd6c9 100644 --- a/packages/wrap/src/types.ts +++ b/packages/wrap/src/types.ts @@ -1,7 +1,4 @@ import { - GraphQLSchema, - GraphQLFieldResolver, - BuildSchemaOptions, GraphQLInputFieldConfig, GraphQLFieldConfig, FieldNode, @@ -13,18 +10,7 @@ import { GraphQLEnumValueConfig, } from 'graphql'; import { DelegationContext } from '@graphql-tools/delegate'; -import { Executor, Subscriber, Request, Maybe } from '@graphql-tools/utils'; - -export interface IMakeRemoteExecutableSchemaOptions> { - schema: GraphQLSchema | string; - executor: Executor; - subscriber?: Subscriber; - createResolver?: ( - executor: Executor, - subscriber?: Subscriber | undefined - ) => GraphQLFieldResolver; - buildSchemaOptions?: BuildSchemaOptions; -} +import { Request, Maybe } from '@graphql-tools/utils'; export type InputFieldTransformer = ( typeName: string, diff --git a/packages/wrap/src/wrapSchema.ts b/packages/wrap/src/wrapSchema.ts index 6d7f9d1ed9c..2532cd8611c 100644 --- a/packages/wrap/src/wrapSchema.ts +++ b/packages/wrap/src/wrapSchema.ts @@ -33,16 +33,16 @@ function createWrappingSchema( const config = type.toConfig(); const fieldConfigMap = config.fields; - Object.keys(fieldConfigMap).forEach(fieldName => { + for (const fieldName in fieldConfigMap) { const field = fieldConfigMap[fieldName]; if (field == null) { - return; + continue; } fieldConfigMap[fieldName] = { ...field, ...proxyingResolvers[type.name]?.[fieldName], }; - }); + } return new GraphQLObjectType(config); }, @@ -50,14 +50,14 @@ function createWrappingSchema( const config = type.toConfig(); config.isTypeOf = undefined; - Object.keys(config.fields).forEach(fieldName => { + for (const fieldName in config.fields) { const field = config.fields[fieldName]; if (field == null) { - return; + continue; } field.resolve = defaultMergedResolver; field.subscribe = undefined; - }); + } return new GraphQLObjectType(config); }, diff --git a/packages/wrap/tests/fixtures/schemas.ts b/packages/wrap/tests/fixtures/schemas.ts index 8c405f4dfa0..b29511136b2 100644 --- a/packages/wrap/tests/fixtures/schemas.ts +++ b/packages/wrap/tests/fixtures/schemas.ts @@ -12,8 +12,6 @@ import { GraphQLInterfaceType, } from 'graphql'; -import isPromise from 'is-promise'; - import { ValueOrPromise } from 'value-or-promise'; import { introspectSchema } from '../../src/introspect'; @@ -21,6 +19,7 @@ import { IResolvers, ExecutionResult, mapAsyncIterator, + isAsyncIterable, } from '@graphql-tools/utils'; import { makeExecutableSchema } from '@graphql-tools/schema'; import { SubschemaConfig, ExecutionParams } from '@graphql-tools/delegate'; @@ -188,10 +187,6 @@ export const sampleData: { }, }; -function values(o: { [s: string]: T }): Array { - return Object.keys(o).map((k) => o[k]); -} - function coerceString(value: any): string { if (Array.isArray(value)) { throw new TypeError( @@ -225,9 +220,9 @@ function parseLiteral(ast: ValueNode): any { return parseFloat(ast.value); case Kind.OBJECT: { const value = Object.create(null); - ast.fields.forEach((field) => { + for (const field of ast.fields) { value[field.name.value] = parseLiteral(field.value); - }); + } return value; } @@ -349,7 +344,7 @@ const propertyResolvers: IResolvers = { }, properties(_root, { limit }) { - const list = values(sampleData.Property); + const list = Object.values(sampleData.Property); return limit ? list.slice(0, limit) : list; }, @@ -464,7 +459,7 @@ const productTypeDefs = ` const productResolvers: IResolvers = { Query: { products(_root) { - const list = values(sampleData.Product); + const list = Object.values(sampleData.Product); return list; }, }, @@ -550,7 +545,7 @@ const bookingResolvers: IResolvers = { return sampleData.Booking[id]; }, bookingsByPropertyId(_parent, { propertyId, limit }) { - const list = values(sampleData.Booking).filter( + const list = Object.values(sampleData.Booking).filter( (booking: Booking) => booking.propertyId === propertyId, ); return limit ? list.slice(0, limit) : list; @@ -559,11 +554,11 @@ const bookingResolvers: IResolvers = { return sampleData.Customer[id]; }, bookings(_parent, { limit }) { - const list = values(sampleData.Booking); + const list = Object.values(sampleData.Booking); return limit ? list.slice(0, limit) : list; }, customers(_parent, { limit }) { - const list = values(sampleData.Customer); + const list = Object.values(sampleData.Customer); return limit ? list.slice(0, limit) : list; }, }, @@ -600,13 +595,13 @@ const bookingResolvers: IResolvers = { Customer: { bookings(parent: Customer, { limit }) { - const list = values(sampleData.Booking).filter( + const list = Object.values(sampleData.Booking).filter( (booking: Booking) => booking.customerId === parent.id, ); return limit ? list.slice(0, limit) : list; }, vehicle(parent: Customer) { - return sampleData.Vehicle[parent.vehicleId]; + return parent.vehicleId && sampleData.Vehicle[parent.vehicleId]; }, error() { throw new Error('Customer.error error'); @@ -699,16 +694,15 @@ function makeExecutorFromSchema(schema: GraphQLSchema) { function makeSubscriberFromSchema(schema: GraphQLSchema) { return async ({ document, variables, context }: ExecutionParams) => { - const result = subscribe( + const result = await subscribe( schema, document, null, context, variables, - ) as Promise> | ExecutionResult>; - if (isPromise(result)) { - return result.then(asyncIterator => - mapAsyncIterator(asyncIterator as AsyncIterator, (originalResult: ExecutionResult) => JSON.parse(JSON.stringify(originalResult)))); + ); + if (isAsyncIterable>(result)) { + return mapAsyncIterator, TReturn>(result, (originalResult: ExecutionResult) => JSON.parse(JSON.stringify(originalResult))); } return JSON.parse(JSON.stringify(result)); }; diff --git a/packages/wrap/tests/fragmentsAreNotDuplicated.test.ts b/packages/wrap/tests/fragmentsAreNotDuplicated.test.ts index 02df525b46a..7cc4a106df4 100644 --- a/packages/wrap/tests/fragmentsAreNotDuplicated.test.ts +++ b/packages/wrap/tests/fragmentsAreNotDuplicated.test.ts @@ -83,6 +83,8 @@ const variables = { function assertNoDuplicateFragmentErrors(result: ExecutionResult) { // Run assertion against each array element for better test failure output. if (result.errors != null) { - result.errors.forEach((error) => expect(error.message).toBe('')); + for (const error of result.errors) { + expect(error.message).toBe('') + } } } diff --git a/packages/wrap/tests/makeRemoteExecutableSchema.test.ts b/packages/wrap/tests/makeRemoteExecutableSchema.test.ts index 554d656108b..fbfeeb7669a 100644 --- a/packages/wrap/tests/makeRemoteExecutableSchema.test.ts +++ b/packages/wrap/tests/makeRemoteExecutableSchema.test.ts @@ -6,9 +6,10 @@ import { execute, print, ExecutionResult, + buildSchema, } from 'graphql'; -import { makeRemoteExecutableSchema } from '../src/index'; +import { wrapSchema } from '../src/index'; import { propertySchema, @@ -22,7 +23,7 @@ describe('remote queries', () => { let schema: GraphQLSchema; beforeAll(async () => { const remoteSubschemaConfig = await makeSchemaRemote(propertySchema); - schema = makeRemoteExecutableSchema(remoteSubschemaConfig); + schema = wrapSchema(remoteSubschemaConfig); }); test('should work', async () => { @@ -60,7 +61,7 @@ describe('remote subscriptions', () => { let schema: GraphQLSchema; beforeAll(async () => { const remoteSubschemaConfig = await makeSchemaRemote(subscriptionSchema); - schema = makeRemoteExecutableSchema(remoteSubschemaConfig); + schema = wrapSchema(remoteSubschemaConfig); }); test('should work', async () => { @@ -133,7 +134,7 @@ describe('remote subscriptions', () => { }); describe('respects buildSchema options', () => { - const schema = ` + const typeDefs = /* GraphQL */` type Query { # Field description custom: CustomScalar! @@ -144,26 +145,25 @@ describe('respects buildSchema options', () => { `; test('without comment descriptions', () => { - const remoteSchema = makeRemoteExecutableSchema({ schema }); + const remoteSchema = wrapSchema({ schema: buildSchema(typeDefs) }); const customScalar = remoteSchema.getType('CustomScalar'); - expect(customScalar.description).toBeUndefined(); + expect(customScalar?.description).toBeUndefined(); }); test('with comment descriptions', () => { - const remoteSchema = makeRemoteExecutableSchema({ - schema, - buildSchemaOptions: { commentDescriptions: true }, + const remoteSchema = wrapSchema({ + schema: buildSchema(typeDefs, { commentDescriptions: true }), }); - const field = remoteSchema.getQueryType().getFields()['custom']; + const field = remoteSchema.getQueryType()!.getFields()['custom']; expect(field.description).toBe('Field description'); const customScalar = remoteSchema.getType('CustomScalar'); - expect(customScalar.description).toBe('Scalar description'); + expect(customScalar?.description).toBe('Scalar description'); }); describe('when query for multiple fields', () => { - const schema = ` + const typeDefs = ` type Query { fieldA: Int! fieldB: Int! @@ -188,8 +188,8 @@ describe('respects buildSchema options', () => { }, }); }; - const remoteSchema = makeRemoteExecutableSchema({ - schema, + const remoteSchema = wrapSchema({ + schema: buildSchema(typeDefs), executor, }); diff --git a/packages/wrap/tests/requests.test.ts b/packages/wrap/tests/requests.test.ts index f38108cabba..b103715b453 100644 --- a/packages/wrap/tests/requests.test.ts +++ b/packages/wrap/tests/requests.test.ts @@ -12,11 +12,11 @@ function removeLocations(value: any): any { return value.map((v) => removeLocations(v)); } else if (typeof value === 'object') { const newValue = {}; - Object.keys(value).forEach((key) => { + for (const key in value) { if (key !== 'loc') { newValue[key] = removeLocations(value[key]); } - }); + } return newValue; } diff --git a/packages/wrap/tests/transformFilterInputObjectFields.test.ts b/packages/wrap/tests/transformFilterInputObjectFields.test.ts index 810d9b60858..9bd1e99a415 100644 --- a/packages/wrap/tests/transformFilterInputObjectFields.test.ts +++ b/packages/wrap/tests/transformFilterInputObjectFields.test.ts @@ -67,7 +67,7 @@ describe('FilterInputObjectFields', () => { const result = await graphql(transformedSchema, query); assertSome(result.data) - expect(result.data.test.field1).toBe('field1'); - expect(result.data.test.field2).toBe('field2'); + expect(result.data['test'].field1).toBe('field1'); + expect(result.data['test'].field2).toBe('field2'); }); }); diff --git a/packages/wrap/tests/transformFilterObjectFieldDirectives.test.ts b/packages/wrap/tests/transformFilterObjectFieldDirectives.test.ts index 733cf13b34d..d122d815e32 100644 --- a/packages/wrap/tests/transformFilterObjectFieldDirectives.test.ts +++ b/packages/wrap/tests/transformFilterObjectFieldDirectives.test.ts @@ -23,10 +23,10 @@ describe('FilterObjectFieldDirectives', () => { ], }); - const fields = transformedSchema.getType('Query').getFields(); - expect(fields.alpha.astNode.directives.length).toEqual(0); - expect(fields.bravo.astNode.directives.length).toEqual(1); - expect(fields.charlie.astNode.directives.length).toEqual(0); - expect(fields.delta.astNode.directives.length).toEqual(1); + const fields = transformedSchema.getQueryType()?.getFields(); + expect(fields?.['alpha']?.astNode?.directives?.length).toEqual(0); + expect(fields?.['bravo']?.astNode?.directives?.length).toEqual(1); + expect(fields?.['charlie']?.astNode?.directives?.length).toEqual(0); + expect(fields?.['delta']?.astNode?.directives?.length).toEqual(1); }); }); diff --git a/packages/wrap/tests/transformMapLeafValues.test.ts b/packages/wrap/tests/transformMapLeafValues.test.ts index 09650ac9cbb..21712a5f4b8 100644 --- a/packages/wrap/tests/transformMapLeafValues.test.ts +++ b/packages/wrap/tests/transformMapLeafValues.test.ts @@ -50,7 +50,7 @@ describe('MapLeafValues', () => { const result = await graphql(transformedSchema, query); assertSome(result.data) - expect(result.data.testEnum).toBe('THREE'); - expect(result.data.testScalar).toBe(15); + expect(result.data['testEnum']).toBe('THREE'); + expect(result.data['testScalar']).toBe(15); }); }); diff --git a/packages/wrap/tests/transformRemoveObjectFieldDeprecations.test.ts b/packages/wrap/tests/transformRemoveObjectFieldDeprecations.test.ts index f4b06c18ad9..888a87da065 100644 --- a/packages/wrap/tests/transformRemoveObjectFieldDeprecations.test.ts +++ b/packages/wrap/tests/transformRemoveObjectFieldDeprecations.test.ts @@ -25,12 +25,12 @@ describe('RemoveObjectFieldDeprecations', () => { const Test = transformedSchema.getType('Test') assertGraphQLObjectType(Test) const fields = Test.getFields(); - assertSome(fields.first) - expect(fields.first.deprecationReason).toEqual('do not remove'); - assertSome(fields.second) - expect(fields.second.deprecationReason).toBeUndefined(); - expect(fields.first.astNode?.directives?.length).toEqual(1); - expect(fields.second.astNode?.directives?.length).toEqual(0); + assertSome(fields['first']) + expect(fields['first'].deprecationReason).toEqual('do not remove'); + assertSome(fields['second']) + expect(fields['second'].deprecationReason).toBeUndefined(); + expect(fields['first'].astNode?.directives?.length).toEqual(1); + expect(fields['second'].astNode?.directives?.length).toEqual(0); }); test('removes deprecations by reason regex', async () => { @@ -44,11 +44,11 @@ describe('RemoveObjectFieldDeprecations', () => { const Test = transformedSchema.getType('Test') assertGraphQLObjectType(Test) const fields = Test.getFields(); - assertSome(fields.first) - expect(fields.first.deprecationReason).toBeUndefined(); - assertSome(fields.second) - expect(fields.second.deprecationReason).toBeUndefined(); - expect(fields.first.astNode?.directives?.length).toEqual(0); - expect(fields.second.astNode?.directives?.length).toEqual(0); + assertSome(fields['first']) + expect(fields['first'].deprecationReason).toBeUndefined(); + assertSome(fields['second']) + expect(fields['second'].deprecationReason).toBeUndefined(); + expect(fields['first'].astNode?.directives?.length).toEqual(0); + expect(fields['second'].astNode?.directives?.length).toEqual(0); }); }); diff --git a/packages/wrap/tests/transformRemoveObjectFieldDirectives.test.ts b/packages/wrap/tests/transformRemoveObjectFieldDirectives.test.ts index 6186cada3a6..745a885d748 100644 --- a/packages/wrap/tests/transformRemoveObjectFieldDirectives.test.ts +++ b/packages/wrap/tests/transformRemoveObjectFieldDirectives.test.ts @@ -29,14 +29,14 @@ describe('RemoveObjectFieldDirectives', () => { const Test = transformedSchema.getType('Test') assertGraphQLObjectType(Test) const fields = Test.getFields(); - assertSome(fields.id) - expect(fields.id.astNode?.directives?.length).toEqual(1); - assertSome(fields.first) - expect(fields.first.astNode?.directives?.length).toEqual(0); - assertSome(fields.second) - expect(fields.second.astNode?.directives?.length).toEqual(0); - assertSome(fields.third) - expect(fields.third.astNode?.directives?.length).toEqual(0); + assertSome(fields['id']) + expect(fields['id'].astNode?.directives?.length).toEqual(1); + assertSome(fields['first']) + expect(fields['first'].astNode?.directives?.length).toEqual(0); + assertSome(fields['second']) + expect(fields['second'].astNode?.directives?.length).toEqual(0); + assertSome(fields['third']) + expect(fields['third'].astNode?.directives?.length).toEqual(0); }); test('removes directives by name regex', async () => { @@ -50,14 +50,14 @@ describe('RemoveObjectFieldDirectives', () => { const Test = transformedSchema.getType('Test') assertGraphQLObjectType(Test) const fields = Test.getFields(); - assertSome(fields.id) - expect(fields.id.astNode?.directives?.length).toEqual(1); - assertSome(fields.first) - expect(fields.first.astNode?.directives?.length).toEqual(0); - assertSome(fields.second) - expect(fields.second.astNode?.directives?.length).toEqual(0); - assertSome(fields.third) - expect(fields.third.astNode?.directives?.length).toEqual(0); + assertSome(fields['id']) + expect(fields['id'].astNode?.directives?.length).toEqual(1); + assertSome(fields['first']) + expect(fields['first'].astNode?.directives?.length).toEqual(0); + assertSome(fields['second']) + expect(fields['second'].astNode?.directives?.length).toEqual(0); + assertSome(fields['third']) + expect(fields['third'].astNode?.directives?.length).toEqual(0); }); test('removes directives by argument', async () => { @@ -71,14 +71,14 @@ describe('RemoveObjectFieldDirectives', () => { const Test = transformedSchema.getType('Test') assertGraphQLObjectType(Test) const fields = Test.getFields(); - assertSome(fields.id) - expect(fields.id.astNode?.directives?.length).toEqual(0); - assertSome(fields.first) - expect(fields.first.astNode?.directives?.length).toEqual(1); - assertSome(fields.second) - expect(fields.second.astNode?.directives?.length).toEqual(0); - assertSome(fields.third) - expect(fields.third.astNode?.directives?.length).toEqual(0); + assertSome(fields['id']) + expect(fields['id'].astNode?.directives?.length).toEqual(0); + assertSome(fields['first']) + expect(fields['first'].astNode?.directives?.length).toEqual(1); + assertSome(fields['second']) + expect(fields['second'].astNode?.directives?.length).toEqual(0); + assertSome(fields['third']) + expect(fields['third'].astNode?.directives?.length).toEqual(0); }); test('removes directives by argument regex', async () => { @@ -92,13 +92,13 @@ describe('RemoveObjectFieldDirectives', () => { const Test = transformedSchema.getType('Test') assertGraphQLObjectType(Test) const fields = Test.getFields(); - assertSome(fields.id) - expect(fields.id.astNode?.directives?.length).toEqual(0); - assertSome(fields.first) - expect(fields.first.astNode?.directives?.length).toEqual(0); - assertSome(fields.second) - expect(fields.second.astNode?.directives?.length).toEqual(0); - assertSome(fields.third) - expect(fields.third.astNode?.directives?.length).toEqual(0); + assertSome(fields['id']) + expect(fields['id'].astNode?.directives?.length).toEqual(0); + assertSome(fields['first']) + expect(fields['first'].astNode?.directives?.length).toEqual(0); + assertSome(fields['second']) + expect(fields['second'].astNode?.directives?.length).toEqual(0); + assertSome(fields['third']) + expect(fields['third'].astNode?.directives?.length).toEqual(0); }); }); diff --git a/packages/wrap/tests/transformRemoveObjectFieldsWithDeprecation.test.ts b/packages/wrap/tests/transformRemoveObjectFieldsWithDeprecation.test.ts index 1989214acfa..a60dabb5615 100644 --- a/packages/wrap/tests/transformRemoveObjectFieldsWithDeprecation.test.ts +++ b/packages/wrap/tests/transformRemoveObjectFieldsWithDeprecation.test.ts @@ -24,8 +24,8 @@ describe('RemoveObjectFieldsWithDeprecation', () => { const Test = transformedSchema.getType('Test') assertGraphQLObjectType(Test) const fields = Test.getFields(); - expect(fields.first).toBeDefined(); - expect(fields.second).toBeUndefined(); + expect(fields['first']).toBeDefined(); + expect(fields['second']).toBeUndefined(); }); test('removes deprecated fields by reason regex', async () => { @@ -39,7 +39,7 @@ describe('RemoveObjectFieldsWithDeprecation', () => { const Test = transformedSchema.getType('Test') assertGraphQLObjectType(Test) const fields = Test.getFields(); - expect(fields.first).toBeUndefined(); - expect(fields.second).toBeUndefined(); + expect(fields['first']).toBeUndefined(); + expect(fields['second']).toBeUndefined(); }); }); diff --git a/packages/wrap/tests/transformRemoveObjectFieldsWithDirective.test.ts b/packages/wrap/tests/transformRemoveObjectFieldsWithDirective.test.ts index 8edc9c142ff..978bae84505 100644 --- a/packages/wrap/tests/transformRemoveObjectFieldsWithDirective.test.ts +++ b/packages/wrap/tests/transformRemoveObjectFieldsWithDirective.test.ts @@ -29,10 +29,10 @@ describe('RemoveObjectFieldsWithDirective', () => { const Test = transformedSchema.getType('Test') assertGraphQLObjectType(Test) const fields = Test.getFields(); - expect(fields.first).toBeUndefined(); - expect(fields.second).toBeUndefined(); - expect(fields.third).toBeUndefined(); - expect(fields.fourth).toBeDefined(); + expect(fields['first']).toBeUndefined(); + expect(fields['second']).toBeUndefined(); + expect(fields['third']).toBeUndefined(); + expect(fields['fourth']).toBeDefined(); }); test('removes directive fields by name regex', async () => { @@ -46,10 +46,10 @@ describe('RemoveObjectFieldsWithDirective', () => { const Test = transformedSchema.getType('Test') assertGraphQLObjectType(Test) const fields = Test.getFields(); - expect(fields.first).toBeUndefined(); - expect(fields.second).toBeUndefined(); - expect(fields.third).toBeUndefined(); - expect(fields.fourth).toBeDefined(); + expect(fields['first']).toBeUndefined(); + expect(fields['second']).toBeUndefined(); + expect(fields['third']).toBeUndefined(); + expect(fields['fourth']).toBeDefined(); }); test('removes directive fields by argument', async () => { @@ -63,10 +63,10 @@ describe('RemoveObjectFieldsWithDirective', () => { const Test = transformedSchema.getType('Test') assertGraphQLObjectType(Test) const fields = Test.getFields(); - expect(fields.first).toBeDefined(); - expect(fields.second).toBeUndefined(); - expect(fields.third).toBeUndefined(); - expect(fields.third).toBeUndefined(); + expect(fields['first']).toBeDefined(); + expect(fields['second']).toBeUndefined(); + expect(fields['third']).toBeUndefined(); + expect(fields['third']).toBeUndefined(); }); test('removes directive fields by argument regex', async () => { @@ -80,9 +80,9 @@ describe('RemoveObjectFieldsWithDirective', () => { const Test = transformedSchema.getType('Test') assertGraphQLObjectType(Test) const fields = Test.getFields(); - expect(fields.first).toBeUndefined(); - expect(fields.second).toBeUndefined(); - expect(fields.third).toBeUndefined(); - expect(fields.third).toBeUndefined(); + expect(fields['first']).toBeUndefined(); + expect(fields['second']).toBeUndefined(); + expect(fields['third']).toBeUndefined(); + expect(fields['third']).toBeUndefined(); }); }); diff --git a/packages/wrap/tests/transformRenameInputObjectFields.test.ts b/packages/wrap/tests/transformRenameInputObjectFields.test.ts index 4ad2abd005e..f40682d05d6 100644 --- a/packages/wrap/tests/transformRenameInputObjectFields.test.ts +++ b/packages/wrap/tests/transformRenameInputObjectFields.test.ts @@ -55,8 +55,8 @@ describe('RenameInputObjectFields', () => { const result = await graphql(transformedSchema, query); assertSome(result.data) - expect(result.data.test.field1).toBe('field1'); - expect(result.data.test.field2).toBe('field2'); + expect(result.data['test'].field1).toBe('field1'); + expect(result.data['test'].field2).toBe('field2'); }); test('renaming with variables works', async () => { @@ -118,7 +118,7 @@ describe('RenameInputObjectFields', () => { } const result = await graphql(transformedSchema, query, {}, {}, variables); assertSome(result.data) - expect(result.data.test.field1).toBe('field1'); - expect(result.data.test.field2).toBe('field2'); + expect(result.data['test'].field1).toBe('field1'); + expect(result.data['test'].field2).toBe('field2'); }); }); diff --git a/patches/bob-the-bundler+1.4.1.patch b/patches/bob-the-bundler+1.4.1.patch new file mode 100644 index 00000000000..ed97b313bda --- /dev/null +++ b/patches/bob-the-bundler+1.4.1.patch @@ -0,0 +1,22 @@ +diff --git a/node_modules/bob-the-bundler/dist/commands/build.js b/node_modules/bob-the-bundler/dist/commands/build.js +index e1c8d4e..cb9fa6a 100644 +--- a/node_modules/bob-the-bundler/dist/commands/build.js ++++ b/node_modules/bob-the-bundler/dist/commands/build.js +@@ -87,7 +87,7 @@ async function buildSingle({ distDir, distPath = '' }) { + // generates + const commonOutputOptions = { + preferConst: true, +- sourcemap: true, ++ sourcemap: false, + }; + const generates = [ + { +@@ -148,7 +148,7 @@ async function build({ packagePath, cwd, pkg, fullName, config, reporter, distDi + // generates + const commonOutputOptions = { + preferConst: true, +- sourcemap: true, ++ sourcemap: false, + }; + const generates = [ + { diff --git a/tsconfig.build.es5.json b/tsconfig.build.es5.json index c42f1e0d7d6..879594f93ff 100644 --- a/tsconfig.build.es5.json +++ b/tsconfig.build.es5.json @@ -2,6 +2,9 @@ "extends": "./tsconfig.build.json", "compilerOptions": { "outDir": "dist-es5", - "target": "es5" + "target": "es5", + "tsBuildInfoFile": "./node_modules/tsconfig.build.es5.tsbuildinfo", + "sourceMap": false, + "inlineSourceMap": false } } diff --git a/tsconfig.build.json b/tsconfig.build.json index 4abd5dd07b0..a4e8b33e761 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,5 +1,10 @@ { "extends": "./tsconfig.json", + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/tsconfig.build.tsbuildinfo", + "sourceMap": false, + "inlineSourceMap": false + }, "exclude": [ "**/test/*.ts", "*.spec.ts", diff --git a/tsconfig.json b/tsconfig.json index 86feb85ea87..3221d62b0b1 100755 --- a/tsconfig.json +++ b/tsconfig.json @@ -30,8 +30,10 @@ "paths": { "@graphql-tools/*-loader": ["packages/loaders/*/src/index.ts", "packages/*-loader/src/index.ts"], "@graphql-tools/*": ["packages/*/src/index.ts"] - } + }, + + "tsBuildInfoFile": "./node_modules/tsconfig.tsbuildinfo" }, "include": ["packages"], - "exclude": ["**/node_modules", "**/dist"] + "exclude": ["**/node_modules", "**/dist", "**/test-files"] } diff --git a/website/docs/remote-schemas.md b/website/docs/remote-schemas.md index 08b774d0a42..a07a70c7edb 100644 --- a/website/docs/remote-schemas.md +++ b/website/docs/remote-schemas.md @@ -261,23 +261,3 @@ const schema = wrapSchema({ ``` Note that within the `defaultCreateProxyingResolver` function, `delegateToSchema` receives `executor` and `subscriber` functions stored on the subschema config object originally passed to `wrapSchema`. As above, use of the the `createProxyingResolver` option is helpful when you want to customize additional functionality at resolver creation time. If you just want to customize how things are proxied at the time that they are proxied, you can make do just with custom executors and subscribers. - -### makeRemoteExecutableSchema(options) - -What about `makeRemoteExecutableSchema`, the function used in older versions to access remote schemas? It still works -- just now under the hood calling `wrapSchema`. There is essentially no longer any need to use `makeRemoteExecutableSchema` directly, but we've kept it around for backwards compatibility. - -You can still pass a `createResolver` function to `makeRemoteExecutableSchema` to override how the fetch resolvers are created and executed. The `createResolver` param accepts an `Executor` as its first argument (and a `Subscriber` as its second) and returns a resolver function. This opens up the possibility for users to create batching mechanisms for fetches. As above, it is likely easier to just customize the `executor` function itself. - -Given a GraphQL.js schema (can be a non-executable client schema made by `buildClientSchema`) and a [executor](#creating-an-executor), `makeRemoteExecutableSchema` produce a GraphQL Schema that routes all requests to the executor: - -```js -import { makeRemoteExecutableSchema } from '@graphql-tools/wrap'; - -const createResolver: (executor: Executor) => GraphQLFieldResolver = // . . . - -const schema = makeRemoteExecutableSchema({ - schema, - executor, - createResolver -}); -``` diff --git a/website/docs/schema-directives.md b/website/docs/schema-directives.md index f9ced8a2d55..124bbfaaf07 100644 --- a/website/docs/schema-directives.md +++ b/website/docs/schema-directives.md @@ -615,9 +615,9 @@ function uniqueIDDirective(directiveName: string) { resolve(object: any) { const hash = createHash('sha1'); hash.update(type.name); - from.forEach((fieldName: string) => { + for (const fieldName of from) { hash.update(String(object[fieldName])); - }); + } return hash.digest('hex'); }, }; @@ -702,7 +702,7 @@ export function attachDirectiveResolvers( const newFieldConfig = { ...fieldConfig }; const directives = getDirectives(schema, fieldConfig); - Object.keys(directives).forEach(directiveName => { + for (const directiveName in directives) { if (directiveResolvers[directiveName]) { const resolver = directiveResolvers[directiveName]; const originalResolver = newFieldConfig.resolve != null ? newFieldConfig.resolve : defaultFieldResolver; @@ -724,7 +724,7 @@ export function attachDirectiveResolvers( ); }; } - }); + } return newFieldConfig; },