Bug 866927 - Enhance Bugzilla WebServices to allow data access using REST
r=glob,a=justdavetrunk
parent
6f97373a58
commit
44d42898e8
|
@ -26,3 +26,9 @@ Options -Indexes
|
|||
</IfModule>
|
||||
</IfModule>
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteBase /866927/
|
||||
RewriteRule ^rest/(.*)$ rest.cgi/$1 [NE]
|
||||
</IfModule>
|
||||
|
|
|
@ -469,6 +469,9 @@ sub usage_mode {
|
|||
elsif ($newval == USAGE_MODE_TEST) {
|
||||
$class->error_mode(ERROR_MODE_TEST);
|
||||
}
|
||||
elsif ($newval == USAGE_MODE_REST) {
|
||||
$class->error_mode(ERROR_MODE_REST);
|
||||
}
|
||||
else {
|
||||
ThrowCodeError('usage_mode_invalid',
|
||||
{'invalid_usage_mode', $newval});
|
||||
|
|
|
@ -56,7 +56,7 @@ sub new {
|
|||
# the rendering of pages.
|
||||
my $script = basename($0);
|
||||
if (my $path_info = $self->path_info) {
|
||||
my @whitelist;
|
||||
my @whitelist = ("rest.cgi");
|
||||
Bugzilla::Hook::process('path_info_whitelist', { whitelist => \@whitelist });
|
||||
if (!grep($_ eq $script, @whitelist)) {
|
||||
# IIS includes the full path to the script in PATH_INFO,
|
||||
|
|
|
@ -122,12 +122,14 @@ use Memoize;
|
|||
USAGE_MODE_EMAIL
|
||||
USAGE_MODE_JSON
|
||||
USAGE_MODE_TEST
|
||||
USAGE_MODE_REST
|
||||
|
||||
ERROR_MODE_WEBPAGE
|
||||
ERROR_MODE_DIE
|
||||
ERROR_MODE_DIE_SOAP_FAULT
|
||||
ERROR_MODE_JSON_RPC
|
||||
ERROR_MODE_TEST
|
||||
ERROR_MODE_REST
|
||||
|
||||
COLOR_ERROR
|
||||
COLOR_SUCCESS
|
||||
|
@ -459,6 +461,7 @@ use constant USAGE_MODE_XMLRPC => 2;
|
|||
use constant USAGE_MODE_EMAIL => 3;
|
||||
use constant USAGE_MODE_JSON => 4;
|
||||
use constant USAGE_MODE_TEST => 5;
|
||||
use constant USAGE_MODE_REST => 6;
|
||||
|
||||
# Error modes. Default set by Bugzilla->usage_mode (so ERROR_MODE_WEBPAGE
|
||||
# usually). Use with Bugzilla->error_mode.
|
||||
|
@ -467,6 +470,7 @@ use constant ERROR_MODE_DIE => 1;
|
|||
use constant ERROR_MODE_DIE_SOAP_FAULT => 2;
|
||||
use constant ERROR_MODE_JSON_RPC => 3;
|
||||
use constant ERROR_MODE_TEST => 4;
|
||||
use constant ERROR_MODE_REST => 5;
|
||||
|
||||
# The ANSI colors of messages that command-line scripts use
|
||||
use constant COLOR_ERROR => 'red';
|
||||
|
|
|
@ -104,7 +104,8 @@ sub _throw_error {
|
|||
die("$message\n");
|
||||
}
|
||||
elsif (Bugzilla->error_mode == ERROR_MODE_DIE_SOAP_FAULT
|
||||
|| Bugzilla->error_mode == ERROR_MODE_JSON_RPC)
|
||||
|| Bugzilla->error_mode == ERROR_MODE_JSON_RPC
|
||||
|| Bugzilla->error_mode == ERROR_MODE_REST)
|
||||
{
|
||||
# Clone the hash so we aren't modifying the constant.
|
||||
my %error_map = %{ WS_ERROR_CODE() };
|
||||
|
@ -121,13 +122,20 @@ sub _throw_error {
|
|||
}
|
||||
else {
|
||||
my $server = Bugzilla->_json_server;
|
||||
|
||||
my $status_code = 0;
|
||||
if (Bugzilla->error_mode == ERROR_MODE_REST) {
|
||||
my %status_code_map = %{ REST_STATUS_CODE_MAP() };
|
||||
$status_code = $status_code_map{$code} || $status_code_map{'_default'};
|
||||
}
|
||||
# Technically JSON-RPC isn't allowed to have error numbers
|
||||
# higher than 999, but we do this to avoid conflicts with
|
||||
# the internal JSON::RPC error codes.
|
||||
$server->raise_error(code => 100000 + $code,
|
||||
message => $message,
|
||||
id => $server->{_bz_request_id},
|
||||
version => $server->version);
|
||||
$server->raise_error(code => 100000 + $code,
|
||||
status_code => $status_code,
|
||||
message => $message,
|
||||
id => $server->{_bz_request_id},
|
||||
version => $server->version);
|
||||
# Most JSON-RPC Throw*Error calls happen within an eval inside
|
||||
# of JSON::RPC. So, in that circumstance, instead of exiting,
|
||||
# we die with no message. JSON::RPC checks raise_error before
|
||||
|
|
|
@ -284,7 +284,7 @@ sub OPTIONAL_MODULES {
|
|||
package => 'JSON-RPC',
|
||||
module => 'JSON::RPC',
|
||||
version => 0,
|
||||
feature => ['jsonrpc'],
|
||||
feature => ['jsonrpc', 'rest'],
|
||||
},
|
||||
{
|
||||
package => 'JSON-XS',
|
||||
|
@ -298,7 +298,7 @@ sub OPTIONAL_MODULES {
|
|||
module => 'Test::Taint',
|
||||
# 1.06 no longer throws warnings with Perl 5.10+.
|
||||
version => 1.06,
|
||||
feature => ['jsonrpc', 'xmlrpc'],
|
||||
feature => ['jsonrpc', 'xmlrpc', 'rest'],
|
||||
},
|
||||
{
|
||||
# We need the 'utf8_mode' method of HTML::Parser, for HTML::Scrubber.
|
||||
|
@ -397,6 +397,7 @@ use constant FEATURE_FILES => (
|
|||
jsonrpc => ['Bugzilla/WebService/Server/JSONRPC.pm', 'jsonrpc.cgi'],
|
||||
xmlrpc => ['Bugzilla/WebService/Server/XMLRPC.pm', 'xmlrpc.cgi',
|
||||
'Bugzilla/WebService.pm', 'Bugzilla/WebService/*.pm'],
|
||||
rest => ['Bugzilla/WebService/Server/REST.pm', 'rest.cgi'],
|
||||
moving => ['importxml.pl'],
|
||||
auth_ldap => ['Bugzilla/Auth/Verify/LDAP.pm'],
|
||||
auth_radius => ['Bugzilla/Auth/Verify/RADIUS.pm'],
|
||||
|
|
|
@ -136,6 +136,8 @@ sub MessageToMTA {
|
|||
Bugzilla::Hook::process('mailer_before_send',
|
||||
{ email => $email, mailer_args => \@args });
|
||||
|
||||
return if $email->header('to') eq '';
|
||||
|
||||
$email->walk_parts(sub {
|
||||
my ($part) = @_;
|
||||
return if $part->parts > 1; # Top-level
|
||||
|
|
|
@ -45,15 +45,20 @@ This is the standard API for external programs that want to interact
|
|||
with Bugzilla. It provides various methods in various modules.
|
||||
|
||||
You can interact with this API via
|
||||
L<XML-RPC|Bugzilla::WebService::Server::XMLRPC> or
|
||||
L<JSON-RPC|Bugzilla::WebService::Server::JSONRPC>.
|
||||
L<XML-RPC|Bugzilla::WebService::Server::XMLRPC>,
|
||||
L<JSON-RPC|Bugzilla::WebService::Server::JSONRPC> or
|
||||
L<REST|Bugzilla::WebService::Server::REST>.
|
||||
|
||||
=head1 CALLING METHODS
|
||||
|
||||
Methods are grouped into "packages", like C<Bug> for
|
||||
Methods are grouped into "packages", like C<Bug> for
|
||||
L<Bugzilla::WebService::Bug>. So, for example,
|
||||
L<Bugzilla::WebService::Bug/get>, is called as C<Bug.get>.
|
||||
|
||||
For REST, the "package" is more determined by the path
|
||||
used to access the resource. See each relevant method
|
||||
for specific details on how to access via REST.
|
||||
|
||||
=head1 PARAMETERS
|
||||
|
||||
The Bugzilla API takes the following various types of parameters:
|
||||
|
@ -135,7 +140,7 @@ There are various ways to log in:
|
|||
|
||||
=item C<User.login>
|
||||
|
||||
You can use L<Bugzilla::WebService::User/login> to log in as a Bugzilla
|
||||
You can use L<Bugzilla::WebService::User/login> to log in as a Bugzilla
|
||||
user. This issues standard HTTP cookies that you must then use in future
|
||||
calls, so your client must be capable of receiving and transmitting
|
||||
cookies.
|
||||
|
@ -165,13 +170,17 @@ not expire.
|
|||
=back
|
||||
|
||||
The C<Bugzilla_restrictlogin> and C<Bugzilla_rememberlogin> options
|
||||
are only used when you have also specified C<Bugzilla_login> and
|
||||
are only used when you have also specified C<Bugzilla_login> and
|
||||
C<Bugzilla_password>.
|
||||
|
||||
Note that Bugzilla will return HTTP cookies along with the method
|
||||
response when you use these arguments (just like the C<User.login> method
|
||||
above).
|
||||
|
||||
For REST, you may also use the C<username> and C<password> variable
|
||||
names instead of C<Bugzilla_login> and C<Bugzilla_password> as a
|
||||
convenience.
|
||||
|
||||
=back
|
||||
|
||||
=head1 STABLE, EXPERIMENTAL, and UNSTABLE
|
||||
|
@ -266,6 +275,9 @@ would return something like:
|
|||
|
||||
{ users => [{ id => 1, name => 'user@domain.com' }] }
|
||||
|
||||
Note for REST, C<include_fields> may instead be a comma delimited string
|
||||
for GET type requests.
|
||||
|
||||
=item C<exclude_fields>
|
||||
|
||||
C<array> An array of strings, representing the (case-sensitive) names of
|
||||
|
@ -295,6 +307,9 @@ would return something like:
|
|||
|
||||
{ users => [{ id => 1, real_name => 'John Smith' }] }
|
||||
|
||||
Note for REST, C<exclude_fields> may instead be a comma delimited string
|
||||
for GET type requests.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
|
|
@ -1075,6 +1075,10 @@ or get information about bugs that have already been filed.
|
|||
See L<Bugzilla::WebService> for a description of how parameters are passed,
|
||||
and what B<STABLE>, B<UNSTABLE>, and B<EXPERIMENTAL> mean.
|
||||
|
||||
Although the data input and output is the same for JSONRPC, XMLRPC and REST,
|
||||
the directions for how to access the data via REST is noted in each method
|
||||
where applicable.
|
||||
|
||||
=head1 Utility Functions
|
||||
|
||||
=head2 fields
|
||||
|
@ -1088,11 +1092,26 @@ B<UNSTABLE>
|
|||
Get information about valid bug fields, including the lists of legal values
|
||||
for each field.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
You have several options for retreiving information about fields. The first
|
||||
part is the request method and the rest is the related path needed.
|
||||
|
||||
To get information about all fields:
|
||||
|
||||
GET /field/bug
|
||||
|
||||
To get information related to a single field:
|
||||
|
||||
GET /field/bug/<id_or_name>
|
||||
|
||||
The returned data format is the same as below.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
You can pass either field ids or field names.
|
||||
|
||||
B<Note>: If neither C<ids> nor C<names> is specified, then all
|
||||
B<Note>: If neither C<ids> nor C<names> is specified, then all
|
||||
non-obsolete fields will be returned.
|
||||
|
||||
In addition to the parameters below, this method also accepts the
|
||||
|
@ -1288,6 +1307,8 @@ You specified an invalid field name or id.
|
|||
|
||||
=item C<is_active> return key for C<values> was added in Bugzilla B<4.4>.
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
@ -1303,6 +1324,18 @@ B<DEPRECATED> - Use L</fields> instead.
|
|||
|
||||
Tells you what values are allowed for a particular field.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
To get information on the values for a field based on field name:
|
||||
|
||||
GET /field/bug/<field_name>/values
|
||||
|
||||
To get information based on field name and a specific product:
|
||||
|
||||
GET /field/bug/<field_name>/<product_id>/values
|
||||
|
||||
The returned data format is the same as below.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
=over
|
||||
|
@ -1335,6 +1368,14 @@ You specified a field that doesn't exist or isn't a drop-down field.
|
|||
|
||||
=back
|
||||
|
||||
=item B<History>
|
||||
|
||||
=over
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
||||
=head1 Bug Information
|
||||
|
@ -1353,6 +1394,18 @@ and/or attachment ids.
|
|||
B<Note>: Private attachments will only be returned if you are in the
|
||||
insidergroup or if you are the submitter of the attachment.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
To get all current attachments for a bug:
|
||||
|
||||
GET /bug/<bug_id>/attachment
|
||||
|
||||
To get a specific attachment based on attachment ID:
|
||||
|
||||
GET /bug/attachment/<attachment_id>
|
||||
|
||||
The returned data format is the same as below.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
B<Note>: At least one of C<ids> or C<attachment_ids> is required.
|
||||
|
@ -1550,6 +1603,8 @@ C<summary>.
|
|||
|
||||
=item The C<flags> array was added in Bugzilla B<4.4>.
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
@ -1566,6 +1621,18 @@ B<STABLE>
|
|||
This allows you to get data about comments, given a list of bugs
|
||||
and/or comment ids.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
To get all comments for a particular bug using the bug ID or alias:
|
||||
|
||||
GET /bug/<id_or_alias>/comment
|
||||
|
||||
To get a specific comment based on the comment ID:
|
||||
|
||||
GET /bug/comment/<comment_id>
|
||||
|
||||
The returned data format is the same as below.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
B<Note>: At least one of C<ids> or C<comment_ids> is required.
|
||||
|
@ -1711,6 +1778,8 @@ C<creator>.
|
|||
|
||||
=item C<creation_time> was added in Bugzilla B<4.4>.
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
@ -1728,6 +1797,14 @@ Gets information about particular bugs in the database.
|
|||
|
||||
Note: Can also be called as "get_bugs" for compatibilty with Bugzilla 3.0 API.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
To get information about a particular bug using its ID or alias:
|
||||
|
||||
GET /bug/<id_or_alias>
|
||||
|
||||
The returned data format is the same as below.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
In addition to the parameters below, this method also accepts the
|
||||
|
@ -2060,6 +2137,8 @@ You do not have access to the bug_id you specified.
|
|||
=item The following properties were added to this method's return values
|
||||
in Bugzilla B<3.4>:
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>
|
||||
|
||||
=over
|
||||
|
||||
=item For C<bugs>
|
||||
|
@ -2117,6 +2196,14 @@ B<EXPERIMENTAL>
|
|||
|
||||
Gets the history of changes for particular bugs in the database.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
To get the history for a specific bug ID:
|
||||
|
||||
GET /bug/<bug_id>/history
|
||||
|
||||
The returned data format will be the same as below.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
=over
|
||||
|
@ -2208,6 +2295,8 @@ The same as L</get>.
|
|||
consistent with other methods. Since Bugzilla B<4.4>, they now match
|
||||
names used by L<Bug.update|/"update"> for consistency.
|
||||
|
||||
=item REST API call added Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
@ -2223,6 +2312,14 @@ B<UNSTABLE>
|
|||
|
||||
Allows you to search for bugs based on particular criteria.
|
||||
|
||||
=item <REST>
|
||||
|
||||
To search for bugs:
|
||||
|
||||
GET /bug
|
||||
|
||||
The URL parameters and the returned data format are the same as below.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
Unless otherwise specified in the description of a parameter, bugs are
|
||||
|
@ -2408,6 +2505,8 @@ in Bugzilla B<4.0>.
|
|||
C<limit> is set equal to zero. Otherwise maximum results returned are limited
|
||||
by system configuration.
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
@ -2434,10 +2533,19 @@ The WebService interface may allow you to set things other than those listed
|
|||
here, but realize that anything undocumented is B<UNSTABLE> and will very
|
||||
likely change in the future.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
To create a new bug in Bugzilla:
|
||||
|
||||
POST /bug
|
||||
|
||||
The params to include in the POST body as well as the returned data format,
|
||||
are the same as below.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
Some params must be set, or an error will be thrown. These params are
|
||||
marked B<Required>.
|
||||
marked B<Required>.
|
||||
|
||||
Some parameters can have defaults set in Bugzilla, by the administrator.
|
||||
If these parameters have defaults set, you can omit them. These parameters
|
||||
|
@ -2598,6 +2706,8 @@ loop errors had a generic code of C<32000>.
|
|||
=item The ability to file new bugs with a C<resolution> was added in
|
||||
Bugzilla B<4.4>.
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
@ -2613,6 +2723,16 @@ B<STABLE>
|
|||
|
||||
This allows you to add an attachment to a bug in Bugzilla.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
To create attachment on a current bug:
|
||||
|
||||
POST /bug/<bug_id>/attachment
|
||||
|
||||
The params to include in the POST body, as well as the returned
|
||||
data format are the same as below. The C<ids> param will be
|
||||
overridden as it it pulled from the URL path.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
=over
|
||||
|
@ -2710,6 +2830,8 @@ You set the "data" field to an empty string.
|
|||
|
||||
=item The return value has changed in Bugzilla B<4.4>.
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
@ -2725,6 +2847,15 @@ B<STABLE>
|
|||
|
||||
This allows you to add a comment to a bug in Bugzilla.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
To create a comment on a current bug:
|
||||
|
||||
POST /bug/<bug_id>/comment
|
||||
|
||||
The params to include in the POST body as well as the returned data format,
|
||||
are the same as below.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
=over
|
||||
|
@ -2800,6 +2931,8 @@ purposes if you wish.
|
|||
=item Before Bugzilla B<3.6>, error 54 and error 114 had a generic error
|
||||
code of 32000.
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
@ -2816,6 +2949,16 @@ B<UNSTABLE>
|
|||
Allows you to update the fields of a bug. Automatically sends emails
|
||||
out about the changes.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
To update the fields of a current bug:
|
||||
|
||||
PUT /bug/<bug_id>
|
||||
|
||||
The params to include in the PUT body as well as the returned data format,
|
||||
are the same as below. The C<ids> param will be overridden as it is
|
||||
pulled from the URL path.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
=over
|
||||
|
@ -3260,6 +3403,8 @@ rules don't allow that change.
|
|||
|
||||
=item Added in Bugzilla B<4.0>.
|
||||
|
||||
=item REST API call added Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
|
|
@ -121,12 +121,12 @@ sub time {
|
|||
sub last_audit_time {
|
||||
my ($self, $params) = validate(@_, 'class');
|
||||
my $dbh = Bugzilla->dbh;
|
||||
|
||||
|
||||
my $sql_statement = "SELECT MAX(at_time) FROM audit_log";
|
||||
my $class_values = $params->{class};
|
||||
my @class_values_quoted;
|
||||
foreach my $class_value (@$class_values) {
|
||||
push (@class_values_quoted, $dbh->quote($class_value))
|
||||
push (@class_values_quoted, $dbh->quote($class_value))
|
||||
if $class_value =~ /^Bugzilla(::[a-zA-Z0-9_]+)*$/;
|
||||
}
|
||||
|
||||
|
@ -135,11 +135,11 @@ sub last_audit_time {
|
|||
}
|
||||
|
||||
my $last_audit_time = $dbh->selectrow_array("$sql_statement");
|
||||
|
||||
|
||||
# All Webservices return times in UTC; Use UTC here for backwards compat.
|
||||
# Hardcode values where appropriate
|
||||
$last_audit_time = datetime_from($last_audit_time, 'UTC');
|
||||
|
||||
|
||||
return {
|
||||
last_audit_time => $self->type('dateTime', $last_audit_time)
|
||||
};
|
||||
|
@ -181,6 +181,10 @@ This provides functions that tell you about Bugzilla in general.
|
|||
See L<Bugzilla::WebService> for a description of how parameters are passed,
|
||||
and what B<STABLE>, B<UNSTABLE>, and B<EXPERIMENTAL> mean.
|
||||
|
||||
Although the data input and output is the same for JSONRPC, XMLRPC and REST,
|
||||
the directions for how to access the data via REST is noted in each method
|
||||
where applicable.
|
||||
|
||||
=head2 version
|
||||
|
||||
B<STABLE>
|
||||
|
@ -191,6 +195,12 @@ B<STABLE>
|
|||
|
||||
Returns the current version of Bugzilla.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
GET /version
|
||||
|
||||
The returned data format is the same as below.
|
||||
|
||||
=item B<Params> (none)
|
||||
|
||||
=item B<Returns>
|
||||
|
@ -200,6 +210,14 @@ string.
|
|||
|
||||
=item B<Errors> (none)
|
||||
|
||||
=item B<History>
|
||||
|
||||
=over
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
||||
=head2 extensions
|
||||
|
@ -213,6 +231,12 @@ B<EXPERIMENTAL>
|
|||
Gets information about the extensions that are currently installed and enabled
|
||||
in this Bugzilla.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
GET /extensions
|
||||
|
||||
The returned data format is the same as below.
|
||||
|
||||
=item B<Params> (none)
|
||||
|
||||
=item B<Returns>
|
||||
|
@ -243,6 +267,8 @@ The return value looks something like this:
|
|||
that the extensions define themselves. Before 3.6, the names of the
|
||||
extensions depended on the directory they were in on the Bugzilla server.
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
@ -258,6 +284,12 @@ Use L</time> instead.
|
|||
|
||||
Returns the timezone that Bugzilla expects dates and times in.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
GET /timezone
|
||||
|
||||
The returned data format is the same as below.
|
||||
|
||||
=item B<Params> (none)
|
||||
|
||||
=item B<Returns>
|
||||
|
@ -272,6 +304,8 @@ string in (+/-)XXXX (RFC 2822) format.
|
|||
=item As of Bugzilla B<3.6>, the timezone returned is always C<+0000>
|
||||
(the UTC timezone).
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
@ -288,6 +322,12 @@ B<STABLE>
|
|||
Gets information about what time the Bugzilla server thinks it is, and
|
||||
what timezone it's running in.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
GET /time
|
||||
|
||||
The returned data format is the same as below.
|
||||
|
||||
=item B<Params> (none)
|
||||
|
||||
=item B<Returns>
|
||||
|
@ -298,7 +338,7 @@ A struct with the following items:
|
|||
|
||||
=item C<db_time>
|
||||
|
||||
C<dateTime> The current time in UTC, according to the Bugzilla
|
||||
C<dateTime> The current time in UTC, according to the Bugzilla
|
||||
I<database server>.
|
||||
|
||||
Note that Bugzilla assumes that the database and the webserver are running
|
||||
|
@ -308,7 +348,7 @@ rely on for doing searches and other input to the WebService.
|
|||
|
||||
=item C<web_time>
|
||||
|
||||
C<dateTime> This is the current time in UTC, according to Bugzilla's
|
||||
C<dateTime> This is the current time in UTC, according to Bugzilla's
|
||||
I<web server>.
|
||||
|
||||
This might be different by a second from C<db_time> since this comes from
|
||||
|
@ -324,7 +364,7 @@ versions of Bugzilla before 3.6.)
|
|||
=item C<tz_name>
|
||||
|
||||
C<string> The literal string C<UTC>. (Exists only for backwards-compatibility
|
||||
with versions of Bugzilla before 3.6.)
|
||||
with versions of Bugzilla before 3.6.)
|
||||
|
||||
=item C<tz_short_name>
|
||||
|
||||
|
@ -348,6 +388,8 @@ with versions of Bugzilla before 3.6.)
|
|||
were in the UTC timezone, instead of returning information in the server's
|
||||
local timezone.
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
@ -362,6 +404,12 @@ B<UNSTABLE>
|
|||
|
||||
Returns parameter values currently used in this Bugzilla.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
GET /parameters
|
||||
|
||||
The returned data format is the same as below.
|
||||
|
||||
=item B<Params> (none)
|
||||
|
||||
=item B<Returns>
|
||||
|
@ -419,6 +467,8 @@ never be stable.
|
|||
|
||||
=item Added in Bugzilla B<4.4>.
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
@ -433,9 +483,15 @@ B<EXPERIMENTAL>
|
|||
|
||||
Gets the latest time of the audit_log table.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
GET /last_audit_time
|
||||
|
||||
The returned data format is the same as below.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
You can pass the optional parameter C<class> to get the maximum for only
|
||||
You can pass the optional parameter C<class> to get the maximum for only
|
||||
the listed classes.
|
||||
|
||||
=over
|
||||
|
@ -460,6 +516,8 @@ at_time from the audit_log.
|
|||
|
||||
=item Added in Bugzilla B<4.4>.
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
|
|
@ -86,7 +86,7 @@ Bugzilla::Webservice::Classification - The Classification API
|
|||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This part of the Bugzilla API allows you to deal with the available Classifications.
|
||||
This part of the Bugzilla API allows you to deal with the available Classifications.
|
||||
You will be able to get information about them as well as manipulate them.
|
||||
|
||||
=head1 METHODS
|
||||
|
@ -94,6 +94,10 @@ You will be able to get information about them as well as manipulate them.
|
|||
See L<Bugzilla::WebService> for a description of how parameters are passed,
|
||||
and what B<STABLE>, B<UNSTABLE>, and B<EXPERIMENTAL> mean.
|
||||
|
||||
Although the data input and output is the same for JSONRPC, XMLRPC and REST,
|
||||
the directions for how to access the data via REST is noted in each method
|
||||
where applicable.
|
||||
|
||||
=head1 Classification Retrieval
|
||||
|
||||
=head2 get
|
||||
|
@ -106,13 +110,21 @@ B<EXPERIMENTAL>
|
|||
|
||||
Returns a hash containing information about a set of classifications.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
To return information on a single classification:
|
||||
|
||||
GET /classification/<classification_id_or_name>
|
||||
|
||||
The returned data format will be the same as below.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
In addition to the parameters below, this method also accepts the
|
||||
standard L<include_fields|Bugzilla::WebService/include_fields> and
|
||||
L<exclude_fields|Bugzilla::WebService/exclude_fields> arguments.
|
||||
|
||||
You could get classifications info by supplying their names and/or ids.
|
||||
You could get classifications info by supplying their names and/or ids.
|
||||
So, this method accepts the following parameters:
|
||||
|
||||
=over
|
||||
|
@ -127,10 +139,10 @@ An array of classification names.
|
|||
|
||||
=back
|
||||
|
||||
=item B<Returns>
|
||||
=item B<Returns>
|
||||
|
||||
A hash with the key C<classifications> and an array of hashes as the corresponding value.
|
||||
Each element of the array represents a classification that the user is authorized to see
|
||||
Each element of the array represents a classification that the user is authorized to see
|
||||
and has the following keys:
|
||||
|
||||
=over
|
||||
|
@ -190,6 +202,8 @@ Classification is not enabled on this installation.
|
|||
|
||||
=item Added in Bugzilla B<4.4>.
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
|
|
@ -14,9 +14,22 @@ use parent qw(Exporter);
|
|||
|
||||
our @EXPORT = qw(
|
||||
WS_ERROR_CODE
|
||||
|
||||
STATUS_OK
|
||||
STATUS_CREATED
|
||||
STATUS_ACCEPTED
|
||||
STATUS_NO_CONTENT
|
||||
STATUS_MULTIPLE_CHOICES
|
||||
STATUS_BAD_REQUEST
|
||||
STATUS_NOT_FOUND
|
||||
STATUS_GONE
|
||||
REST_STATUS_CODE_MAP
|
||||
|
||||
ERROR_UNKNOWN_FATAL
|
||||
ERROR_UNKNOWN_TRANSIENT
|
||||
|
||||
XMLRPC_CONTENT_TYPE_WHITELIST
|
||||
REST_CONTENT_TYPE_WHITELIST
|
||||
|
||||
WS_DISPATCH
|
||||
);
|
||||
|
@ -172,8 +185,47 @@ use constant WS_ERROR_CODE => {
|
|||
unknown_method => -32601,
|
||||
json_rpc_post_only => 32610,
|
||||
json_rpc_invalid_callback => 32611,
|
||||
xmlrpc_illegal_content_type => 32612,
|
||||
json_rpc_illegal_content_type => 32613,
|
||||
xmlrpc_illegal_content_type => 32612,
|
||||
json_rpc_illegal_content_type => 32613,
|
||||
rest_invalid_resource => 32614,
|
||||
};
|
||||
|
||||
# RESTful webservices use the http status code
|
||||
# to describe whether a call was successful or
|
||||
# to describe the type of error that occurred.
|
||||
use constant STATUS_OK => 200;
|
||||
use constant STATUS_CREATED => 201;
|
||||
use constant STATUS_ACCEPTED => 202;
|
||||
use constant STATUS_NO_CONTENT => 204;
|
||||
use constant STATUS_MULTIPLE_CHOICES => 300;
|
||||
use constant STATUS_BAD_REQUEST => 400;
|
||||
use constant STATUS_NOT_AUTHORIZED => 401;
|
||||
use constant STATUS_NOT_FOUND => 404;
|
||||
use constant STATUS_GONE => 410;
|
||||
|
||||
# The integer value is the error code above returned by
|
||||
# the related webvservice call. We choose the appropriate
|
||||
# http status code based on the error code or use the
|
||||
# default STATUS_BAD_REQUEST.
|
||||
use constant REST_STATUS_CODE_MAP => {
|
||||
51 => STATUS_NOT_FOUND,
|
||||
101 => STATUS_NOT_FOUND,
|
||||
102 => STATUS_NOT_AUTHORIZED,
|
||||
106 => STATUS_NOT_AUTHORIZED,
|
||||
109 => STATUS_NOT_AUTHORIZED,
|
||||
110 => STATUS_NOT_AUTHORIZED,
|
||||
113 => STATUS_NOT_AUTHORIZED,
|
||||
115 => STATUS_NOT_AUTHORIZED,
|
||||
120 => STATUS_NOT_AUTHORIZED,
|
||||
300 => STATUS_NOT_AUTHORIZED,
|
||||
301 => STATUS_NOT_AUTHORIZED,
|
||||
302 => STATUS_NOT_AUTHORIZED,
|
||||
303 => STATUS_NOT_AUTHORIZED,
|
||||
304 => STATUS_NOT_AUTHORIZED,
|
||||
410 => STATUS_NOT_AUTHORIZED,
|
||||
504 => STATUS_NOT_AUTHORIZED,
|
||||
505 => STATUS_NOT_AUTHORIZED,
|
||||
_default => STATUS_BAD_REQUEST
|
||||
};
|
||||
|
||||
# These are the fallback defaults for errors not in ERROR_CODE.
|
||||
|
@ -187,6 +239,13 @@ use constant XMLRPC_CONTENT_TYPE_WHITELIST => qw(
|
|||
application/xml
|
||||
);
|
||||
|
||||
use constant REST_CONTENT_TYPE_WHITELIST => qw(
|
||||
text/html
|
||||
application/javascript
|
||||
application/json
|
||||
text/javascript
|
||||
);
|
||||
|
||||
sub WS_DISPATCH {
|
||||
# We "require" here instead of "use" above to avoid a dependency loop.
|
||||
require Bugzilla::Hook;
|
||||
|
|
|
@ -113,6 +113,10 @@ get information about them.
|
|||
See L<Bugzilla::WebService> for a description of how parameters are passed,
|
||||
and what B<STABLE>, B<UNSTABLE>, and B<EXPERIMENTAL> mean.
|
||||
|
||||
Although the data input and output is the same for JSONRPC, XMLRPC and REST,
|
||||
the directions for how to access the data via REST is noted in each method
|
||||
where applicable.
|
||||
|
||||
=head1 Group Creation and Modification
|
||||
|
||||
=head2 create
|
||||
|
@ -125,9 +129,16 @@ B<UNSTABLE>
|
|||
|
||||
This allows you to create a new group in Bugzilla.
|
||||
|
||||
=item B<Params>
|
||||
=item B<REST>
|
||||
|
||||
Some params must be set, or an error will be thrown. These params are
|
||||
POST /group
|
||||
|
||||
The params to include in the POST body as well as the returned data format,
|
||||
are the same as below.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
Some params must be set, or an error will be thrown. These params are
|
||||
marked B<Required>.
|
||||
|
||||
=over
|
||||
|
@ -148,7 +159,7 @@ name of the group.
|
|||
C<string> A regular expression. Any user whose Bugzilla username matches
|
||||
this regular expression will automatically be granted membership in this group.
|
||||
|
||||
=item C<is_active>
|
||||
=item C<is_active>
|
||||
|
||||
C<boolean> C<True> if new group can be used for bugs, C<False> if this
|
||||
is a group that will only contain users and no bugs will be restricted
|
||||
|
@ -162,7 +173,7 @@ if they are in this group.
|
|||
|
||||
=back
|
||||
|
||||
=item B<Returns>
|
||||
=item B<Returns>
|
||||
|
||||
A hash with one element, C<id>. This is the id of the newly-created group.
|
||||
|
||||
|
@ -188,7 +199,15 @@ You specified an invalid regular expression in the C<user_regexp> field.
|
|||
|
||||
=back
|
||||
|
||||
=back
|
||||
=item B<History>
|
||||
|
||||
=over
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
||||
=head2 update
|
||||
|
||||
|
@ -200,6 +219,14 @@ B<UNSTABLE>
|
|||
|
||||
This allows you to update a group in Bugzilla.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
PUT /group/<group_name_or_id>
|
||||
|
||||
The params to include in the PUT body as well as the returned data format,
|
||||
are the same as below. The C<ids> param will be overridden as it is pulled
|
||||
from the URL path.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
At least C<ids> or C<names> must be set, or an error will be thrown.
|
||||
|
@ -278,6 +305,14 @@ comma-and-space-separated list if multiple values were removed.
|
|||
|
||||
The same as L</create>.
|
||||
|
||||
=item B<History>
|
||||
|
||||
=over
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
|
|
@ -336,6 +336,10 @@ get information about them.
|
|||
See L<Bugzilla::WebService> for a description of how parameters are passed,
|
||||
and what B<STABLE>, B<UNSTABLE>, and B<EXPERIMENTAL> mean.
|
||||
|
||||
Although the data input and output is the same for JSONRPC, XMLRPC and REST,
|
||||
the directions for how to access the data via REST is noted in each method
|
||||
where applicable.
|
||||
|
||||
=head1 List Products
|
||||
|
||||
=head2 get_selectable_products
|
||||
|
@ -348,15 +352,29 @@ B<EXPERIMENTAL>
|
|||
|
||||
Returns a list of the ids of the products the user can search on.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
GET /product?type=selectable
|
||||
|
||||
the returned data format is same as below.
|
||||
|
||||
=item B<Params> (none)
|
||||
|
||||
=item B<Returns>
|
||||
=item B<Returns>
|
||||
|
||||
A hash containing one item, C<ids>, that contains an array of product
|
||||
ids.
|
||||
|
||||
=item B<Errors> (none)
|
||||
|
||||
=item B<History>
|
||||
|
||||
=over
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
||||
=head2 get_enterable_products
|
||||
|
@ -370,6 +388,12 @@ B<EXPERIMENTAL>
|
|||
Returns a list of the ids of the products the user can enter bugs
|
||||
against.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
GET /product?type=enterable
|
||||
|
||||
the returned data format is same as below.
|
||||
|
||||
=item B<Params> (none)
|
||||
|
||||
=item B<Returns>
|
||||
|
@ -379,6 +403,14 @@ ids.
|
|||
|
||||
=item B<Errors> (none)
|
||||
|
||||
=item B<History>
|
||||
|
||||
=over
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
||||
=head2 get_accessible_products
|
||||
|
@ -392,6 +424,12 @@ B<UNSTABLE>
|
|||
Returns a list of the ids of the products the user can search or enter
|
||||
bugs against.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
GET /product?type=accessible
|
||||
|
||||
the returned data format is same as below.
|
||||
|
||||
=item B<Params> (none)
|
||||
|
||||
=item B<Returns>
|
||||
|
@ -401,6 +439,14 @@ ids.
|
|||
|
||||
=item B<Errors> (none)
|
||||
|
||||
=item B<History>
|
||||
|
||||
=over
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
||||
=head2 get
|
||||
|
@ -417,6 +463,12 @@ B<Note>: You must at least specify one of C<ids> or C<names>.
|
|||
|
||||
B<Note>: Can also be called as "get_products" for compatibilty with Bugzilla 3.0 API.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
GET /product/<product_id_or_name>
|
||||
|
||||
the returned data format is same as below.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
In addition to the parameters below, this method also accepts the
|
||||
|
@ -612,6 +664,8 @@ been removed.
|
|||
=item In Bugzilla B<4.4>, C<flag_types> was added to the fields returned
|
||||
by C<get>.
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
@ -628,9 +682,16 @@ B<EXPERIMENTAL>
|
|||
|
||||
This allows you to create a new product in Bugzilla.
|
||||
|
||||
=item B<Params>
|
||||
=item B<REST>
|
||||
|
||||
Some params must be set, or an error will be thrown. These params are
|
||||
POST /product
|
||||
|
||||
The params to include in the POST body as well as the returned data format,
|
||||
are the same as below.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
Some params must be set, or an error will be thrown. These params are
|
||||
marked B<Required>.
|
||||
|
||||
=over
|
||||
|
@ -709,6 +770,14 @@ You must specify a version for this product.
|
|||
|
||||
=back
|
||||
|
||||
=item B<History>
|
||||
|
||||
=over
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
||||
=head2 update
|
||||
|
@ -721,6 +790,14 @@ B<EXPERIMENTAL>
|
|||
|
||||
This allows you to update a product in Bugzilla.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
PUT /product/<product_id_or_name>
|
||||
|
||||
The params to include in the PUT body as well as the returned data format,
|
||||
are the same as below. The C<ids> and C<names> params will be overridden as
|
||||
it is pulled from the URL path.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
B<Note:> The following parameters specify which products you are updating.
|
||||
|
@ -859,6 +936,8 @@ You must define a default milestone.
|
|||
|
||||
=item Added in Bugzilla B<4.4>.
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
|
|
@ -0,0 +1,617 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
# defined by the Mozilla Public License, v. 2.0.
|
||||
|
||||
package Bugzilla::WebService::Server::REST;
|
||||
|
||||
use 5.10.1;
|
||||
use strict;
|
||||
|
||||
use parent qw(Bugzilla::WebService::Server::JSONRPC);
|
||||
|
||||
use Bugzilla;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::WebService::Constants;
|
||||
use Bugzilla::WebService::Util qw(taint_data);
|
||||
use Bugzilla::Util qw(correct_urlbase html_quote);
|
||||
|
||||
# Load resource modules
|
||||
use Bugzilla::WebService::Server::REST::Resources::Bug;
|
||||
use Bugzilla::WebService::Server::REST::Resources::Bugzilla;
|
||||
use Bugzilla::WebService::Server::REST::Resources::Classification;
|
||||
use Bugzilla::WebService::Server::REST::Resources::Group;
|
||||
use Bugzilla::WebService::Server::REST::Resources::Product;
|
||||
use Bugzilla::WebService::Server::REST::Resources::User;
|
||||
|
||||
use Scalar::Util qw(blessed reftype);
|
||||
use MIME::Base64 qw(decode_base64);
|
||||
|
||||
###########################
|
||||
# Public Method Overrides #
|
||||
###########################
|
||||
|
||||
sub handle {
|
||||
my ($self) = @_;
|
||||
|
||||
# Determine how the data should be represented. We do this early so
|
||||
# errors will also be returned with the proper content type.
|
||||
$self->content_type($self->_best_content_type(REST_CONTENT_TYPE_WHITELIST()));
|
||||
|
||||
# Using current path information, decide which class/method to
|
||||
# use to serve the request. Throw error if no resource was found
|
||||
# unless we were looking for OPTIONS
|
||||
if (!$self->_find_resource($self->cgi->path_info)) {
|
||||
if ($self->request->method eq 'OPTIONS'
|
||||
&& $self->bz_rest_options)
|
||||
{
|
||||
my $response = $self->response_header(STATUS_OK, "");
|
||||
my $options_string = join(', ', @{ $self->bz_rest_options });
|
||||
$response->header('Allow' => $options_string,
|
||||
'Access-Control-Allow-Methods' => $options_string);
|
||||
return $self->response($response);
|
||||
}
|
||||
|
||||
ThrowUserError("rest_invalid_resource",
|
||||
{ path => $self->cgi->path_info,
|
||||
method => $self->request->method });
|
||||
}
|
||||
|
||||
# Dispatch to the proper module
|
||||
my $class = $self->bz_class_name;
|
||||
my ($path) = $class =~ /::([^:]+)$/;
|
||||
$self->path_info($path);
|
||||
delete $self->{dispatch_path};
|
||||
$self->dispatch({ $path => $class });
|
||||
|
||||
my $params = $self->_retrieve_json_params;
|
||||
|
||||
$self->_fix_credentials($params);
|
||||
|
||||
# Fix includes/excludes for each call
|
||||
rest_include_exclude($params);
|
||||
|
||||
# Set callback name if exists
|
||||
$self->_bz_callback($params->{'callback'}) if $params->{'callback'};
|
||||
|
||||
Bugzilla->input_params($params);
|
||||
|
||||
# Set the JSON version to 1.1 and the id to the current urlbase
|
||||
# also set up the correct handler method
|
||||
my $obj = {
|
||||
version => '1.1',
|
||||
id => correct_urlbase(),
|
||||
method => $self->bz_method_name,
|
||||
params => $params
|
||||
};
|
||||
|
||||
# Execute the handler
|
||||
my $result = $self->_handle($obj);
|
||||
|
||||
if (!$self->error_response_header) {
|
||||
return $self->response(
|
||||
$self->response_header($self->bz_success_code || STATUS_OK, $result));
|
||||
}
|
||||
|
||||
$self->response($self->error_response_header);
|
||||
}
|
||||
|
||||
sub response {
|
||||
my ($self, $response) = @_;
|
||||
|
||||
# If we have thrown an error, the 'error' key will exist
|
||||
# otherwise we use 'result'. JSONRPC returns other data
|
||||
# along with the result/error such as version and id which
|
||||
# we will strip off for REST calls.
|
||||
my $content = $response->content;
|
||||
my $json_data = {};
|
||||
if ($content) {
|
||||
$json_data = $self->json->decode($content);
|
||||
}
|
||||
|
||||
my $result = {};
|
||||
if (exists $json_data->{error}) {
|
||||
$result = $json_data->{error};
|
||||
$result->{error} = $self->type('boolean', 1);
|
||||
delete $result->{'name'}; # Remove JSONRPCError
|
||||
}
|
||||
elsif (exists $json_data->{result}) {
|
||||
$result = $json_data->{result};
|
||||
}
|
||||
|
||||
# Access Control
|
||||
$response->header("Access-Control-Allow-Origin", "*");
|
||||
|
||||
# If accessing through web browser, then display in readable format
|
||||
if ($self->content_type eq 'text/html') {
|
||||
$result = $self->json->pretty->canonical->encode($result);
|
||||
|
||||
my $template = Bugzilla->template;
|
||||
$content = "";
|
||||
$template->process("rest.html.tmpl", { result => $result }, \$content)
|
||||
|| ThrowTemplateError($template->error());
|
||||
|
||||
$response->content_type('text/html');
|
||||
}
|
||||
else {
|
||||
$content = $self->json->encode($result);
|
||||
}
|
||||
|
||||
$response->content($content);
|
||||
|
||||
$self->SUPER::response($response);
|
||||
}
|
||||
|
||||
#######################################
|
||||
# Bugzilla::WebService Implementation #
|
||||
#######################################
|
||||
|
||||
sub handle_login {
|
||||
my $self = shift;
|
||||
|
||||
# If we're being called using GET, we don't allow cookie-based or Env
|
||||
# login, because GET requests can be done cross-domain, and we don't
|
||||
# want private data showing up on another site unless the user
|
||||
# explicitly gives that site their username and password. (This is
|
||||
# particularly important for JSONP, which would allow a remote site
|
||||
# to use private data without the user's knowledge, unless we had this
|
||||
# protection in place.)
|
||||
if (!grep($_ eq $self->request->method, ('POST', 'PUT'))) {
|
||||
# XXX There's no particularly good way for us to get a parameter
|
||||
# to Bugzilla->login at this point, so we pass this information
|
||||
# around using request_cache, which is a bit of a hack. The
|
||||
# implementation of it is in Bugzilla::Auth::Login::Stack.
|
||||
Bugzilla->request_cache->{'auth_no_automatic_login'} = 1;
|
||||
}
|
||||
|
||||
my $class = $self->bz_class_name;
|
||||
my $method = $self->bz_method_name;
|
||||
my $full_method = $class . "." . $method;
|
||||
|
||||
# Bypass JSONRPC::handle_login
|
||||
Bugzilla::WebService::Server->handle_login($class, $method, $full_method);
|
||||
}
|
||||
|
||||
############################
|
||||
# Private Method Overrides #
|
||||
############################
|
||||
|
||||
# We do not want to run Bugzilla::WebService::Server::JSONRPC->_find_prodedure
|
||||
# as it determines the method name differently.
|
||||
sub _find_procedure {
|
||||
my $self = shift;
|
||||
if ($self->isa('JSON::RPC::Server::CGI')) {
|
||||
return JSON::RPC::Server::_find_procedure($self, @_);
|
||||
}
|
||||
else {
|
||||
return JSON::RPC::Legacy::Server::_find_procedure($self, @_);
|
||||
}
|
||||
}
|
||||
|
||||
sub _argument_type_check {
|
||||
my $self = shift;
|
||||
my $params;
|
||||
|
||||
if ($self->isa('JSON::RPC::Server::CGI')) {
|
||||
$params = JSON::RPC::Server::_argument_type_check($self, @_);
|
||||
}
|
||||
else {
|
||||
$params = JSON::RPC::Legacy::Server::_argument_type_check($self, @_);
|
||||
}
|
||||
|
||||
# JSON-RPC 1.0 requires all parameters to be passed as an array, so
|
||||
# we just pull out the first item and assume it's an object.
|
||||
my $params_is_array;
|
||||
if (ref $params eq 'ARRAY') {
|
||||
$params = $params->[0];
|
||||
$params_is_array = 1;
|
||||
}
|
||||
|
||||
taint_data($params);
|
||||
|
||||
Bugzilla->input_params($params);
|
||||
|
||||
# Now, convert dateTime fields on input.
|
||||
my $method = $self->bz_method_name;
|
||||
my $pkg = $self->{dispatch_path}->{$self->path_info};
|
||||
my @date_fields = @{ $pkg->DATE_FIELDS->{$method} || [] };
|
||||
foreach my $field (@date_fields) {
|
||||
if (defined $params->{$field}) {
|
||||
my $value = $params->{$field};
|
||||
if (ref $value eq 'ARRAY') {
|
||||
$params->{$field} =
|
||||
[ map { $self->datetime_format_inbound($_) } @$value ];
|
||||
}
|
||||
else {
|
||||
$params->{$field} = $self->datetime_format_inbound($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
my @base64_fields = @{ $pkg->BASE64_FIELDS->{$method} || [] };
|
||||
foreach my $field (@base64_fields) {
|
||||
if (defined $params->{$field}) {
|
||||
$params->{$field} = decode_base64($params->{$field});
|
||||
}
|
||||
}
|
||||
|
||||
# This is the best time to do login checks.
|
||||
$self->handle_login();
|
||||
|
||||
# Bugzilla::WebService packages call internal methods like
|
||||
# $self->_some_private_method. So we have to inherit from
|
||||
# that class as well as this Server class.
|
||||
my $new_class = ref($self) . '::' . $pkg;
|
||||
my $isa_string = 'our @ISA = qw(' . ref($self) . " $pkg)";
|
||||
eval "package $new_class;$isa_string;";
|
||||
bless $self, $new_class;
|
||||
|
||||
if ($params_is_array) {
|
||||
$params = [$params];
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
###################
|
||||
# Utility Methods #
|
||||
###################
|
||||
|
||||
sub bz_method_name {
|
||||
my ($self, $method) = @_;
|
||||
$self->{_bz_method_name} = $method if $method;
|
||||
return $self->{_bz_method_name};
|
||||
}
|
||||
|
||||
sub bz_class_name {
|
||||
my ($self, $class) = @_;
|
||||
$self->{_bz_class_name} = $class if $class;
|
||||
return $self->{_bz_class_name};
|
||||
}
|
||||
|
||||
sub bz_success_code {
|
||||
my ($self, $value) = @_;
|
||||
$self->{_bz_success_code} = $value if $value;
|
||||
return $self->{_bz_success_code};
|
||||
}
|
||||
|
||||
sub bz_rest_params {
|
||||
my ($self, $params) = @_;
|
||||
$self->{_bz_rest_params} = $params if $params;
|
||||
return $self->{_bz_rest_params};
|
||||
}
|
||||
|
||||
sub bz_rest_options {
|
||||
my ($self, $options) = @_;
|
||||
$self->{_bz_rest_options} = $options if $options;
|
||||
return $self->{_bz_rest_options};
|
||||
}
|
||||
|
||||
sub rest_include_exclude {
|
||||
my ($params) = @_;
|
||||
|
||||
# _all is same as default columns
|
||||
if ($params->{'include_fields'}
|
||||
&& ($params->{'include_fields'} eq '_all'
|
||||
|| $params->{'include_fields'} eq '_default'))
|
||||
{
|
||||
delete $params->{'include_fields'};
|
||||
delete $params->{'exclude_fields'} if $params->{'exclude_fields'};
|
||||
}
|
||||
|
||||
if ($params->{'include_fields'} && !ref $params->{'include_fields'}) {
|
||||
$params->{'include_fields'} = [ split(/[\s+,]/, $params->{'include_fields'}) ];
|
||||
}
|
||||
if ($params->{'exclude_fields'} && !ref $params->{'exclude_fields'}) {
|
||||
$params->{'exclude_fields'} = [ split(/[\s+,]/, $params->{'exclude_fields'}) ];
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
##########################
|
||||
# Private Custom Methods #
|
||||
##########################
|
||||
|
||||
sub _retrieve_json_params {
|
||||
my $self = shift;
|
||||
|
||||
# Make a copy of the current input_params rather than edit directly
|
||||
my $params = {};
|
||||
%{$params} = %{ Bugzilla->input_params };
|
||||
|
||||
# First add any params we were able to pull out of the path
|
||||
# based on the resource regexp
|
||||
%{$params} = (%{$params}, %{$self->bz_rest_params}) if $self->bz_rest_params;
|
||||
|
||||
# Merge any additional query key/values with $obj->{params} if not a GET request
|
||||
# We do this manually cause CGI.pm doesn't understand JSON strings.
|
||||
if ($self->request->method ne 'GET') {
|
||||
my $extra_params = {};
|
||||
my $json = delete $params->{'POSTDATA'} || delete $params->{'PUTDATA'};
|
||||
if ($json) {
|
||||
eval { $extra_params = $self->json->decode($json); };
|
||||
if ($@) {
|
||||
ThrowUserError('json_rpc_invalid_params', { err_msg => $@ });
|
||||
}
|
||||
}
|
||||
%{$params} = (%{$params}, %{$extra_params}) if %{$extra_params};
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
sub _find_resource {
|
||||
my ($self, $path) = @_;
|
||||
|
||||
# Load in the WebService module from the dispatch map and then call
|
||||
# $module->rest_resources to get the resources array ref.
|
||||
my $resources = {};
|
||||
foreach my $module (values %{ $self->{dispatch_path} }) {
|
||||
eval("require $module") || die $@;
|
||||
next if !$module->can('rest_resources');
|
||||
$resources->{$module} = $module->rest_resources;
|
||||
}
|
||||
|
||||
# Use the resources hash from each module loaded earlier to determine
|
||||
# which handler to use based on a regex match of the CGI path.
|
||||
# Also any matches found in the regex will be passed in later to the
|
||||
# handler for possible use.
|
||||
my $request_method = $self->request->method;
|
||||
|
||||
my (@matches, $handler_found, $handler_method, $handler_class);
|
||||
foreach my $class (keys %{ $resources }) {
|
||||
# The resource data for each module needs to be
|
||||
# an array ref with an even number of elements
|
||||
# to work correctly.
|
||||
next if (ref $resources->{$class} ne 'ARRAY'
|
||||
|| scalar @{ $resources->{$class} } % 2 != 0);
|
||||
|
||||
while (my $regex = shift @{ $resources->{$class} }) {
|
||||
my $options_data = shift @{ $resources->{$class} };
|
||||
next if ref $options_data ne 'HASH';
|
||||
|
||||
if (@matches = ($path =~ $regex)) {
|
||||
# If a specific path is accompanied by a OPTIONS request
|
||||
# method, the user is asking for a list of possible request
|
||||
# methods for a specific path.
|
||||
$self->bz_rest_options([ keys %{ $options_data } ]);
|
||||
|
||||
if ($options_data->{$request_method}) {
|
||||
my $resource_data = $options_data->{$request_method};
|
||||
$self->bz_class_name($class);
|
||||
|
||||
# The method key/value can be a simple scalar method name
|
||||
# or a anonymous subroutine so we execute it here.
|
||||
my $method = ref $resource_data->{method} eq 'CODE'
|
||||
? $resource_data->{method}->($self)
|
||||
: $resource_data->{method};
|
||||
$self->bz_method_name($method);
|
||||
|
||||
# Pull out any parameters parsed from the URL path
|
||||
# and store them for use by the method.
|
||||
if ($resource_data->{params}) {
|
||||
$self->bz_rest_params($resource_data->{params}->(@matches));
|
||||
}
|
||||
|
||||
# If a special success code is needed for this particular
|
||||
# method, then store it for later when generating response.
|
||||
if ($resource_data->{success_code}) {
|
||||
$self->bz_success_code($resource_data->{success_code});
|
||||
}
|
||||
$handler_found = 1;
|
||||
}
|
||||
}
|
||||
last if $handler_found;
|
||||
}
|
||||
last if $handler_found;
|
||||
}
|
||||
|
||||
return $handler_found;
|
||||
}
|
||||
|
||||
sub _fix_credentials {
|
||||
my ($self, $params) = @_;
|
||||
# Allow user to pass in &username=foo&password=bar
|
||||
if (exists $params->{'username'} && exists $params->{'password'}) {
|
||||
$params->{'Bugzilla_login'} = delete $params->{'username'};
|
||||
$params->{'Bugzilla_password'} = delete $params->{'password'};
|
||||
}
|
||||
}
|
||||
|
||||
sub _best_content_type {
|
||||
my ($self, @types) = @_;
|
||||
return ($self->_simple_content_negotiation(@types))[0] || '*/*';
|
||||
}
|
||||
|
||||
sub _simple_content_negotiation {
|
||||
my ($self, @types) = @_;
|
||||
my @accept_types = $self->_get_content_prefs();
|
||||
my $score = sub { $self->_score_type(shift, @accept_types) };
|
||||
return sort {$score->($b) <=> $score->($a)} @types;
|
||||
}
|
||||
|
||||
sub _score_type {
|
||||
my ($self, $type, @accept_types) = @_;
|
||||
my $score = scalar(@accept_types);
|
||||
for my $accept_type (@accept_types) {
|
||||
return $score if $type eq $accept_type;
|
||||
$score--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub _get_content_prefs {
|
||||
my $self = shift;
|
||||
my $default_weight = 1;
|
||||
my @prefs;
|
||||
|
||||
# Parse the Accept header, and save type name, score, and position.
|
||||
my @accept_types = split /,/, $self->cgi->http('accept') || '';
|
||||
my $order = 0;
|
||||
for my $accept_type (@accept_types) {
|
||||
my ($weight) = ($accept_type =~ /q=(\d\.\d+|\d+)/);
|
||||
my ($name) = ($accept_type =~ m#(\S+/[^;]+)#);
|
||||
next unless $name;
|
||||
push @prefs, { name => $name, order => $order++};
|
||||
if (defined $weight) {
|
||||
$prefs[-1]->{score} = $weight;
|
||||
} else {
|
||||
$prefs[-1]->{score} = $default_weight;
|
||||
$default_weight -= 0.001;
|
||||
}
|
||||
}
|
||||
|
||||
# Sort the types by score, subscore by order, and pull out just the name
|
||||
@prefs = map {$_->{name}} sort {$b->{score} <=> $a->{score} ||
|
||||
$a->{order} <=> $b->{order}} @prefs;
|
||||
return @prefs, '*/*'; # Allows allow for */*
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::WebService::Server::REST - The REST Interface to Bugzilla
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This documentation describes things about the Bugzilla WebService that
|
||||
are specific to REST. For a general overview of the Bugzilla WebServices,
|
||||
see L<Bugzilla::WebService>. The L<Bugzilla::WebService::Server::REST>
|
||||
module is a sub-class of L<Bugzilla::WebService::Server::JSONRPC> so any
|
||||
method documentation not found here can be viewed in it's POD.
|
||||
|
||||
Please note that I<everything> about this REST interface is
|
||||
B<EXPERIMENTAL>. If you want a fully stable API, please use the
|
||||
C<Bugzilla::WebService::Server::XMLRPC|XML-RPC> interface.
|
||||
|
||||
=head1 CONNECTING
|
||||
|
||||
The endpoint for the REST interface is the C<rest.cgi> script in
|
||||
your Bugzilla installation. If using Apache and mod_rewrite is installed
|
||||
and enabled, you can also use /rest/ as your endpoint. For example, if your
|
||||
Bugzilla is at C<bugzilla.yourdomain.com>, then your REST client would
|
||||
access the API via: C<http://bugzilla.yourdomain.com/rest/bug/35> which
|
||||
looks cleaner.
|
||||
|
||||
=head1 BROWSING
|
||||
|
||||
If the Accept: header of a request is set to text/html (as it is by an
|
||||
ordinary web browser) then the API will return the JSON data as a HTML
|
||||
page which the browser can display. In other words, you can play with the
|
||||
API using just your browser and see results in a human-readable form.
|
||||
This is a good way to try out the various GET calls, even if you can't use
|
||||
it for POST or PUT.
|
||||
|
||||
=head1 DATA FORMAT
|
||||
|
||||
The REST API only supports JSON input, and either JSON and JSONP output.
|
||||
So objects sent and received must be in JSON format. Basically since
|
||||
the REST API is a sub class of the JSONRPC API, you can refer to
|
||||
L<JSONRPC|Bugzilla::WebService::Server::JSONRPC> for more information
|
||||
on data types that are valid for REST.
|
||||
|
||||
On every request, you must set both the "Accept" and "Content-Type" HTTP
|
||||
headers to the MIME type of the data format you are using to communicate with
|
||||
the API. Content-Type tells the API how to interpret your request, and Accept
|
||||
tells it how you want your data back. "Content-Type" must be "application/json".
|
||||
"Accept" can be either that, or "application/javascript" for JSONP - add a "callback"
|
||||
parameter to name your callback.
|
||||
|
||||
=head1 AUTHENTICATION
|
||||
|
||||
Along with viewing data as an anonymous user, you may also see private information
|
||||
if you have a Bugzilla account by providing your login credentials.
|
||||
|
||||
=over
|
||||
|
||||
=item Username and password
|
||||
|
||||
Pass in as query parameters of any request:
|
||||
|
||||
username=fred@bedrock.com&password=ilovewilma
|
||||
|
||||
Remember to URL encode any special characters, which are often seen in passwords and to
|
||||
also enable SSL support.
|
||||
|
||||
=back
|
||||
|
||||
=head1 ERRORS
|
||||
|
||||
When an error occurs over REST, a hash structure is returned with the key C<error>
|
||||
set to C<true>.
|
||||
|
||||
The error contents look similar to:
|
||||
|
||||
{ "error": true, "message": "Some message here", "code": 123 }
|
||||
|
||||
Every error has a "code", as described in L<Bugzilla::WebService/ERRORS>.
|
||||
Errors with a numeric C<code> higher than 100000 are errors thrown by
|
||||
the JSON-RPC library that Bugzilla uses, not by Bugzilla.
|
||||
|
||||
=head1 UTILITY FUNCTIONS
|
||||
|
||||
=over
|
||||
|
||||
=item B<handle>
|
||||
|
||||
This method overrides the handle method provided by JSONRPC so that certain
|
||||
actions related to REST such as determining the proper resource to use,
|
||||
loading query parameters, etc. can be done before the proper WebService
|
||||
method is executed.
|
||||
|
||||
=item B<response>
|
||||
|
||||
This method overrides the response method provided by JSONRPC so that
|
||||
the response content can be altered for REST before being returned to
|
||||
the client.
|
||||
|
||||
=item B<handle_login>
|
||||
|
||||
This method determines the proper WebService all to make based on class
|
||||
and method name determined earlier. Then calls L<Bugzilla::WebService::Server::handle_login>
|
||||
which will attempt to authenticate the client.
|
||||
|
||||
=item B<bz_method_name>
|
||||
|
||||
The WebService method name that matches the path used by the client.
|
||||
|
||||
=item B<bz_class_name>
|
||||
|
||||
The WebService class containing the method that matches the path used by the client.
|
||||
|
||||
=item B<bz_rest_params>
|
||||
|
||||
Each REST resource contains a hash key called C<params> that is a subroutine reference.
|
||||
This subroutine will return a hash structure based on matched values from the path
|
||||
information that is formatted properly for the WebService method that will be called.
|
||||
|
||||
=item B<bz_rest_options>
|
||||
|
||||
When a client uses the OPTIONS request method along with a specific path, they are
|
||||
requesting the list of request methods that are valid for the path. Such as for the
|
||||
path /bug, the valid request methods are GET (search) and POST (create). So the
|
||||
client would receive in the response header, C<Access-Control-Allow-Methods: GET, POST>.
|
||||
|
||||
=item B<bz_success_code>
|
||||
|
||||
Each resource can specify a specific SUCCESS CODE if the operation completes successfully.
|
||||
OTherwise STATUS OK (200) is the default returned.
|
||||
|
||||
=item B<rest_include_exclude>
|
||||
|
||||
Normally the WebService methods required C<include_fields> and C<exclude_fields> to be an
|
||||
array of field names. REST allows for the values for these to be instead comma delimited
|
||||
string of field names. This method converts the latter into the former so the WebService
|
||||
methods will not complain.
|
||||
|
||||
=back
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Bugzilla::WebService>
|
|
@ -0,0 +1,152 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
# defined by the Mozilla Public License, v. 2.0.
|
||||
|
||||
package Bugzilla::WebService::Server::REST::Resources::Bug;
|
||||
|
||||
use 5.10.1;
|
||||
use strict;
|
||||
|
||||
use Bugzilla::WebService::Constants;
|
||||
use Bugzilla::WebService::Bug;
|
||||
|
||||
BEGIN {
|
||||
*Bugzilla::WebService::Bug::rest_resources = \&_rest_resources;
|
||||
};
|
||||
|
||||
sub _rest_resources {
|
||||
my $rest_resources = [
|
||||
qr{^/bug$}, {
|
||||
GET => {
|
||||
method => 'search',
|
||||
},
|
||||
POST => {
|
||||
method => 'create',
|
||||
status_code => STATUS_CREATED
|
||||
}
|
||||
},
|
||||
qr{^/bug/([^/]+)$}, {
|
||||
GET => {
|
||||
method => 'get',
|
||||
params => sub {
|
||||
return { ids => [ $_[0] ] };
|
||||
}
|
||||
},
|
||||
PUT => {
|
||||
method => 'update',
|
||||
params => sub {
|
||||
return { ids => [ $_[0] ] };
|
||||
}
|
||||
}
|
||||
},
|
||||
qr{^/bug/([^/]+)/comment$}, {
|
||||
GET => {
|
||||
method => 'comments',
|
||||
params => sub {
|
||||
return { ids => [ $_[0] ] };
|
||||
}
|
||||
},
|
||||
POST => {
|
||||
method => 'add_comment',
|
||||
params => sub {
|
||||
return { id => $_[0] };
|
||||
},
|
||||
success_code => STATUS_CREATED
|
||||
}
|
||||
},
|
||||
qr{^/bug/comment/([^/]+)$}, {
|
||||
GET => {
|
||||
method => 'comments',
|
||||
params => sub {
|
||||
return { comment_ids => [ $_[0] ] };
|
||||
}
|
||||
}
|
||||
},
|
||||
qr{^/bug/([^/]+)/history$}, {
|
||||
GET => {
|
||||
method => 'history',
|
||||
params => sub {
|
||||
return { ids => [ $_[0] ] };
|
||||
},
|
||||
}
|
||||
},
|
||||
qr{^/bug/([^/]+)/attachment$}, {
|
||||
GET => {
|
||||
method => 'attachments',
|
||||
params => sub {
|
||||
return { ids => [ $_[0] ] };
|
||||
}
|
||||
},
|
||||
POST => {
|
||||
method => 'add_attachment',
|
||||
params => sub {
|
||||
return { ids => [ $_[0] ] };
|
||||
},
|
||||
success_code => STATUS_CREATED
|
||||
}
|
||||
},
|
||||
qr{^/bug/attachment/([^/]+)$}, {
|
||||
GET => {
|
||||
method => 'attachments',
|
||||
params => sub {
|
||||
return { attachment_ids => [ $_[0] ] };
|
||||
}
|
||||
}
|
||||
},
|
||||
qr{^/field/bug$}, {
|
||||
GET => {
|
||||
method => 'fields',
|
||||
}
|
||||
},
|
||||
qr{^/field/bug/([^/]+)$}, {
|
||||
GET => {
|
||||
method => 'fields',
|
||||
params => sub {
|
||||
my $value = $_[0];
|
||||
my $param = 'names';
|
||||
$param = 'ids' if $value =~ /^\d+$/;
|
||||
return { $param => [ $_[0] ] };
|
||||
}
|
||||
}
|
||||
},
|
||||
qr{^/field/bug/([^/]+)/values$}, {
|
||||
GET => {
|
||||
method => 'legal_values',
|
||||
params => sub {
|
||||
return { field => $_[0] };
|
||||
}
|
||||
}
|
||||
},
|
||||
qr{^/field/bug/([^/]+)/([^/]+)/values$}, {
|
||||
GET => {
|
||||
method => 'legal_values',
|
||||
params => sub {
|
||||
return { field => $_[0],
|
||||
product_id => $_[1] };
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
];
|
||||
return $rest_resources;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Webservice::Server::REST::Resources::Bug - The REST API for creating,
|
||||
changing, and getting the details of bugs.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This part of the Bugzilla REST API allows you to file a new bug in Bugzilla,
|
||||
or get information about bugs that have already been filed.
|
||||
|
||||
See L<Bugzilla::WebService::Bug> for more details on how to use this part of
|
||||
the REST API.
|
|
@ -0,0 +1,69 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
# defined by the Mozilla Public License, v. 2.0.
|
||||
|
||||
package Bugzilla::WebService::Server::REST::Resources::Bugzilla;
|
||||
|
||||
use 5.10.1;
|
||||
use strict;
|
||||
|
||||
use Bugzilla::WebService::Constants;
|
||||
use Bugzilla::WebService::Bugzilla;
|
||||
|
||||
BEGIN {
|
||||
*Bugzilla::WebService::Bugzilla::rest_resources = \&_rest_resources;
|
||||
};
|
||||
|
||||
sub _rest_resources {
|
||||
my $rest_resources = [
|
||||
qr{^/version$}, {
|
||||
GET => {
|
||||
method => 'version'
|
||||
}
|
||||
},
|
||||
qr{^/extensions$}, {
|
||||
GET => {
|
||||
method => 'extensions'
|
||||
}
|
||||
},
|
||||
qr{^/timezone$}, {
|
||||
GET => {
|
||||
method => 'timezone'
|
||||
}
|
||||
},
|
||||
qr{^/time$}, {
|
||||
GET => {
|
||||
method => 'time'
|
||||
}
|
||||
},
|
||||
qr{^/last_audit_time$}, {
|
||||
GET => {
|
||||
method => 'last_audit_time'
|
||||
}
|
||||
},
|
||||
qr{^/parameters$}, {
|
||||
GET => {
|
||||
method => 'parameters'
|
||||
}
|
||||
}
|
||||
];
|
||||
return $rest_resources;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::WebService::Bugzilla - Global functions for the webservice interface.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This provides functions that tell you about Bugzilla in general.
|
||||
|
||||
See L<Bugzilla::WebService::Bugzilla> for more details on how to use this part
|
||||
of the REST API.
|
|
@ -0,0 +1,49 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
# defined by the Mozilla Public License, v. 2.0.
|
||||
|
||||
package Bugzilla::WebService::Server::REST::Resources::Classification;
|
||||
|
||||
use 5.10.1;
|
||||
use strict;
|
||||
|
||||
use Bugzilla::WebService::Constants;
|
||||
use Bugzilla::WebService::Classification;
|
||||
|
||||
BEGIN {
|
||||
*Bugzilla::WebService::Classification::rest_resources = \&_rest_resources;
|
||||
};
|
||||
|
||||
sub _rest_resources {
|
||||
my $rest_resources = [
|
||||
qr{^/classification/([^/]+)$}, {
|
||||
GET => {
|
||||
method => 'get',
|
||||
params => sub {
|
||||
my $param = $_[0] =~ /^\d+$/ ? 'ids' : 'names';
|
||||
return { $param => [ $_[0] ] };
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
return $rest_resources;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Webservice::Server::REST::Resources::Classification - The Classification REST API
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This part of the Bugzilla REST API allows you to deal with the available Classifications.
|
||||
You will be able to get information about them as well as manipulate them.
|
||||
|
||||
See L<Bugzilla::WebService::Bug> for more details on how to use this part
|
||||
of the REST API.
|
|
@ -0,0 +1,56 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
# defined by the Mozilla Public License, v. 2.0.
|
||||
|
||||
package Bugzilla::WebService::Server::REST::Resources::Group;
|
||||
|
||||
use 5.10.1;
|
||||
use strict;
|
||||
|
||||
use Bugzilla::WebService::Constants;
|
||||
use Bugzilla::WebService::Group;
|
||||
|
||||
BEGIN {
|
||||
*Bugzilla::WebService::Group::rest_resources = \&_rest_resources;
|
||||
};
|
||||
|
||||
sub _rest_resources {
|
||||
my $rest_resources = [
|
||||
qr{^/group$}, {
|
||||
POST => {
|
||||
method => 'create',
|
||||
success_code => STATUS_CREATED
|
||||
}
|
||||
},
|
||||
qr{^/group/([^/]+)$}, {
|
||||
PUT => {
|
||||
method => 'update',
|
||||
params => sub {
|
||||
my $param = $_[0] =~ /^\d+$/ ? 'ids' : 'names';
|
||||
return { $param => [ $_[0] ] };
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
return $rest_resources;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Webservice::Server::REST::Resources::Group - The REST API for
|
||||
creating, changing, and getting information about Groups.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This part of the Bugzilla REST API allows you to create Groups and
|
||||
get information about them.
|
||||
|
||||
See L<Bugzilla::WebService::Group> for more details on how to use this part
|
||||
of the REST API.
|
|
@ -0,0 +1,75 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
# defined by the Mozilla Public License, v. 2.0.
|
||||
|
||||
package Bugzilla::WebService::Server::REST::Resources::Product;
|
||||
|
||||
use 5.10.1;
|
||||
use strict;
|
||||
|
||||
use Bugzilla::WebService::Constants;
|
||||
use Bugzilla::WebService::Product;
|
||||
|
||||
use Bugzilla::Error;
|
||||
|
||||
BEGIN {
|
||||
*Bugzilla::WebService::Product::rest_resources = \&_rest_resources;
|
||||
};
|
||||
|
||||
sub _rest_resources {
|
||||
my $rest_resources = [
|
||||
qr{^/product$}, {
|
||||
GET => {
|
||||
method => sub {
|
||||
my $type = Bugzilla->input_params->{type};
|
||||
return 'get_accessible_products'
|
||||
if !defined $type || $type eq 'accessible';
|
||||
return 'get_enterable_products' if $type eq 'enterable';
|
||||
return 'get_selectable_products' if $type eq 'selectable';
|
||||
ThrowUserError('rest_get_products_invalid_type',
|
||||
{ type => $type });
|
||||
},
|
||||
},
|
||||
POST => {
|
||||
method => 'create',
|
||||
success_code => STATUS_CREATED
|
||||
}
|
||||
},
|
||||
qr{^/product/([^/]+)$}, {
|
||||
GET => {
|
||||
method => 'get',
|
||||
params => sub {
|
||||
my $param = $_[0] =~ /^\d+$/ ? 'ids' : 'names';
|
||||
return { $param => [ $_[0] ] };
|
||||
}
|
||||
},
|
||||
PUT => {
|
||||
method => 'update',
|
||||
params => sub {
|
||||
my $param = $_[0] =~ /^\d+$/ ? 'ids' : 'names';
|
||||
return { $param => [ $_[0] ] };
|
||||
}
|
||||
}
|
||||
},
|
||||
];
|
||||
return $rest_resources;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Webservice::Server::REST::Resources::Product - The Product REST API
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This part of the Bugzilla REST API allows you to list the available Products and
|
||||
get information about them.
|
||||
|
||||
See L<Bugzilla::WebService::Bug> for more details on how to use this part of
|
||||
the REST API.
|
|
@ -0,0 +1,65 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
# defined by the Mozilla Public License, v. 2.0.
|
||||
|
||||
package Bugzilla::WebService::Server::REST::Resources::User;
|
||||
|
||||
use 5.10.1;
|
||||
use strict;
|
||||
|
||||
use Bugzilla::WebService::Constants;
|
||||
use Bugzilla::WebService::User;
|
||||
|
||||
BEGIN {
|
||||
*Bugzilla::WebService::User::rest_resources = \&_rest_resources;
|
||||
};
|
||||
|
||||
sub _rest_resources {
|
||||
my $rest_resources = [
|
||||
qr{^/user$}, {
|
||||
GET => {
|
||||
method => 'get'
|
||||
},
|
||||
POST => {
|
||||
method => 'create',
|
||||
success_code => STATUS_CREATED
|
||||
}
|
||||
},
|
||||
qr{^/user/([^/]+)$}, {
|
||||
GET => {
|
||||
method => 'get',
|
||||
params => sub {
|
||||
my $param = $_[0] =~ /^\d+$/ ? 'ids' : 'names';
|
||||
return { $param => [ $_[0] ] };
|
||||
}
|
||||
},
|
||||
PUT => {
|
||||
method => 'update',
|
||||
params => sub {
|
||||
my $param = $_[0] =~ /^\d+$/ ? 'ids' : 'names';
|
||||
return { $param => [ $_[0] ] };
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
return $rest_resources;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::Webservice::Server::REST::Resources::User - The User Account REST API
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This part of the Bugzilla REST API allows you to get User information as well
|
||||
as create User Accounts.
|
||||
|
||||
See L<Bugzilla::WebService::Bug> for more details on how to use this part of
|
||||
the REST API.
|
|
@ -127,7 +127,7 @@ sub create {
|
|||
# $call = $rpc->call( 'User.get', { ids => [1,2,3],
|
||||
# names => ['testusera@redhat.com', 'testuserb@redhat.com'] });
|
||||
sub get {
|
||||
my ($self, $params) = validate(@_, 'names', 'ids');
|
||||
my ($self, $params) = validate(@_, 'names', 'ids', 'match', 'group_ids', 'groups');
|
||||
|
||||
Bugzilla->switch_to_shadow_db();
|
||||
|
||||
|
@ -399,6 +399,10 @@ log in/out using an existing account.
|
|||
See L<Bugzilla::WebService> for a description of how parameters are passed,
|
||||
and what B<STABLE>, B<UNSTABLE>, and B<EXPERIMENTAL> mean.
|
||||
|
||||
Although the data input and output is the same for JSONRPC, XMLRPC and REST,
|
||||
the directions for how to access the data via REST is noted in each method
|
||||
where applicable.
|
||||
|
||||
=head1 Logging In and Out
|
||||
|
||||
=head2 login
|
||||
|
@ -417,7 +421,7 @@ etc. This method logs in an user.
|
|||
|
||||
=over
|
||||
|
||||
=item C<login> (string) - The user's login name.
|
||||
=item C<login> (string) - The user's login name.
|
||||
|
||||
=item C<password> (string) - The user's password.
|
||||
|
||||
|
@ -541,6 +545,13 @@ actually receive an email. This function does not check that.
|
|||
You must be logged in and have the C<editusers> privilege in order to
|
||||
call this function.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
POST /user
|
||||
|
||||
The params to include in the POST body as well as the returned data format,
|
||||
are the same as below.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
=over
|
||||
|
@ -584,6 +595,8 @@ password is under three characters.)
|
|||
|
||||
=item Error 503 (Password Too Long) removed in Bugzilla B<3.6>.
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
@ -598,6 +611,14 @@ B<EXPERIMENTAL>
|
|||
|
||||
Updates user accounts in Bugzilla.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
PUT /user/<user_id_or_name>
|
||||
|
||||
The params to include in the PUT body as well as the returned data format,
|
||||
are the same as below. The C<ids> and C<names> params are overridden as they
|
||||
are pulled from the URL path.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
=over
|
||||
|
@ -684,6 +705,14 @@ Logged-in users are not authorized to edit other users.
|
|||
|
||||
=back
|
||||
|
||||
=item B<History>
|
||||
|
||||
=over
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
||||
=head1 User Info
|
||||
|
@ -698,6 +727,18 @@ B<STABLE>
|
|||
|
||||
Gets information about user accounts in Bugzilla.
|
||||
|
||||
=item B<REST>
|
||||
|
||||
To get information about a single user:
|
||||
|
||||
GET /user/<user_id_or_name>
|
||||
|
||||
To search for users by name, group using URL params same as below:
|
||||
|
||||
GET /user
|
||||
|
||||
The returned data format is the same as below.
|
||||
|
||||
=item B<Params>
|
||||
|
||||
B<Note>: At least one of C<ids>, C<names>, or C<match> must be specified.
|
||||
|
@ -920,6 +961,8 @@ illegal to pass a group name you don't belong to.
|
|||
=item C<groups>, C<saved_searches>, and C<saved_reports> were added
|
||||
in Bugzilla B<4.4>.
|
||||
|
||||
=item REST API call added in Bugzilla B<5.0>.
|
||||
|
||||
=back
|
||||
|
||||
=back
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/perl -wT
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
# defined by the Mozilla Public License, v. 2.0.
|
||||
|
||||
use 5.10.1;
|
||||
use strict;
|
||||
use lib qw(. lib);
|
||||
|
||||
use Bugzilla;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::WebService::Constants;
|
||||
BEGIN {
|
||||
if (!Bugzilla->feature('rest')
|
||||
|| !Bugzilla->feature('jsonrpc'))
|
||||
{
|
||||
ThrowUserError('feature_disabled', { feature => 'rest' });
|
||||
}
|
||||
}
|
||||
use Bugzilla::WebService::Server::REST;
|
||||
Bugzilla->usage_mode(USAGE_MODE_REST);
|
||||
local @INC = (bz_locations()->{extensionsdir}, @INC);
|
||||
my $server = new Bugzilla::WebService::Server::REST;
|
||||
$server->version('1.1');
|
||||
$server->handle();
|
|
@ -1073,6 +1073,13 @@
|
|||
For security reasons, you must use HTTP POST to call the
|
||||
'[% method FILTER html %]' method.
|
||||
|
||||
[% ELSIF error == "rest_invalid_resource" %]
|
||||
A REST API resource was not found for '[% method FILTER html +%] [%+ path FILTER html %]'.
|
||||
|
||||
[% ELSIF error == "rest_get_products_invalid_type" %]
|
||||
The type '[% type FILTER html %]' is invalid for 'GET /product'. Valid choices
|
||||
are 'accessible' (default if type value is undefined), 'selectable', and 'enterable'.
|
||||
|
||||
[% ELSIF error == "keyword_already_exists" %]
|
||||
[% title = "Keyword Already Exists" %]
|
||||
A keyword with the name [% name FILTER html %] already exists.
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
[%# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
# defined by the Mozilla Public License, v. 2.0.
|
||||
#%]
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Bugzilla::REST::API</title>
|
||||
<link href="[% urlbase FILTER none %][% 'skins/standard/global.css' FILTER mtime %]"
|
||||
rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<pre>[% result FILTER html %]</pre>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue