line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package GraphQL::Schema; |
2
|
|
|
|
|
|
|
|
3
|
17
|
|
|
17
|
|
72936
|
use 5.014; |
|
5877
|
|
|
|
|
55538
|
|
4
|
5877
|
|
|
17
|
|
23955
|
use strict; |
|
3342
|
|
|
|
|
12648
|
|
|
3347
|
|
|
|
|
21232
|
|
5
|
1130
|
|
|
17
|
|
2614
|
use warnings; |
|
1126
|
|
|
|
|
2840
|
|
|
1168
|
|
|
|
|
3305
|
|
6
|
1173
|
|
|
17
|
|
3900
|
use Moo; |
|
94
|
|
|
|
|
3894
|
|
|
620
|
|
|
|
|
12977
|
|
7
|
1520
|
|
|
17
|
|
136801
|
use Types::Standard -all; |
|
429
|
|
|
|
|
908
|
|
|
426
|
|
|
|
|
1251
|
|
8
|
1741
|
|
|
17
|
|
815001
|
use GraphQL::Type::Library -all; |
|
739
|
|
|
|
|
38367
|
|
|
779
|
|
|
|
|
23592
|
|
9
|
2605
|
|
|
17
|
|
241135
|
use GraphQL::MaybeTypeCheck; |
|
2808
|
|
|
|
|
8903
|
|
|
2563
|
|
|
|
|
19825
|
|
10
|
1125
|
|
|
17
|
|
29812
|
use GraphQL::Debug qw(_debug); |
|
1161
|
|
|
|
|
3852
|
|
|
70
|
|
|
|
|
1186
|
|
11
|
68
|
|
|
17
|
|
8486
|
use GraphQL::Directive; |
|
68
|
|
|
|
|
920
|
|
|
163
|
|
|
|
|
1681
|
|
12
|
63
|
|
|
17
|
|
33858
|
use GraphQL::Introspection qw($SCHEMA_META_TYPE); |
|
59
|
|
|
|
|
27905
|
|
|
17
|
|
|
|
|
2381
|
|
13
|
17
|
|
|
17
|
|
9087
|
use GraphQL::Language::Parser qw(parse); |
|
17
|
|
|
|
|
58
|
|
|
17
|
|
|
|
|
1130
|
|
14
|
17
|
|
|
17
|
|
123
|
use GraphQL::Plugin::Type; |
|
17
|
|
|
|
|
35
|
|
|
17
|
|
|
|
|
144
|
|
15
|
17
|
|
|
17
|
|
89
|
use Module::Runtime qw(require_module); |
|
17
|
|
|
|
|
38
|
|
|
17
|
|
|
|
|
134
|
|
16
|
17
|
|
|
17
|
|
1186
|
use Exporter 'import'; |
|
17
|
|
|
|
|
42
|
|
|
17
|
|
|
|
|
1092
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
our $VERSION = '0.02'; |
19
|
|
|
|
|
|
|
our @EXPORT_OK = qw(lookup_type); |
20
|
17
|
|
|
17
|
|
106
|
use constant DEBUG => $ENV{GRAPHQL_DEBUG}; |
|
17
|
|
|
|
|
36
|
|
|
17
|
|
|
|
|
7619
|
|
21
|
|
|
|
|
|
|
my @TYPE_ATTRS = qw(query mutation subscription); |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 NAME |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
GraphQL::Schema - GraphQL schema object |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=head1 SYNOPSIS |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
use GraphQL::Schema; |
30
|
|
|
|
|
|
|
use GraphQL::Type::Object; |
31
|
|
|
|
|
|
|
my $schema = GraphQL::Schema->new( |
32
|
|
|
|
|
|
|
query => GraphQL::Type::Object->new( |
33
|
|
|
|
|
|
|
name => 'Query', |
34
|
|
|
|
|
|
|
fields => { |
35
|
|
|
|
|
|
|
getObject => { |
36
|
|
|
|
|
|
|
type => $interfaceType, |
37
|
|
|
|
|
|
|
resolve => sub { |
38
|
|
|
|
|
|
|
return {}; |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
) |
43
|
|
|
|
|
|
|
); |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=head1 DESCRIPTION |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
Class implementing GraphQL schema. |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
=head2 query |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
=cut |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
has query => (is => 'ro', isa => InstanceOf['GraphQL::Type::Object'], required => 1); |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=head2 mutation |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=cut |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
has mutation => (is => 'ro', isa => InstanceOf['GraphQL::Type::Object']); |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=head2 subscription |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=cut |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
has subscription => (is => 'ro', isa => InstanceOf['GraphQL::Type::Object']); |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=head2 types |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
Defaults to the types returned by L<GraphQL::Plugin::Type/registered>. |
72
|
|
|
|
|
|
|
Note that this includes the standard scalar types always loaded by |
73
|
|
|
|
|
|
|
L<GraphQL::Type::Scalar>. If you |
74
|
|
|
|
|
|
|
wish to supply an overriding value for this attribute, bear that in mind. |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
=cut |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
has types => ( |
79
|
|
|
|
|
|
|
is => 'ro', |
80
|
|
|
|
|
|
|
isa => ArrayRef[ConsumerOf['GraphQL::Role::Named']], |
81
|
|
|
|
|
|
|
default => sub { [ GraphQL::Plugin::Type->registered ] }, |
82
|
|
|
|
|
|
|
); |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
=head2 directives |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=cut |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
has directives => ( |
89
|
|
|
|
|
|
|
is => 'ro', |
90
|
|
|
|
|
|
|
isa => ArrayRef[InstanceOf['GraphQL::Directive']], |
91
|
|
|
|
|
|
|
default => sub { \@GraphQL::Directive::SPECIFIED_DIRECTIVES }, |
92
|
|
|
|
|
|
|
); |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
=head1 METHODS |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=head2 name2type |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
In this schema, returns a hash-ref mapping all types' names to their |
99
|
|
|
|
|
|
|
type object. |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=cut |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
has name2type => (is => 'lazy', isa => Map[StrNameValid, ConsumerOf['GraphQL::Role::Named']]); |
104
|
|
|
|
|
|
|
sub _build_name2type { |
105
|
|
|
|
75
|
|
|
my ($self) = @_; |
106
|
|
|
|
|
|
|
my @types = grep $_, (map $self->$_, @TYPE_ATTRS), $SCHEMA_META_TYPE; |
107
|
|
|
|
|
|
|
push @types, @{ $self->types || [] }; |
108
|
|
|
|
|
|
|
my %name2type; |
109
|
|
|
|
|
|
|
map _expand_type(\%name2type, $_), @types; |
110
|
|
|
|
|
|
|
\%name2type; |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
=head2 get_possible_types($abstract_type) |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
In this schema, get all of either the implementation types |
116
|
|
|
|
|
|
|
(if interface) or possible types (if union) of the C<$abstract_type>. |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=cut |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
fun _expand_type( |
121
|
|
|
|
|
|
|
(Map[StrNameValid, ConsumerOf['GraphQL::Role::Named']]) $map, |
122
|
|
|
|
|
|
|
(InstanceOf['GraphQL::Type']) $type, |
123
|
5848
|
50
|
|
5848
|
|
12389
|
) :ReturnType(ArrayRef[ConsumerOf['GraphQL::Role::Named']]) { |
|
5848
|
50
|
|
|
|
11448
|
|
|
5848
|
100
|
|
|
|
9197
|
|
|
5848
|
100
|
|
|
|
12366
|
|
|
17
|
|
|
|
|
25564
|
|
|
17
|
|
|
|
|
38
|
|
124
|
17
|
100
|
|
|
|
129
|
@_ = ($map, $type->of), goto &_expand_type if $type->can('of'); # avoid blowing out the stack |
125
|
12
|
100
|
|
|
|
38
|
my $name = $type->name if $type->can('name'); |
126
|
12
|
100
|
100
|
|
|
44
|
return [] if $name and $map->{$name} and $map->{$name} == $type; # seen |
|
|
|
100
|
|
|
|
|
127
|
12
|
100
|
|
|
|
29
|
die "Duplicate type $name" if $map->{$name}; |
128
|
12
|
|
|
|
|
28
|
$map->{$name} = $type; |
129
|
12
|
|
|
|
|
42
|
my @types; |
130
|
17
|
100
|
|
|
|
24561
|
push @types, ($type, map @{ _expand_type($map, $_) }, @{ $type->interfaces || [] }) |
|
17
|
100
|
|
|
|
44
|
|
|
17
|
|
|
|
|
101
|
|
131
|
|
|
|
|
|
|
if $type->isa('GraphQL::Type::Object'); |
132
|
14
|
100
|
|
|
|
498
|
push @types, ($type, map @{ _expand_type($map, $_) }, @{ $type->get_types }) |
|
14
|
|
|
|
|
39
|
|
|
14
|
|
|
|
|
27
|
|
133
|
|
|
|
|
|
|
if $type->isa('GraphQL::Type::Union'); |
134
|
14
|
100
|
|
|
|
33
|
if (grep $type->DOES($_), qw(GraphQL::Role::FieldsInput GraphQL::Role::FieldsOutput)) { |
135
|
14
|
|
50
|
|
|
52
|
my $fields = $type->fields||{}; |
136
|
|
|
|
|
|
|
push @types, map { |
137
|
14
|
50
|
|
|
|
708
|
map @{ _expand_type($map, $_->{type}) }, $_, values %{ $_->{args}||{} } |
|
19
|
|
|
|
|
25675
|
|
|
19
|
|
|
|
|
91
|
|
|
19
|
|
|
|
|
124
|
|
138
|
|
|
|
|
|
|
} values %$fields; |
139
|
|
|
|
|
|
|
} |
140
|
57
|
|
|
|
|
7524
|
DEBUG and _debug('_expand_type', \@types); |
141
|
5905
|
|
|
|
|
1357957
|
\@types; |
142
|
17
|
|
|
17
|
|
45802
|
} |
|
17
|
|
|
|
|
44
|
|
|
17
|
|
|
|
|
104
|
|
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
has _interface2types => (is => 'lazy', isa => Map[StrNameValid, ArrayRef[InstanceOf['GraphQL::Type::Object']]]); |
145
|
|
|
|
|
|
|
sub _build__interface2types { |
146
|
|
|
|
6
|
|
|
my ($self) = @_; |
147
|
|
|
|
|
|
|
my $name2type = $self->name2type||{}; |
148
|
|
|
|
|
|
|
my %interface2types; |
149
|
|
|
|
|
|
|
map { |
150
|
|
|
|
|
|
|
my $o = $_; |
151
|
|
|
|
|
|
|
map { |
152
|
|
|
|
|
|
|
push @{$interface2types{$_->name}}, $o; |
153
|
|
|
|
|
|
|
# TODO assert_object_implements_interface |
154
|
|
|
|
|
|
|
} @{ $o->interfaces||[] }; |
155
|
|
|
|
|
|
|
} grep $_->isa('GraphQL::Type::Object'), values %$name2type; |
156
|
|
|
|
|
|
|
\%interface2types; |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
method get_possible_types( |
160
|
|
|
|
|
|
|
(ConsumerOf['GraphQL::Role::Abstract']) $abstract_type |
161
|
18
|
50
|
|
12
|
1
|
9623
|
) :ReturnType(ArrayRef[InstanceOf['GraphQL::Type::Object']]) { |
|
18
|
50
|
|
|
|
69
|
|
|
18
|
50
|
|
|
|
166
|
|
|
56
|
|
|
|
|
261990
|
|
|
56
|
|
|
|
|
178
|
|
|
56
|
|
|
|
|
107
|
|
162
|
56
|
50
|
|
|
|
218
|
return $abstract_type->get_types if $abstract_type->isa('GraphQL::Type::Union'); |
163
|
56
|
50
|
|
|
|
197
|
$self->_interface2types->{$abstract_type->name} || []; |
164
|
57
|
|
|
17
|
|
252
|
} |
|
56
|
|
|
|
|
232
|
|
|
56
|
|
|
|
|
168
|
|
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=head2 is_possible_type($abstract_type, $possible_type) |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
In this schema, is the given C<$possible_type> either an implementation |
169
|
|
|
|
|
|
|
(if interface) or a possibility (if union) of the C<$abstract_type>? |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
=cut |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
has _possible_type_map => (is => 'rw', isa => Map[StrNameValid, Map[StrNameValid, Bool]]); |
174
|
|
|
|
|
|
|
method is_possible_type( |
175
|
|
|
|
|
|
|
(ConsumerOf['GraphQL::Role::Abstract']) $abstract_type, |
176
|
|
|
|
|
|
|
(InstanceOf['GraphQL::Type::Object']) $possible_type, |
177
|
17
|
50
|
|
14
|
1
|
42531
|
) :ReturnType(Bool) { |
|
17
|
100
|
|
|
|
49
|
|
|
17
|
0
|
|
|
|
104
|
|
|
348
|
0
|
|
|
|
6986
|
|
|
348
|
|
|
|
|
753
|
|
|
348
|
|
|
|
|
657
|
|
|
348
|
|
|
|
|
882
|
|
178
|
348
|
|
|
|
|
2862
|
my $map = $self->_possible_type_map || {}; |
179
|
|
|
|
|
|
|
return $map->{$abstract_type->name}{$possible_type->name} |
180
|
75
|
0
|
|
|
|
1515
|
if $map->{$abstract_type->name}; # we know about the abstract_type |
181
|
75
|
50
|
|
|
|
705
|
my @possibles = @{ $self->get_possible_types($abstract_type)||[] }; |
|
75
|
|
|
|
|
180
|
|
182
|
75
|
100
|
|
|
|
374
|
die <<EOF if !@possibles; |
183
|
75
|
|
|
|
|
134
|
Could not find possible implementing types for @{[$abstract_type->name]} |
184
|
|
|
|
|
|
|
in schema. Check that schema.types is defined and is an array of |
185
|
|
|
|
|
|
|
all possible types in the schema. |
186
|
|
|
|
|
|
|
EOF |
187
|
75
|
|
|
|
|
323
|
$map->{$abstract_type->name} = { map { ($_->name => 1) } @possibles }; |
|
71
|
|
|
|
|
1778
|
|
188
|
6
|
|
|
|
|
83
|
$self->_possible_type_map($map); |
189
|
6
|
|
|
|
|
120
|
$map->{$abstract_type->name}{$possible_type->name}; |
190
|
56
|
|
|
17
|
|
835
|
} |
|
56
|
|
|
|
|
525
|
|
|
56
|
|
|
|
|
203
|
|
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=head2 assert_object_implements_interface($type, $iface) |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
In this schema, does the given C<$type> implement interface C<$iface>? If |
195
|
|
|
|
|
|
|
not, throw exception. |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=cut |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
method assert_object_implements_interface( |
200
|
|
|
|
|
|
|
(ConsumerOf['GraphQL::Role::Abstract']) $abstract_type, |
201
|
|
|
|
|
|
|
(InstanceOf['GraphQL::Type::Object']) $possible_type, |
202
|
|
|
|
0
|
1
|
|
) { |
203
|
|
|
|
|
|
|
my @types = @{ $self->types }; |
204
|
|
|
|
|
|
|
return; |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=head2 from_ast($ast[, \%kind2class]) |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
Class method. Takes AST (array-ref of hash-refs) made by |
210
|
|
|
|
|
|
|
L<GraphQL::Language::Parser/parse> and returns a schema object. Will |
211
|
|
|
|
|
|
|
not be a complete schema since it will have only default resolvers. |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
If C<\%kind2class> is given, it will override the default |
214
|
|
|
|
|
|
|
mapping of SDL keywords to Perl classes. This is probably most |
215
|
|
|
|
|
|
|
useful for L<GraphQL::Type::Object>. The default is available as |
216
|
|
|
|
|
|
|
C<%GraphQL::Schema::KIND2CLASS>. E.g. |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
my $schema = GraphQL::Schema->from_ast( |
219
|
|
|
|
|
|
|
$doc, |
220
|
|
|
|
|
|
|
{ %GraphQL::Schema::KIND2CLASS, type => 'GraphQL::Type::Object::DBIC' } |
221
|
|
|
|
|
|
|
); |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
Makes available the additional types returned by |
224
|
|
|
|
|
|
|
L<GraphQL::Plugin::Type/registered>. |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=cut |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
our %KIND2CLASS = qw( |
229
|
|
|
|
|
|
|
type GraphQL::Type::Object |
230
|
|
|
|
|
|
|
enum GraphQL::Type::Enum |
231
|
|
|
|
|
|
|
interface GraphQL::Type::Interface |
232
|
|
|
|
|
|
|
union GraphQL::Type::Union |
233
|
|
|
|
|
|
|
scalar GraphQL::Type::Scalar |
234
|
|
|
|
|
|
|
input GraphQL::Type::InputObject |
235
|
|
|
|
|
|
|
); |
236
|
|
|
|
|
|
|
my %CLASS2KIND = reverse %KIND2CLASS; |
237
|
|
|
|
|
|
|
method from_ast( |
238
|
|
|
|
|
|
|
ArrayRef[HashRef] $ast, |
239
|
|
|
|
|
|
|
HashRef $kind2class = \%KIND2CLASS, |
240
|
49
|
50
|
|
55
|
1
|
66
|
) :ReturnType(InstanceOf[__PACKAGE__]) { |
|
7
|
50
|
|
|
|
198
|
|
|
7
|
|
|
|
|
37
|
|
|
49
|
|
|
|
|
854
|
|
|
6
|
|
|
|
|
275
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
241
|
0
|
|
|
|
|
0
|
DEBUG and _debug('Schema.from_ast', $ast); |
242
|
0
|
|
|
|
|
0
|
my @type_nodes = grep $kind2class->{$_->{kind}}, @$ast; |
243
|
0
|
|
|
|
|
0
|
my ($schema_node, $e) = grep $_->{kind} eq 'schema', @$ast; |
244
|
0
|
|
|
|
|
0
|
die "Must provide only one schema definition.\n" if $e; |
245
|
0
|
|
|
|
|
0
|
my %name2type = (map { $_->name => $_ } GraphQL::Plugin::Type->registered); |
|
0
|
|
|
|
|
0
|
|
246
|
0
|
|
|
|
|
0
|
for (@type_nodes) { |
247
|
|
|
|
|
|
|
die "Type '$_->{name}' was defined more than once.\n" |
248
|
0
|
|
|
|
|
0
|
if $name2type{$_->{name}}; |
249
|
26
|
|
|
|
|
665
|
require_module $kind2class->{$_->{kind}}; |
250
|
26
|
|
|
|
|
44
|
$name2type{$_->{name}} = $kind2class->{$_->{kind}}->from_ast(\%name2type, $_); |
251
|
|
|
|
|
|
|
} |
252
|
26
|
|
|
|
|
269
|
if (!$schema_node) { |
253
|
|
|
|
|
|
|
# infer one |
254
|
|
|
|
|
|
|
$schema_node = +{ |
255
|
19
|
|
|
|
|
98
|
map { $name2type{ucfirst $_} ? ($_ => ucfirst $_) : () } @TYPE_ATTRS |
|
21
|
|
|
|
|
182
|
|
256
|
|
|
|
|
|
|
}; |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
die "Must provide schema definition with query type or a type named Query.\n" |
259
|
26
|
|
|
|
|
129
|
unless $schema_node->{query}; |
260
|
|
|
|
|
|
|
my @directives = map GraphQL::Directive->from_ast(\%name2type, $_), |
261
|
132
|
|
|
|
|
347
|
grep $_->{kind} eq 'directive', @$ast; |
262
|
|
|
|
|
|
|
my $schema = $self->new( |
263
|
|
|
|
|
|
|
(map { |
264
|
26
|
|
|
|
|
78
|
$schema_node->{$_} |
265
|
0
|
|
|
|
|
0
|
? ($_ => $name2type{$schema_node->{$_}} |
266
|
|
|
|
|
|
|
// die "Specified $_ type '$schema_node->{$_}' not found.\n") |
267
|
|
|
|
|
|
|
: () |
268
|
|
|
|
|
|
|
} @TYPE_ATTRS), |
269
|
|
|
|
|
|
|
(@directives ? (directives => [ @GraphQL::Directive::SPECIFIED_DIRECTIVES, @directives ]) : ()), |
270
|
|
|
|
|
|
|
types => [ values %name2type ], |
271
|
|
|
|
|
|
|
); |
272
|
26
|
|
|
|
|
287
|
$schema->name2type; # walks all types, fields, args - finds undefined types |
273
|
26
|
|
|
|
|
583
|
$schema; |
274
|
6
|
|
|
17
|
|
2637
|
} |
|
6
|
|
|
|
|
103
|
|
|
49
|
|
|
|
|
1277
|
|
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
=head2 from_doc($doc[, \%kind2class]) |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
Class method. Takes text that is a Schema Definition Language (SDL) (aka |
279
|
|
|
|
|
|
|
Interface Definition Language) document and returns a schema object. Will |
280
|
|
|
|
|
|
|
not be a complete schema since it will have only default resolvers. |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
As of v0.32, this accepts both old-style "meaningful comments" and |
283
|
|
|
|
|
|
|
new-style string values, as field or type descriptions. |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
If C<\%kind2class> is given, it will override the default |
286
|
|
|
|
|
|
|
mapping of SDL keywords to Perl classes. This is probably most |
287
|
|
|
|
|
|
|
useful for L<GraphQL::Type::Object>. The default is available as |
288
|
|
|
|
|
|
|
C<%GraphQL::Schema::KIND2CLASS>. |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
=cut |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
method from_doc( |
293
|
|
|
|
|
|
|
Str $doc, |
294
|
|
|
|
|
|
|
HashRef $kind2class = \%KIND2CLASS, |
295
|
2
|
|
|
56
|
1
|
3
|
) :ReturnType(InstanceOf[__PACKAGE__]) { |
|
2
|
|
|
|
|
3
|
|
|
10
|
|
|
|
|
67
|
|
|
2
|
|
|
|
|
11
|
|
296
|
|
|
|
|
|
|
$self->from_ast(parse($doc), $kind2class); |
297
|
2
|
|
|
17
|
|
85
|
} |
|
2
|
|
|
|
|
8
|
|
|
2
|
|
|
|
|
5
|
|
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=head2 to_doc($doc) |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
Returns Schema Definition Language (SDL) document that describes this |
302
|
|
|
|
|
|
|
schema object. |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
As of v0.32, this produces the new-style descriptions that are string |
305
|
|
|
|
|
|
|
values, rather than old-style "meaningful comments". |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
As of v0.33, will not return a description of types supplied with the |
308
|
|
|
|
|
|
|
attribute L</types>. Obviously, by default this includes types returned |
309
|
|
|
|
|
|
|
by L<GraphQL::Plugin::Type/registered>. |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
=cut |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
has to_doc => (is => 'lazy', isa => Str); |
314
|
|
|
|
|
|
|
my %directive2builtin = map { ($_=>1) } @GraphQL::Directive::SPECIFIED_DIRECTIVES; |
315
|
|
|
|
|
|
|
sub _build_to_doc { |
316
|
|
|
|
26
|
|
|
my ($self) = @_; |
317
|
|
|
|
|
|
|
my $schema_doc; |
318
|
|
|
|
|
|
|
if (grep $self->$_->name ne ucfirst $_, grep $self->$_, @TYPE_ATTRS) { |
319
|
|
|
|
|
|
|
$schema_doc = join('', map "$_\n", "schema {", |
320
|
|
|
|
|
|
|
(map " $_: @{[$self->$_->name]}", grep $self->$_, @TYPE_ATTRS), |
321
|
|
|
|
|
|
|
"}"); |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
my %supplied_type = (map {$_->name => 1} GraphQL::Plugin::Type->registered); |
324
|
|
|
|
|
|
|
join "\n", grep defined, |
325
|
|
|
|
|
|
|
$schema_doc, |
326
|
|
|
|
|
|
|
(map $_->to_doc, |
327
|
|
|
|
|
|
|
sort { $a->name cmp $b->name } |
328
|
|
|
|
|
|
|
grep !$directive2builtin{$_}, |
329
|
|
|
|
|
|
|
@{ $self->directives }), |
330
|
|
|
|
|
|
|
(map $self->name2type->{$_}->to_doc, |
331
|
|
|
|
|
|
|
grep !/^__/, |
332
|
|
|
|
|
|
|
grep $CLASS2KIND{ref $self->name2type->{$_}}, |
333
|
|
|
|
|
|
|
grep !$supplied_type{$_}, |
334
|
|
|
|
|
|
|
sort keys %{$self->name2type}), |
335
|
|
|
|
|
|
|
; |
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
=head2 name2directive |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
In this schema, returns a hash-ref mapping all directives' names to their |
341
|
|
|
|
|
|
|
directive object. |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
=cut |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
has name2directive => (is => 'lazy', isa => Map[StrNameValid, InstanceOf['GraphQL::Directive']]); |
346
|
|
|
|
2
|
|
|
method _build_name2directive() { |
347
|
|
|
|
|
|
|
+{ map { ($_->name => $_) } @{ $self->directives } }; |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=head1 FUNCTIONS |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
=head2 lookup_type($typedef, $name2type) |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
Turns given AST fragment into a type. |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
If the hash-ref's C<type> member is a string, will return a type of that name. |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
If an array-ref, first element must be either C<list> or C<non_null>, |
359
|
|
|
|
|
|
|
second will be a recursive AST fragment, which will be passed into a |
360
|
|
|
|
|
|
|
recursive call. The result will then have the modifier method (C<list> |
361
|
|
|
|
|
|
|
or C<non_null>) called, and that will be returned. |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
=cut |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
fun lookup_type( |
366
|
|
|
|
|
|
|
HashRef $typedef, |
367
|
|
|
|
|
|
|
(Map[StrNameValid, InstanceOf['GraphQL::Type']]) $name2type, |
368
|
|
|
|
348
|
1
|
|
) :ReturnType(InstanceOf['GraphQL::Type']) { |
369
|
|
|
|
|
|
|
my $type = $typedef->{type}; |
370
|
|
|
|
|
|
|
die "Undefined type given\n" if !defined $type; |
371
|
|
|
|
|
|
|
return $name2type->{$type} // die "Unknown type '$type'.\n" |
372
|
|
|
|
|
|
|
if is_Str($type); |
373
|
|
|
|
|
|
|
my ($wrapper_type, $wrapped) = @$type; |
374
|
|
|
|
|
|
|
lookup_type($wrapped, $name2type)->$wrapper_type; |
375
|
|
|
|
17
|
|
|
} |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable(); |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
1; |