From 6601affa9270d803d3298b92eb42c2a5e924a51b Mon Sep 17 00:00:00 2001 From: Arda TANRIKULU Date: Wed, 7 Dec 2022 17:07:14 +0300 Subject: [PATCH 1/6] fix: add cancellation to AsyncIterable correctly and leak --- .changeset/brown-vans-search.md | 6 ++++++ packages/executors/http/src/index.ts | 4 ++-- packages/utils/src/withCancel.ts | 8 ++------ 3 files changed, 10 insertions(+), 8 deletions(-) create mode 100644 .changeset/brown-vans-search.md diff --git a/.changeset/brown-vans-search.md b/.changeset/brown-vans-search.md new file mode 100644 index 00000000000..31d78d1cf06 --- /dev/null +++ b/.changeset/brown-vans-search.md @@ -0,0 +1,6 @@ +--- +'@graphql-tools/executor-http': patch +'@graphql-tools/utils': patch +--- + +Fix leak on Node 14 and add cancellation to async iterables correctly diff --git a/packages/executors/http/src/index.ts b/packages/executors/http/src/index.ts index e345436e53a..a7536c930fa 100644 --- a/packages/executors/http/src/index.ts +++ b/packages/executors/http/src/index.ts @@ -8,7 +8,6 @@ import { SyncExecutor, } from '@graphql-tools/utils'; import { GraphQLResolveInfo, print } from 'graphql'; -import { cancelNeeded } from './addCancelToResponseStream.js'; import { isLiveQueryOperationDefinitionNode } from './isLiveQueryOperationDefinitionNode.js'; import { prepareGETUrl } from './prepareGETUrl.js'; import { ValueOrPromise } from 'value-or-promise'; @@ -78,7 +77,7 @@ export function buildHTTPExecutor( export function buildHTTPExecutor(options?: HTTPExecutorOptions): Executor { const executor = (request: ExecutionRequest) => { const fetchFn = request.extensions?.fetch ?? options?.fetch ?? defaultFetch; - const controller = cancelNeeded() ? new AbortController() : undefined; + let controller: AbortController; let method = request.extensions?.method || options?.method || 'POST'; const operationAst = getOperationASTFromRequest(request); @@ -113,6 +112,7 @@ export function buildHTTPExecutor(options?: HTTPExecutorOptions): Executor { if (!controller?.signal.aborted) { controller?.abort(); diff --git a/packages/utils/src/withCancel.ts b/packages/utils/src/withCancel.ts index 7f11f2fdb2a..904441e071d 100644 --- a/packages/utils/src/withCancel.ts +++ b/packages/utils/src/withCancel.ts @@ -49,15 +49,11 @@ export function getAsyncIterableWithCancel = Reflect.apply( - existingPropValue[Symbol.asyncIterator], - asyncIterable, - [] - ); + const asyncIterator: AsyncIterator = Reflect.apply(existingPropValue, asyncIterable, []); return getAsyncIteratorWithCancel(asyncIterator, onCancel); }; } else if (typeof existingPropValue === 'function') { - return proxyMethodFactory(asyncIterable, existingPropValue[Symbol.asyncIterator]); + return proxyMethodFactory(asyncIterable, existingPropValue); } return existingPropValue; }, From 7ae2f5c9d5073945982560264e1092917380c4ff Mon Sep 17 00:00:00 2001 From: Arda TANRIKULU Date: Wed, 7 Dec 2022 17:20:38 +0300 Subject: [PATCH 2/6] Go --- packages/executors/http/src/index.ts | 7 ++++++- packages/loaders/url/tests/yoga-compat.spec.ts | 1 + packages/utils/src/withCancel.ts | 4 ++-- yarn.lock | 18 ++++++++++++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/packages/executors/http/src/index.ts b/packages/executors/http/src/index.ts index a7536c930fa..9ba154a3dcc 100644 --- a/packages/executors/http/src/index.ts +++ b/packages/executors/http/src/index.ts @@ -15,6 +15,7 @@ import { createFormDataFromVariables } from './createFormDataFromVariables.js'; import { handleEventStreamResponse } from './handleEventStreamResponse.js'; import { handleMultipartMixedResponse } from './handleMultipartMixedResponse.js'; import { fetch as defaultFetch, AbortController } from '@whatwg-node/fetch'; +import { cancelNeeded } from './addCancelToResponseStream.js'; export type SyncFetchFn = (url: string, init?: RequestInit, context?: any, info?: GraphQLResolveInfo) => SyncResponse; export type SyncResponse = Omit & { @@ -77,7 +78,7 @@ export function buildHTTPExecutor( export function buildHTTPExecutor(options?: HTTPExecutorOptions): Executor { const executor = (request: ExecutionRequest) => { const fetchFn = request.extensions?.fetch ?? options?.fetch ?? defaultFetch; - let controller: AbortController; + let controller: AbortController | undefined; let method = request.extensions?.method || options?.method || 'POST'; const operationAst = getOperationASTFromRequest(request); @@ -120,6 +121,10 @@ export function buildHTTPExecutor(options?: HTTPExecutorOptions): Executor { switch (method) { case 'GET': { diff --git a/packages/loaders/url/tests/yoga-compat.spec.ts b/packages/loaders/url/tests/yoga-compat.spec.ts index 3fe3c461cda..2f018dfdf10 100644 --- a/packages/loaders/url/tests/yoga-compat.spec.ts +++ b/packages/loaders/url/tests/yoga-compat.spec.ts @@ -151,6 +151,7 @@ describe('Yoga Compatibility', () => { if (httpServer !== undefined) { await new Promise(resolve => httpServer.close(() => resolve())); } + await sleep(1000); }); it('should handle defer', async () => { diff --git a/packages/utils/src/withCancel.ts b/packages/utils/src/withCancel.ts index 904441e071d..ef9da10eb0c 100644 --- a/packages/utils/src/withCancel.ts +++ b/packages/utils/src/withCancel.ts @@ -49,11 +49,11 @@ export function getAsyncIterableWithCancel = Reflect.apply(existingPropValue, asyncIterable, []); + const asyncIterator: AsyncIterator = Reflect.apply(existingPropValue as any, asyncIterable as any, []); return getAsyncIteratorWithCancel(asyncIterator, onCancel); }; } else if (typeof existingPropValue === 'function') { - return proxyMethodFactory(asyncIterable, existingPropValue); + return proxyMethodFactory(asyncIterable, existingPropValue); } return existingPropValue; }, diff --git a/yarn.lock b/yarn.lock index 6b473de13cd..4a4581f26ce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1670,6 +1670,24 @@ dependencies: giscus "^1.2.3" +"@graphql-tools/executor@0.0.9": + version "0.0.9" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-0.0.9.tgz#f55f8cbe12d7989b0ff58cca9a041fbdde3dbd40" + integrity sha512-qLhQWXTxTS6gbL9INAQa4FJIqTd2tccnbs4HswOx35KnyLaLtREuQ8uTfU+5qMrRIBhuzpGdkP2ssqxLyOJ5rA== + dependencies: + "@graphql-tools/utils" "9.1.1" + "@graphql-typed-document-node/core" "3.1.1" + "@repeaterjs/repeater" "3.0.4" + tslib "^2.4.0" + value-or-promise "1.0.11" + +"@graphql-tools/utils@9.1.1": + version "9.1.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-9.1.1.tgz#b47ea8f0d18c038c5c1c429e72caa5c25039fbab" + integrity sha512-DXKLIEDbihK24fktR2hwp/BNIVwULIHaSTNTNhXS+19vgT50eX9wndx1bPxGwHnVBOONcwjXy0roQac49vdt/w== + dependencies: + tslib "^2.4.0" + "@graphql-tools/utils@^8.5.2", "@graphql-tools/utils@^8.8.0": version "8.13.1" resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-8.13.1.tgz#b247607e400365c2cd87ff54654d4ad25a7ac491" From 59f32b928dceb161098eef1aac3396ba2dfc8448 Mon Sep 17 00:00:00 2001 From: Arda TANRIKULU Date: Wed, 7 Dec 2022 17:39:57 +0300 Subject: [PATCH 3/6] Go --- .changeset/cuddly-wasps-listen.md | 5 +++++ packages/delegate/src/finalizeGatewayRequest.ts | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/cuddly-wasps-listen.md diff --git a/.changeset/cuddly-wasps-listen.md b/.changeset/cuddly-wasps-listen.md new file mode 100644 index 00000000000..c4f60e34150 --- /dev/null +++ b/.changeset/cuddly-wasps-listen.md @@ -0,0 +1,5 @@ +--- +'@graphql-tools/delegate': patch +--- + +Fix handling variables diff --git a/packages/delegate/src/finalizeGatewayRequest.ts b/packages/delegate/src/finalizeGatewayRequest.ts index aec7447b35a..4f1bd324053 100644 --- a/packages/delegate/src/finalizeGatewayRequest.ts +++ b/packages/delegate/src/finalizeGatewayRequest.ts @@ -16,7 +16,7 @@ import { SelectionSetNode, TypeInfo, TypeNameMetaFieldDef, - valueFromAST, + valueFromASTUntyped, VariableDefinitionNode, versionInfo as graphqlVersionInfo, visit, @@ -225,7 +225,7 @@ function updateArguments( let value: any; const existingValueNode = argumentNodeMap[argName]?.value; if (existingValueNode != null) { - value = valueFromAST(existingValueNode, argType, variableValues); + value = valueFromASTUntyped(existingValueNode, variableValues); } if (value == null) { value = serializeInputValue(argType, newArgs[argName]); From 4037c853d163c8af9b3f6cf95ef10b0c7d36cb4e Mon Sep 17 00:00:00 2001 From: Arda TANRIKULU Date: Wed, 7 Dec 2022 17:55:06 +0300 Subject: [PATCH 4/6] Go --- packages/loaders/url/tests/url-loader-browser.spec.ts | 1 - packages/loaders/url/tests/yoga-compat.spec.ts | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/loaders/url/tests/url-loader-browser.spec.ts b/packages/loaders/url/tests/url-loader-browser.spec.ts index fdddb10f73b..82a2457564f 100644 --- a/packages/loaders/url/tests/url-loader-browser.spec.ts +++ b/packages/loaders/url/tests/url-loader-browser.spec.ts @@ -265,7 +265,6 @@ describe('[url-loader] webpack bundle compat', () => { { data: { countdown: [3, 2] } }, { data: { countdown: [3, 2, 1] } }, { data: { countdown: [3, 2, 1, 0] } }, - { data: { countdown: [3, 2, 1, 0] } }, ]); }); diff --git a/packages/loaders/url/tests/yoga-compat.spec.ts b/packages/loaders/url/tests/yoga-compat.spec.ts index 2f018dfdf10..3954dc2f5ef 100644 --- a/packages/loaders/url/tests/yoga-compat.spec.ts +++ b/packages/loaders/url/tests/yoga-compat.spec.ts @@ -12,6 +12,7 @@ import { useEngine } from '@envelop/core'; import { useDeferStream } from '@graphql-yoga/plugin-defer-stream'; describe('Yoga Compatibility', () => { + jest.setTimeout(10000); const loader = new UrlLoader(); let httpServer: http.Server; const liveQueryStore = new InMemoryLiveQueryStore(); From d6155b22be3a023102ad22f03b13762da4232c27 Mon Sep 17 00:00:00 2001 From: Arda TANRIKULU Date: Wed, 7 Dec 2022 17:58:43 +0300 Subject: [PATCH 5/6] Hola --- .../loaders/url/tests/url-loader-browser.spec.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/loaders/url/tests/url-loader-browser.spec.ts b/packages/loaders/url/tests/url-loader-browser.spec.ts index 82a2457564f..b26c9177b74 100644 --- a/packages/loaders/url/tests/url-loader-browser.spec.ts +++ b/packages/loaders/url/tests/url-loader-browser.spec.ts @@ -259,13 +259,11 @@ describe('[url-loader] webpack bundle compat', () => { document as any ); - expect(results).toEqual([ - { data: { countdown: [] } }, - { data: { countdown: [3] } }, - { data: { countdown: [3, 2] } }, - { data: { countdown: [3, 2, 1] } }, - { data: { countdown: [3, 2, 1, 0] } }, - ]); + expect(results[0]).toEqual({ data: { countdown: [] } }); + expect(results[1]).toEqual({ data: { countdown: [3] } }); + expect(results[2]).toEqual({ data: { countdown: [3, 2] } }); + expect(results[3]).toEqual({ data: { countdown: [3, 2, 1] } }); + expect(results[4]).toEqual({ data: { countdown: [3, 2, 1, 0] } }); }); it('handles SSE subscription operations', async () => { From e71284640950351baf275c2025c7ddc7c9e89c6b Mon Sep 17 00:00:00 2001 From: Arda TANRIKULU Date: Wed, 7 Dec 2022 18:03:01 +0300 Subject: [PATCH 6/6] Go --- .github/workflows/tests.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3715a851a76..d8d262f9b73 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -127,6 +127,8 @@ jobs: - name: Build Packages run: yarn build - name: Test - run: yarn jest --no-watchman --ci browser - env: - TEST_BROWSER: true + uses: nick-fields/retry@v2 + with: + timeout_minutes: 10 + max_attempts: 5 + command: TEST_BROWSER=true yarn jest --no-watchman --ci browser