1
1
import type { ImplicitlyInstallablePlugin } from '../../../ApolloServer' ;
2
-
3
- export interface ApolloServerPluginLandingPageDefaultBaseOptions {
4
- /**
5
- * By default, the landing page plugin uses the latest version of the landing
6
- * page published to Apollo's CDN. If you'd like to pin the current version,
7
- * pass the SHA served at
8
- * https://apollo-server-landing-page.cdn.apollographql.com/_latest/version.txt
9
- * here.
10
- */
11
- version ?: string ;
12
- /**
13
- * Set to false to suppress the footer which explains how to configure the
14
- * landing page.
15
- */
16
- footer ?: boolean ;
17
- /**
18
- * Users can configure their landing page to link to Studio Explorer with a
19
- * document loaded in the UI.
20
- */
21
- document ?: string ;
22
- /**
23
- * Users can configure their landing page to link to Studio Explorer with
24
- * variables loaded in the UI.
25
- */
26
- variables ?: Record < string , string > ;
27
- /**
28
- * Users can configure their landing page to link to Studio Explorer with
29
- * headers loaded in the UI.
30
- */
31
- headers ?: Record < string , string > ;
32
- /**
33
- * Users can configure their landing page to link to Studio Explorer with the
34
- * setting to include/exclude cookies loaded in the UI.
35
- */
36
- includeCookies ?: boolean ;
37
- // For Apollo use only.
38
- __internal_apolloStudioEnv__ ?: 'staging' | 'prod' ;
39
- }
40
-
41
- export interface ApolloServerPluginLandingPageLocalDefaultOptions
42
- extends ApolloServerPluginLandingPageDefaultBaseOptions { }
43
-
44
- export interface ApolloServerPluginLandingPageProductionDefaultOptions
45
- extends ApolloServerPluginLandingPageDefaultBaseOptions {
46
- /**
47
- * If specified, provide a link (with opt-in auto-redirect) to the Studio page
48
- * for the given graphRef. (You need to explicitly pass this here rather than
49
- * relying on the server's ApolloConfig, because if your server is publicly
50
- * accessible you may not want to display the graph ref publicly.)
51
- */
52
- graphRef ?: string ;
53
- }
54
-
55
- // The actual config object read by the landing page's React component.
56
- interface LandingPageConfig {
57
- graphRef ?: string | undefined ;
58
- isProd ?: boolean ;
59
- apolloStudioEnv ?: 'staging' | 'prod' ;
60
- document ?: string ;
61
- variables ?: Record < string , string > ;
62
- headers ?: Record < string , string > ;
63
- includeCookies ?: boolean ;
64
- footer ?: boolean ;
65
- }
2
+ import type {
3
+ ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions ,
4
+ ApolloServerPluginLandingPageLocalDefaultOptions ,
5
+ ApolloServerPluginLandingPageProductionDefaultOptions ,
6
+ LandingPageConfig ,
7
+ } from './types' ;
66
8
67
9
export function ApolloServerPluginLandingPageLocalDefault (
68
10
options : ApolloServerPluginLandingPageLocalDefaultOptions = { } ,
69
11
) : ImplicitlyInstallablePlugin {
70
- // We list known keys explicitly to get better typechecking, but we pass
71
- // through extras in case we've added new keys to the splash page and haven't
72
- // quite updated the plugin yet.
73
- const {
74
- version,
75
- __internal_apolloStudioEnv__,
76
- footer,
77
- document,
78
- variables,
79
- headers,
80
- includeCookies,
81
- ...rest
82
- } = options ;
83
- return ApolloServerPluginLandingPageDefault (
84
- version ,
85
- encodeConfig ( {
86
- isProd : false ,
87
- apolloStudioEnv : __internal_apolloStudioEnv__ ,
88
- footer,
89
- document,
90
- variables,
91
- headers,
92
- includeCookies,
93
- ...rest ,
94
- } ) ,
95
- ) ;
12
+ const { version, __internal_apolloStudioEnv__, ...rest } = options ;
13
+ return ApolloServerPluginLandingPageDefault ( version , {
14
+ isProd : false ,
15
+ apolloStudioEnv : __internal_apolloStudioEnv__ ,
16
+ ...rest ,
17
+ } ) ;
96
18
}
97
19
98
20
export function ApolloServerPluginLandingPageProductionDefault (
99
21
options : ApolloServerPluginLandingPageProductionDefaultOptions = { } ,
100
22
) : ImplicitlyInstallablePlugin {
101
- // We list known keys explicitly to get better typechecking, but we pass
102
- // through extras in case we've added new keys to the splash page and haven't
103
- // quite updated the plugin yet.
104
- const {
105
- version,
106
- __internal_apolloStudioEnv__,
107
- footer,
108
- document,
109
- variables,
110
- headers,
111
- includeCookies,
112
- graphRef,
113
- ...rest
114
- } = options ;
115
- return ApolloServerPluginLandingPageDefault (
116
- version ,
117
- encodeConfig ( {
118
- isProd : true ,
119
- apolloStudioEnv : __internal_apolloStudioEnv__ ,
120
- footer,
121
- document,
122
- variables,
123
- headers,
124
- includeCookies,
125
- graphRef,
126
- ...rest ,
127
- } ) ,
128
- ) ;
23
+ const { version, __internal_apolloStudioEnv__, ...rest } = options ;
24
+ return ApolloServerPluginLandingPageDefault ( version , {
25
+ isProd : true ,
26
+ apolloStudioEnv : __internal_apolloStudioEnv__ ,
27
+ ...rest ,
28
+ } ) ;
129
29
}
130
30
131
31
// A triple encoding! Wow! First we use JSON.stringify to turn our object into a
@@ -140,12 +40,131 @@ function encodeConfig(config: LandingPageConfig): string {
140
40
return JSON . stringify ( encodeURIComponent ( JSON . stringify ( config ) ) ) ;
141
41
}
142
42
43
+ // This function turns an object into a string and replaces
44
+ // <, >, &, ' with their unicode chars to avoid adding html tags to
45
+ // the landing page html that might be passed from the config.
46
+ // The only place these characters can appear in the output of
47
+ // JSON.stringify is within string literals, where they can equally
48
+ // well appear \u-escaped. This specifically means that
49
+ // `</script>` won't terminate the script block early.
50
+ // (Perhaps we should have done this instead of the triple-encoding
51
+ // of encodeConfig for the main landing page.)
52
+ function getConfigStringForHtml ( config : LandingPageConfig ) {
53
+ return JSON . stringify ( config )
54
+ . replace ( '<' , '\\u003c' )
55
+ . replace ( '>' , '\\u003e' )
56
+ . replace ( '&' , '\\u0026' )
57
+ . replace ( "'" , '\\u0027' ) ;
58
+ }
59
+
60
+ const getEmbeddedExplorerHTML = (
61
+ version : string ,
62
+ config : ApolloServerPluginEmbeddedLandingPageProductionDefaultOptions ,
63
+ ) => {
64
+ interface EmbeddableExplorerOptions {
65
+ graphRef : string ;
66
+ target : string ;
67
+
68
+ initialState ?: {
69
+ document ?: string ;
70
+ variables ?: Record < string , any > ;
71
+ headers ?: Record < string , string > ;
72
+ displayOptions : {
73
+ docsPanelState ?: 'open' | 'closed' ; // default to 'open',
74
+ showHeadersAndEnvVars ?: boolean ; // default to `false`
75
+ theme ?: 'dark' | 'light' ;
76
+ } ;
77
+ } ;
78
+ persistExplorerState ?: boolean ; // defaults to 'false'
79
+
80
+ endpointUrl : string ;
81
+ }
82
+ const productionLandingPageConfigOrDefault = {
83
+ displayOptions : { } ,
84
+ persistExplorerState : false ,
85
+ ...( typeof config . embed === 'boolean' ? { } : config . embed ) ,
86
+ } ;
87
+ const embeddedExplorerParams : Omit < EmbeddableExplorerOptions , 'endpointUrl' > =
88
+ {
89
+ ...config ,
90
+ target : '#embeddableExplorer' ,
91
+ initialState : {
92
+ ...config ,
93
+ displayOptions : {
94
+ ...productionLandingPageConfigOrDefault . displayOptions ,
95
+ } ,
96
+ } ,
97
+ persistExplorerState :
98
+ productionLandingPageConfigOrDefault . persistExplorerState ,
99
+ } ;
100
+
101
+ return `
102
+ <style>
103
+ iframe {
104
+ background-color: white;
105
+ }
106
+ </style>
107
+ <div
108
+ style="width: 100vw; height: 100vh; position: absolute; top: 0;"
109
+ id="embeddableExplorer"
110
+ ></div>
111
+ <script src="https://embeddable-explorer.cdn.apollographql.com/${ version } /embeddable-explorer.umd.production.min.js"></script>
112
+ <script>
113
+ var endpointUrl = window.location.href;
114
+ var embeddedExplorerConfig = ${ getConfigStringForHtml (
115
+ embeddedExplorerParams ,
116
+ ) } ;
117
+ new window.EmbeddedExplorer({
118
+ ...embeddedExplorerConfig,
119
+ endpointUrl,
120
+ });
121
+ </script>
122
+ ` ;
123
+ } ;
124
+
125
+ const getEmbeddedSandboxHTML = ( version : string ) => {
126
+ return `
127
+ <style>
128
+ iframe {
129
+ background-color: white;
130
+ }
131
+ </style>
132
+ <div
133
+ style="width: 100vw; height: 100vh; position: absolute; top: 0;"
134
+ id="embeddableSandbox"
135
+ ></div>
136
+ <script src="https://embeddable-sandbox.cdn.apollographql.com/${ version } /embeddable-sandbox.umd.production.min.js"></script>
137
+ <script>
138
+ var initialEndpoint = window.location.href;
139
+ new window.EmbeddedSandbox({
140
+ target: '#embeddableSandbox',
141
+ initialEndpoint,
142
+ });
143
+ </script>
144
+ ` ;
145
+ } ;
146
+
147
+ const getNonEmbeddedLandingPageHTML = (
148
+ version : string ,
149
+ config : LandingPageConfig ,
150
+ ) => {
151
+ const encodedConfig = encodeConfig ( config ) ;
152
+
153
+ return `
154
+ <script>window.landingPage = ${ encodedConfig } ;</script>
155
+ <script src="https://apollo-server-landing-page.cdn.apollographql.com/${ version } /static/js/main.js"></script>` ;
156
+ } ;
157
+
143
158
// Helper for the two actual plugin functions.
144
159
function ApolloServerPluginLandingPageDefault (
145
160
maybeVersion : string | undefined ,
146
- encodedConfig : string ,
161
+ config : LandingPageConfig & {
162
+ isProd : boolean ;
163
+ apolloStudioEnv : 'staging' | 'prod' | undefined ;
164
+ } ,
147
165
) : ImplicitlyInstallablePlugin {
148
166
const version = maybeVersion ?? '_latest' ;
167
+
149
168
return {
150
169
__internal_installed_implicitly__ : false ,
151
170
async serverWillStart ( ) {
@@ -203,9 +222,14 @@ curl --request POST \\
203
222
--url '<script>document.write(window.location.href)</script>' \\
204
223
--data '{"query":"query { __typename }"}'</code>
205
224
</div>
225
+ ${
226
+ config . embed
227
+ ? 'graphRef' in config && config . graphRef
228
+ ? getEmbeddedExplorerHTML ( version , config )
229
+ : getEmbeddedSandboxHTML ( version )
230
+ : getNonEmbeddedLandingPageHTML ( version , config )
231
+ }
206
232
</div>
207
- <script>window.landingPage = ${ encodedConfig } ;</script>
208
- <script src="https://apollo-server-landing-page.cdn.apollographql.com/${ version } /static/js/main.js"></script>
209
233
</body>
210
234
</html>
211
235
` ;
0 commit comments