Skip to content

Commit

Permalink
NEBULA-1385: Add initial state to embeddable sandbox and HTML unit te…
Browse files Browse the repository at this point in the history
…sts (#6633)

Allow configuration of embeddable sandbox initial state. The initial `document`,
`variables`, and `headers` can now be provided to the plugin config to populate
these values on page load.

Ported for AS4 from #6628
  • Loading branch information
William010x authored and trevor-scheer committed Jul 12, 2022
1 parent 91e034d commit 19b9e2b
Show file tree
Hide file tree
Showing 7 changed files with 1,260 additions and 486 deletions.
1,355 changes: 978 additions & 377 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Expand Up @@ -91,6 +91,7 @@
"jest-junit": "14.0.0",
"jest-mock": "28.1.1",
"jest-mock-random": "1.1.1",
"jest-serializer-html": "^7.1.0",
"lodash.sumby": "4.6.0",
"nock": "13.2.8",
"node-fetch": "2.6.7",
Expand Down
@@ -0,0 +1,83 @@
import { getEmbeddedExplorerHTML } from '../../../plugin/landingPage/default/getEmbeddedHTML';
import type { ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions } from '../../../plugin/landingPage/default/types';

const version = '_latest';
expect.addSnapshotSerializer(require('jest-serializer-html'));

describe('Embedded Explorer Landing Page Config HTML', () => {
it('with document, variables, headers and displayOptions provided', () => {
const config: ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions =
{
includeCookies: true,
document: 'query Test { id }',
variables: {
option: {
a: 'val',
b: 1,
c: true,
},
},
headers: { authorization: 'true' },
embed: {
displayOptions: {
showHeadersAndEnvVars: true,
docsPanelState: 'open',
theme: 'light',
},
persistExplorerState: true,
},
graphRef: 'graph@current',
};
expect(getEmbeddedExplorerHTML(version, config)).toMatchInlineSnapshot(`
<style>
iframe {
background-color: white;
}
</style>
<div style="width: 100vw; height: 100vh; position: absolute; top: 0;"
id="embeddableExplorer"
>
</div>
<script src="https://embeddable-explorer.cdn.apollographql.com/_latest/embeddable-explorer.umd.production.min.js">
</script>
<script>
var endpointUrl = window.location.href;
var embeddedExplorerConfig = {"includeCookies":true,"document":"query Test { id }","variables":{"option":{"a":"val","b":1,"c":true}},"headers":{"authorization":"true"},"embed":{"displayOptions":{"showHeadersAndEnvVars":true,"docsPanelState":"open","theme":"light"},"persistExplorerState":true},"graphRef":"graph@current","target":"#embeddableExplorer","initialState":{"includeCookies":true,"document":"query Test { id }","variables":{"option":{"a":"val","b":1,"c":true}},"headers":{"authorization":"true"},"embed":{"displayOptions":{"showHeadersAndEnvVars":true,"docsPanelState":"open","theme":"light"},"persistExplorerState":true},"graphRef":"graph@current","displayOptions":{"showHeadersAndEnvVars":true,"docsPanelState":"open","theme":"light"}},"persistExplorerState":true};
new window.EmbeddedExplorer({
...embeddedExplorerConfig,
endpointUrl,
});
</script>
`);
});

it('for embedded explorer with document, variables, headers and displayOptions excluded', () => {
const config: ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions =
{
includeCookies: false,
embed: true as true,
graphRef: 'graph@current',
};
expect(getEmbeddedExplorerHTML(version, config)).toMatchInlineSnapshot(`
<style>
iframe {
background-color: white;
}
</style>
<div style="width: 100vw; height: 100vh; position: absolute; top: 0;"
id="embeddableExplorer"
>
</div>
<script src="https://embeddable-explorer.cdn.apollographql.com/_latest/embeddable-explorer.umd.production.min.js">
</script>
<script>
var endpointUrl = window.location.href;
var embeddedExplorerConfig = {"includeCookies":false,"embed":true,"graphRef":"graph@current","target":"#embeddableExplorer","initialState":{"includeCookies":false,"embed":true,"graphRef":"graph@current","displayOptions":{}},"persistExplorerState":false};
new window.EmbeddedExplorer({
...embeddedExplorerConfig,
endpointUrl,
});
</script>
`);
});
});
@@ -0,0 +1,74 @@
import { getEmbeddedSandboxHTML } from '../../../plugin/landingPage/default/getEmbeddedHTML';
import type { LandingPageConfig } from '../../../plugin/landingPage/default/types';

const version = '_latest';
expect.addSnapshotSerializer(require('jest-serializer-html'));

describe('Landing Page Config HTML', () => {
it('for embedded sandbox with document, variables and headers provided', () => {
const config: LandingPageConfig = {
includeCookies: true,
document: 'query Test { id }',
variables: {
option: {
a: 'val',
b: 1,
c: true,
},
},
headers: { authorization: 'true' },
embed: true,
};
expect(getEmbeddedSandboxHTML(version, config)).toMatchInlineSnapshot(`
<style>
iframe {
background-color: white;
}
</style>
<div style="width: 100vw; height: 100vh; position: absolute; top: 0;"
id="embeddableSandbox"
>
</div>
<script src="https://embeddable-sandbox.cdn.apollographql.com/_latest/embeddable-sandbox.umd.production.min.js">
</script>
<script>
var initialEndpoint = window.location.href;
new window.EmbeddedSandbox({
target: '#embeddableSandbox',
initialEndpoint,
includeCookies: true,
initialState: {"document":"query Test { id }","variables":{"option":{"a":"val","b":1,"c":true}},"headers":{"authorization":"true"}},
});
</script>
`);
});

it('for embedded sandbox with document, variables and headers excluded', () => {
const config: LandingPageConfig = {
includeCookies: false,
embed: true,
};
expect(getEmbeddedSandboxHTML(version, config)).toMatchInlineSnapshot(`
<style>
iframe {
background-color: white;
}
</style>
<div style="width: 100vw; height: 100vh; position: absolute; top: 0;"
id="embeddableSandbox"
>
</div>
<script src="https://embeddable-sandbox.cdn.apollographql.com/_latest/embeddable-sandbox.umd.production.min.js">
</script>
<script>
var initialEndpoint = window.location.href;
new window.EmbeddedSandbox({
target: '#embeddableSandbox',
initialEndpoint,
includeCookies: false,
initialState: {},
});
</script>
`);
});
});
119 changes: 119 additions & 0 deletions packages/server/src/plugin/landingPage/default/getEmbeddedHTML.ts
@@ -0,0 +1,119 @@
import type {
ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions,
LandingPageConfig,
} from './types';

// This function turns an object into a string and replaces
// <, >, &, ' with their unicode chars to avoid adding html tags to
// the landing page html that might be passed from the config.
// The only place these characters can appear in the output of
// JSON.stringify is within string literals, where they can equally
// well appear \u-escaped. This specifically means that
// `</script>` won't terminate the script block early.
// (Perhaps we should have done this instead of the triple-encoding
// of encodeConfig for the main landing page.)
function getConfigStringForHtml(config: LandingPageConfig) {
return JSON.stringify(config)
.replace('<', '\\u003c')
.replace('>', '\\u003e')
.replace('&', '\\u0026')
.replace("'", '\\u0027');
}

export const getEmbeddedExplorerHTML = (
version: string,
config: ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions,
) => {
interface EmbeddableExplorerOptions {
graphRef: string;
target: string;

initialState?: {
document?: string;
variables?: Record<string, any>;
headers?: Record<string, string>;
displayOptions: {
docsPanelState?: 'open' | 'closed'; // default to 'open',
showHeadersAndEnvVars?: boolean; // default to `false`
theme?: 'dark' | 'light';
};
};
persistExplorerState?: boolean; // defaults to 'false'

endpointUrl: string;

includeCookies?: boolean; // defaults to 'false'
}
const productionLandingPageConfigOrDefault = {
displayOptions: {},
persistExplorerState: false,
...(typeof config.embed === 'boolean' ? {} : config.embed),
};
const embeddedExplorerParams: Omit<EmbeddableExplorerOptions, 'endpointUrl'> =
{
...config,
target: '#embeddableExplorer',
initialState: {
...config,
displayOptions: {
...productionLandingPageConfigOrDefault.displayOptions,
},
},
persistExplorerState:
productionLandingPageConfigOrDefault.persistExplorerState,
};

return `
<style>
iframe {
background-color: white;
}
</style>
<div
style="width: 100vw; height: 100vh; position: absolute; top: 0;"
id="embeddableExplorer"
></div>
<script src="https://embeddable-explorer.cdn.apollographql.com/${version}/embeddable-explorer.umd.production.min.js"></script>
<script>
var endpointUrl = window.location.href;
var embeddedExplorerConfig = ${getConfigStringForHtml(
embeddedExplorerParams,
)};
new window.EmbeddedExplorer({
...embeddedExplorerConfig,
endpointUrl,
});
</script>
`;
};

export const getEmbeddedSandboxHTML = (
version: string,
config: LandingPageConfig,
) => {
return `
<style>
iframe {
background-color: white;
}
</style>
<div
style="width: 100vw; height: 100vh; position: absolute; top: 0;"
id="embeddableSandbox"
></div>
<script src="https://embeddable-sandbox.cdn.apollographql.com/${version}/embeddable-sandbox.umd.production.min.js"></script>
<script>
var initialEndpoint = window.location.href;
new window.EmbeddedSandbox({
target: '#embeddableSandbox',
initialEndpoint,
includeCookies: ${config.includeCookies ?? 'false'},
initialState: ${getConfigStringForHtml({
document: config.document ?? undefined,
variables: config.variables ?? undefined,
headers: config.headers ?? undefined,
})},
});
</script>
`;
};

0 comments on commit 19b9e2b

Please sign in to comment.