App-Presto

 view release on metacpan or  search on metacpan

README  view on Meta::CPAN

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
    opposite end of the spectrum) just using curl directly on the
    command-line. This tool attempts to find some sort of middle ground by
    providing a quasi-DSL for interacting with a RESTful service in an
    interactive way.
 
FEATURES
 
 Basic HTTP methods
 
    All HTTP methods are implemented as commands in presto. The URL that is
    given is appended to the endpoint specified when presto is invoked as
    shown in the SYNOPSIS above.
 
 Request Building
 
    If the endpoint contains a * character the URL fragment specified in
    the GET/POST/etc command is inserted at that point. This allows you to
    do things like auto-append a file extension to all URLs. For instance:
 
            bash$ presto http://my-server.com*.json
            http://my-server.com> GET /product/1
 
    In this case, the full URL would be
    http://my-server.com/product/1.json. If no * is found in the URL, the
    URL fragment is simply appended at the end of the endpoint.
 
    All arguments after the first will be treated as query parameters (for
    GET/HEAD/DELETE requests) or request content (for POST/PUT requests).
    For instance:
 
            http://my-server.com> GET /products limit=10 offset=20
     
            http://my-server.com> POST /products '{"name":"A New Product"}'
      # request goes to http://my-sever.com/products with the body as specified

README  view on Meta::CPAN

123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
   like the -B file-test operator), the output is not printed to STDOUT.
   Instead, you may want to use output redirection as show above and send
   the response body to a file.
 
           http://my-server.com> GET /some-image.jpg >foo.jpg
 
Persistent Configuration
 
   As demonstrated above, you can use the config command to change the
   behavior of presto. These configuration options are persisted in a
   config file specific to the endpoint provided at the command-line and
   will be reloaded the next time you invoke presto with the same
   endpoint.
 
   Current valid config keys are:
 
     * verbose
 
     Boolean, when enabled, dumps request/response to STDOUT (defaults to
     "0")
 
     * deserialize_response

README  view on Meta::CPAN

149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
     * pretty_printer
 
     Must be one of the supported modules (i.e. Data::Dumper or JSON). Use
     tab completion to see currently supported values (defaults to
     "JSON").
 
     * binmode
 
     Used to set encoding of STDIN and STDOUT handles (defaults to "utf8")
 
   TODO: provide a means for aliasing endpoints so that configuration is
   shared across multiple endpoints.
 
History and Scripting
 
   Just like configuration, command history is maintained separately for
   each endpoint specified on the command-line and is persisted across
   sessions (assuming you have a capable Term::Readline library
   installed). You can interrogate the history using the (surprisingly
   named) history command. It supports a small subset of the bash history
   command:
 
           # dump all history
           http://my-server.com> history
    
           # dump last 5 entries
           http://my-server.com> history 5

README.mkdn  view on Meta::CPAN

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
answers typically point to some horrible GUI or (on the complete opposite
end of the spectrum) just using `curl` directly on the command-line.
This tool attempts to find some sort of middle ground by providing a
quasi-DSL for interacting with a RESTful service in an interactive way.
 
# FEATURES
 
## Basic HTTP methods
 
All HTTP methods are implemented as commands in presto.  The URL that is
given is appended to the endpoint specified when presto is invoked as
shown in the SYNOPSIS above.
 
## Request Building
 
If the endpoint contains a `*` character the URL fragment specified in
the GET/POST/etc command is inserted at that point. This allows you to
do things like auto-append a file extension to all URLs.  For instance:
 
        bash$ presto http://my-server.com*.json
        http://my-server.com> GET /product/1
 
In this case, the full URL would be
`http://my-server.com/product/1.json`.  If no `*` is found in the URL,
the URL fragment is simply appended at the end of the endpoint.
 
All arguments after the first will be treated as query parameters (for
GET/HEAD/DELETE requests) or request content (for POST/PUT requests). For
instance:
 
          http://my-server.com> GET /products limit=10 offset=20
 
          http://my-server.com> POST /products '{"name":"A New Product"}'
    # request goes to http://my-sever.com/products with the body as specified

README.mkdn  view on Meta::CPAN

128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
like the `-B` file-test operator), the output is not printed to STDOUT.
Instead, you may want to use output redirection as show above and send
the response body to a file.
 
        http://my-server.com> GET /some-image.jpg >foo.jpg
 
## Persistent Configuration
 
As demonstrated above, you can use the `config` command to change the
behavior of presto.  These configuration options are persisted in a
config file specific to the endpoint provided at the command-line and
will be reloaded the next time you invoke presto with the same endpoint.
 
Current valid config keys are:
 
- verbose
 
    Boolean, when enabled, dumps request/response to STDOUT (defaults to "0")
 
- deserialize\_response
 
    Boolean, when enabled response body is parsed based on the `Content-Type`

README.mkdn  view on Meta::CPAN

151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
- pretty\_printer
 
    Must be one of the supported modules (i.e. Data::Dumper or JSON).
    Use tab completion to see currently supported values (defaults to "JSON").
 
- binmode
 
    Used to set encoding of STDIN and STDOUT handles (defaults to "utf8")
 
**TODO:** provide a means for aliasing endpoints so that configuration
is shared across multiple endpoints.
 
## History and Scripting
 
Just like configuration, command history is maintained separately for each
endpoint specified on the command-line and is persisted across sessions
(assuming you have a capable Term::Readline library installed).  You can
interrogate the history using the (surprisingly named) `history` command.
It supports a small subset of the `bash` history command:
 
        # dump all history
        http://my-server.com> history
 
        # dump last 5 entries
        http://my-server.com> history 5

bin/presto  view on Meta::CPAN

48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
answers typically point to some horrible GUI or (on the complete opposite
end of the spectrum) just using C<curl> directly on the command-line.
This tool attempts to find some sort of middle ground by providing a
quasi-DSL for interacting with a RESTful service in an interactive way.
 
=head1 FEATURES
 
=head2 Basic HTTP methods
 
All HTTP methods are implemented as commands in presto.  The URL that is
given is appended to the endpoint specified when presto is invoked as
shown in the SYNOPSIS above.
 
=head2 Request Building
 
If the endpoint contains a C<*> character the URL fragment specified in
the GET/POST/etc command is inserted at that point. This allows you to
do things like auto-append a file extension to all URLs.  For instance:
 
        bash$ presto http://my-server.com*.json
        http://my-server.com> GET /product/1
 
In this case, the full URL would be
C<http://my-server.com/product/1.json>.  If no C<*> is found in the URL,
the URL fragment is simply appended at the end of the endpoint.
 
All arguments after the first will be treated as query parameters (for
GET/HEAD/DELETE requests) or request content (for POST/PUT requests). For
instance:
 
        http://my-server.com> GET /products limit=10 offset=20
 
        http://my-server.com> POST /products '{"name":"A New Product"}'
  # request goes to http://my-sever.com/products with the body as specified

bin/presto  view on Meta::CPAN

143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
like the C<-B> file-test operator), the output is not printed to STDOUT.
Instead, you may want to use output redirection as show above and send
the response body to a file.
 
        http://my-server.com> GET /some-image.jpg >foo.jpg
 
=head2 Persistent Configuration
 
As demonstrated above, you can use the C<config> command to change the
behavior of presto.  These configuration options are persisted in a
config file specific to the endpoint provided at the command-line and
will be reloaded the next time you invoke presto with the same endpoint.
 
Current valid config keys are:
 
=over 4
 
=item * verbose
 
Boolean, when enabled, dumps request/response to STDOUT (defaults to "0")
 
=item * deserialize_response

bin/presto  view on Meta::CPAN

170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
Must be one of the supported modules (i.e. Data::Dumper or JSON).
Use tab completion to see currently supported values (defaults to "JSON").
 
