The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

WebService::Redmine - Wrapper for RedMine REST API (http://www.redmine.org/projects/redmine/wiki/Rest_api).

SYNOPSIS

        use WebService::Redmine;
        my $redminer = WebService::Redmine->new(
                host => 'example.com/redmine',
                key  => 'xxx',
        );
        # password-based auth is also supported:
        #my $redminer = WebService::Redmine->new(
        #       host => 'example.com/redmine',
        #       user => 'redminer',
        #       pass => 'p@s$w0rD',
        #);

        my $project = $redminer->createProject({ project => {
                identifier  => 'my-project',
                name        => 'My Project',
                description => 'My project, created with *WebService::Redmine*',
        }});
        if (!$project) {
                say STDERR 'Error(s) creating project: ', join("\n", map { $_ } @{ $redminer->errorDetails->{errors} });
                exit 1;
        }
        my $project_id = $project->{project}{id};

        $redminer->updateProject($project_id, { project => {
                parent_id       => 42, # Make a project with numeric ID 42  parent for $project_id
                inherit_members => 1,  # Inherit all members and their permissions from the parent
        }});
        
        my $issue = $redminer->createIssue({ issue => {
                project_id  => $project_id,
                subject     => 'Test issue for WebService::Redmine',
                description => 'Issue description',
        }});

        $redminer->deleteProject($project_id);

DESCRIPTION

This module is a client for RedMine REST API. Please note that although RedMine API is designed to support both JSON and XML, this module is JSON only.

METHODS NAMING AND OTHER CALL CONVENTIONS

All methods are dynamically converted to actual HTTP requests using following conventions.

Getting a Collection of Objects

        $redminer->projects;     # ->users,     ->issues,     ->timeEntries     ...
        $redminer->getProjects;  # ->getUsers,  ->getIssues,  ->getTimeEntries  ...
        $redminer->readProjects; # ->readUsers, ->readIssues, ->readTimeEntries ...
        
        # Second page when displaying 10 items per page:
        $redminer->projects({ offset => 9, limit => 10 });

        # Filtering issues:
        $redminer->issues({ project_id => 42, assigned_to_id => 'me' });

Getting an Object

        $redminer->project(1);     # ->user(1),      ->issue(1),     ->timeEntry(1)     ...
        $redminer->getProject(1);  # ->getUser(1),   ->getIssue(1),  ->getTimeEntry(1)  ...
        $redminer->readProject(1); # ->readUsers(1), ->readIssue(1), ->readTimeEntry(1) ...
        
        # Showing an object with additional metadata:
        $redminer->issue(1, { include => 'relations,changesets' });

Creating an Object

        $redminer->createProject({
                # ...
        }); # ->createUser, ->createIssue, ->createTimeEntry ...

Updating an Object

        $redminer->updateProject(1, {
                # ...
        }); # ->updateUser(...), ->updateIssue(...), ->updateTimeEntry(...) ...

Deleting an Object

        $redminer->deleteProject(1); # ->deleteUser(1), ->deleteIssue(1), ->deleteTimeEntry(1) ...

Objects Belonging to Other Objects

        #
        # Example for project membership(s)
        #
        my $project_id    = 42;
        my $membership_id = 42;

        # Listing *project* memberships and creating a membership within a *project*
        # require identifying a project and thus have to be spelled like this:
        $redminer->projectMemberships($project_id, { limit => 50 });
        $redminer->createProjectMembership($project_id, { ... });

        # Viewing/Updating/Deleting a membership is performed directly by its ID, thus:
        my $membership = $redminer->membership($membership_id);
        $redminer->updateMembership($membership_id, { ... });
        $redminer->deleteMembership($membership_id);

Complex Object Names

Such complex names as TimeEntry which should be dispatched to time_entries are recognized and thus can be spelled in CamelCase (see examples above). If this is not the case, please report bugs.

Return Values

All successfull calls return hash references. For update* and delete* calls hash references are empty.

If a call fails, undef is returned. In this case detailed error information can be retrieved using errorDetails method:

        if (!$redminer->deleteIssue(42)) {
                my $details = $redminer->errorDetails;
                # Process $details here...
        }

METHODS

new

        my $redminer = WebService::Redmine->new(%options);

Following options are recognized:

  • host: RedMine host. Beside host name, may include port, path and/or URL scheme (http is used by default).

  • key: API key. For details, please refer to http://www.redmine.org/projects/redmine/wiki/Rest_api#Authentication

  • user, pass: User name and password for password-based authentication

  • work_as: User login for impersonation. For details, please refer to http://www.redmine.org/projects/redmine/wiki/Rest_api#User-Impersonation.

  • no_wrapper_object: Automatically add/remove wrapper object for data. See below.

no_wrapper_object

By default RedMine API requires you to wrap you object data:

        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 turn on the no_wrapper_object flag

        my $redminer = WebService::Redmine->new(
                host => 'example.com/redmine',
                key => 'xxx',
                no_wrapper_object => 1,
        );

you can skip "wrapping" object data, which results in simpler data structures:

        my $project = $redminer->createProject({
                identifier => 'some-id',
                name       => 'Some Name',
        });
        # $project contains something like
        # { id => 42, identifier => 'some-id', name => 'Some Name' ... }

Please note that wrapping can be skipped only while operating on single objects, i.e. this flag is honored for create* and update* requests as well as for getting individual objects. This flag is ignored for delete* calls and calls like issues.

error

Error during the last call. This is an empty string for successfull calls, otherwise it contains an HTTP status line.

If the call failed before sending an actual request (e.g. method name could not be dispatched into an HTTP request), contains description of the client error.

errorDetails

Contains detailed error messages from the last call. This is an empty hash reference for successfull calls, otherwise please see http://www.redmine.org/projects/redmine/wiki/Rest_api#Validation-errors.

If the call failed before sending an actual request (e.g. method name could not be dispatched into an HTTP request), return value is

        {
                client_error => 1
        }

SEE ALSO

Redmine::API (https://metacpan.org/pod/Redmine::API). Major differences between this module and Redmine::API are:

  • Dependencies. Redmine::API depends on Moo and REST::Client which in turn depends on LWP::UserAgent, URI and possibly others. WebService::Redmine uses pure Perl OOP and depends directly on LWP::UserAgent and URI.

  • Call conventions. Although both modules use dynamic dispatching for building actual HTTP requests, they do it in a different manner. In particular, WebService::Redmine tries to dispatch a single method name without using chains of interrim objects as Redmine::API does.

Fork this project on GitHub: https://github.com/igelhaus/redminer

AUTHOR

Anton Soldatov, <igelhaus@gmail.com>

COPYRIGHT AND LICENSE

Copyright (C) 2014 by Anton Soldatov

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.10.0 or, at your option, any later version of Perl 5 you may have available.