From 5db92db16c884f16fb253d3b93f1361e9247cca1 Mon Sep 17 00:00:00 2001 From: Marais Rossouw Date: Tue, 26 Jan 2021 17:21:42 +1000 Subject: [PATCH 1/9] feat: move to meros --- packages/graphiql-create-fetcher/README.md | 2 +- packages/graphiql-create-fetcher/package.json | 2 +- .../src/createFetcher.ts | 2 +- packages/graphiql-create-fetcher/src/lib.ts | 45 +++++++++++++------ packages/graphiql-toolkit/src/types.ts | 4 +- packages/graphiql/package.json | 2 - packages/graphiql/src/components/GraphiQL.tsx | 38 ++-------------- yarn.lock | 10 ++--- 8 files changed, 46 insertions(+), 59 deletions(-) diff --git a/packages/graphiql-create-fetcher/README.md b/packages/graphiql-create-fetcher/README.md index 0854f76c192..8df848552df 100644 --- a/packages/graphiql-create-fetcher/README.md +++ b/packages/graphiql-create-fetcher/README.md @@ -2,7 +2,7 @@ a utility for generating a full-featured fetcher for GraphiQL. -under the hood, it uses [`graphql-ws`](https://www.npmjs.com/package/graphql-ws) and [`fetch-multipart-graphql`](https://www.npmjs.com/package/fetch-multipart-graphql) to follow the [GraphQL over HTTP Working Group Spec](https://github.com/graphql/graphql-over-http) both accepted and advanced proposals. +under the hood, it uses [`graphql-ws`](https://www.npmjs.com/package/graphql-ws) and [`meros`](https://www.npmjs.com/package/meros) to follow the [GraphQL over HTTP Working Group Spec](https://github.com/graphql/graphql-over-http) both accepted and advanced proposals. ### Setup diff --git a/packages/graphiql-create-fetcher/package.json b/packages/graphiql-create-fetcher/package.json index ddc3d690330..06863ad9500 100644 --- a/packages/graphiql-create-fetcher/package.json +++ b/packages/graphiql-create-fetcher/package.json @@ -21,7 +21,7 @@ "dependencies": { "graphql-ws": "^4.1.0", "subscriptions-transport-ws": "^0.9.18", - "fetch-multipart-graphql": "^3.0.0", + "meros": "^1.0.0", "@n1ru4l/push-pull-async-iterable-iterator": "^2.0.1", "@graphiql/toolkit": "^0.0.1" }, diff --git a/packages/graphiql-create-fetcher/src/createFetcher.ts b/packages/graphiql-create-fetcher/src/createFetcher.ts index ef2ee015e4e..e0897787584 100644 --- a/packages/graphiql-create-fetcher/src/createFetcher.ts +++ b/packages/graphiql-create-fetcher/src/createFetcher.ts @@ -46,7 +46,7 @@ export function createGraphiQLFetcher(options: CreateFetcherOptions): Fetcher { } const httpFetcher = options.enableIncrementalDelivery - ? createMultipartFetcher(options) + ? createMultipartFetcher(options, httpFetch) : simpleFetcher; return (graphQLParams, opts) => { diff --git a/packages/graphiql-create-fetcher/src/lib.ts b/packages/graphiql-create-fetcher/src/lib.ts index a44c3bc92cd..17abdb2afaf 100644 --- a/packages/graphiql-create-fetcher/src/lib.ts +++ b/packages/graphiql-create-fetcher/src/lib.ts @@ -1,8 +1,11 @@ import { DocumentNode, visit } from 'graphql'; -import fetchMultipart from 'fetch-multipart-graphql'; +import { meros } from 'meros'; import { createClient, Client } from 'graphql-ws'; import { SubscriptionClient } from 'subscriptions-transport-ws'; -import { makeAsyncIterableIteratorFromSink } from '@n1ru4l/push-pull-async-iterable-iterator'; +import { + isAsyncIterable, + makeAsyncIterableIteratorFromSink, +} from '@n1ru4l/push-pull-async-iterable-iterator'; import type { Fetcher, @@ -124,24 +127,40 @@ export const createLegacyWebsocketsFetcher = ( */ export const createMultipartFetcher = ( options: CreateFetcherOptions, -): Fetcher => async (graphQLParams: FetcherParams, fetcherOpts?: FetcherOpts) => - makeAsyncIterableIteratorFromSink(sink => { - fetchMultipart(options.url, { + httpFetch: typeof fetch, +): Fetcher => + async function* (graphQLParams: FetcherParams, fetcherOpts?: FetcherOpts) { + const response = await httpFetch(options.url, { method: 'POST', body: JSON.stringify(graphQLParams), headers: { 'content-type': 'application/json', + accept: 'application/json, multipart/mixed', ...options.headers, // allow user-defined headers to override // the static provided headers ...fetcherOpts?.headers, }, - onNext: parts => { - // @ts-ignore - sink.next(parts); - }, - onError: sink.error, - onComplete: sink.complete, + }).then(response => { + // TODO: Can we make this payload type-safe? + return meros(response); }); - return () => undefined; - }); + + // Follows the same as createSimpleFetcher above, in that we simply return it as json. + if (!isAsyncIterable(response)) { + yield response.json(); + } + + // @ts-expect-error come on TypeScript flow analyse, I've already checked that you are an AsyncIterator + for (const part of response) { + if (!part.json) { + throw new Error( + `Expected multipart to be of json type, but got\n\nHeaders: ${part.headers}\n\nBody:${part.body}`, + ); + } + yield part.body; + } + + // @ts-expect-error as state above, this is an AsyncIterable. + return () => response.return?.(); + }; diff --git a/packages/graphiql-toolkit/src/types.ts b/packages/graphiql-toolkit/src/types.ts index a192b88f670..0a97b11097d 100644 --- a/packages/graphiql-toolkit/src/types.ts +++ b/packages/graphiql-toolkit/src/types.ts @@ -43,12 +43,12 @@ export type FetcherResult = | string | { data?: any; errors?: Array; hasNext?: boolean } // for IncrementalDelivery - | Array<{ + | { data?: any; errors?: any[]; path?: [string, number]; hasNext: boolean; - }>; + }; export type MaybePromise = T | Promise; diff --git a/packages/graphiql/package.json b/packages/graphiql/package.json index 3af1d2746ed..3d9892c7b69 100644 --- a/packages/graphiql/package.json +++ b/packages/graphiql/package.json @@ -65,7 +65,6 @@ "@types/markdown-it": "^0.0.9", "@types/node": "^13.7.1", "@types/testing-library__jest-dom": "^5.0.1", - "@n1ru4l/push-pull-async-iterable-iterator": "^2.0.1", "babel-loader": "^8.1.0", "babel-plugin-macros": "^2.8.0", "cross-env": "^7.0.0", @@ -77,7 +76,6 @@ "graphql": "experimental-stream-defer", "graphql-ws": "^4.1.0", "graphql-transport-ws": "^1.9.0", - "fetch-multipart-graphql": "^3.0.0", "html-webpack-plugin": "^4.0.4", "identity-obj-proxy": "^3.0.0", "jest": "^24.8.0", diff --git a/packages/graphiql/src/components/GraphiQL.tsx b/packages/graphiql/src/components/GraphiQL.tsx index b21127c5139..a7a8e6030c8 100644 --- a/packages/graphiql/src/components/GraphiQL.tsx +++ b/packages/graphiql/src/components/GraphiQL.tsx @@ -1066,8 +1066,6 @@ export class GraphiQL extends React.Component { ); } - const totalResponse: FetcherResult = { data: {} }; - // _fetchQuery may return a subscription. const subscription = await this._fetchQuery( editedQuery as string, @@ -1077,38 +1075,10 @@ export class GraphiQL extends React.Component { shouldPersistHeaders as boolean, (result: FetcherResult) => { if (queryID === this._editorQueryID) { - if (Array.isArray(result)) { - // for `IncrementalDelivery` - // https://github.com/graphql/graphql-over-http/blob/master/rfcs/IncrementalDelivery.md - // TODO: typescript types - const response = result.reduce((result, increment) => { - if (increment.errors) { - result.errors = [ - ...(result?.errors || []), - ...increment?.errors, - ]; - } - if (increment.path) { - const [path, index] = increment.path; - const data = result?.data[path] || []; - // place them at the exact index. this matters a lot - data[index] = increment.data; - result.data = { ...result?.data, [path]: data }; - } else { - result.data = { ...result?.data, ...increment.data }; - } - return result; - }, totalResponse); - this.setState({ - isWaitingForResponse: false, - response: GraphiQL.formatResult(response), - }); - } else { - this.setState({ - isWaitingForResponse: false, - response: GraphiQL.formatResult(result), - }); - } + this.setState({ + isWaitingForResponse: false, + response: GraphiQL.formatResult(result), + }); } }, ); diff --git a/yarn.lock b/yarn.lock index 229e151768f..d7e2e506bc6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11149,11 +11149,6 @@ fetch-mock@6.5.2: glob-to-regexp "^0.4.0" path-to-regexp "^2.2.1" -fetch-multipart-graphql@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/fetch-multipart-graphql/-/fetch-multipart-graphql-3.0.0.tgz#61c9c1623beb4a7330e86ee72e6906148cd20acf" - integrity sha512-asrZG9CMaRUJmzqU+jqyBp52IB3MEzO38bhrPVB73BNAZ8ShBYgQUPOR9d/kYO4dfIrJIki/sTjeNYbUGuqOzQ== - figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: version "3.5.2" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" @@ -15593,6 +15588,11 @@ merge@^1.2.0: resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145" integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ== +meros@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/meros/-/meros-1.0.0.tgz#cb1fc818a60afc56bd86c577a9758b4510b3f027" + integrity sha512-gGhudbEI/7Z/5Mc3Mh2VsxWTm0byUB7O34azwchvvDRZ3oaxt5RaN80tF9mwN+plfi2JKDeIQQfJJWwuWmgqBQ== + methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" From 4534d8a9b7fb54ce2057a9040b69331086a7ebda Mon Sep 17 00:00:00 2001 From: Marais Rossouw Date: Tue, 26 Jan 2021 17:26:52 +1000 Subject: [PATCH 2/9] chore: should be an await :eyes: --- packages/graphiql-create-fetcher/src/lib.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/graphiql-create-fetcher/src/lib.ts b/packages/graphiql-create-fetcher/src/lib.ts index 17abdb2afaf..11ec6560044 100644 --- a/packages/graphiql-create-fetcher/src/lib.ts +++ b/packages/graphiql-create-fetcher/src/lib.ts @@ -152,7 +152,7 @@ export const createMultipartFetcher = ( } // @ts-expect-error come on TypeScript flow analyse, I've already checked that you are an AsyncIterator - for (const part of response) { + for await (const part of response) { if (!part.json) { throw new Error( `Expected multipart to be of json type, but got\n\nHeaders: ${part.headers}\n\nBody:${part.body}`, From eb5b8eecad9b8a54295134182466e1d64c7df43f Mon Sep 17 00:00:00 2001 From: Marais Rossouw Date: Tue, 26 Jan 2021 18:30:48 +1000 Subject: [PATCH 3/9] fix: now we are properly setting the data --- packages/graphiql-create-fetcher/src/lib.ts | 12 ++--- packages/graphiql-toolkit/src/types.ts | 19 ++++--- packages/graphiql/package.json | 3 +- packages/graphiql/src/components/GraphiQL.tsx | 54 +++++++++++++++++-- yarn.lock | 6 +++ 5 files changed, 71 insertions(+), 23 deletions(-) diff --git a/packages/graphiql-create-fetcher/src/lib.ts b/packages/graphiql-create-fetcher/src/lib.ts index 11ec6560044..4730ea81fcd 100644 --- a/packages/graphiql-create-fetcher/src/lib.ts +++ b/packages/graphiql-create-fetcher/src/lib.ts @@ -12,6 +12,7 @@ import type { FetcherResult, FetcherParams, FetcherOpts, + IncrementalDeliveryResult, } from '@graphiql/toolkit'; import type { CreateFetcherOptions } from './types'; @@ -141,17 +142,13 @@ export const createMultipartFetcher = ( // the static provided headers ...fetcherOpts?.headers, }, - }).then(response => { - // TODO: Can we make this payload type-safe? - return meros(response); - }); + }).then(response => meros(response)); // Follows the same as createSimpleFetcher above, in that we simply return it as json. if (!isAsyncIterable(response)) { - yield response.json(); + return yield response.json(); } - // @ts-expect-error come on TypeScript flow analyse, I've already checked that you are an AsyncIterator for await (const part of response) { if (!part.json) { throw new Error( @@ -160,7 +157,4 @@ export const createMultipartFetcher = ( } yield part.body; } - - // @ts-expect-error as state above, this is an AsyncIterable. - return () => response.return?.(); }; diff --git a/packages/graphiql-toolkit/src/types.ts b/packages/graphiql-toolkit/src/types.ts index 0a97b11097d..79eb9676318 100644 --- a/packages/graphiql-toolkit/src/types.ts +++ b/packages/graphiql-toolkit/src/types.ts @@ -35,20 +35,23 @@ export type FetcherOpts = { documentAST?: DocumentNode; }; -export type FetcherResult = +export type IncrementalDeliveryResult = { + data?: any; + errors?: any[]; + path?: [string, number]; + hasNext: boolean; +}; + +export type FetcherResultObject = | { data: IntrospectionQuery; errors?: Array; } - | string | { data?: any; errors?: Array; hasNext?: boolean } // for IncrementalDelivery - | { - data?: any; - errors?: any[]; - path?: [string, number]; - hasNext: boolean; - }; + | IncrementalDeliveryResult; + +export type FetcherResult = FetcherResultObject | string; export type MaybePromise = T | Promise; diff --git a/packages/graphiql/package.json b/packages/graphiql/package.json index 3d9892c7b69..73ce8b50e29 100644 --- a/packages/graphiql/package.json +++ b/packages/graphiql/package.json @@ -50,7 +50,8 @@ "graphql-language-service": "^3.1.2", "markdown-it": "^10.0.0", "@graphiql/toolkit": "^0.0.1", - "@graphiql/create-fetcher": "^0.0.1" + "@graphiql/create-fetcher": "^0.0.1", + "dset": "^2.0.1" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0", diff --git a/packages/graphiql/src/components/GraphiQL.tsx b/packages/graphiql/src/components/GraphiQL.tsx index a7a8e6030c8..a63a09a97ee 100644 --- a/packages/graphiql/src/components/GraphiQL.tsx +++ b/packages/graphiql/src/components/GraphiQL.tsx @@ -26,6 +26,8 @@ import { } from 'graphql'; import copyToClipboard from 'copy-to-clipboard'; import { getFragmentDependenciesForAST } from 'graphql-language-service-utils'; +// @ts-expect-error sadly no types +import dset from 'dset'; import { ExecuteButton } from './ExecuteButton'; import { ImagePreview } from './ImagePreview'; @@ -61,6 +63,8 @@ import type { SyncFetcherResult, Observable, Unsubscribable, + IncrementalDeliveryResult, + FetcherResultObject, } from '@graphiql/toolkit'; const DEFAULT_DOC_EXPLORER_WIDTH = 350; @@ -1066,6 +1070,15 @@ export class GraphiQL extends React.Component { ); } + const isIncrementalDelivery = ( + result: FetcherResult, + ): result is IncrementalDeliveryResult => { + // @ts-ignore + return typeof result === 'object' && 'hasNext' in result; + }; + + const totalResponse: FetcherResultObject = { data: {} }; + // _fetchQuery may return a subscription. const subscription = await this._fetchQuery( editedQuery as string, @@ -1075,10 +1088,42 @@ export class GraphiQL extends React.Component { shouldPersistHeaders as boolean, (result: FetcherResult) => { if (queryID === this._editorQueryID) { - this.setState({ - isWaitingForResponse: false, - response: GraphiQL.formatResult(result), - }); + if (isIncrementalDelivery(result)) { + // @ts-ignore + totalResponse.hasNext = result.hasNext; + + if (result.errors) { + // We dont care about "index" here, just concat. + totalResponse.errors = [ + ...(totalResponse?.errors || []), + ...result?.errors, + ]; + } + + if (result.path) { + const pathKey = result.path.map(String).join('.'); + if (!('data' in result)) { + throw new Error( + `Expected part to contain a data property, but got ${result}`, + ); + } + dset(totalResponse.data, pathKey, result.data); + } else if ('data' in result) { + // If there is no path, we don't know what to do with the payload, + // so we just set it. + totalResponse.data = result.data; + } + + this.setState({ + isWaitingForResponse: false, + response: GraphiQL.formatResult(totalResponse), + }); + } else { + this.setState({ + isWaitingForResponse: false, + response: GraphiQL.formatResult(result), + }); + } } }, ); @@ -1711,7 +1756,6 @@ function asyncIterableToPromise( iteratorNext() .then(result => { - console.log(result.value); resolve(result.value); // ensure cleanup iteratorReturn?.(); diff --git a/yarn.lock b/yarn.lock index d7e2e506bc6..a47adbbfb6c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10040,6 +10040,11 @@ dotenv@^6.2.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064" integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w== +dset@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/dset/-/dset-2.0.1.tgz#a15fff3d1e4d60ac0c95634625cbd5441a76deb1" + integrity sha512-nI29OZMRYq36hOcifB6HTjajNAAiBKSXsyWZrq+VniusseuP2OpNlTiYgsaNRSGvpyq5Wjbc2gQLyBdTyWqhnQ== + duplexer2@~0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" @@ -12116,6 +12121,7 @@ grapheme-breaker@^0.3.2: codemirror "^5.54.0" codemirror-graphql "^0.15.2" copy-to-clipboard "^3.2.0" + dset "^2.0.1" entities "^2.0.0" graphql-language-service "^3.1.2" markdown-it "^10.0.0" From 192559a2ae03a6bbf57285756205e73b772be9c2 Mon Sep 17 00:00:00 2001 From: Marais Rossouw Date: Wed, 27 Jan 2021 08:04:29 +1000 Subject: [PATCH 4/9] chore: applied PR feedback --- packages/graphiql-create-fetcher/src/lib.ts | 6 +++-- packages/graphiql-toolkit/src/types.ts | 19 +++++++------ packages/graphiql/src/components/GraphiQL.tsx | 27 ++++++++----------- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/packages/graphiql-create-fetcher/src/lib.ts b/packages/graphiql-create-fetcher/src/lib.ts index 4730ea81fcd..4a8cc689510 100644 --- a/packages/graphiql-create-fetcher/src/lib.ts +++ b/packages/graphiql-create-fetcher/src/lib.ts @@ -12,7 +12,7 @@ import type { FetcherResult, FetcherParams, FetcherOpts, - IncrementalDeliveryResult, + FetcherResultPayload, } from '@graphiql/toolkit'; import type { CreateFetcherOptions } from './types'; @@ -142,7 +142,9 @@ export const createMultipartFetcher = ( // the static provided headers ...fetcherOpts?.headers, }, - }).then(response => meros(response)); + }).then(response => + meros>(response), + ); // Follows the same as createSimpleFetcher above, in that we simply return it as json. if (!isAsyncIterable(response)) { diff --git a/packages/graphiql-toolkit/src/types.ts b/packages/graphiql-toolkit/src/types.ts index 79eb9676318..0c1747934b0 100644 --- a/packages/graphiql-toolkit/src/types.ts +++ b/packages/graphiql-toolkit/src/types.ts @@ -35,23 +35,22 @@ export type FetcherOpts = { documentAST?: DocumentNode; }; -export type IncrementalDeliveryResult = { - data?: any; - errors?: any[]; - path?: [string, number]; - hasNext: boolean; -}; - -export type FetcherResultObject = +export type FetcherResultPayload = | { data: IntrospectionQuery; errors?: Array; + hasNext?: boolean; } | { data?: any; errors?: Array; hasNext?: boolean } // for IncrementalDelivery - | IncrementalDeliveryResult; + | { + data?: any; + errors?: any[]; + path?: [string, number]; + hasNext: boolean; + }; -export type FetcherResult = FetcherResultObject | string; +export type FetcherResult = FetcherResultPayload | string; export type MaybePromise = T | Promise; diff --git a/packages/graphiql/src/components/GraphiQL.tsx b/packages/graphiql/src/components/GraphiQL.tsx index a63a09a97ee..9a10abe8292 100644 --- a/packages/graphiql/src/components/GraphiQL.tsx +++ b/packages/graphiql/src/components/GraphiQL.tsx @@ -63,8 +63,7 @@ import type { SyncFetcherResult, Observable, Unsubscribable, - IncrementalDeliveryResult, - FetcherResultObject, + FetcherResultPayload, } from '@graphiql/toolkit'; const DEFAULT_DOC_EXPLORER_WIDTH = 350; @@ -1070,14 +1069,7 @@ export class GraphiQL extends React.Component { ); } - const isIncrementalDelivery = ( - result: FetcherResult, - ): result is IncrementalDeliveryResult => { - // @ts-ignore - return typeof result === 'object' && 'hasNext' in result; - }; - - const totalResponse: FetcherResultObject = { data: {} }; + const totalResponse: FetcherResultPayload = { data: {} }; // _fetchQuery may return a subscription. const subscription = await this._fetchQuery( @@ -1088,10 +1080,11 @@ export class GraphiQL extends React.Component { shouldPersistHeaders as boolean, (result: FetcherResult) => { if (queryID === this._editorQueryID) { - if (isIncrementalDelivery(result)) { - // @ts-ignore - totalResponse.hasNext = result.hasNext; - + if ( + typeof result === 'object' && + result !== null && + 'hasNext' in result + ) { if (result.errors) { // We dont care about "index" here, just concat. totalResponse.errors = [ @@ -1100,8 +1093,10 @@ export class GraphiQL extends React.Component { ]; } - if (result.path) { - const pathKey = result.path.map(String).join('.'); + totalResponse.hasNext = result.hasNext; + + if ('path' in result) { + const pathKey = result.path!.map(String).join('.'); if (!('data' in result)) { throw new Error( `Expected part to contain a data property, but got ${result}`, From f11d7520da8abfa810f96edd92aff6e6413d468b Mon Sep 17 00:00:00 2001 From: Marais Rossouw Date: Wed, 27 Jan 2021 09:11:04 +1000 Subject: [PATCH 5/9] fix: unit tests and mocks --- .../src/__tests__/buildFetcher.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/graphiql-create-fetcher/src/__tests__/buildFetcher.spec.ts b/packages/graphiql-create-fetcher/src/__tests__/buildFetcher.spec.ts index c078a62ce08..2fd6de568bd 100644 --- a/packages/graphiql-create-fetcher/src/__tests__/buildFetcher.spec.ts +++ b/packages/graphiql-create-fetcher/src/__tests__/buildFetcher.spec.ts @@ -41,7 +41,7 @@ describe('createGraphiQLFetcher', () => { const fetcher = createGraphiQLFetcher({ url: serverURL }); expect(createWebsocketsFetcherFromUrl.mock.calls).toEqual([]); expect(createMultipartFetcher.mock.calls).toEqual([ - [{ enableIncrementalDelivery: true, url: serverURL }], + [{ enableIncrementalDelivery: true, url: serverURL }, fetch], ]); }); @@ -50,7 +50,7 @@ describe('createGraphiQLFetcher', () => { const fetcher = createGraphiQLFetcher({ url: serverURL }); expect(createWebsocketsFetcherFromUrl.mock.calls).toEqual([]); expect(createMultipartFetcher.mock.calls).toEqual([ - [{ enableIncrementalDelivery: true, url: serverURL }], + [{ enableIncrementalDelivery: true, url: serverURL }, fetch], ]); expect(createSimpleFetcher.mock.calls).toEqual([ [{ enableIncrementalDelivery: true, url: serverURL }, fetch], @@ -84,7 +84,7 @@ describe('createGraphiQLFetcher', () => { createGraphiQLFetcher(args); - expect(createMultipartFetcher.mock.calls).toEqual([[args]]); + expect(createMultipartFetcher.mock.calls).toEqual([[args, fetch]]); expect(createWebsocketsFetcherFromUrl.mock.calls).toEqual([ [args.subscriptionUrl], ]); @@ -103,7 +103,7 @@ describe('createGraphiQLFetcher', () => { createGraphiQLFetcher(args); - expect(createMultipartFetcher.mock.calls).toEqual([[args]]); + expect(createMultipartFetcher.mock.calls).toEqual([[args, fetch]]); expect(createWebsocketsFetcherFromUrl.mock.calls).toEqual([]); }); }); From 5538c2b4182612c2272284b9e29b2aa98ff348ff Mon Sep 17 00:00:00 2001 From: Marais Rossouw Date: Wed, 27 Jan 2021 09:11:12 +1000 Subject: [PATCH 6/9] fix: cypress tests resolved --- .../graphiql/cypress/integration/incrementalDelivery.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/graphiql/cypress/integration/incrementalDelivery.spec.ts b/packages/graphiql/cypress/integration/incrementalDelivery.spec.ts index ad618a072e4..c2ae01a07b4 100644 --- a/packages/graphiql/cypress/integration/incrementalDelivery.spec.ts +++ b/packages/graphiql/cypress/integration/incrementalDelivery.spec.ts @@ -41,6 +41,7 @@ const mockStreamSuccess = { }, ], }, + hasNext: false, }; describe('IncrementalDelivery support via fetcher', () => { From c0cdaca08d0a34583df98c844306dff0133b917e Mon Sep 17 00:00:00 2001 From: Marais Rossouw Date: Wed, 27 Jan 2021 09:20:25 +1000 Subject: [PATCH 7/9] chore: dset can take an array --- packages/graphiql/src/components/GraphiQL.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/graphiql/src/components/GraphiQL.tsx b/packages/graphiql/src/components/GraphiQL.tsx index 9a10abe8292..5b8860d2bc1 100644 --- a/packages/graphiql/src/components/GraphiQL.tsx +++ b/packages/graphiql/src/components/GraphiQL.tsx @@ -1096,13 +1096,12 @@ export class GraphiQL extends React.Component { totalResponse.hasNext = result.hasNext; if ('path' in result) { - const pathKey = result.path!.map(String).join('.'); if (!('data' in result)) { throw new Error( `Expected part to contain a data property, but got ${result}`, ); } - dset(totalResponse.data, pathKey, result.data); + dset(totalResponse.data, result.path!, result.data); } else if ('data' in result) { // If there is no path, we don't know what to do with the payload, // so we just set it. From cd18d8754c92ae76ac65eb8e04b6b65c347b59a7 Mon Sep 17 00:00:00 2001 From: Marais Rossouw Date: Wed, 27 Jan 2021 18:49:55 +1000 Subject: [PATCH 8/9] chore: upgraded dset --- packages/graphiql-toolkit/src/types.ts | 2 +- packages/graphiql/package.json | 2 +- packages/graphiql/src/components/GraphiQL.tsx | 3 +-- yarn.lock | 10 +++++----- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/graphiql-toolkit/src/types.ts b/packages/graphiql-toolkit/src/types.ts index 0c1747934b0..72bf741e969 100644 --- a/packages/graphiql-toolkit/src/types.ts +++ b/packages/graphiql-toolkit/src/types.ts @@ -46,7 +46,7 @@ export type FetcherResultPayload = | { data?: any; errors?: any[]; - path?: [string, number]; + path?: (string | number)[]; hasNext: boolean; }; diff --git a/packages/graphiql/package.json b/packages/graphiql/package.json index 73ce8b50e29..f780658f584 100644 --- a/packages/graphiql/package.json +++ b/packages/graphiql/package.json @@ -51,7 +51,7 @@ "markdown-it": "^10.0.0", "@graphiql/toolkit": "^0.0.1", "@graphiql/create-fetcher": "^0.0.1", - "dset": "^2.0.1" + "dset": "^2.1.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0", diff --git a/packages/graphiql/src/components/GraphiQL.tsx b/packages/graphiql/src/components/GraphiQL.tsx index 5b8860d2bc1..fa6278110cb 100644 --- a/packages/graphiql/src/components/GraphiQL.tsx +++ b/packages/graphiql/src/components/GraphiQL.tsx @@ -26,7 +26,6 @@ import { } from 'graphql'; import copyToClipboard from 'copy-to-clipboard'; import { getFragmentDependenciesForAST } from 'graphql-language-service-utils'; -// @ts-expect-error sadly no types import dset from 'dset'; import { ExecuteButton } from './ExecuteButton'; @@ -1101,7 +1100,7 @@ export class GraphiQL extends React.Component { `Expected part to contain a data property, but got ${result}`, ); } - dset(totalResponse.data, result.path!, result.data); + dset(totalResponse.data, result.path!.map(String), result.data); } else if ('data' in result) { // If there is no path, we don't know what to do with the payload, // so we just set it. diff --git a/yarn.lock b/yarn.lock index a47adbbfb6c..f06b67415fd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10040,10 +10040,10 @@ dotenv@^6.2.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064" integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w== -dset@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/dset/-/dset-2.0.1.tgz#a15fff3d1e4d60ac0c95634625cbd5441a76deb1" - integrity sha512-nI29OZMRYq36hOcifB6HTjajNAAiBKSXsyWZrq+VniusseuP2OpNlTiYgsaNRSGvpyq5Wjbc2gQLyBdTyWqhnQ== +dset@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/dset/-/dset-2.1.0.tgz#cd1e99e55cf32366d8f144f906c42f7fb3bf431e" + integrity sha512-hlQYwNEdW7Qf8zxysy+yN1E8C/SxRst3Z9n+IvXOR35D9bPVwNHhnL8ZBeoZjvinuGrlvGg6pAMDwhmjqFDgjA== duplexer2@~0.1.4: version "0.1.4" @@ -12121,7 +12121,7 @@ grapheme-breaker@^0.3.2: codemirror "^5.54.0" codemirror-graphql "^0.15.2" copy-to-clipboard "^3.2.0" - dset "^2.0.1" + dset "^2.1.0" entities "^2.0.0" graphql-language-service "^3.1.2" markdown-it "^10.0.0" From ae9137f24bf7958b9581b45569a7becfff6a0582 Mon Sep 17 00:00:00 2001 From: Marais Rossouw Date: Fri, 29 Jan 2021 12:41:44 +1000 Subject: [PATCH 9/9] chore: upgraded dset to v3 --- packages/graphiql/package.json | 2 +- packages/graphiql/src/components/GraphiQL.tsx | 2 +- yarn.lock | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/graphiql/package.json b/packages/graphiql/package.json index f780658f584..3529585502e 100644 --- a/packages/graphiql/package.json +++ b/packages/graphiql/package.json @@ -51,7 +51,7 @@ "markdown-it": "^10.0.0", "@graphiql/toolkit": "^0.0.1", "@graphiql/create-fetcher": "^0.0.1", - "dset": "^2.1.0" + "dset": "^3.0.0" }, "peerDependencies": { "graphql": "^14.0.0 || ^15.0.0", diff --git a/packages/graphiql/src/components/GraphiQL.tsx b/packages/graphiql/src/components/GraphiQL.tsx index fa6278110cb..00ae5d7f023 100644 --- a/packages/graphiql/src/components/GraphiQL.tsx +++ b/packages/graphiql/src/components/GraphiQL.tsx @@ -26,7 +26,7 @@ import { } from 'graphql'; import copyToClipboard from 'copy-to-clipboard'; import { getFragmentDependenciesForAST } from 'graphql-language-service-utils'; -import dset from 'dset'; +import { dset } from 'dset'; import { ExecuteButton } from './ExecuteButton'; import { ImagePreview } from './ImagePreview'; diff --git a/yarn.lock b/yarn.lock index f06b67415fd..c987bb6935f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10040,10 +10040,10 @@ dotenv@^6.2.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064" integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w== -dset@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/dset/-/dset-2.1.0.tgz#cd1e99e55cf32366d8f144f906c42f7fb3bf431e" - integrity sha512-hlQYwNEdW7Qf8zxysy+yN1E8C/SxRst3Z9n+IvXOR35D9bPVwNHhnL8ZBeoZjvinuGrlvGg6pAMDwhmjqFDgjA== +dset@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/dset/-/dset-3.0.0.tgz#b49ef4a6a092c2c5328618eca2ccf6885fafb431" + integrity sha512-pp0B9VgLwMem6bfSDJujcXa41swmXkhWICL1jwC7WbD/NaxXPCXO0Z1sOrVshIQaD4D/pi5lDS7NCt6qIytWaA== duplexer2@~0.1.4: version "0.1.4" @@ -12121,7 +12121,7 @@ grapheme-breaker@^0.3.2: codemirror "^5.54.0" codemirror-graphql "^0.15.2" copy-to-clipboard "^3.2.0" - dset "^2.1.0" + dset "^3.0.0" entities "^2.0.0" graphql-language-service "^3.1.2" markdown-it "^10.0.0"