From 07639bb35e5994ff58fedc64d59b4ef2e1598a21 Mon Sep 17 00:00:00 2001 From: William San Date: Tue, 5 Jul 2022 16:05:23 -0400 Subject: [PATCH] NEBULA-1385: Add `initialState` to embeddable sandbox based on config (#6628) 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. --- CHANGELOG.md | 2 +- docs/source/api/plugin/landing-pages.md | 48 ++++- package-lock.json | 182 ++++++++++++++++++ package.json | 1 + .../__tests__/getEmbeddedExplorerHTML.test.ts | 83 ++++++++ .../__tests__/getEmbeddedSandboxHTML.test.ts | 74 +++++++ .../landingPage/__tests__/tsconfig.json | 7 + .../src/plugin/landingPage/default/index.ts | 12 +- .../src/plugin/landingPage/default/types.ts | 2 +- tsconfig.test.json | 1 + 10 files changed, 407 insertions(+), 5 deletions(-) create mode 100644 packages/apollo-server-core/src/plugin/landingPage/__tests__/getEmbeddedExplorerHTML.test.ts create mode 100644 packages/apollo-server-core/src/plugin/landingPage/__tests__/getEmbeddedSandboxHTML.test.ts create mode 100644 packages/apollo-server-core/src/plugin/landingPage/__tests__/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index e612c9b2022..a88e999bbc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ The version headers in this history reflect the versions of Apollo Server itself ## vNEXT -- Nothing yet! Stay tuned. +- Add `document`, `variables`, `headers` as an option in the `ApolloServerPluginLandingPageLocalDefault` plugins. The embedded version of Apollo Sandbox can now use these options as an initial state. [PR #6628](https://github.com/apollographql/apollo-server/pull/6628) ## v3.9.0 diff --git a/docs/source/api/plugin/landing-pages.md b/docs/source/api/plugin/landing-pages.md index c4595cb8674..6413e0c74ef 100644 --- a/docs/source/api/plugin/landing-pages.md +++ b/docs/source/api/plugin/landing-pages.md @@ -106,6 +106,52 @@ By default, the landing page displays a footer that links to the documentation t +###### `document` + +`string` + + + +A GraphQL document (eg, query or mutation) to populate in the Studio Explorer's editor on load. + +If you omit this, the Explorer initially loads an example query based on your schema. + + + + + + + +###### `variables` + +`Record` + + + +An object containing initial variable values to populate in the Explorer on load. + +If provided, these variables should apply to the initial query you provide in `document`. + + + + + + + +###### `headers` + +`Record` + + + +An object containing initial HTTP header values to populate in the Explorer on load. + + + + + + + ###### `includeCookies` `boolean` @@ -228,7 +274,7 @@ If you omit this, the Explorer initially loads an example query based on your sc ###### `variables` -`Record` +`Record` diff --git a/package-lock.json b/package-lock.json index d12f5157aa1..076320b433a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -87,6 +87,7 @@ "jest-config": "28.1.2", "jest-junit": "14.0.0", "jest-mock-random": "1.1.1", + "jest-serializer-html": "^7.1.0", "js-sha256": "0.9.0", "koa": "2.13.4", "koa-router": "10.1.1", @@ -9994,6 +9995,15 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, + "node_modules/diffable-html": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/diffable-html/-/diffable-html-4.1.0.tgz", + "integrity": "sha512-++kyNek+YBLH8cLXS+iTj/Hiy2s5qkRJEJ8kgu/WHbFrVY2vz9xPFUT+fii2zGF0m1CaojDlQJjkfrCt7YWM1g==", + "dev": true, + "dependencies": { + "htmlparser2": "^3.9.2" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "dev": true, @@ -10005,6 +10015,62 @@ "node": ">=8" } }, + "node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "node_modules/domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, "node_modules/dot-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", @@ -10142,6 +10208,12 @@ "once": "^1.4.0" } }, + "node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, "node_modules/env-paths": { "version": "2.2.1", "dev": true, @@ -11763,6 +11835,20 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "dependencies": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, "node_modules/http-assert": { "version": "1.4.1", "license": "MIT", @@ -13997,6 +14083,15 @@ "node": ">=8" } }, + "node_modules/jest-serializer-html": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/jest-serializer-html/-/jest-serializer-html-7.1.0.tgz", + "integrity": "sha512-xYL2qC7kmoYHJo8MYqJkzrl/Fdlx+fat4U1AqYg+kafqwcKPiMkOcjWHPKhueuNEgr+uemhGc+jqXYiwCyRyLA==", + "dev": true, + "dependencies": { + "diffable-html": "^4.1.0" + } + }, "node_modules/jest-snapshot": { "version": "28.1.2", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.2.tgz", @@ -28773,6 +28868,15 @@ "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", "dev": true }, + "diffable-html": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/diffable-html/-/diffable-html-4.1.0.tgz", + "integrity": "sha512-++kyNek+YBLH8cLXS+iTj/Hiy2s5qkRJEJ8kgu/WHbFrVY2vz9xPFUT+fii2zGF0m1CaojDlQJjkfrCt7YWM1g==", + "dev": true, + "requires": { + "htmlparser2": "^3.9.2" + } + }, "dir-glob": { "version": "3.0.1", "dev": true, @@ -28780,6 +28884,55 @@ "path-type": "^4.0.0" } }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + } + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, "dot-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", @@ -28888,6 +29041,12 @@ "once": "^1.4.0" } }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, "env-paths": { "version": "2.2.1", "dev": true @@ -30046,6 +30205,20 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, "http-assert": { "version": "1.4.1", "requires": { @@ -31637,6 +31810,15 @@ } } }, + "jest-serializer-html": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/jest-serializer-html/-/jest-serializer-html-7.1.0.tgz", + "integrity": "sha512-xYL2qC7kmoYHJo8MYqJkzrl/Fdlx+fat4U1AqYg+kafqwcKPiMkOcjWHPKhueuNEgr+uemhGc+jqXYiwCyRyLA==", + "dev": true, + "requires": { + "diffable-html": "^4.1.0" + } + }, "jest-snapshot": { "version": "28.1.2", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.2.tgz", diff --git a/package.json b/package.json index 959db70c0ef..b180456001d 100644 --- a/package.json +++ b/package.json @@ -108,6 +108,7 @@ "jest-config": "28.1.2", "jest-junit": "14.0.0", "jest-mock-random": "1.1.1", + "jest-serializer-html": "^7.1.0", "js-sha256": "0.9.0", "koa": "2.13.4", "koa-router": "10.1.1", diff --git a/packages/apollo-server-core/src/plugin/landingPage/__tests__/getEmbeddedExplorerHTML.test.ts b/packages/apollo-server-core/src/plugin/landingPage/__tests__/getEmbeddedExplorerHTML.test.ts new file mode 100644 index 00000000000..d0f4972648b --- /dev/null +++ b/packages/apollo-server-core/src/plugin/landingPage/__tests__/getEmbeddedExplorerHTML.test.ts @@ -0,0 +1,83 @@ +import { getEmbeddedExplorerHTML } from '../default/index'; +import type { ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions } from '../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(` + +
+
+ + + `); + }); + + 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(` + +
+
+ + + `); + }); +}); diff --git a/packages/apollo-server-core/src/plugin/landingPage/__tests__/getEmbeddedSandboxHTML.test.ts b/packages/apollo-server-core/src/plugin/landingPage/__tests__/getEmbeddedSandboxHTML.test.ts new file mode 100644 index 00000000000..08a70cf2f91 --- /dev/null +++ b/packages/apollo-server-core/src/plugin/landingPage/__tests__/getEmbeddedSandboxHTML.test.ts @@ -0,0 +1,74 @@ +import { getEmbeddedSandboxHTML } from '../default'; +import type { LandingPageConfig } from '../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(` + +
+
+ + + `); + }); + + it('for embedded sandbox with document, variables and headers excluded', () => { + const config: LandingPageConfig = { + includeCookies: false, + embed: true, + }; + expect(getEmbeddedSandboxHTML(version, config)).toMatchInlineSnapshot(` + +
+
+ + + `); + }); +}); diff --git a/packages/apollo-server-core/src/plugin/landingPage/__tests__/tsconfig.json b/packages/apollo-server-core/src/plugin/landingPage/__tests__/tsconfig.json new file mode 100644 index 00000000000..c8ad92bd012 --- /dev/null +++ b/packages/apollo-server-core/src/plugin/landingPage/__tests__/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../../../../tsconfig.test.base", + "include": ["**/*"], + "references": [ + { "path": "../../../../" }, + ] +} diff --git a/packages/apollo-server-core/src/plugin/landingPage/default/index.ts b/packages/apollo-server-core/src/plugin/landingPage/default/index.ts index 48db41a175d..ac9b4ab163b 100644 --- a/packages/apollo-server-core/src/plugin/landingPage/default/index.ts +++ b/packages/apollo-server-core/src/plugin/landingPage/default/index.ts @@ -57,7 +57,7 @@ function getConfigStringForHtml(config: LandingPageConfig) { .replace("'", '\\u0027'); } -const getEmbeddedExplorerHTML = ( +export const getEmbeddedExplorerHTML = ( version: string, config: ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions, ) => { @@ -124,7 +124,10 @@ id="embeddableExplorer" `; }; -const getEmbeddedSandboxHTML = (version: string, config: LandingPageConfig) => { +export const getEmbeddedSandboxHTML = ( + version: string, + config: LandingPageConfig, +) => { return `