]> Dogcows Code - chaz/graphql-client/blobdiff - bin/graphql
Release GraphQL-Client 0.605
[chaz/graphql-client] / bin / graphql
index a94aa018a147d6f5100015e644c49246bda348e1..2b5c03b8f1daf353d9ddab51a2a8dafeb075a03f 100755 (executable)
 #! perl
-# PODNAME: graphql
 # ABSTRACT: Command-line GraphQL client
-
-# FATPACK - Do not remove this line.
-
-use warnings;
-use strict;
-
-use Getopt::Long;
-use GraphQL::Client;
-use JSON::MaybeXS;
-
-our $VERSION = '999.999'; # VERSION
-
-my $url;
-my $transport       = {};
-my $query           = '-';
-my $variables       = {};
-my $operation_name;
-my $format          = 'json:pretty';
-my $unpack          = 0;
-my $outfile;
-my $version;
-my $help;
-my $manual;
-GetOptions(
-    'url|u=s'               => \$url,
-    'query|mutation=s'      => \$query,
-    'variables|vars|V=s'    => \$variables,
-    'variable|var|d=s%'     => \$variables,
-    'operation-name=s'      => \$operation_name,
-    'transport|t=s%'        => \$transport,
-    'format|f=s'            => \$format,
-    'unpack!'               => \$unpack,
-    'output|o=s'            => \$outfile,
-    'version'               => \$version,
-    'help|h|?'              => \$help,
-    'manual|man'            => \$manual,
-) or pod2usage(2);
-
-if ($version) {
-    print "graphql $VERSION\n";
-    exit 0;
-}
-if ($help) {
-    pod2usage(-exitval => 0, -verbose => 99, -sections => [qw(NAME SYNOPSIS OPTIONS)]);
-}
-if ($manual) {
-    pod2usage(-exitval => 0, -verbose => 2);
-}
-
-$url    = shift if !$url;
-$query  = shift if !$query || $query eq '-';
-
-$transport = expand_vars($transport);
-
-if (ref $variables) {
-    $variables = expand_vars($variables);
-}
-else {
-    $variables = JSON::MaybeXS->new->decode($variables);
-}
-
-my $client = GraphQL::Client->new(url => $url);
-$client->transport;     # just make sure we can load the transport
-
-if (!$query || $query eq '-') {
-    print STDERR "Interactive mode engaged! Waiting for a query on <STDIN>...\n"
-        if -t STDIN; ## no critic (InputOutput::ProhibitInteractiveTest)
-    $query = do { local $/; <> };
-}
-
-my $resp = $client->request($query, $variables, $operation_name, $transport);
-my $err  = $resp->{errors};
-$unpack = 0 if $err;
-my $data = $unpack ? $resp->{data} : $resp;
-
-if ($outfile) {
-    open(my $out, '>', $outfile) or die "Open $outfile failed: $!";
-    *STDOUT = $out;
-}
-
-print_data($data, $format);
-
-exit($unpack && $err ? 1 : 0);
-
-sub print_data {
-    my ($data, $format) = @_;
-    $format = lc($format || 'json:pretty');
-    if ($format eq 'json' || $format eq 'json:pretty') {
-        my %opts = (canonical => 1, utf8 => 1);
-        $opts{pretty} = 1 if $format eq 'json:pretty';
-        print JSON::MaybeXS->new(%opts)->encode($data);
-    }
-    elsif ($format eq 'yaml') {
-        eval { require YAML } or die "Missing dependency: YAML\n";
-        print YAML::Dump($data);
-    }
-    elsif ($format eq 'csv' || $format eq 'tsv' || $format eq 'table') {
-        my $sep = $format eq 'tsv' ? "\t" : ',';
-
-        my $unpacked = $data;
-        $unpacked = $data->{data} if !$unpack && !$err;
-
-        # check the response to see if it can be formatted
-        my @columns;
-        my $rows = [];
-        if (keys %$unpacked == 1) {
-            my ($val) = values %$unpacked;
-            if (ref $val eq 'ARRAY') {
-                my $first = $val->[0];
-                if ($first && ref $first eq 'HASH') {
-                    @columns = sort keys %$first;
-                    $rows = [
-                        map { [@{$_}{@columns}] } @$val
-                    ];
-                }
-                elsif ($first) {
-                    @columns = keys %$unpacked;
-                    $rows = [map { [$_] } @$val];
-                }
-            }
-        }
-
-        if (@columns) {
-            if ($format eq 'table') {
-                eval { require Text::Table::Any } or die "Missing dependency: Text::Table::Any\n";
-                my $table = Text::Table::Any::table(
-                    header_row  => 1,
-                    rows        => [[@columns], @$rows],
-                    backend     => $ENV{PERL_TEXT_TABLE},
-                );
-                print $table;
-            }
-            else {
-                eval { require Text::CSV } or die "Missing dependency: Text::CSV\n";
-                my $csv = Text::CSV->new({binary => 1, sep => $sep, eol => $/});
-                $csv->print(*STDOUT, [@columns]);
-                for my $row (@$rows) {
-                    $csv->print(*STDOUT, $row);
-                }
-            }
-        }
-        else {
-            print_data($data);
-            print STDERR sprintf("Error: Response could not be formatted as %s.\n", uc($format));
-            exit 3;
-        }
-    }
-    elsif ($format eq 'perl') {
-        eval { require Data::Dumper } or die "Missing dependency: Data::Dumper\n";
-        print Data::Dumper::Dumper($data);
-    }
-    else {
-        print STDERR "Error: Format not supported: $format\n";
-        print_data($data);
-        exit 3;
-    }
-}
-
-sub expand_vars {
-    my $vars = shift;
-
-    my %out;
-    while (my ($key, $value) = each %$vars) {
-        my $var = $value;
-        my @segments = split(/\./, $key);
-        for my $segment (reverse @segments) {
-            my $saved = $var;
-            if ($segment =~ /^(\d+)$/) {
-                $var = [];
-                $var->[$segment] = $saved;
-            }
-            else {
-                $var = {};
-                $var->{$segment} = $saved;
-            }
-        }
-        %out = (%out, %$var);
-    }
-
-    return \%out;
-}
-
-sub pod2usage {
-    eval { require Pod::Usage };
-    if ($@) {
-        my $ref  = $VERSION eq '999.999' ? 'master' : "v$VERSION";
-        my $exit = (@_ == 1 && $_[0] =~ /^\d+$/ && $_[0]) //
-                   (@_ % 2 == 0 && {@_}->{'-exitval'})    // 2;
-        print STDERR <<END;
-Online documentation is available at:
-
-  https://github.com/chazmcgarvey/graphql-client/blob/$ref/README.md
-
-Tip: To enable inline documentation, install the Pod::Usage module.
-
-END
-        exit $exit;
-    }
-    else {
-        goto &Pod::Usage::pod2usage;
-    }
-}
+# PODNAME: graphql
 
 =head1 SYNOPSIS
 
