JIRA-REST-Class
view release on metacpan or search on metacpan
lib/JIRA/REST/Class.pm view on Meta::CPAN
#pod
#pod sub get_test_client {
#pod state $test =
#pod JIRA::REST::Class->new(TestServer_url(), 'username', 'password');
#pod $test->REST_CLIENT->setTimeout(5);
#pod return $test;
#pod };
#pod
#pod =end test
#pod
#pod =cut
#---------------------------------------------------------------------------
#pod =begin testing new 5
#pod
#pod my $jira;
#pod try {
#pod $jira = JIRA::REST::Class->new({
#pod url => TestServer_url(),
#pod username => 'user',
#pod password => 'pass',
#pod proxy => '',
#pod anonymous => 0,
#pod ssl_verify_none => 1,
#pod rest_client_config => {},
#pod });
#pod }
#pod catch {
#pod $jira = $_; # as good a place as any to stash the error, because
#pod # isa_ok() will complain that it's not an object.
#pod };
#pod
#pod isa_ok($jira, 'JIRA::REST::Class', 'JIRA::REST::Class->new');
#pod
#pod my $needs_url_regexp = qr/'?url'? argument must be defined/i;
#pod
#pod throws_ok(
#pod sub {
#pod JIRA::REST::Class->new();
#pod },
#pod $needs_url_regexp,
#pod 'JIRA::REST::Class->new with no parameters throws an exception',
#pod );
#pod
#pod throws_ok(
#pod sub {
#pod JIRA::REST::Class->new({
#pod username => 'user',
#pod password => 'pass',
#pod });
#pod },
#pod $needs_url_regexp,
#pod 'JIRA::REST::Class->new with no url throws an exception',
#pod );
#pod
#pod throws_ok(
#pod sub {
#pod JIRA::REST::Class->new('http://not.a.good.server.com');
#pod },
#pod qr/No credentials found/,
#pod q{JIRA::REST::Class->new with just url tries to find credentials},
#pod );
#pod
#pod lives_ok(
#pod sub {
#pod JIRA::REST::Class->new(TestServer_url(), 'user', 'pass');
#pod },
#pod q{JIRA::REST::Class->new with url, username, and password does't croak!},
#pod );
#pod
#pod =end testing
#pod
#pod =cut
#---------------------------------------------------------------------------
#pod =method B<issues> QUERY
#pod
#pod =method B<issues> KEY [, KEY...]
#pod
#pod The C<issues> method can be called two ways: either by providing a list of
#pod issue keys, or by proving a single hash reference which describes a JIRA
#pod query in the same format used by L<JIRA::REST|JIRA::REST> (essentially,
#pod C<< jql => "JQL query string" >>).
#pod
#pod The return value is an array of L<JIRA::REST::Class::Issue|JIRA::REST::Class::Issue> objects.
#pod
#pod =cut
sub issues {
my ( $self, @args ) = @_;
if ( @args == 1 && ref $args[0] eq 'HASH' ) {
return $self->query( $args[0] )->issues;
}
else {
my $jql = sprintf 'key in (%s)', join q{,} => @args;
return $self->query( { jql => $jql } )->issues;
}
}
#---------------------------------------------------------------------------
#
# =begin testing issues
# =end testing
#
#---------------------------------------------------------------------------
#pod =method B<query> QUERY
#pod
#pod The C<query> method takes a single parameter: a hash reference which
#pod describes a JIRA query in the same format used by L<JIRA::REST|JIRA::REST>
#pod (essentially, C<< jql => "JQL query string" >>).
#pod
#pod The return value is a single L<JIRA::REST::Class::Query|JIRA::REST::Class::Query> object.
#pod
#pod =cut
sub query {
my $self = shift;
my $args = shift;
lib/JIRA/REST/Class.pm view on Meta::CPAN
=head1 DESCRIPTION
An OO Class module built atop L<JIRA::REST|JIRA::REST> for dealing with JIRA
issues and their data as objects.
This code is a work in progress, so it's bound to be incomplete. I add methods
to it as I discover I need them. I have also coded for fields that might exist
in my JIRA server's configuration but not in yours. It is my I<intent>,
however, to make things more generic as I go on so they will "just work" no
matter how your server is configured.
I'm actively working with the author of L<JIRA::REST|JIRA::REST> (thanks
gnustavo!) to keep the arguments for C<< JIRA::REST::Class->new >> exactly
the same as C<< JIRA::REST->new >>, so I'm just duplicating the
documentation for L<< JIRA::REST->new|JIRA::REST/CONSTRUCTOR >>:
=head1 CONSTRUCTOR
=head2 B<new> I<HASHREF>
=head2 B<new> I<URL>, I<USERNAME>, I<PASSWORD>, I<REST_CLIENT_CONFIG>, I<ANONYMOUS>, I<PROXY>, I<SSL_VERIFY_NONE>
The constructor can take its arguments from a single hash reference or from
a list of positional parameters. The first form is preferred because it lets
you specify only the arguments you need. The second form forces you to pass
undefined values if you need to pass a specific value to an argument further
to the right.
The arguments are described below with the names which must be used as the
hash keys:
=over 4
=item * B<url>
A string or a URI object denoting the base URL of the JIRA server. This is a
required argument.
The REST methods described below all accept as a first argument the
endpoint's path of the specific API method to call. In general you can pass
the complete path, beginning with the prefix denoting the particular API to
use (C</rest/api/VERSION>, C</rest/servicedeskapi>, or
C</rest/agile/VERSION>). However, to make it easier to invoke JIRA's Core
API if you pass a path not starting with C</rest/> it will be prefixed with
C</rest/api/latest> or with this URL's path if it has one. This way you can
choose a specific version of the JIRA Core API to use instead of the latest
one. For example:
my $jira = JIRA::REST::Class->new({
url => 'https://jira.example.net/rest/api/1',
});
=item * B<username>
=item * B<password>
The username and password of a JIRA user to use for authentication.
If B<anonymous> is false then, if either B<username> or B<password> isn't
defined the module looks them up in either the C<.netrc> file or via
L<Config::Identity|Config::Identity> (which allows C<gpg> encrypted credentials).
L<Config::Identity|Config::Identity> will look for F<~/.jira-identity> or
F<~/.jira>. You can change the filename stub from C<jira> to a custom stub
with the C<JIRA_REST_IDENTITY> environment variable.
=item * B<rest_client_config>
A JIRA::REST object uses a L<REST::Client|REST::Client> object to make the REST
invocations. This optional argument must be a hash reference that can be fed
to the REST::Client constructor. Note that the C<url> argument
overwrites any value associated with the C<host> key in this hash.
As an extension, the hash reference also accepts one additional argument
called B<proxy> that is an extension to the REST::Client configuration and
will be removed from the hash before passing it on to the REST::Client
constructor. However, this argument is deprecated since v0.017 and you
should avoid it. Instead, use the following argument instead.
=item * B<proxy>
To use a network proxy set this argument to the string or URI object
describing the fully qualified URL (including port) to your network proxy.
=item * B<ssl_verify_none>
Sets the C<SSL_verify_mode> and C<verify_hostname ssl> options on the
underlying L<REST::Client|REST::Client>'s user agent to 0, thus disabling
them. This allows access to JIRA servers that have self-signed certificates
that don't pass L<LWP::UserAgent|LWP::UserAgent>'s verification methods.
=item * B<anonymous>
Tells the module that you want to connect to the specified JIRA server with
no username or password. This way you can access public JIRA servers
without needing to authenticate.
=back
=head1 METHODS
=head2 B<issues> QUERY
=head2 B<issues> KEY [, KEY...]
The C<issues> method can be called two ways: either by providing a list of
issue keys, or by proving a single hash reference which describes a JIRA
query in the same format used by L<JIRA::REST|JIRA::REST> (essentially,
C<< jql => "JQL query string" >>).
The return value is an array of L<JIRA::REST::Class::Issue|JIRA::REST::Class::Issue> objects.
=head2 B<query> QUERY
The C<query> method takes a single parameter: a hash reference which
describes a JIRA query in the same format used by L<JIRA::REST|JIRA::REST>
(essentially, C<< jql => "JQL query string" >>).
The return value is a single L<JIRA::REST::Class::Query|JIRA::REST::Class::Query> object.
=head2 B<iterator> QUERY
lib/JIRA/REST/Class.pm view on Meta::CPAN
=head1 INTERNAL METHODS
=head2 B<get>
A wrapper for C<JIRA::REST>'s L<GET|JIRA::REST/"GET RESOURCE [, QUERY]"> method.
=head2 B<post>
Wrapper around C<JIRA::REST>'s L<POST|JIRA::REST/"POST RESOURCE, QUERY, VALUE [, HEADERS]"> method.
=head2 B<put>
Wrapper around C<JIRA::REST>'s L<PUT|JIRA::REST/"PUT RESOURCE, QUERY, VALUE [, HEADERS]"> method.
=head2 B<delete>
Wrapper around C<JIRA::REST>'s L<DELETE|JIRA::REST/"DELETE RESOURCE [, QUERY]"> method.
=head2 B<data_upload>
Similar to
L<< JIRA::REST->attach_files|JIRA::REST/"attach_files ISSUE FILE..." >>,
but entirely from memory and only attaches one file at a time. Returns the
L<HTTP::Response|HTTP::Response> object from the post request. Takes the
following named parameters:
=over 4
=item + B<url>
The relative URL to POST to. This will have the hostname and REST version
information prepended to it, so all you need to provide is something like
C</issue/>I<issueIdOrKey>C</attachments>. I'm allowing the URL to be
specified in case I later discover something this can be used for besides
attaching files to issues.
=item + B<name>
The name that is specified for this file attachment.
=item + B<data>
The actual data to be uploaded. If a reference is provided, it will be
dereferenced before posting the data.
=back
I guess that makes it only a I<little> like
C<< JIRA::REST->attach_files >>...
=head2 B<rest_api_url_base>
Returns the base URL for this JIRA server's REST API. For example, if your JIRA server is at C<http://jira.example.com>, this would return C<http://jira.example.com/rest/api/latest>.
=head2 B<strip_protocol_and_host>
A method to take the provided URL and strip the protocol and host from it. For example, if the URL C<http://jira.example.com/rest/api/latest> was passed to this method, C</rest/api/latest> would be returned.
=head2 B<jira>
Returns a L<JIRA::REST::Class|JIRA::REST::Class> object with credentials for the last JIRA user.
=head2 B<factory>
An accessor for the L<JIRA::REST::Class::Factory|JIRA::REST::Class::Factory>.
=head2 B<JIRA_REST>
An accessor that returns the L<JIRA::REST|JIRA::REST> object being used.
=head2 B<REST_CLIENT>
An accessor that returns the L<REST::Client|REST::Client> object inside the L<JIRA::REST|JIRA::REST> object being used.
=head2 B<JSON>
An accessor that returns the L<JSON|JSON> object inside the L<JIRA::REST|JIRA::REST> object being used.
=head2 B<make_object>
A pass-through method that calls L<JIRA::REST::Class::Factory::make_object()|JIRA::REST::Class::Factory/make_object>.
=head2 B<make_date>
A pass-through method that calls L<JIRA::REST::Class::Factory::make_date()|JIRA::REST::Class::Factory/make_date>.
=head2 B<class_for>
A pass-through method that calls L<JIRA::REST::Class::Factory::get_factory_class()|JIRA::REST::Class::Factory/get_factory_class>.
=head2 B<obj_isa>
When passed a scalar that I<could> be an object and a class string,
returns whether the scalar is, in fact, an object of that class.
Looks up the actual class using C<class_for()>, which calls
L<JIRA::REST::Class::Factory::get_factory_class()|JIRA::REST::Class::Factory/get_factory_class>.
=head2 B<cosmetic_copy> I<THING>
A utility function to produce a "cosmetic" copy of a thing: it clones
the data structure, but if anything in the structure (other than the
structure itself) is a blessed object, it replaces it with a
stringification of that object that probably doesn't contain all the
data in the object. For instance, if the object has a
C<JIRA::REST::Class::Issue> object in it for an issue with the key
C<'JRC-1'>, the object would be represented as the string
C<< 'JIRA::REST::Class::Issue->key(JRC-1)' >>. The goal is to provide a
gist of what the contents of the object are without exhaustively dumping
EVERYTHING.
=head1 RELATED CLASSES
=over 2
=item * L<JIRA::REST::Class::Factory|JIRA::REST::Class::Factory>
=item * L<JIRA::REST::Class::Issue|JIRA::REST::Class::Issue>
=item * L<JIRA::REST::Class::Issue::Type|JIRA::REST::Class::Issue::Type>
=item * L<JIRA::REST::Class::Iterator|JIRA::REST::Class::Iterator>
lib/JIRA/REST/Class.pm view on Meta::CPAN
TestServer_stop();
}
use_ok('JIRA::REST::Class');
sub get_test_client {
state $test =
JIRA::REST::Class->new(TestServer_url(), 'username', 'password');
$test->REST_CLIENT->setTimeout(5);
return $test;
};
=end test
=begin testing new 5
my $jira;
try {
$jira = JIRA::REST::Class->new({
url => TestServer_url(),
username => 'user',
password => 'pass',
proxy => '',
anonymous => 0,
ssl_verify_none => 1,
rest_client_config => {},
});
}
catch {
$jira = $_; # as good a place as any to stash the error, because
# isa_ok() will complain that it's not an object.
};
isa_ok($jira, 'JIRA::REST::Class', 'JIRA::REST::Class->new');
my $needs_url_regexp = qr/'?url'? argument must be defined/i;
throws_ok(
sub {
JIRA::REST::Class->new();
},
$needs_url_regexp,
'JIRA::REST::Class->new with no parameters throws an exception',
);
throws_ok(
sub {
JIRA::REST::Class->new({
username => 'user',
password => 'pass',
});
},
$needs_url_regexp,
'JIRA::REST::Class->new with no url throws an exception',
);
throws_ok(
sub {
JIRA::REST::Class->new('http://not.a.good.server.com');
},
qr/No credentials found/,
q{JIRA::REST::Class->new with just url tries to find credentials},
);
lives_ok(
sub {
JIRA::REST::Class->new(TestServer_url(), 'user', 'pass');
},
q{JIRA::REST::Class->new with url, username, and password does't croak!},
);
=end testing
=begin testing get
validate_wrapper_method( sub { get_test_client()->get('/test'); },
{ GET => 'SUCCESS' }, 'get() method works' );
=end testing
=begin testing post
validate_wrapper_method( sub { get_test_client()->post('/test', "key=value"); },
{ POST => 'SUCCESS' }, 'post() method works' );
=end testing
=begin testing put
validate_wrapper_method( sub { get_test_client()->put('/test', "key=value"); },
{ PUT => 'SUCCESS' }, 'put() method works' );
=end testing
=begin testing delete
validate_wrapper_method( sub { get_test_client()->delete('/test'); },
{ DELETE => 'SUCCESS' }, 'delete() method works' );
=end testing
=begin testing data_upload
my $expected = {
"Content-Disposition" => "form-data; name=\"file\"; filename=\"file.txt\"",
POST => "SUCCESS",
data => "An OO Class module built atop L<JIRA::REST|JIRA::REST> for dealing with "
. "JIRA issues and their data as objects.",
name => "file.txt"
};
my $test1_name = 'return value from data_upload()';
my $test2_name = 'data_upload() method succeeded';
my $test3_name = 'data_upload() method returned expected data';
my $test = get_test_client();
my $results;
my $got;
( run in 0.685 second using v1.01-cache-2.11-cpan-e1769b4cff6 )