Skip to content

Commit

Permalink
Merge pull request #155 from apollographql/william/embedded-defer
Browse files Browse the repository at this point in the history
NEBULA-1271: Support @defer in embedded Explorer and embedded Sandbox
  • Loading branch information
William010x authored Aug 10, 2022
2 parents fb8a943 + 102024e commit 0461312
Show file tree
Hide file tree
Showing 9 changed files with 562 additions and 43 deletions.
6 changes: 6 additions & 0 deletions .changeset/lemon-glasses-bow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@apollo/explorer': minor
'@apollo/sandbox': minor
---

Added support for @defer and @stream
50 changes: 47 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,6 @@
"typescript": "3.9.10"
},
"dependencies": {
"zen-observable-ts": "^1.1.0"
}
}
1 change: 1 addition & 0 deletions packages/explorer/src/helpers/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const SCHEMA_RESPONSE = 'SchemaResponse';
// Message types for queries and mutations
export const EXPLORER_QUERY_MUTATION_REQUEST = 'ExplorerRequest';
export const EXPLORER_QUERY_MUTATION_RESPONSE = 'ExplorerResponse';
export const TRACE_KEY = 'ftv1';

// Message types for subscriptions
export const EXPLORER_SUBSCRIPTION_REQUEST = 'ExplorerSubscriptionRequest';
Expand Down
125 changes: 105 additions & 20 deletions packages/explorer/src/helpers/postMessageRelayHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ import {
EXPLORER_SUBSCRIPTION_RESPONSE,
EXPLORER_SET_SOCKET_ERROR,
EXPLORER_SET_SOCKET_STATUS,
TRACE_KEY,
} from './constants';
import {
MultipartResponse,
readMultipartWebStream,
} from './readMultipartWebStream';
import { Observable, Observer } from 'zen-observable-ts';
import type { JSONObject, JSONValue } from './types';

export type HandleRequest = (
Expand Down Expand Up @@ -52,7 +58,7 @@ export function sendPostMessageToEmbed({
embeddedIFrameElement?.contentWindow?.postMessage(message, embedUrl);
}

type ResponseError = {
export type ResponseError = {
message: string;
stack?: string;
};
Expand Down Expand Up @@ -84,11 +90,15 @@ export type OutgoingEmbedMessage =
name: typeof EXPLORER_QUERY_MUTATION_RESPONSE;
operationId: string;
response: {
data?: JSONValue;
data?: Record<string, unknown> | JSONValue;
error?: ResponseError;
errors?: [ResponseError];
errors?: GraphQLError[];
status?: number;
headers?: Headers;
headers?: Headers | Record<string, string>;
hasNext?: boolean;
path?: Array<string | number>;
size?: number;
extensions?: { [TRACE_KEY]?: string };
};
}
| {
Expand Down Expand Up @@ -195,28 +205,102 @@ export function executeOperation({
}),
})
.then(async (response) => {
const json = await response.json();

const responseHeaders: Record<string, string> = {};
response.headers.forEach((value, key) => {
responseHeaders[key] = value;
});

sendPostMessageToEmbed({
message: {
// Include the same operation ID in the response message's name
// so the Explorer knows which operation it's associated with
name: EXPLORER_QUERY_MUTATION_RESPONSE,
operationId,
response: {
...json,
status: response.status,
headers: responseHeaders,
const contentType = response.headers?.get('content-type');
if (contentType !== null && /^multipart\/mixed/.test(contentType)) {
const observable = new Observable<MultipartResponse>(
(observer: Observer<MultipartResponse>) =>
readMultipartWebStream(response, contentType, observer)
);

observable.subscribe({
next(data) {
sendPostMessageToEmbed({
message: {
// Include the same operation ID in the response message's name
// so the Explorer knows which operation it's associated with
name: EXPLORER_QUERY_MUTATION_RESPONSE,
operationId,
response: {
data: data.data.data,
errors: data.data.errors,
extensions: data.data.extensions,
path: data.data.path,
status: response.status,
headers: responseHeaders,
hasNext: true,
size: data.size,
},
},
embeddedIFrameElement,
embedUrl,
});
},
},
embeddedIFrameElement,
embedUrl,
});
error(err) {
sendPostMessageToEmbed({
message: {
// Include the same operation ID in the response message's name
// so the Explorer knows which operation it's associated with
name: EXPLORER_QUERY_MUTATION_RESPONSE,
operationId,
response: {
data: null,
error: {
message: err.message,
...(err.stack ? { stack: err.stack } : {}),
},
size: 0,
hasNext: false,
},
},
embeddedIFrameElement,
embedUrl,
});
},
complete() {
sendPostMessageToEmbed({
message: {
// Include the same operation ID in the response message's name
// so the Explorer knows which operation it's associated with
name: EXPLORER_QUERY_MUTATION_RESPONSE,
operationId,
response: {
data: null,
size: 0,
status: response.status,
headers: responseHeaders,
hasNext: false,
},
},
embeddedIFrameElement,
embedUrl,
});
},
});
} else {
const json = await response.json();

sendPostMessageToEmbed({
message: {
// Include the same operation ID in the response message's name
// so the Explorer knows which operation it's associated with
name: EXPLORER_QUERY_MUTATION_RESPONSE,
operationId,
response: {
...json,
status: response.status,
headers: responseHeaders,
hasNext: false,
},
},
embeddedIFrameElement,
embedUrl,
});
}
})
.catch((response) => {
sendPostMessageToEmbed({
Expand All @@ -230,6 +314,7 @@ export function executeOperation({
message: response.message,
...(response.stack ? { stack: response.stack } : {}),
},
hasNext: false,
},
},
embeddedIFrameElement,
Expand Down
Loading

0 comments on commit 0461312

Please sign in to comment.