-    graphql <URL> <QUERY> [--var key=value]... [--transport key=value]...
-            [--[no-]unpack] [--format json|json:pretty|yaml|csv|tsv]
+    graphql <URL> <QUERY> [ [--variables JSON] | [--variable KEY=VALUE]... ]
+            [--operation-name NAME] [--transport KEY=VALUE]...
+            [--[no-]unpack] [--filter JSONPATH]
+            [--format json|json:pretty|yaml|perl|csv|tsv|table] [--output FILE]
+
+    graphql --version|--help|--manual
 
 =head1 DESCRIPTION
 
@@ -239,36 +41,38 @@ To hack on the code, clone the repo instead:
 
 =head1 OPTIONS
 
-=head2 --url STR
+=head2 C<--url URL>
 
 The URL of the GraphQL server endpoint.
 
 If no C<--url> option is given, the first argument is assumed to be the URL.
 
+This option is required.
+
 Alias: C<-u>
 
-=head2 --query STR
+=head2 C<--query STR>
 
 The query or mutation to execute.
 
-If no C<--query> option is given, the first argument (after URL) is assumed to be the query.
+If no C<--query> option is given, the next argument (after URL) is assumed to be the query.
 
-If the value is C<-> (which is the default), the query will be read from C<STDIN>.
+If the value is "-" (which is the default), the query will be read from C<STDIN>.
 
 See: L<https://graphql.org/learn/queries/>
 
 Alias: C<--mutation>
 
-=head2 --variables JSON
+=head2 C<--variables JSON>
 
 Provide the variables as a JSON object.
 
 Aliases: C<--vars>, C<-V>
 
