Skip to content
Snippets Groups Projects
Commit 1b54d4a2 authored by Anton Soldatov's avatar Anton Soldatov
Browse files

* RedMiner::API 0.04: added @no_wrapper_object@ parameter

* redminer.pl corrected for the latest version of RedMiner::API
parent 3664870e
Branches
No related tags found
No related merge requests found
...@@ -18,6 +18,9 @@ key=xxx ...@@ -18,6 +18,9 @@ key=xxx
# http://www.redmine.org/projects/redmine/wiki/Rest_api#Authentication # http://www.redmine.org/projects/redmine/wiki/Rest_api#Authentication
# You can find your API key on your account page ( /my/account ) when logged in, on the right-hand pane of the default layout. # You can find your API key on your account page ( /my/account ) when logged in, on the right-hand pane of the default layout.
# Impersonation settings:
work_as=real_user
# User+Password auth is also possible: # User+Password auth is also possible:
# [redmine] # [redmine]
# host=redmine.example.com # host=redmine.example.com
......
...@@ -4,9 +4,7 @@ use 5.010; ...@@ -4,9 +4,7 @@ use 5.010;
use strict; use strict;
use warnings; use warnings;
our $VERSION = '0.03'; our $VERSION = '0.04';
# 2DO: implement (un)?wrapping
use URI; use URI;
use URI::QueryParam; use URI::QueryParam;
...@@ -169,8 +167,42 @@ B<user>, B<pass>: User name and password for password-based authentication ...@@ -169,8 +167,42 @@ B<user>, B<pass>: User name and password for password-based authentication
B<work_as>: User login for impersonation. For details, please refer to http://www.redmine.org/projects/redmine/wiki/Rest_api#User-Impersonation. B<work_as>: User login for impersonation. For details, please refer to http://www.redmine.org/projects/redmine/wiki/Rest_api#User-Impersonation.
=item *
B<no_wrapper_object>: Automatically add/remove wrapper object for data. See below.
=back =back
=head3 no_wrapper_object
By default RedMine API requires you to wrap you object data like this:
my $project = $redminer->createProject({
project => {
identifier => 'some-id',
name => 'Some Name',
}
});
# $project contains something like
# { project => { id => 42, identifier => 'some-id', name => 'Some Name' ... } }
By default this module follows this convention. However, if you specify something like
my $redminer = RedMiner::API->new(
host => 'example.com/redmine',
key => 'xxx',
no_wrapper_object => 1,
);
you can skip "wrapping" object data like this:
my $project = $redminer->createProject({
identifier => 'some-id',
name => 'Some Name',
});
# $project contains something like
# { id => 42, identifier => 'some-id', name => 'Some Name' ... }
=cut =cut
sub new sub new
...@@ -184,7 +216,7 @@ sub new ...@@ -184,7 +216,7 @@ sub new
ua => LWP::UserAgent->new, ua => LWP::UserAgent->new,
}; };
foreach my $param (qw/host user pass key work_as/) { foreach my $param (qw/host user pass key work_as no_wrapper_object/) {
$self->{$param} = $arg{$param} // ''; $self->{$param} = $arg{$param} // '';
} }
...@@ -321,6 +353,10 @@ sub _response ...@@ -321,6 +353,10 @@ sub _response
return $self->_set_error($@); return $self->_set_error($@);
} }
if ($self->{expect_single_object} && $self->{no_wrapper_object}) {
$content = delete $content->{$self->{expect_single_object}};
}
return $content; return $content;
} }
...@@ -369,6 +405,7 @@ sub _dispatch_name ...@@ -369,6 +405,7 @@ sub _dispatch_name
} }
$objects = $self->_normalize_objects($objects); $objects = $self->_normalize_objects($objects);
delete $self->{expect_single_object};
my $i = 0; my $i = 0;
my @objects; my @objects;
...@@ -393,9 +430,12 @@ sub _dispatch_name ...@@ -393,9 +430,12 @@ sub _dispatch_name
push @objects, $object_id; push @objects, $object_id;
} }
# Add wrapping object, if necessary: if (pos($objects) == length($objects)) { # Last object in the chain:
if (defined $data->{content} && pos($objects) == length($objects)) { if ($action eq 'get' || $action eq 'create') {
if (!exists $data->{content}{$object}) { $self->{expect_single_object} = $object;
}
if (defined $data->{content} && $self->{no_wrapper_object}) {
# Automatically wrap object data, otherwise we pass everything as is:
$data->{content} = { $data->{content} = {
$object => $data->{content} $object => $data->{content}
}; };
......
...@@ -49,6 +49,8 @@ my $redminer = RedMiner::API->new( ...@@ -49,6 +49,8 @@ my $redminer = RedMiner::API->new(
user => $conf->val('redmine', 'user') // '', user => $conf->val('redmine', 'user') // '',
pass => $conf->val('redmine', 'pass') // '', pass => $conf->val('redmine', 'pass') // '',
key => $conf->val('redmine', 'key') // '', key => $conf->val('redmine', 'key') // '',
work_as => $conf->val('redmine', 'work_as') // '',
no_wrapper_object => 1,
); );
my $description = $layout? $layout->val('project', 'description') // '' : ''; my $description = $layout? $layout->val('project', 'description') // '' : '';
...@@ -67,10 +69,10 @@ if (!$project) { ...@@ -67,10 +69,10 @@ if (!$project) {
exit 255; exit 255;
} }
my $pid = $project->{project}{id}; my $pid = $project->{id};
say 'Project created with ID ' . $pid; say 'Project created with ID ' . $pid;
$redminer->updateProject($project->{project}{id}, { $redminer->updateProject($pid, {
inherit_members => 1, inherit_members => 1,
}); });
...@@ -93,8 +95,8 @@ if ($layout) { ...@@ -93,8 +95,8 @@ if ($layout) {
next; next;
} }
say 'Subproject created with ID ' . $subproject->{project}{id}; say 'Subproject created with ID ' . $subproject->{id};
$redminer->updateProject($subproject->{project}{id}, { $redminer->updateProject($subproject->{id}, {
parent_id => $pid, parent_id => $pid,
inherit_members => 1, inherit_members => 1,
}); });
......
...@@ -109,7 +109,7 @@ is_deeply($r, { ...@@ -109,7 +109,7 @@ is_deeply($r, {
query => undef, query => undef,
}, 'project'); }, 'project');
$r = $redminer->_dispatch_name('createProject', { name => 'My Project' }); $r = $redminer->_dispatch_name('createProject', { project => { name => 'My Project' } });
is_deeply($r, { is_deeply($r, {
method => 'POST', method => 'POST',
path => 'projects', path => 'projects',
...@@ -117,7 +117,7 @@ is_deeply($r, { ...@@ -117,7 +117,7 @@ is_deeply($r, {
query => undef, query => undef,
}, 'createProject'); }, 'createProject');
$r = $redminer->_dispatch_name('updateProject', 1, { name => 'My Project' }); $r = $redminer->_dispatch_name('updateProject', 1, { project => { name => 'My Project' } });
is_deeply($r, { is_deeply($r, {
method => 'PUT', method => 'PUT',
path => 'projects/1', path => 'projects/1',
...@@ -145,7 +145,7 @@ is_deeply($r, { ...@@ -145,7 +145,7 @@ is_deeply($r, {
query => { limit => 10, offset => 9 }, query => { limit => 10, offset => 9 },
}, 'projectMemberships'); }, 'projectMemberships');
$r = $redminer->_dispatch_name('createProjectMembership', 1, { user_id => 1, role_ids => [ 1 ] }); $r = $redminer->_dispatch_name('createProjectMembership', 1, { membership => { user_id => 1, role_ids => [ 1 ] } });
is_deeply($r, { is_deeply($r, {
method => 'POST', method => 'POST',
path => 'projects/1/memberships', path => 'projects/1/memberships',
...@@ -153,7 +153,7 @@ is_deeply($r, { ...@@ -153,7 +153,7 @@ is_deeply($r, {
query => undef, query => undef,
}, 'createProjectMembership'); }, 'createProjectMembership');
$r = $redminer->_dispatch_name('createIssueWatcher', 1, { user_id => 1 }); $r = $redminer->_dispatch_name('createIssueWatcher', 1, { watcher => { user_id => 1 } });
is_deeply($r, { is_deeply($r, {
method => 'POST', method => 'POST',
path => 'issues/1/watchers', path => 'issues/1/watchers',
...@@ -189,7 +189,7 @@ is_deeply($r, { ...@@ -189,7 +189,7 @@ is_deeply($r, {
query => undef, query => undef,
}, 'timeEntry'); }, 'timeEntry');
$r = $redminer->_dispatch_name('createTimeEntry', { issue_id => 42, hours => 1 }); $r = $redminer->_dispatch_name('createTimeEntry', { time_entry => { issue_id => 42, hours => 1 } });
is_deeply($r, { is_deeply($r, {
method => 'POST', method => 'POST',
path => 'time_entries', path => 'time_entries',
...@@ -197,7 +197,7 @@ is_deeply($r, { ...@@ -197,7 +197,7 @@ is_deeply($r, {
query => undef, query => undef,
}, 'createTimeEntry'); }, 'createTimeEntry');
$r = $redminer->_dispatch_name('updateTimeEntry', 1, { issue_id => 42, hours => 1 }); $r = $redminer->_dispatch_name('updateTimeEntry', 1, { time_entry => { issue_id => 42, hours => 1 } });
is_deeply($r, { is_deeply($r, {
method => 'PUT', method => 'PUT',
path => 'time_entries/1', path => 'time_entries/1',
...@@ -225,7 +225,7 @@ is_deeply($r, { ...@@ -225,7 +225,7 @@ is_deeply($r, {
query => { limit => 10, offset => 9 }, query => { limit => 10, offset => 9 },
}, 'projectIssueCategories'); }, 'projectIssueCategories');
$r = $redminer->_dispatch_name('createProjectIssueCategory', 1, { name => 'My Category', assign_to_id => 1 }); $r = $redminer->_dispatch_name('createProjectIssueCategory', 1, { issue_category => { name => 'My Category', assign_to_id => 1 } });
is_deeply($r, { is_deeply($r, {
method => 'POST', method => 'POST',
path => 'projects/1/issue_categories', path => 'projects/1/issue_categories',
......
...@@ -7,7 +7,7 @@ use JSON::XS qw/encode_json/; ...@@ -7,7 +7,7 @@ use JSON::XS qw/encode_json/;
if ($ENV{REDMINER_API_DEVEL}) { if ($ENV{REDMINER_API_DEVEL}) {
plan tests => 5; plan tests => 5;
} else{ } else{
plan skip_all => 'Tests require RedMine installation'; plan skip_all => 'Development tests (REDMINER_API_DEVEL not set)';
} }
eval 'use RedMiner::API'; eval 'use RedMiner::API';
...@@ -34,25 +34,25 @@ my $redminer = RedMiner::API->new( ...@@ -34,25 +34,25 @@ my $redminer = RedMiner::API->new(
key => $key, key => $key,
); );
my $project = $redminer->createProject({ my $project = $redminer->createProject({ project => {
identifier => 'redminer-api-test', identifier => 'redminer-api-test',
name => 'RedMiner API test', name => 'RedMiner API test',
}); }});
my $project_id = $project->{project}{id}; my $project_id = $project->{project}{id};
ok(defined $project_id, 'New project created with internal ID ' . $project_id); ok(defined $project_id, 'New project created with internal ID ' . $project_id);
ok(!defined $redminer->createProject({ ok(!defined $redminer->createProject({ project => {
identifier => 'redminer-api-test', identifier => 'redminer-api-test',
name => 'RedMiner API test', name => 'RedMiner API test',
}), 'Project already exists, error object is ' . JSON::XS::encode_json($redminer->errorDetails)); }}), 'Project already exists, error object is ' . JSON::XS::encode_json($redminer->errorDetails));
ok($redminer->updateProject($project_id, { inherit_members => 1 }), 'Project updated'); ok($redminer->updateProject($project_id, { project => { inherit_members => 1 } }), 'Project updated');
my $issue = $redminer->createIssue({ my $issue = $redminer->createIssue({ issue => {
project_id => $project_id, project_id => $project_id,
subject => 'Test issue for RedMiner::API', subject => 'Test issue for RedMiner::API',
description => 'Test description', description => 'Test description',
}); }});
ok(defined $issue->{issue}{id}, 'Issue created with ID #' . $issue->{issue}{id}); ok(defined $issue->{issue}{id}, 'Issue created with ID #' . $issue->{issue}{id});
ok($redminer->deleteProject($project_id), 'Project deleted'); ok($redminer->deleteProject($project_id), 'Project deleted');
......
use strict;
use warnings;
use Test::More;
use JSON::XS qw/encode_json/;
if ($ENV{REDMINER_API_DEVEL}) {
plan tests => 5;
} else{
plan skip_all => 'Development tests (REDMINER_API_DEVEL not set)';
}
eval 'use RedMiner::API';
#
# Read API key from a simple config file in the format 'host;key'
#
my $host = '';
my $key = '';
my $key_fname = $ENV{HOME} . '/.redminer/key';
if (!-e $key_fname) {
BAIL_OUT('REDMINER_API_DEVEL set, but key file is not accessible');
}
open my $FH_key, '<', $key_fname;
my $key_data = <$FH_key>;
($host, $key) = split /\s*;\s*/, $key_data;
chomp $key_data;
close $FH_key;
my $redminer = RedMiner::API->new(
host => $host,
key => $key,
no_wrapper_object => 1,
);
my $project = $redminer->createProject({
identifier => 'redminer-api-test',
name => 'RedMiner API test',
});
my $project_id = $project->{id};
ok(defined $project_id, 'New project created with internal ID ' . $project_id);
ok(!defined $redminer->createProject({
identifier => 'redminer-api-test',
name => 'RedMiner API test',
}), 'Project already exists, error object is ' . JSON::XS::encode_json($redminer->errorDetails));
ok($redminer->updateProject($project_id, { inherit_members => 1 }), 'Project updated');
my $issue = $redminer->createIssue({
project_id => $project_id,
subject => 'Test issue for RedMiner::API',
description => 'Test description',
});
ok(defined $issue->{id}, 'Issue created with ID #' . $issue->{id});
ok($redminer->deleteProject($project_id), 'Project deleted');
exit;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment