]>
Dogcows Code - chaz/graphql-client/blob - lib/GraphQL/Client/CLI.pm
1 package GraphQL
::Client
::CLI
;
2 # ABSTRACT: Implementation of the graphql CLI program
8 use Getopt
::Long
2.39 qw(GetOptionsFromArray);
10 use JSON
::MaybeXS
qw(encode_json);
13 our $VERSION = '999.999'; # VERSION
15 sub _croak
{ require Carp
; goto &Carp
::croak
}
24 $self = $self->new if !ref $self;
26 my $options = eval { $self->_get_options(@_) };
32 if ($options->{version
}) {
33 print "graphql $VERSION\n";
36 if ($options->{help
}) {
37 _pod2usage
(-exitval
=> 0, -verbose
=> 99, -sections
=> [qw(NAME SYNOPSIS OPTIONS)]);
39 if ($options->{manual
}) {
40 _pod2usage
(-exitval
=> 0, -verbose
=> 2);
43 my $url = $options->{url
};
45 print STDERR
"The <URL> or --url option argument is required.\n";
49 my $variables = $options->{variables
};
50 my $query = $options->{query
};
51 my $operation_name = $options->{operation_name
};
52 my $unpack = $options->{unpack};
53 my $outfile = $options->{outfile
};
54 my $format = $options->{format
};
55 my $transport = $options->{transport
};
57 my $client = GraphQL
::Client-
>new(url
=> $url);
59 eval { $client->transport };
61 warn $err if $ENV{GRAPHQL_CLIENT_DEBUG
};
62 print STDERR
"Could not construct a transport for URL: $url\n";
63 print STDERR
"Is this URL correct?\n";
68 print STDERR
"Interactive mode engaged! Waiting for a query on <STDIN>...\n"
69 if -t STDIN
; ## no critic (InputOutput::ProhibitInteractiveTest)
70 $query = do { local $/; <STDIN
> };
73 my $resp = $client->execute($query, $variables, $operation_name, $transport);
74 my $err = $resp->{errors
};
76 my $data = $unpack ? $resp->{data
} : $resp;
79 open(my $out, '>', $outfile) or die "Open $outfile failed: $!";
83 _print_data
($data, $format);
85 exit($unpack && $err ? 1 : 0);
92 unshift @args, shellwords
($ENV{GRAPHQL_CLIENT_OPTIONS
} || '');
95 format
=> 'json:pretty',
99 GetOptionsFromArray
(\
@args,
100 'version' => \
$options{version
},
101 'help|h|?' => \
$options{help
},
102 'manual|man' => \
$options{manual
},
103 'url|u=s' => \
$options{url
},
104 'query|mutation=s' => \
$options{query
},
105 'variables|vars|V=s' => \
$options{variables
},
106 'variable|var|d=s%' => \
$options{variables
},
107 'operation-name|n=s' => \
$options{operation_name
},
108 'transport|t=s%' => \
$options{transport
},
109 'format|f=s' => \
$options{format
},
110 'unpack!' => \
$options{unpack},
111 'output|o=s' => \
$options{outfile
},
114 $options{url
} = shift @args if !$options{url
};
115 $options{query
} = shift @args if !$options{query
};
117 $options{query
} ||= '-';
119 my $transport = eval { _expand_vars
($options{transport
}) };
120 die "Two or more --transport keys are incompatible.\n" if $@;
122 if (ref $options{variables
}) {
123 $options{variables
} = eval { _expand_vars
($options{variables
}) };
124 die "Two or more --variable keys are incompatible.\n" if $@;
126 elsif ($options{variables
}) {
127 $options{variables
} = eval { JSON
::MaybeXS-
>new->decode($options{variables
}) };
128 die "The --variables JSON does not parse.\n" if $@;
136 if (ref($item) eq 'ARRAY') {
137 my $first = @$item && $item->[0];
138 return join(',', @$item) if !ref($first);
139 return join(',', map { encode_json
($_) } @$item);
141 return encode_json
($item) if ref($item) eq 'HASH';
146 my ($data, $format) = @_;
147 $format = lc($format || 'json:pretty');
148 if ($format eq 'json' || $format eq 'json:pretty') {
149 my %opts = (allow_nonref
=> 1, canonical
=> 1, utf8
=> 1);
150 $opts{pretty
} = 1 if $format eq 'json:pretty';
151 print JSON
::MaybeXS-
>new(%opts)->encode($data);
153 elsif ($format eq 'yaml') {
154 eval { require YAML
} or die "Missing dependency: YAML\n";
155 print YAML
::Dump
($data);
157 elsif ($format eq 'csv' || $format eq 'tsv' || $format eq 'table') {
158 my $sep = $format eq 'tsv' ? "\t" : ',';
160 my $unpacked = $data;
161 # $unpacked = $data->{data} if !$unpack && !$err;
162 $unpacked = $data->{data
} if $data && $data->{data
};
164 # check the response to see if it can be formatted
167 if (keys %$unpacked == 1) {
168 my ($val) = values %$unpacked;
169 if (ref $val eq 'ARRAY') {
170 my $first = $val->[0];
171 if ($first && ref $first eq 'HASH') {
172 @columns = sort keys %$first;
174 map { [map { _stringify
($_) } @{$_}{@columns}] } @$val
178 @columns = keys %$unpacked;
179 $rows = [map { [map { _stringify
($_) } $_] } @$val];
185 if ($format eq 'table') {
186 eval { require Text
::Table
::Any
} or die "Missing dependency: Text::Table::Any\n";
187 my $table = Text
::Table
::Any
::table
(
189 rows
=> [[@columns], @$rows],
190 backend
=> $ENV{PERL_TEXT_TABLE
},
195 eval { require Text
::CSV
} or die "Missing dependency: Text::CSV\n";
196 my $csv = Text
::CSV-
>new({binary
=> 1, sep
=> $sep, eol
=> $/});
197 $csv->print(*STDOUT
, [@columns]);
198 for my $row (@$rows) {
199 $csv->print(*STDOUT
, $row);
205 print STDERR
sprintf("Error: Response could not be formatted as %s.\n", uc($format));
209 elsif ($format eq 'perl') {
210 eval { require Data
::Dumper
} or die "Missing dependency: Data::Dumper\n";
211 print Data
::Dumper
::Dumper
($data);
214 print STDERR
"Error: Format not supported: $format\n";
225 my @segments = map { split(/\./, $_) } split(/(\[[^\.\]]+\])\.?/, $path);
226 for my $segment (@segments) {
227 if ($segment =~ /\[([^\.\]]+)\]/) {
228 $path[-1]{type
} = 'ARRAY' if @path;
235 $path[-1]{type
} = 'HASH' if @path;
250 while (my ($key, $value) = each %$vars) {
251 my $parsed_path = _parse_path
($key);
254 for my $segment (@$parsed_path) {
255 my $name = $segment->{name
};
256 my $type = $segment->{type
} || '';
257 my $next = $type eq 'HASH' ? {} : $type eq 'ARRAY' ? [] : $value;
258 if (ref $curr eq 'HASH') {
259 _croak
'Conflicting keys' if $segment->{index};
260 if (defined $curr->{$name}) {
261 _croak
'Conflicting keys' if $type ne ref $curr->{$name};
262 $next = $curr->{$name};
265 $curr->{$name} = $next;
268 elsif (ref $curr eq 'ARRAY') {
269 _croak
'Conflicting keys' if !$segment->{index};
270 if (defined $curr->[$name]) {
271 _croak
'Conflicting keys' if $type ne ref $curr->[$name];
272 $next = $curr->[$name];
275 $curr->[$name] = $next;
279 _croak
'Conflicting keys';
289 eval { require Pod
::Usage
};
291 my $ref = $VERSION eq '999.999' ? 'master' : "v$VERSION";
292 my $exit = (@_ == 1 && $_[0] =~ /^\d+$/ && $_[0]) //
293 (@_ % 2 == 0 && {@_}->{'-exitval'}) // 2;
295 Online documentation is available at:
297 https://github.com/chazmcgarvey/graphql-client/blob/$ref/README.md
299 Tip: To enable inline documentation, install the Pod::Usage module.
305 goto &Pod
::Usage
::pod2usage
;
314 This is the actual implementation of L<graphql>.
316 The interface is B<EXPERIMENTAL>. Don't rely on it.
This page took 0.051437 seconds and 4 git commands to generate.