Skip to content

Commit

Permalink
fix & improve hybrid subscriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
PabloSzx committed Aug 18, 2021
1 parent 1ad8db1 commit 50e8840
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 65 deletions.
11 changes: 10 additions & 1 deletion examples/fastify/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,16 @@ app.route({

if (shouldRenderGraphiQL(request)) {
res.type("text/html");
res.send(renderGraphiQL({}));
res.send(
renderGraphiQL({
hybridSubscriptionTransportConfig: {
default: "sse",
config: {
sse: "/graphql",
},
},
})
);
} else {
const request = {
body: req.body,
Expand Down
1 change: 0 additions & 1 deletion packages/graphiql/browser/drop-down.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ export const ToolbarDropDown = <TOption extends ToolbarDropDownOption>(props: {
}) => {
const [isOpen, setIsOpen] = React.useState(false);
const selectedOption = props.options[props.activeOptionIndex] ?? null;
console.log(props.options);
return (
<div className="toolbar-drop-down">
<ToolbarButton
Expand Down
141 changes: 86 additions & 55 deletions packages/graphiql/browser/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@ import React from "react";
import ReactDOM from "react-dom";

import {
LoadFromUrlOptions,
SubscriptionProtocol,
UrlLoader,
LoadFromUrlOptions,
} from "@graphql-tools/url-loader";
import { isAsyncIterable, AsyncExecutor } from "@graphql-tools/utils";
import {
AsyncExecutor,
ExecutionRequest,
isAsyncIterable,
} from "@graphql-tools/utils";
import { isLiveQueryOperationDefinitionNode } from "@n1ru4l/graphql-live-query";

import { ToolbarDropDown } from "./drop-down";

import type { Fetcher } from "@graphiql/toolkit";

import type { HybridSubscriptionTransportConfig } from "../src/types";
Expand All @@ -30,7 +36,8 @@ export interface Options {
}

const buildHybridMenuOptions = (
hybridConfig: HybridSubscriptionTransportConfig
hybridConfig: HybridSubscriptionTransportConfig,
endpoint: string
) => {
const options: Array<{
title: string;
Expand All @@ -41,21 +48,27 @@ const buildHybridMenuOptions = (
options.push({
value: "sse",
title: "Subscriptions with SSE",
url: hybridConfig.sse,
url: typeof hybridConfig.sse === "string" ? hybridConfig.sse : endpoint,
});
}
if (hybridConfig.transportWS) {
options.push({
value: "transportWS",
title: "Subscriptions with GraphQL over WebSocket",
url: hybridConfig.transportWS,
url:
typeof hybridConfig.transportWS === "string"
? hybridConfig.transportWS
: getWebsocketSubscriptionsEndpointBasedOnEndpoint(endpoint),
});
}
if (hybridConfig.legacyWS) {
options.push({
value: "legacyWS",
title: "Subscriptions with GraphQL over WebSocket",
url: hybridConfig.legacyWS,
url:
typeof hybridConfig.legacyWS === "string"
? hybridConfig.legacyWS
: getWebsocketSubscriptionsEndpointBasedOnEndpoint(endpoint),
});
}

Expand Down Expand Up @@ -99,6 +112,12 @@ const getOperationWithFragments = (
};
};

function getWebsocketSubscriptionsEndpointBasedOnEndpoint(endpoint: string) {
const url = new URL(endpoint, window.location.href);
url.protocol = url.protocol.replace("http", "ws");
return url.href;
}

export const init = async ({
defaultQuery,
defaultVariableEditorOpen,
Expand All @@ -113,7 +132,7 @@ export const init = async ({

const menuOptions =
hybridSubscriptionTransportConfig &&
buildHybridMenuOptions(hybridSubscriptionTransportConfig.config);
buildHybridMenuOptions(hybridSubscriptionTransportConfig.config, endpoint);
let startHybridIndex: null | number = null;
if (hybridSubscriptionTransportConfig && menuOptions) {
startHybridIndex = menuOptions.findIndex(
Expand All @@ -134,9 +153,7 @@ export const init = async ({
return endpoint;
}

const url = new URL(endpoint, window.location.href);
url.protocol = url.protocol.replace("http", "ws");
return url.href;
return getWebsocketSubscriptionsEndpointBasedOnEndpoint(endpoint);
})();

const subscriptionsProtocol: SubscriptionProtocol =
Expand Down Expand Up @@ -167,7 +184,7 @@ export const init = async ({
const [hybridTransportIndex, setHybridTransportIndex] =
React.useState(startHybridIndex);
const [networkInterface, setNetworkInterface] =
React.useState<null | AsyncExecutor>(null);
React.useState<AsyncExecutor>(() => executor);

React.useEffect(() => {
let isCanceled = false;
Expand All @@ -186,13 +203,10 @@ export const init = async ({
options.subscriptionsProtocol = SubscriptionProtocol.SSE;
options.subscriptionsEndpoint = target.url;
} else if (target.value === "legacyWS") {
// options.useSSEForSubscription = false;
options.subscriptionsProtocol = SubscriptionProtocol.LEGACY_WS;
options.subscriptionsEndpoint = target.url;
// useWebSocketLegacyProtocol = true;
} else if (target.value === "transportWS") {
// options.useSSEForSubscription = false;
options.subscriptionsProtocol = SubscriptionProtocol.LEGACY_WS;
options.subscriptionsProtocol = SubscriptionProtocol.WS;
options.subscriptionsEndpoint = target.url;
}
}
Expand All @@ -203,7 +217,7 @@ export const init = async ({
if (isCanceled) {
return;
}
setNetworkInterface(networkInterface);
setNetworkInterface(() => networkInterface);
})
.catch(console.error);

Expand Down Expand Up @@ -231,13 +245,22 @@ export const init = async ({
);
};

const fetcher = React.useMemo<null | Fetcher>(() => {
if (!networkInterface) {
return null;
}

const fetcher = React.useMemo<Fetcher>(() => {
return (graphQLParams, opts) => ({
subscribe: (observer: any) => {
subscribe: (observerArg: any) => {
const observer:
| {
next: (value: unknown) => void;
error: (error: any) => void;
complete: () => void;
(value: unknown): void;
}
| {
next: (value: unknown) => void;
error: (error: any) => void;
complete: () => void;
} = observerArg;

let stopSubscription = () => {};
Promise.resolve().then(async () => {
try {
Expand All @@ -246,14 +269,24 @@ export const init = async ({
parse(graphQLParams.query),
graphQLParams.operationName
);
const executionParams = {

const operationAST = getOperationAST(filteredDocument);

if (!operationAST)
throw Error("Invalid query document: " + graphQLParams.query);

const executionParams: ExecutionRequest = {
document: filteredDocument,
variables: graphQLParams.variables,
context: {
headers: opts?.headers || {},
},
extensions: {
headers: opts?.headers || {},
},
operationType: operationAST.operation,
};
const queryFn: any = executor;
const queryFn = networkInterface;
const res = await queryFn(executionParams);
if (isAsyncIterable(res)) {
const asyncIterable = res[Symbol.asyncIterator]();
Expand Down Expand Up @@ -293,37 +326,35 @@ export const init = async ({
}, [networkInterface]);

return (
fetcher && (
<GraphiQL
defaultQuery={defaultQuery}
defaultVariableEditorOpen={defaultVariableEditorOpen}
fetcher={fetcher}
headers={headers}
headerEditorEnabled={headerEditorEnabled}
operationName={initialOperationName}
query={initialQuery}
ref={graphiqlRef}
toolbar={{
additionalContent: (
<>
<button className="toolbar-button" onClick={onShare}>
Share
</button>
{menuOptions && hybridTransportIndex != null && (
<ToolbarDropDown
options={menuOptions}
activeOptionIndex={hybridTransportIndex}
onSelectOption={(index) => {
setHybridTransportIndex(index);
}}
/>
)}
</>
),
}}
variables={initialVariables}
/>
)
<GraphiQL
defaultQuery={defaultQuery}
defaultVariableEditorOpen={defaultVariableEditorOpen}
fetcher={fetcher}
headers={headers}
headerEditorEnabled={headerEditorEnabled}
operationName={initialOperationName}
query={initialQuery}
ref={graphiqlRef}
toolbar={{
additionalContent: (
<>
<button className="toolbar-button" onClick={onShare}>
Share
</button>
{menuOptions && hybridTransportIndex != null && (
<ToolbarDropDown
options={menuOptions}
activeOptionIndex={hybridTransportIndex}
onSelectOption={(index) => {
setHybridTransportIndex(index);
}}
/>
)}
</>
),
}}
variables={initialVariables}
/>
);
}, {}),
document.body
Expand Down
2 changes: 1 addition & 1 deletion packages/graphiql/src/index.ts

Large diffs are not rendered by default.

20 changes: 13 additions & 7 deletions packages/graphiql/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
export interface HybridSubscriptionTransportConfig {
/* Enable SSE transport as an option */
sse?: string;
/* Enable Legacy graphql-ws protocol transport as an option. */
legacyWS?: string;
/* Enable graphql-transport-ws protocol transport as an option */
transportWS?: string;
/* Enable SSE transport as an option, if set as "true", it re-uses `endpoint` */
sse?: string | boolean;
/* Enable Legacy graphql-ws protocol transport as an option, if set as "true", re-uses `endpoint` with "ws:" or "wss:" protocol */
legacyWS?: string | boolean;
/* Enable graphql-transport-ws protocol transport as an option, if set as "true" re-uses `endpoint` with "ws:" or "wss:" protocol */
transportWS?: string | boolean;
}

export interface RenderGraphiQLOptions {
Expand Down Expand Up @@ -48,5 +48,11 @@ export interface RenderGraphiQLOptions {
*/
subscriptionsProtocol?: "WS" | "LEGACY_WS" | "SSE";

hybridSubscriptionTransportConfig?: HybridSubscriptionTransportConfig;
/**
* Enable selecting subscriptions protocol via dropdown in interface
*/
hybridSubscriptionTransportConfig?: {
default: keyof HybridSubscriptionTransportConfig;
config: HybridSubscriptionTransportConfig;
};
}

0 comments on commit 50e8840

Please sign in to comment.