=item * binmode
 
Used to set encoding of STDIN and STDOUT handles (defaults to "utf8")
 
=back
 
B<TODO:> provide a means for aliasing endpoints so that configuration
is shared across multiple endpoints.
 
=head2 History and Scripting
 
Just like configuration, command history is maintained separately for each
endpoint specified on the command-line and is persisted across sessions
(assuming you have a capable Term::Readline library installed).  You can
interrogate the history using the (surprisingly named) C<history> command.
It supports a small subset of the C<bash> history command:
 
        # dump all history
        http://my-server.com> history
 
        # dump last 5 entries
        http://my-server.com> history 5

lib/App/Presto.pm  view on Meta::CPAN

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
        is       => 'lazy',
);
 
sub _build_client {
        my $self = shift;
        return App::Presto::Client->new(config => $self->config);
}
 
has config => (
        is       => 'rw',
        handles  => ['endpoint'],
);
 
has _stash => (
        is       => 'lazy',
        handles  => ['stash'],
);
 
sub _build__stash {
        my $self = shift;
        return App::Presto::Stash->new;

lib/App/Presto.pm  view on Meta::CPAN

72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
                        },
            "history" => {
                exclude_from_completion => 1,
                exclude_from_history    => 1,
                desc                    => "Prints the command history",
                args                    => "[-c] [-d] [number]",
                method                  => sub { shift->history_call(@_) },
                doc => "Specify a number to list the last N lines of history Pass -c to clear the command history, -d NUM to delete a single item\n",
            },
        },
        prompt       => sprintf( '%s> ', $self->endpoint ),
        history_file => $self->config->file('history'),
    );
                $term->ornaments('md,me,,');
                return $term;
}
 
has command_factory => (
        is => 'lazy',
);
sub _build_command_factory { return App::Presto::CommandFactory->new }

lib/App/Presto.pm  view on Meta::CPAN

94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
my $SINGLETON;
sub instance {
        my $class = shift;
        return $SINGLETON ||= $class->new(@_);
}
sub run {
        my $class = shift;
        my $self = $class->instance;
        my @args  = shift;
        my $config;
        if(my $endpoint = shift(@args)){
                $self->config( $config = App::Presto::Config->new( endpoint => $endpoint ) );
        } else {
                die "Base endpoint (i.e. http://some-host.com) must be specified as command-line argument\n";
        }
 
        $config->init_defaults;
 
        $self->command_factory->install_commands($self);
 
        my $binmode = $config->get('binmode');
        binmode(STDOUT,":encoding($binmode)");
        binmode(STDIN,":encoding($binmode)");

lib/App/Presto/Client.pm  view on Meta::CPAN

89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
        my $uri  = $self->_make_uri(shift);
        $self->_rest_client->PUT( $uri, shift );
}
 
sub _make_uri {
        my $self      = shift;
        my $local_uri = shift;
        my @args      = @_;
        my $config    = $self->config;
 
        my $endpoint;
        $local_uri = '/' if ! defined $local_uri;
        $local_uri = URI->new($local_uri);
        if ( $local_uri->scheme ) {
                $endpoint = $local_uri;
        } else {
                my $local_path = $local_uri->path;
                $endpoint = $config->endpoint;
                $endpoint .= '*' unless $endpoint =~ m/\*/;
                $endpoint =~ s{\*}{$local_path};
                $endpoint .= '?' . $local_uri->query if $local_uri->query;
        }
 
        my $u = $self->_append_query_params( $endpoint, @args );
        return "$u";
}
 