-=head2 --variable KEY=VALUE
+=head2 C<--variable KEY=VALUE>
 
-An alternative way to provide variables individually. Repeat this option to provide multiple
-variables.
+An alternative way to provide variables one at a time. This option can be repeated to provide
+multiple variables.
 
 If used in combination with L</"--variables JSON">, this option is silently ignored.
 
@@ -276,37 +80,58 @@ See: L<https://graphql.org/learn/queries/#variables>
 
 Aliases: C<--var>, C<-d>
 
-=head2 --transport KEY=VALUE
+=head2 C<--operation-name NAME>
+
+Inform the server which query/mutation to execute.
+
+Alias: C<-n>
+
+=head2 C<--output FILE>
+
+Write the response to a file instead of STDOUT.
+
+Alias: C<-o>
+
+=head2 C<--transport KEY=VALUE>
 
 Key-value pairs for configuring the transport (usually HTTP).
 
 Alias: C<-t>
 
-=head2 --format STR
+=head2 C<--format STR>
 
 Specify the output format to use. See L</FORMAT>.
 
 Alias: C<-f>
 
-=head2 --unpack
+=head2 C<--unpack>
 
-Enables C<unpack> mode.
+Enables unpack mode.
 
 By default, the response structure is printed as-is from the server, and the program exits 0.
 
-When C<unpack> mode is enabled, if the response completes with no errors, only the data section of
+When unpack mode is enabled, if the response completes with no errors, only the data section of
 the response is printed and the program exits 0. If the response has errors, the whole response
-structure is printed as-is and the program exits 1.
+structure is printed as-is and the program exits 1. See L</EXAMPLES> to see what this looks like in
+practice.
 
-See L</EXAMPLES>.
+Use C<--no-unpack> to disable if unpack mode was enabled via C<GRAPHQL_CLIENT_OPTIONS>.
+
+=head2 C<--filter JSONPATH>
+
+Filter the response based on a L<JSONPath|JSON::Path/SYNOPSIS> expression.
+
+Requires L<JSON::Path>.
+
+Alias: C<-p>
 
 =head1 FORMAT
 
-The C<--format> argument can be one of:
+The argument for L</"--format STR"> can be one of:
 
 =for :list
 * C<csv> - Comma-separated values (requires L<Text::CSV>)
-* C<json:pretty> - Pretty JSON (default)
+* C<json:pretty> - Human-readable JSON (default)
 * C<json> - JSON
 * C<perl> - Perl code (requires L<Data::Dumper>)
 * C<table> - Table (requires L<Text::Table::Any>)
@@ -379,11 +204,13 @@ Execute a query with variables:
     > }
     > END
 
+    graphql http://myserver/graphql --vars '{"episode":"JEDI"}'
+
 Configure the transport:
 
     graphql http://myserver/graphql {hello} -t headers.authorization='Basic s3cr3t'
 
-This example shows the effect of L<--unpack>:
+This example shows the effect of L</--unpack>:
 
     graphql http://myserver/graphql {hello}
 
@@ -407,5 +234,34 @@ Some environment variables affect the way C<graphql> behaves:
 
 =for :list
 * C<GRAPHQL_CLIENT_DEBUG> - Set to 1 to print diagnostic messages to STDERR.
+* C<GRAPHQL_CLIENT_HTTP_USER_AGENT> - Set the HTTP user agent string.
+* C<GRAPHQL_CLIENT_OPTIONS> - Set the default set of options.
 * C<PERL_TEXT_TABLE> - Set table format backend; see L</FORMAT>.
 
+=head1 EXIT STATUS
+
+Here is a consolidated summary of what exit statuses mean:
+
+=for :list
+* C<0> - Success
+* C<1> - Client or server errors
+* C<2> - Option usage is wrong
+* C<3> - Could not format the response as requested
+
+=head1 SEE ALSO
+
+=for :list
+* L<GraphQL::Client> - Programmatic interface
+
+=cut
+
+# FATPACK - Do not remove this line.
+
+use warnings;
+use strict;
+
+use GraphQL::Client::CLI;
+
+our $VERSION = '999.999'; # VERSION
+
+GraphQL::Client::CLI->main(@ARGV);
This page took 0.024793 seconds and 4 git commands to generate.