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
|
|
|
|
|
|
|
|