async_graphql/http/
playground_source.rs

1use std::collections::HashMap;
2
3use serde::Serialize;
4
5use crate::Value;
6
7/// Generate the page for GraphQL Playground
8///
9/// # Example
10///
11/// ```rust
12/// use async_graphql::http::*;
13///
14/// playground_source(GraphQLPlaygroundConfig::new("http://localhost:8000"));
15/// ```
16pub fn playground_source(config: GraphQLPlaygroundConfig) -> String {
17    let title = config.title.unwrap_or("GraphQL Playground");
18    r##"
19<!DOCTYPE html>
20
21<html>
22
23<head>
24  <meta charset=utf-8 />
25  <meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
26  <title>%GRAPHQL_PLAYGROUND_TITLE%</title>
27  <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/graphql-playground-react/build/static/css/index.css" />
28  <link rel="shortcut icon" href="//cdn.jsdelivr.net/npm/graphql-playground-react/build/favicon.png" />
29  <script src="//cdn.jsdelivr.net/npm/graphql-playground-react/build/static/js/middleware.js"></script>
30  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700|Source+Code+Pro:400,700" />
31</head>
32
33<body>
34  <style type="text/css">
35    html {
36      font-family: "Open Sans", sans-serif;
37      overflow: hidden;
38    }
39
40    body {
41      margin: 0;
42      background: #172a3a;
43    }
44
45    .playgroundIn {
46      -webkit-animation: playgroundIn 0.5s ease-out forwards;
47      animation: playgroundIn 0.5s ease-out forwards;
48    }
49
50    @-webkit-keyframes playgroundIn {
51      from {
52        opacity: 0;
53        -webkit-transform: translateY(10px);
54        -ms-transform: translateY(10px);
55        transform: translateY(10px);
56      }
57      to {
58        opacity: 1;
59        -webkit-transform: translateY(0);
60        -ms-transform: translateY(0);
61        transform: translateY(0);
62      }
63    }
64
65    @keyframes playgroundIn {
66      from {
67        opacity: 0;
68        -webkit-transform: translateY(10px);
69        -ms-transform: translateY(10px);
70        transform: translateY(10px);
71      }
72      to {
73        opacity: 1;
74        -webkit-transform: translateY(0);
75        -ms-transform: translateY(0);
76        transform: translateY(0);
77      }
78    }
79  </style>
80
81  <style type="text/css">
82    .fadeOut {
83      -webkit-animation: fadeOut 0.5s ease-out forwards;
84      animation: fadeOut 0.5s ease-out forwards;
85    }
86
87    @-webkit-keyframes fadeIn {
88      from {
89        opacity: 0;
90        -webkit-transform: translateY(-10px);
91        -ms-transform: translateY(-10px);
92        transform: translateY(-10px);
93      }
94      to {
95        opacity: 1;
96        -webkit-transform: translateY(0);
97        -ms-transform: translateY(0);
98        transform: translateY(0);
99      }
100    }
101
102    @keyframes fadeIn {
103      from {
104        opacity: 0;
105        -webkit-transform: translateY(-10px);
106        -ms-transform: translateY(-10px);
107        transform: translateY(-10px);
108      }
109      to {
110        opacity: 1;
111        -webkit-transform: translateY(0);
112        -ms-transform: translateY(0);
113        transform: translateY(0);
114      }
115    }
116
117    @-webkit-keyframes fadeOut {
118      from {
119        opacity: 1;
120        -webkit-transform: translateY(0);
121        -ms-transform: translateY(0);
122        transform: translateY(0);
123      }
124      to {
125        opacity: 0;
126        -webkit-transform: translateY(-10px);
127        -ms-transform: translateY(-10px);
128        transform: translateY(-10px);
129      }
130    }
131
132    @keyframes fadeOut {
133      from {
134        opacity: 1;
135        -webkit-transform: translateY(0);
136        -ms-transform: translateY(0);
137        transform: translateY(0);
138      }
139      to {
140        opacity: 0;
141        -webkit-transform: translateY(-10px);
142        -ms-transform: translateY(-10px);
143        transform: translateY(-10px);
144      }
145    }
146
147    @-webkit-keyframes appearIn {
148      from {
149        opacity: 0;
150        -webkit-transform: translateY(0px);
151        -ms-transform: translateY(0px);
152        transform: translateY(0px);
153      }
154      to {
155        opacity: 1;
156        -webkit-transform: translateY(0);
157        -ms-transform: translateY(0);
158        transform: translateY(0);
159      }
160    }
161
162    @keyframes appearIn {
163      from {
164        opacity: 0;
165        -webkit-transform: translateY(0px);
166        -ms-transform: translateY(0px);
167        transform: translateY(0px);
168      }
169      to {
170        opacity: 1;
171        -webkit-transform: translateY(0);
172        -ms-transform: translateY(0);
173        transform: translateY(0);
174      }
175    }
176
177    @-webkit-keyframes scaleIn {
178      from {
179        -webkit-transform: scale(0);
180        -ms-transform: scale(0);
181        transform: scale(0);
182      }
183      to {
184        -webkit-transform: scale(1);
185        -ms-transform: scale(1);
186        transform: scale(1);
187      }
188    }
189
190    @keyframes scaleIn {
191      from {
192        -webkit-transform: scale(0);
193        -ms-transform: scale(0);
194        transform: scale(0);
195      }
196      to {
197        -webkit-transform: scale(1);
198        -ms-transform: scale(1);
199        transform: scale(1);
200      }
201    }
202
203    @-webkit-keyframes innerDrawIn {
204      0% {
205        stroke-dashoffset: 70;
206      }
207      50% {
208        stroke-dashoffset: 140;
209      }
210      100% {
211        stroke-dashoffset: 210;
212      }
213    }
214
215    @keyframes innerDrawIn {
216      0% {
217        stroke-dashoffset: 70;
218      }
219      50% {
220        stroke-dashoffset: 140;
221      }
222      100% {
223        stroke-dashoffset: 210;
224      }
225    }
226
227    @-webkit-keyframes outerDrawIn {
228      0% {
229        stroke-dashoffset: 76;
230      }
231      100% {
232        stroke-dashoffset: 152;
233      }
234    }
235
236    @keyframes outerDrawIn {
237      0% {
238        stroke-dashoffset: 76;
239      }
240      100% {
241        stroke-dashoffset: 152;
242      }
243    }
244
245    .hHWjkv {
246      -webkit-transform-origin: 0px 0px;
247      -ms-transform-origin: 0px 0px;
248      transform-origin: 0px 0px;
249      -webkit-transform: scale(0);
250      -ms-transform: scale(0);
251      transform: scale(0);
252      -webkit-animation: scaleIn 0.25s linear forwards 0.2222222222222222s;
253      animation: scaleIn 0.25s linear forwards 0.2222222222222222s;
254    }
255
256    .gCDOzd {
257      -webkit-transform-origin: 0px 0px;
258      -ms-transform-origin: 0px 0px;
259      transform-origin: 0px 0px;
260      -webkit-transform: scale(0);
261      -ms-transform: scale(0);
262      transform: scale(0);
263      -webkit-animation: scaleIn 0.25s linear forwards 0.4222222222222222s;
264      animation: scaleIn 0.25s linear forwards 0.4222222222222222s;
265    }
266
267    .hmCcxi {
268      -webkit-transform-origin: 0px 0px;
269      -ms-transform-origin: 0px 0px;
270      transform-origin: 0px 0px;
271      -webkit-transform: scale(0);
272      -ms-transform: scale(0);
273      transform: scale(0);
274      -webkit-animation: scaleIn 0.25s linear forwards 0.6222222222222222s;
275      animation: scaleIn 0.25s linear forwards 0.6222222222222222s;
276    }
277
278    .eHamQi {
279      -webkit-transform-origin: 0px 0px;
280      -ms-transform-origin: 0px 0px;
281      transform-origin: 0px 0px;
282      -webkit-transform: scale(0);
283      -ms-transform: scale(0);
284      transform: scale(0);
285      -webkit-animation: scaleIn 0.25s linear forwards 0.8222222222222223s;
286      animation: scaleIn 0.25s linear forwards 0.8222222222222223s;
287    }
288
289    .byhgGu {
290      -webkit-transform-origin: 0px 0px;
291      -ms-transform-origin: 0px 0px;
292      transform-origin: 0px 0px;
293      -webkit-transform: scale(0);
294      -ms-transform: scale(0);
295      transform: scale(0);
296      -webkit-animation: scaleIn 0.25s linear forwards 1.0222222222222221s;
297      animation: scaleIn 0.25s linear forwards 1.0222222222222221s;
298    }
299
300    .llAKP {
301      -webkit-transform-origin: 0px 0px;
302      -ms-transform-origin: 0px 0px;
303      transform-origin: 0px 0px;
304      -webkit-transform: scale(0);
305      -ms-transform: scale(0);
306      transform: scale(0);
307      -webkit-animation: scaleIn 0.25s linear forwards 1.2222222222222223s;
308      animation: scaleIn 0.25s linear forwards 1.2222222222222223s;
309    }
310
311    .bglIGM {
312      -webkit-transform-origin: 64px 28px;
313      -ms-transform-origin: 64px 28px;
314      transform-origin: 64px 28px;
315      -webkit-transform: scale(0);
316      -ms-transform: scale(0);
317      transform: scale(0);
318      -webkit-animation: scaleIn 0.25s linear forwards 0.2222222222222222s;
319      animation: scaleIn 0.25s linear forwards 0.2222222222222222s;
320    }
321
322    .ksxRII {
323      -webkit-transform-origin: 95.98500061035156px 46.510000228881836px;
324      -ms-transform-origin: 95.98500061035156px 46.510000228881836px;
325      transform-origin: 95.98500061035156px 46.510000228881836px;
326      -webkit-transform: scale(0);
327      -ms-transform: scale(0);
328      transform: scale(0);
329      -webkit-animation: scaleIn 0.25s linear forwards 0.4222222222222222s;
330      animation: scaleIn 0.25s linear forwards 0.4222222222222222s;
331    }
332
333    .cWrBmb {
334      -webkit-transform-origin: 95.97162628173828px 83.4900016784668px;
335      -ms-transform-origin: 95.97162628173828px 83.4900016784668px;
336      transform-origin: 95.97162628173828px 83.4900016784668px;
337      -webkit-transform: scale(0);
338      -ms-transform: scale(0);
339      transform: scale(0);
340      -webkit-animation: scaleIn 0.25s linear forwards 0.6222222222222222s;
341      animation: scaleIn 0.25s linear forwards 0.6222222222222222s;
342    }
343
344    .Wnusb {
345      -webkit-transform-origin: 64px 101.97999572753906px;
346      -ms-transform-origin: 64px 101.97999572753906px;
347      transform-origin: 64px 101.97999572753906px;
348      -webkit-transform: scale(0);
349      -ms-transform: scale(0);
350      transform: scale(0);
351      -webkit-animation: scaleIn 0.25s linear forwards 0.8222222222222223s;
352      animation: scaleIn 0.25s linear forwards 0.8222222222222223s;
353    }
354
355    .bfPqf {
356      -webkit-transform-origin: 32.03982162475586px 83.4900016784668px;
357      -ms-transform-origin: 32.03982162475586px 83.4900016784668px;
358      transform-origin: 32.03982162475586px 83.4900016784668px;
359      -webkit-transform: scale(0);
360      -ms-transform: scale(0);
361      transform: scale(0);
362      -webkit-animation: scaleIn 0.25s linear forwards 1.0222222222222221s;
363      animation: scaleIn 0.25s linear forwards 1.0222222222222221s;
364    }
365
366    .edRCTN {
367      -webkit-transform-origin: 32.033552169799805px 46.510000228881836px;
368      -ms-transform-origin: 32.033552169799805px 46.510000228881836px;
369      transform-origin: 32.033552169799805px 46.510000228881836px;
370      -webkit-transform: scale(0);
371      -ms-transform: scale(0);
372      transform: scale(0);
373      -webkit-animation: scaleIn 0.25s linear forwards 1.2222222222222223s;
374      animation: scaleIn 0.25s linear forwards 1.2222222222222223s;
375    }
376
377    .iEGVWn {
378      opacity: 0;
379      stroke-dasharray: 76;
380      -webkit-animation: outerDrawIn 0.5s ease-out forwards 0.3333333333333333s, appearIn 0.1s ease-out forwards 0.3333333333333333s;
381      animation: outerDrawIn 0.5s ease-out forwards 0.3333333333333333s, appearIn 0.1s ease-out forwards 0.3333333333333333s;
382      -webkit-animation-iteration-count: 1, 1;
383      animation-iteration-count: 1, 1;
384    }
385
386    .bsocdx {
387      opacity: 0;
388      stroke-dasharray: 76;
389      -webkit-animation: outerDrawIn 0.5s ease-out forwards 0.5333333333333333s, appearIn 0.1s ease-out forwards 0.5333333333333333s;
390      animation: outerDrawIn 0.5s ease-out forwards 0.5333333333333333s, appearIn 0.1s ease-out forwards 0.5333333333333333s;
391      -webkit-animation-iteration-count: 1, 1;
392      animation-iteration-count: 1, 1;
393    }
394
395    .jAZXmP {
396      opacity: 0;
397      stroke-dasharray: 76;
398      -webkit-animation: outerDrawIn 0.5s ease-out forwards 0.7333333333333334s, appearIn 0.1s ease-out forwards 0.7333333333333334s;
399      animation: outerDrawIn 0.5s ease-out forwards 0.7333333333333334s, appearIn 0.1s ease-out forwards 0.7333333333333334s;
400      -webkit-animation-iteration-count: 1, 1;
401      animation-iteration-count: 1, 1;
402    }
403
404    .hSeArx {
405      opacity: 0;
406      stroke-dasharray: 76;
407      -webkit-animation: outerDrawIn 0.5s ease-out forwards 0.9333333333333333s, appearIn 0.1s ease-out forwards 0.9333333333333333s;
408      animation: outerDrawIn 0.5s ease-out forwards 0.9333333333333333s, appearIn 0.1s ease-out forwards 0.9333333333333333s;
409      -webkit-animation-iteration-count: 1, 1;
410      animation-iteration-count: 1, 1;
411    }
412
413    .bVgqGk {
414      opacity: 0;
415      stroke-dasharray: 76;
416      -webkit-animation: outerDrawIn 0.5s ease-out forwards 1.1333333333333333s, appearIn 0.1s ease-out forwards 1.1333333333333333s;
417      animation: outerDrawIn 0.5s ease-out forwards 1.1333333333333333s, appearIn 0.1s ease-out forwards 1.1333333333333333s;
418      -webkit-animation-iteration-count: 1, 1;
419      animation-iteration-count: 1, 1;
420    }
421
422    .hEFqBt {
423      opacity: 0;
424      stroke-dasharray: 76;
425      -webkit-animation: outerDrawIn 0.5s ease-out forwards 1.3333333333333333s, appearIn 0.1s ease-out forwards 1.3333333333333333s;
426      animation: outerDrawIn 0.5s ease-out forwards 1.3333333333333333s, appearIn 0.1s ease-out forwards 1.3333333333333333s;
427      -webkit-animation-iteration-count: 1, 1;
428      animation-iteration-count: 1, 1;
429    }
430
431    .dzEKCM {
432      opacity: 0;
433      stroke-dasharray: 70;
434      -webkit-animation: innerDrawIn 1s ease-in-out forwards 1.3666666666666667s, appearIn 0.1s linear forwards 1.3666666666666667s;
435      animation: innerDrawIn 1s ease-in-out forwards 1.3666666666666667s, appearIn 0.1s linear forwards 1.3666666666666667s;
436      -webkit-animation-iteration-count: infinite, 1;
437      animation-iteration-count: infinite, 1;
438    }
439
440    .DYnPx {
441      opacity: 0;
442      stroke-dasharray: 70;
443      -webkit-animation: innerDrawIn 1s ease-in-out forwards 1.5333333333333332s, appearIn 0.1s linear forwards 1.5333333333333332s;
444      animation: innerDrawIn 1s ease-in-out forwards 1.5333333333333332s, appearIn 0.1s linear forwards 1.5333333333333332s;
445      -webkit-animation-iteration-count: infinite, 1;
446      animation-iteration-count: infinite, 1;
447    }
448
449    .hjPEAQ {
450      opacity: 0;
451      stroke-dasharray: 70;
452      -webkit-animation: innerDrawIn 1s ease-in-out forwards 1.7000000000000002s, appearIn 0.1s linear forwards 1.7000000000000002s;
453      animation: innerDrawIn 1s ease-in-out forwards 1.7000000000000002s, appearIn 0.1s linear forwards 1.7000000000000002s;
454      -webkit-animation-iteration-count: infinite, 1;
455      animation-iteration-count: infinite, 1;
456    }
457
458    #loading-wrapper {
459      position: absolute;
460      width: 100vw;
461      height: 100vh;
462      display: -webkit-box;
463      display: -webkit-flex;
464      display: -ms-flexbox;
465      display: flex;
466      -webkit-align-items: center;
467      -webkit-box-align: center;
468      -ms-flex-align: center;
469      align-items: center;
470      -webkit-box-pack: center;
471      -webkit-justify-content: center;
472      -ms-flex-pack: center;
473      justify-content: center;
474      -webkit-flex-direction: column;
475      -ms-flex-direction: column;
476      flex-direction: column;
477    }
478
479    .logo {
480      width: 75px;
481      height: 75px;
482      margin-bottom: 20px;
483      opacity: 0;
484      -webkit-animation: fadeIn 0.5s ease-out forwards;
485      animation: fadeIn 0.5s ease-out forwards;
486    }
487
488    .text {
489      font-size: 32px;
490      font-weight: 200;
491      text-align: center;
492      color: rgba(255, 255, 255, 0.6);
493      opacity: 0;
494      -webkit-animation: fadeIn 0.5s ease-out forwards;
495      animation: fadeIn 0.5s ease-out forwards;
496    }
497
498    .dGfHfc {
499      font-weight: 400;
500    }
501  </style>
502  <div id="loading-wrapper">
503    <svg class="logo" viewBox="0 0 128 128" xmlns:xlink="http://www.w3.org/1999/xlink">
504      <title>GraphQL Playground Logo</title>
505      <defs>
506        <linearGradient id="linearGradient-1" x1="4.86%" x2="96.21%" y1="0%" y2="99.66%">
507          <stop stop-color="#E00082" stop-opacity=".8" offset="0%"></stop>
508          <stop stop-color="#E00082" offset="100%"></stop>
509        </linearGradient>
510      </defs>
511      <g>
512        <rect id="Gradient" width="127.96" height="127.96" y="1" fill="url(#linearGradient-1)" rx="4"></rect>
513        <path id="Border" fill="#E00082" fill-rule="nonzero" d="M4.7 2.84c-1.58 0-2.86 1.28-2.86 2.85v116.57c0 1.57 1.28 2.84 2.85 2.84h116.57c1.57 0 2.84-1.26 2.84-2.83V5.67c0-1.55-1.26-2.83-2.83-2.83H4.67zM4.7 0h116.58c3.14 0 5.68 2.55 5.68 5.7v116.58c0 3.14-2.54 5.68-5.68 5.68H4.68c-3.13 0-5.68-2.54-5.68-5.68V5.68C-1 2.56 1.55 0 4.7 0z"></path>
514        <path class="bglIGM" x="64" y="28" fill="#fff" d="M64 36c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8" style="transform: translate(100px, 100px);"></path>
515        <path class="ksxRII" x="95.98500061035156" y="46.510000228881836" fill="#fff" d="M89.04 50.52c-2.2-3.84-.9-8.73 2.94-10.96 3.83-2.2 8.72-.9 10.95 2.94 2.2 3.84.9 8.73-2.94 10.96-3.85 2.2-8.76.9-10.97-2.94"
516          style="transform: translate(100px, 100px);"></path>
517        <path class="cWrBmb" x="95.97162628173828" y="83.4900016784668" fill="#fff" d="M102.9 87.5c-2.2 3.84-7.1 5.15-10.94 2.94-3.84-2.2-5.14-7.12-2.94-10.96 2.2-3.84 7.12-5.15 10.95-2.94 3.86 2.23 5.16 7.12 2.94 10.96"
518          style="transform: translate(100px, 100px);"></path>
519        <path class="Wnusb" x="64" y="101.97999572753906" fill="#fff" d="M64 110c-4.43 0-8-3.6-8-8.02 0-4.44 3.57-8.02 8-8.02s8 3.58 8 8.02c0 4.4-3.57 8.02-8 8.02"
520          style="transform: translate(100px, 100px);"></path>
521        <path class="bfPqf" x="32.03982162475586" y="83.4900016784668" fill="#fff" d="M25.1 87.5c-2.2-3.84-.9-8.73 2.93-10.96 3.83-2.2 8.72-.9 10.95 2.94 2.2 3.84.9 8.73-2.94 10.96-3.85 2.2-8.74.9-10.95-2.94"
522          style="transform: translate(100px, 100px);"></path>
523        <path class="edRCTN" x="32.033552169799805" y="46.510000228881836" fill="#fff" d="M38.96 50.52c-2.2 3.84-7.12 5.15-10.95 2.94-3.82-2.2-5.12-7.12-2.92-10.96 2.2-3.84 7.12-5.15 10.95-2.94 3.83 2.23 5.14 7.12 2.94 10.96"
524          style="transform: translate(100px, 100px);"></path>
525        <path class="iEGVWn" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M63.55 27.5l32.9 19-32.9-19z"></path>
526        <path class="bsocdx" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M96 46v38-38z"></path>
527        <path class="jAZXmP" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M96.45 84.5l-32.9 19 32.9-19z"></path>
528        <path class="hSeArx" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M64.45 103.5l-32.9-19 32.9 19z"></path>
529        <path class="bVgqGk" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M32 84V46v38z"></path>
530        <path class="hEFqBt" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M31.55 46.5l32.9-19-32.9 19z"></path>
531        <path class="dzEKCM" id="Triangle-Bottom" stroke="#fff" stroke-width="4" d="M30 84h70" stroke-linecap="round"></path>
532        <path class="DYnPx" id="Triangle-Left" stroke="#fff" stroke-width="4" d="M65 26L30 87" stroke-linecap="round"></path>
533        <path class="hjPEAQ" id="Triangle-Right" stroke="#fff" stroke-width="4" d="M98 87L63 26" stroke-linecap="round"></path>
534      </g>
535    </svg>
536    <div class="text">Loading
537      <span class="dGfHfc">GraphQL Playground</span>
538    </div>
539  </div>
540
541  <div id="root" />
542  <script type="text/javascript">
543    window.addEventListener('load', function (event) {
544
545      const loadingWrapper = document.getElementById('loading-wrapper');
546      loadingWrapper.classList.add('fadeOut');
547
548
549      const root = document.getElementById('root');
550      root.classList.add('playgroundIn');
551
552      GraphQLPlayground.init(root, GRAPHQL_PLAYGROUND_CONFIG)
553    })
554  </script>
555</body>
556</html>
557  "##.replace("GRAPHQL_PLAYGROUND_CONFIG", &match serde_json::to_string(&config) {
558            Ok(str) => str,
559            Err(_) => "{}".to_string()
560        })
561        .replace("%GRAPHQL_PLAYGROUND_TITLE%", title)
562}
563
564/// Config for GraphQL Playground
565#[derive(Serialize)]
566#[serde(rename_all = "camelCase")]
567pub struct GraphQLPlaygroundConfig<'a> {
568    endpoint: &'a str,
569    subscription_endpoint: Option<&'a str>,
570    headers: Option<HashMap<&'a str, &'a str>>,
571    settings: Option<HashMap<&'a str, Value>>,
572    title: Option<&'a str>,
573}
574
575impl<'a> GraphQLPlaygroundConfig<'a> {
576    /// Create a config for GraphQL playground.
577    pub fn new(endpoint: &'a str) -> Self {
578        Self {
579            endpoint,
580            subscription_endpoint: None,
581            headers: Default::default(),
582            settings: Default::default(),
583            title: Default::default(),
584        }
585    }
586
587    /// Set subscription endpoint, for example: `ws://localhost:8000`.
588    #[must_use]
589    pub fn subscription_endpoint(mut self, endpoint: &'a str) -> Self {
590        self.subscription_endpoint = Some(endpoint);
591        self
592    }
593
594    /// Set HTTP header for per query.
595    #[must_use]
596    pub fn with_header(mut self, name: &'a str, value: &'a str) -> Self {
597        if let Some(headers) = &mut self.headers {
598            headers.insert(name, value);
599        } else {
600            let mut headers = HashMap::new();
601            headers.insert(name, value);
602            self.headers = Some(headers);
603        }
604        self
605    }
606
607    /// Set the html document title.
608    #[must_use]
609    pub fn title(mut self, title: &'a str) -> Self {
610        self.title = Some(title);
611        self
612    }
613
614    /// Set Playground setting for per query.
615    ///
616    /// ```
617    /// # use async_graphql::Value;
618    /// # use async_graphql::http::GraphQLPlaygroundConfig;
619    /// GraphQLPlaygroundConfig::new("/api/graphql")
620    ///     .with_setting("setting", false)
621    ///     .with_setting("other", Value::Null);
622    /// ```
623    #[must_use]
624    pub fn with_setting(mut self, name: &'a str, value: impl Into<Value>) -> Self {
625        let value = value.into();
626
627        if let Some(settings) = &mut self.settings {
628            settings.insert(name, value);
629        } else {
630            let mut settings = HashMap::new();
631            settings.insert(name, value);
632            self.settings = Some(settings);
633        }
634        self
635    }
636}
637
638#[cfg(test)]
639mod tests {
640    use indexmap::IndexMap;
641
642    use super::*;
643
644    #[test]
645    fn test_with_setting_can_use_any_json_value() {
646        let settings = GraphQLPlaygroundConfig::new("")
647            .with_setting("string", "string")
648            .with_setting("bool", false)
649            .with_setting("number", 10)
650            .with_setting("null", Value::Null)
651            .with_setting("array", Vec::from([1, 2, 3]))
652            .with_setting("object", IndexMap::new());
653
654        let json = serde_json::to_value(settings).unwrap();
655        let settings = json["settings"].as_object().unwrap();
656
657        assert!(settings["string"].as_str().is_some());
658        assert!(settings["bool"].as_bool().is_some());
659        assert!(settings["number"].as_u64().is_some());
660        assert!(settings["null"].as_null().is_some());
661        assert!(settings["array"].as_array().is_some());
662        assert!(settings["object"].as_object().is_some());
663    }
664}