| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Plack::App::GraphQL::UITemplate; |
|
2
|
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
3928
|
use Moo; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
8
|
|
|
4
|
1
|
|
|
1
|
|
386
|
use Plack::Util; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
542
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
has tt => ( |
|
7
|
|
|
|
|
|
|
is => 'ro', |
|
8
|
|
|
|
|
|
|
required => 1, |
|
9
|
|
|
|
|
|
|
builder => '_build_tt', |
|
10
|
|
|
|
|
|
|
handles => { |
|
11
|
|
|
|
|
|
|
tt_process => 'process', |
|
12
|
|
|
|
|
|
|
} |
|
13
|
|
|
|
|
|
|
); |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
sub _build_tt { |
|
16
|
0
|
|
|
0
|
|
|
return Plack::Util::load_class('Template::Tiny')->new(); |
|
17
|
|
|
|
|
|
|
} |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
has template => ( |
|
20
|
|
|
|
|
|
|
is => 'ro', |
|
21
|
|
|
|
|
|
|
required => 1, |
|
22
|
|
|
|
|
|
|
builder => '_build_template', |
|
23
|
|
|
|
|
|
|
); |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
sub _build_template { |
|
26
|
0
|
|
|
0
|
|
|
$/ = undef; |
|
27
|
0
|
|
|
|
|
|
return my $data = <DATA>; |
|
28
|
|
|
|
|
|
|
} |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
has json_encoder => ( |
|
31
|
|
|
|
|
|
|
is => 'ro', |
|
32
|
|
|
|
|
|
|
required => 1, |
|
33
|
|
|
|
|
|
|
handles => { |
|
34
|
|
|
|
|
|
|
json_encode => 'encode', |
|
35
|
|
|
|
|
|
|
}, |
|
36
|
|
|
|
|
|
|
); |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
has graphiql_version => ( |
|
39
|
|
|
|
|
|
|
is => 'ro', |
|
40
|
|
|
|
|
|
|
required => 1, |
|
41
|
|
|
|
|
|
|
default => 'latest', |
|
42
|
|
|
|
|
|
|
); |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub safe_serialize { |
|
45
|
0
|
|
|
0
|
0
|
|
my ($self, $data) = @_; |
|
46
|
0
|
0
|
|
|
|
|
if($data) { |
|
47
|
0
|
|
|
|
|
|
my $json = $self->json_encode($data); |
|
48
|
0
|
|
|
|
|
|
$json =~ s#/#\\/#g; |
|
49
|
0
|
|
|
|
|
|
return $json; |
|
50
|
|
|
|
|
|
|
} else { |
|
51
|
0
|
|
|
|
|
|
return 'undefined'; |
|
52
|
|
|
|
|
|
|
} |
|
53
|
|
|
|
|
|
|
} |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
sub process { |
|
56
|
0
|
|
|
0
|
0
|
|
my ($self, $req) = @_; |
|
57
|
0
|
|
|
|
|
|
my $query = $req->query_parameters; |
|
58
|
0
|
|
|
|
|
|
my %args = $self->args_from_query($query); |
|
59
|
0
|
|
|
|
|
|
return my $body = $self->process_args(%args); |
|
60
|
|
|
|
|
|
|
} |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub args_from_query { |
|
63
|
0
|
|
|
0
|
0
|
|
my ($self, $query) = @_; |
|
64
|
|
|
|
|
|
|
return my %args = ( |
|
65
|
|
|
|
|
|
|
graphiql_version => $self->graphiql_version, |
|
66
|
|
|
|
|
|
|
queryString => $self->safe_serialize( $query->{'query'} ), |
|
67
|
|
|
|
|
|
|
operationName => $self->safe_serialize( $query->{'operationName'} ), |
|
68
|
|
|
|
|
|
|
resultString => $self->safe_serialize( $query->{'result'} ), |
|
69
|
0
|
|
|
|
|
|
variablesString => $self->safe_serialize( $query->{'variables'} ), |
|
70
|
|
|
|
|
|
|
); |
|
71
|
|
|
|
|
|
|
} |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
sub process_args { |
|
74
|
0
|
|
|
0
|
0
|
|
my ($self, %args) = @_; |
|
75
|
0
|
|
|
|
|
|
my $input = $self->template; |
|
76
|
0
|
|
|
|
|
|
$self->tt_process(\$input, \%args, \my $output); |
|
77
|
0
|
|
|
|
|
|
return $output; |
|
78
|
|
|
|
|
|
|
} |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
1; |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=head1 NAME |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
Plack::App::GraphQL::UITemplate - Template and processing for the GraphQL UI |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
There's nothing really for end users here. Its just refactored into its own |
|
89
|
|
|
|
|
|
|
package for code organization purposes. |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
This is a package used to prepare and return an HTML response when you have the |
|
94
|
|
|
|
|
|
|
'ui' flag enabled (probably for development) and the client requests an HTML |
|
95
|
|
|
|
|
|
|
response. This is based on L<https://github.com/graphql/graphiql> |
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
Feel free to make your own improved development / query interface and put it on |
|
98
|
|
|
|
|
|
|
CPAN! |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=head1 AUTHOR |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
John Napiorkowski |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
L<Plack::App::GraphQL> |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=cut |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
__DATA__ |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
<!DOCTYPE html> |
|
113
|
|
|
|
|
|
|
<html> |
|
114
|
|
|
|
|
|
|
<head> |
|
115
|
|
|
|
|
|
|
<meta charset="utf-8" /> |
|
116
|
|
|
|
|
|
|
<title>GraphiQL</title> |
|
117
|
|
|
|
|
|
|
<meta name="robots" content="noindex" /> |
|
118
|
|
|
|
|
|
|
<style> |
|
119
|
|
|
|
|
|
|
html, body { |
|
120
|
|
|
|
|
|
|
height: 100%; |
|
121
|
|
|
|
|
|
|
margin: 0; |
|
122
|
|
|
|
|
|
|
overflow: hidden; |
|
123
|
|
|
|
|
|
|
width: 100%; |
|
124
|
|
|
|
|
|
|
} |
|
125
|
|
|
|
|
|
|
</style> |
|
126
|
|
|
|
|
|
|
<link href="//cdn.jsdelivr.net/npm/graphiql@[% graphiql_version %]/graphiql.css" rel="stylesheet" /> |
|
127
|
|
|
|
|
|
|
<script src="//cdn.jsdelivr.net/fetch/0.9.0/fetch.min.js"></script> |
|
128
|
|
|
|
|
|
|
<script src="//cdn.jsdelivr.net/react/15.4.2/react.min.js"></script> |
|
129
|
|
|
|
|
|
|
<script src="//cdn.jsdelivr.net/react/15.4.2/react-dom.min.js"></script> |
|
130
|
|
|
|
|
|
|
<script src="//cdn.jsdelivr.net/npm/graphiql@[% graphiql_version %]/graphiql.min.js"></script> |
|
131
|
|
|
|
|
|
|
</head> |
|
132
|
|
|
|
|
|
|
<body> |
|
133
|
|
|
|
|
|
|
<script> |
|
134
|
|
|
|
|
|
|
// Collect the URL parameters |
|
135
|
|
|
|
|
|
|
var parameters = {}; |
|
136
|
|
|
|
|
|
|
window.location.search.substr(1).split('&').forEach(function (entry) { |
|
137
|
|
|
|
|
|
|
var eq = entry.indexOf('='); |
|
138
|
|
|
|
|
|
|
if (eq >= 0) { |
|
139
|
|
|
|
|
|
|
parameters[decodeURIComponent(entry.slice(0, eq))] = |
|
140
|
|
|
|
|
|
|
decodeURIComponent(entry.slice(eq + 1)); |
|
141
|
|
|
|
|
|
|
} |
|
142
|
|
|
|
|
|
|
}); |
|
143
|
|
|
|
|
|
|
// Produce a Location query string from a parameter object. |
|
144
|
|
|
|
|
|
|
function locationQuery(params) { |
|
145
|
|
|
|
|
|
|
return '?' + Object.keys(params).filter(function (key) { |
|
146
|
|
|
|
|
|
|
return Boolean(params[key]); |
|
147
|
|
|
|
|
|
|
}).map(function (key) { |
|
148
|
|
|
|
|
|
|
return encodeURIComponent(key) + '=' + |
|
149
|
|
|
|
|
|
|
encodeURIComponent(params[key]); |
|
150
|
|
|
|
|
|
|
}).join('&'); |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
// Derive a fetch URL from the current URL, sans the GraphQL parameters. |
|
153
|
|
|
|
|
|
|
var graphqlParamNames = { |
|
154
|
|
|
|
|
|
|
query: true, |
|
155
|
|
|
|
|
|
|
variables: true, |
|
156
|
|
|
|
|
|
|
operationName: true |
|
157
|
|
|
|
|
|
|
}; |
|
158
|
|
|
|
|
|
|
var otherParams = {}; |
|
159
|
|
|
|
|
|
|
for (var k in parameters) { |
|
160
|
|
|
|
|
|
|
if (parameters.hasOwnProperty(k) && graphqlParamNames[k] !== true) { |
|
161
|
|
|
|
|
|
|
otherParams[k] = parameters[k]; |
|
162
|
|
|
|
|
|
|
} |
|
163
|
|
|
|
|
|
|
} |
|
164
|
|
|
|
|
|
|
var fetchURL = locationQuery(otherParams); |
|
165
|
|
|
|
|
|
|
// Defines a GraphQL fetcher using the fetch API. |
|
166
|
|
|
|
|
|
|
function graphQLFetcher(graphQLParams) { |
|
167
|
|
|
|
|
|
|
return fetch(fetchURL, { |
|
168
|
|
|
|
|
|
|
method: 'post', |
|
169
|
|
|
|
|
|
|
headers: { |
|
170
|
|
|
|
|
|
|
'Accept': 'application/json', |
|
171
|
|
|
|
|
|
|
'Content-Type': 'application/json' |
|
172
|
|
|
|
|
|
|
}, |
|
173
|
|
|
|
|
|
|
body: JSON.stringify(graphQLParams), |
|
174
|
|
|
|
|
|
|
credentials: 'include', |
|
175
|
|
|
|
|
|
|
}).then(function (response) { |
|
176
|
|
|
|
|
|
|
return response.text(); |
|
177
|
|
|
|
|
|
|
}).then(function (responseBody) { |
|
178
|
|
|
|
|
|
|
try { |
|
179
|
|
|
|
|
|
|
return JSON.parse(responseBody); |
|
180
|
|
|
|
|
|
|
} catch (error) { |
|
181
|
|
|
|
|
|
|
return responseBody; |
|
182
|
|
|
|
|
|
|
} |
|
183
|
|
|
|
|
|
|
}); |
|
184
|
|
|
|
|
|
|
} |
|
185
|
|
|
|
|
|
|
// When the query and variables string is edited, update the URL bar so |
|
186
|
|
|
|
|
|
|
// that it can be easily shared. |
|
187
|
|
|
|
|
|
|
function onEditQuery(newQuery) { |
|
188
|
|
|
|
|
|
|
parameters.query = newQuery; |
|
189
|
|
|
|
|
|
|
updateURL(); |
|
190
|
|
|
|
|
|
|
} |
|
191
|
|
|
|
|
|
|
function onEditVariables(newVariables) { |
|
192
|
|
|
|
|
|
|
parameters.variables = newVariables; |
|
193
|
|
|
|
|
|
|
updateURL(); |
|
194
|
|
|
|
|
|
|
} |
|
195
|
|
|
|
|
|
|
function onEditOperationName(newOperationName) { |
|
196
|
|
|
|
|
|
|
parameters.operationName = newOperationName; |
|
197
|
|
|
|
|
|
|
updateURL(); |
|
198
|
|
|
|
|
|
|
} |
|
199
|
|
|
|
|
|
|
function updateURL() { |
|
200
|
|
|
|
|
|
|
history.replaceState(null, null, locationQuery(parameters)); |
|
201
|
|
|
|
|
|
|
} |
|
202
|
|
|
|
|
|
|
// Render <GraphiQL /> into the body. |
|
203
|
|
|
|
|
|
|
ReactDOM.render( |
|
204
|
|
|
|
|
|
|
React.createElement(GraphiQL, { |
|
205
|
|
|
|
|
|
|
fetcher: graphQLFetcher, |
|
206
|
|
|
|
|
|
|
onEditQuery: onEditQuery, |
|
207
|
|
|
|
|
|
|
onEditVariables: onEditVariables, |
|
208
|
|
|
|
|
|
|
onEditOperationName: onEditOperationName, |
|
209
|
|
|
|
|
|
|
query: [% queryString %], |
|
210
|
|
|
|
|
|
|
response: [% resultString %], |
|
211
|
|
|
|
|
|
|
variables: [% variablesString %], |
|
212
|
|
|
|
|
|
|
operationName: [% operationName %], |
|
213
|
|
|
|
|
|
|
}), |
|
214
|
|
|
|
|
|
|
document.body |
|
215
|
|
|
|
|
|
|
); |
|
216
|
|
|
|
|
|
|
</script> |
|
217
|
|
|
|
|
|
|
</body> |
|
218
|
|
|
|
|
|
|
</html> |
|
219
|
|
|
|
|
|
|
|