JMAP-Tester
view release on metacpan or search on metacpan
lib/JMAP/Tester.pm view on Meta::CPAN
unless ($res->is_success) {
return Future->fail(
JMAP::Tester::Result::Failure->new({
http_response => $res,
diagnostic_dumper => $self->default_diagnostic_dumper,
})
);
}
return Future->done(
JMAP::Tester::Result::Download->new({
http_response => $res,
diagnostic_dumper => $self->default_diagnostic_dumper,
})
);
});
return $self->should_return_futures ? $future : $future->$Failsafe->get;
}
sub _maybe_auth_header ($self) {
return ($self->_access_token
? (Authorization => "Bearer " . $self->_access_token)
: ());
}
has _jwt_config => (
is => 'rw',
init_arg => undef,
);
sub _now_timestamp {
# 0 1 2 3 4 5
my ($sec, $min, $hour, $mday, $mon, $year) = gmtime;
return sprintf '%04u-%02u-%02uT%02u:%02u:%02uZ',
$year + 1900, $mon + 1, $mday,
$hour, $min, $sec;
}
sub _get_jwt_config ($self) {
return unless my $jwtc = $self->_jwt_config;
return $jwtc unless $jwtc->{signingKeyValidUntil};
return $jwtc if $jwtc->{signingKeyValidUntil} gt $self->_now_timestamp;
$self->update_client_session;
return unless $jwtc = $self->_jwt_config;
return $jwtc;
}
has _access_token => (
is => 'rw',
init_arg => undef,
);
#pod =method get_client_session
#pod
#pod $tester->get_client_session;
#pod $tester->get_client_session($auth_uri);
#pod
#pod This method fetches the content at the authentication endpoint.
#pod
#pod This method respects the C<should_return_futures> attributes of the
#pod JMAP::Tester object, and in futures mode will return a future that will resolve
#pod to the L<JMAP::Tester::Result::Auth> object.
#pod
#pod =cut
sub _get_client_session_future ($self, $auth_uri = undef) {
$auth_uri //= $self->authentication_uri;
my $auth_req = HTTP::Request->new(
GET => $auth_uri,
[
$self->_maybe_auth_header,
'Accept' => 'application/json',
],
);
my $future = $self->ua->request($self, $auth_req, 'auth')->then(sub {
my ($res) = @_;
unless ($res->code == 200) {
return Future->fail(
JMAP::Tester::Result::Failure->new({
ident => 'failure to get updated authentication data',
http_response => $res,
diagnostic_dumper => $self->default_diagnostic_dumper,
})
);
}
my $client_session = $self->json_decode( $res->decoded_content );
my $auth = JMAP::Tester::Result::Auth->new({
http_response => $res,
client_session => $client_session,
diagnostic_dumper => $self->default_diagnostic_dumper,
});
return Future->done($auth);
});
}
sub get_client_session ($self, $auth_uri = undef) {
my $future = $self->_get_client_session_future($auth_uri);
return $self->should_return_futures ? $future : $future->$Failsafe->get;
}
#pod =method update_client_session
#pod
#pod $tester->update_client_session;
#pod $tester->update_client_session($auth_uri);
#pod
#pod This method fetches the content at the authentication endpoint and uses it to
#pod configure the tester's target URIs and signing keys.
#pod
#pod This method respects the C<should_return_futures> attributes of the
#pod JMAP::Tester object, and in futures mode will return a future that will resolve
#pod to the Result.
#pod
#pod =cut
sub update_client_session ($self, $auth_uri = undef) {
my $future = $self->_get_client_session_future($auth_uri)->then(sub {
my ($auth) = @_;
$self->configure_from_client_session($auth->client_session);
return Future->done($auth);
});
return $self->should_return_futures ? $future : $future->$Failsafe->get;
}
#pod =method configure_from_client_session
#pod
#pod $tester->configure_from_client_session($client_session);
#pod
#pod Given a client session object (like those stored in an Auth result), this
#pod reconfigures the testers access token, signing keys, URIs, and so forth. This
#pod method is used internally when logging in.
#pod
#pod =cut
sub configure_from_client_session ($self, $client_session) {
# It's not crazy to think that we'd also try to pull the primary accountId
# out of the accounts in the auth struct, but I don't think there's a lot to
# gain by doing that yet. Maybe later we'd use it to set the default
# X-JMAP-AccountId or other things, but I think there are too many open
# questions. I'm leaving it out on purpose for now. -- rjbs, 2016-11-18
# This is no longer fatal because you might be an anonymous session that
# needs to call this to fetch an updated signing key. -- rjbs, 2017-03-23
# abort("no accessToken in client session object")
# unless $client_session->{accessToken};
$self->_access_token($client_session->{accessToken});
if ($client_session->{signingId} && $client_session->{signingKey}) {
$self->_jwt_config({
signingId => $client_session->{signingId},
signingKey => $client_session->{signingKey},
signingKeyValidUntil => $client_session->{signingKeyValidUntil},
});
} else {
$self->_jwt_config(undef);
}
for my $type (qw(api download upload)) {
if (defined (my $uri = $client_session->{"${type}Url"})) {
my $setter = "$type\_uri";
$self->$setter($uri);
} else {
my $clearer = "clear_$type\_uri";
lib/JMAP/Tester.pm view on Meta::CPAN
}
#pod =method logout
#pod
#pod $tester->logout;
#pod
#pod This method attempts to log out from the server by sending a C<DELETE> request
#pod to the authentication URI.
#pod
#pod This method respects the C<should_return_futures> attributes of the
#pod JMAP::Tester object, and in futures mode will return a future that will resolve
#pod to the Result.
#pod
#pod =cut
sub logout ($self) {
# This is fatal, not a failure return, because it reflects the user screwing
# up, not a possible JMAP-related condition. -- rjbs, 2017-02-10
Carp::confess("can't logout: no authentication_uri configured")
unless $self->has_authentication_uri;
my $req = HTTP::Request->new(
DELETE => $self->authentication_uri,
[
'Content-Type' => 'application/json; charset=utf-8',
'Accept' => 'application/json',
],
);
my $future = $self->ua->request($self, $req, 'auth')->then(sub {
my ($res) = @_;
if ($res->code == 204) {
$self->_access_token(undef);
return Future->done(
JMAP::Tester::Result::Logout->new({
http_response => $res,
})
);
}
return Future->fail(
JMAP::Tester::Result::Failure->new({
ident => "failed to log out",
http_response => $res,
diagnostic_dumper => $self->default_diagnostic_dumper,
})
);
});
return $self->should_return_futures ? $future : $future->$Failsafe->get;
}
#pod =method http_request
#pod
#pod my $response = $jtest->http_request($http_request);
#pod
#pod Sometimes, you may need to make an HTTP request with your existing web
#pod connection. This might be to interact with a custom authentication mechanism,
#pod to access custom endpoints, or just to make very, very specifically crafted
#pod requests. For this reasons, C<http_request> exists.
#pod
#pod Pass this method an L<HTTP::Request> and it will use the tester's UA object to
#pod make the request.
#pod
#pod This method respects the C<should_return_futures> attributes of the
#pod JMAP::Tester object, and in futures mode will return a future that will resolve
#pod to the L<HTTP::Response>.
#pod
#pod =cut
sub http_request ($self, $http_request) {
my $future = $self->ua->request($self, $http_request, 'misc')->then(sub {
my ($res) = @_;
$self->_logger->log_misc_response($self, { http_response => $res });
return Future->done($res);
});
return $self->should_return_futures ? $future : $future->$Failsafe->get;
}
#pod =method http_get
#pod
#pod my $response = $jtest->http_get($url, $headers);
#pod
#pod This method is just sugar for calling C<http_request> to make a GET request for
#pod the given URL. C<$headers> is an optional arrayref of headers.
#pod
#pod =cut
sub http_get ($self, $url, $headers = undef) {
my $req = HTTP::Request->new(
GET => $url,
(defined $headers ? $headers : ()),
);
return $self->http_request($req);
}
#pod =method http_post
#pod
#pod my $response = $jtest->http_post($url, $body, $headers);
#pod
#pod This method is just sugar for calling C<http_request> to make a POST request
#pod for the given URL. C<$headers> is an arrayref of headers and C<$body> is the
#pod byte string to be passed as the body.
#pod
#pod =cut
sub http_post ($self, $url, $body, $headers = undef) {
my $req = HTTP::Request->new(
POST => $url,
$headers // [],
$body,
);
return $self->http_request($req);
}
has default_diagnostic_dumper => (
is => 'ro',
lib/JMAP/Tester.pm view on Meta::CPAN
This method respects the C<should_return_futures> attributes of the
JMAP::Tester object, and in futures mode will return a future that will resolve
to the Result.
=head2 upload
my $result = $tester->upload(\%arg);
Required arguments are:
accountId - the account for which we're uploading (no default)
type - the content-type we want to provide to the server
blob - the data to upload. Must be a reference to a string
This uploads the given blob.
The return value will either be a L<failure
object|JMAP::Tester::Result::Failure> or an L<upload
result|JMAP::Tester::Result::Upload>.
This method respects the C<should_return_futures> attributes of the
JMAP::Tester object, and in futures mode will return a future that will resolve
to the Result.
=head2 download
my $result = $tester->download(\%uri_arg, \%other_arg);
The first hashref provides values that go into the download URI:
blobId - the blob to download (no default)
accountId - the account for which we're downloading (no default)
type - the content-type we want the server to provide back (no default)
name - the name we want the server to provide back (default: "download")
If the download URI template has a C<blobId>, C<accountId>, or C<type>
placeholder but no argument for that is given to C<download>, an exception
will be thrown.
The second hashref, which is optional, provides other arguments to the method.
Right now, there is only one, B<which will go away>. The argument is only here
for legacy purposes, specifically for the Cyrus IMAP project, and may be
removed B<at any time>.
accept - the value of the Accept header to use when downloading
The return value will either be a L<failure
object|JMAP::Tester::Result::Failure> or an L<upload
result|JMAP::Tester::Result::Download>.
This method respects the C<should_return_futures> attributes of the
JMAP::Tester object, and in futures mode will return a future that will resolve
to the Result.
=head2 get_client_session
$tester->get_client_session;
$tester->get_client_session($auth_uri);
This method fetches the content at the authentication endpoint.
This method respects the C<should_return_futures> attributes of the
JMAP::Tester object, and in futures mode will return a future that will resolve
to the L<JMAP::Tester::Result::Auth> object.
=head2 update_client_session
$tester->update_client_session;
$tester->update_client_session($auth_uri);
This method fetches the content at the authentication endpoint and uses it to
configure the tester's target URIs and signing keys.
This method respects the C<should_return_futures> attributes of the
JMAP::Tester object, and in futures mode will return a future that will resolve
to the Result.
=head2 configure_from_client_session
$tester->configure_from_client_session($client_session);
Given a client session object (like those stored in an Auth result), this
reconfigures the testers access token, signing keys, URIs, and so forth. This
method is used internally when logging in.
=head2 logout
$tester->logout;
This method attempts to log out from the server by sending a C<DELETE> request
to the authentication URI.
This method respects the C<should_return_futures> attributes of the
JMAP::Tester object, and in futures mode will return a future that will resolve
to the Result.
=head2 http_request
my $response = $jtest->http_request($http_request);
Sometimes, you may need to make an HTTP request with your existing web
connection. This might be to interact with a custom authentication mechanism,
to access custom endpoints, or just to make very, very specifically crafted
requests. For this reasons, C<http_request> exists.
Pass this method an L<HTTP::Request> and it will use the tester's UA object to
make the request.
This method respects the C<should_return_futures> attributes of the
JMAP::Tester object, and in futures mode will return a future that will resolve
to the L<HTTP::Response>.
=head2 http_get
my $response = $jtest->http_get($url, $headers);
This method is just sugar for calling C<http_request> to make a GET request for
the given URL. C<$headers> is an optional arrayref of headers.
=head2 http_post
my $response = $jtest->http_post($url, $body, $headers);
This method is just sugar for calling C<http_request> to make a POST request
for the given URL. C<$headers> is an arrayref of headers and C<$body> is the
byte string to be passed as the body.
=head1 AUTHOR
Ricardo SIGNES <cpan@semiotic.systems>
=head1 CONTRIBUTORS
=for stopwords Alfie John Matthew Horsfall Michael McClimon Ricardo Signes
=over 4
=item *
Alfie John <alfiej@fastmail.fm>
=item *
Matthew Horsfall <wolfsage@gmail.com>
=item *
Michael McClimon <michael@mcclimon.org>
=item *
Ricardo Signes <rjbs@semiotic.systems>
=back
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2016 by Fastmail Pty. Ltd.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut
( run in 1.270 second using v1.01-cache-2.11-cpan-524268b4103 )