sub _append_query_params {
        my $self = shift;
        my $u    = URI->new(shift);
        my @args = @_;
        foreach my $next (@args) {
                $u->query_param_append( split( /=/, $next, 2 ) );
        }

lib/App/Presto/Config.pm  view on Meta::CPAN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
our $AUTHORITY = 'cpan:MPERRY';
$App::Presto::Config::VERSION = '0.010';
# ABSTRACT: Manage configuration for a given endpoint
 
use Moo;
use JSON qw(decode_json encode_json);
use File::Path 2.08 qw(make_path);
 
has endpoint => (
    is => 'rw',
    required => 1,
    trigger => sub { delete shift->{endpoint_dir} }
);
has config => (
    is => 'lazy',
    clearer => 'reload',
    isa => sub { die "not a HashRef" if ref($_[0])  ne 'HASH'; },
);
sub _build_config {
    my $self = shift;
    my $config_file = $self->file('config.json');
    if (! -e $config_file ) {

lib/App/Presto/Config.pm  view on Meta::CPAN

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
        my $json_text = <$fh>;
        $config = decode_json( $json_text || '{}' );
        1;
    } or do {
        warn "Ignoring invalid config file $config_file: $@\n";
        $config = {};
    };
    return $config;
}
 
has endpoint_dir => (
    is => 'lazy',
);
 
sub _build_endpoint_dir {
    my $self = shift;
    my $root = $ENV{APP_REST_CLI_DIR} || sprintf('%s/.app-presto', File::HomeDir->my_home);
    (my $endpoint_dir = lc $self->endpoint) =~ s/\W+/-/g;
    my $dir = sprintf( '%s/%s', $root, $endpoint_dir);
    if(!-d $dir){
        warn "creating directory $dir\n";
        make_path($dir);
    }
    return $dir;
}
 
sub file {
    my $self = shift;
    (my $file = shift) =~ s{^[\./]+}{}g; # remove leading dots or slashes
    return sprintf('%s/%s', $self->endpoint_dir, $file);
}
 
sub is_set {
    my $self = shift;
    my $key  = shift;
    return exists $self->config->{$key};
}
sub set {
    my $self = shift;
    my $key  = shift;
    my $value = shift;
    if($key eq 'endpoint'){
        return $self->endpoint($value);
    }
    if(!defined $value){
        delete $self->config->{$key};
    } else {
        $self->config->{$key} = $value;
    }
    eval {
        $self->write_config;
        1;
    } or do {

lib/App/Presto/Config.pm  view on Meta::CPAN

90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
    return;
}
sub unset {
    my $self = shift;
    return $self->set($_[0],undef);
}
 
sub get {
    my $self = shift;
    my $key  = shift;
    return $self->endpoint if $key eq 'endpoint';
    return exists $self->config->{$key} ? $self->config->{$key} : undef;
}
 
sub keys {
    my $self = shift;
    return keys %{ $self->config };
}
 
sub write_config {
    my $self = shift;

lib/App/Presto/Config.pm  view on Meta::CPAN

133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
1;
 
__END__
 
=pod
 
=encoding UTF-8
 
=head1 NAME
 
App::Presto::Config - Manage configuration for a given endpoint
 
=head1 VERSION
 
version 0.010
 
=head1 AUTHORS
 
=over 4
 
=item *

t/02-config.t  view on Meta::CPAN

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
my $EMPTY = $d . 'empty-config';
my $EXISTING = $d . 'config';
my $FAKE_HOME = $d . 'config/fake-home';
 
subtest 'empty config' => sub {
    local $ENV{APP_REST_CLI_DIR} = $EMPTY;
 
    #pre-clear
    remove_tree( $EMPTY, { keep_root => 1 } );
 
    my $config = App::Presto::Config->new( endpoint => 'http://myserver.com' );
    is $config->get('endpoint'), 'http://myserver.com', 'endpoint';
    my @warnings = capture_warnings {
        like $config->endpoint_dir, qr{myserver},
          'config dir reflects endpoint name';
    };
 
    like $warnings[0], qr{creating directory},
      'warnings about creating directory';
 
    @warnings = capture_warnings {
        $config->set(foo => 'bar');
    };
    like $warnings[0], qr{creating new config file}, 'warns about creating new config file';
    is $config->get('foo'), 'bar', 'get';

t/02-config.t  view on Meta::CPAN

43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
    $config->unset('foo');
    is_deeply [$config->keys], [], 'empty keys';
 
    # empty it out again
    remove_tree( $EMPTY );
};
 
subtest 'existing config' => sub {
    local $ENV{APP_REST_CLI_DIR} = $EXISTING;
 
    my $config = App::Presto::Config->new( endpoint => 'http://myserver.com' );
    my @warnings = capture_warnings {
        like $config->endpoint_dir, qr{myserver},
          'config dir reflects endpoint name';
    };
 
    ok !@warnings, 'no warnings' or diag explain $config, \@warnings;
 
    like $config->file('foo'), qr{/foo}, 'file has correct name';
    $config->set(endpoint => 'http://anotherserver.com');
    like $config->endpoint_dir, qr{anotherserver}, 'new endpoint == new endpoint_dir';
};
 
subtest 'new endpoint' => sub {
    local $ENV{APP_REST_CLI_DIR} = $EXISTING;
 
    my $config = App::Presto::Config->new( endpoint => 'http://myserver.com' );
    my @warnings = capture_warnings {
        like $config->endpoint_dir, qr{myserver},
          'config dir reflects endpoint name';
    };
 
    ok !@warnings, 'no warnings' or diag explain $config, \@warnings;
    is $config->get('foo'), 1, 'loaded correctly';
 
    like $config->file('foo'), qr{/foo}, 'file has correct name';
    $config->set(endpoint => 'http://anotherserver.com');
    like $config->endpoint_dir, qr{anotherserver}, 'new endpoint == new endpoint_dir';
};
 
subtest 'home directory' => sub {
    no warnings 'redefine';
    *File::HomeDir::my_home = sub { $FAKE_HOME };
    my $config = App::Presto::Config->new( endpoint => 'http://myserver.com' );
    my @warnings = capture_warnings {
        like $config->endpoint_dir, qr{fake-home/\.app-presto/http-myserver-com}, 'created in home directory';
    };
    ok @warnings, 'warns about creating new directory';
    remove_tree $FAKE_HOME;
};
 
done_testing;

t/04-client.t  view on Meta::CPAN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use strict;
 
my $config = Test::MockObject->new;
$config->set_always( endpoint => 'http://my-server.com');
my $rest_client = Test::MockObject->new;
$rest_client->set_true('GET','DELETE','PUT','POST','HEAD','request');
 
my %headers;
$rest_client->mock('addHeader', sub { shift; my($k,$v) = @_; $headers{$k} = $v; });
$rest_client->{_headers} = \%headers;
 
my $client = App::Presto::Client->new(config=>$config, _rest_client => $rest_client);
 
isa_ok($client, 'App::Presto::Client');

t/04-client.t  view on Meta::CPAN

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
        is $args->[2], 'http://another-server.com/blah', 'allows URI override';
}
 
$client->PUT('/bar', 'foobar');
{
        my ( $m, $args ) = $rest_client->next_call;
        is $m, 'PUT', 'rest_client PUT';
        is $args->[2], 'foobar', 'PUT body';
}
 
$config->set_always( endpoint => 'http://my-server.com*.json');
$client->PUT('/bar?blah=1', 'foobar');
{
        my ( $m, $args ) = $rest_client->next_call;
        is $m, 'PUT', 'rest_client PUT';
        is $args->[1], 'http://my-server.com/bar.json?blah=1', 'has suffix + query params';
}
$config->set_always( endpoint => 'http://my-server.com');
 
$client->HEAD;
{
        my ( $m, $args ) = $rest_client->next_call;
        is $m, 'HEAD', 'rest_client HEAD (no uri)';
        is $args->[1], 'http://my-server.com/', 'default URI';
}
 
$client->POST('/foo', q({"a":1}));
{



( run in 0.694 second using v1.01-cache-2.11-cpan-26ccb49234f )