Bug 432907: Create a JSON frontend for WebServices
Patch by Max Kanat-Alexander <mkanat@bugzilla.org> r=dkl, a=mkanat git-svn-id: svn://10.0.0.236/trunk@256768 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
parent
399698f211
commit
22d4b4a1bd
@ -360,6 +360,15 @@ sub error_mode {
|
||||
|| (i_am_cgi() ? ERROR_MODE_WEBPAGE : ERROR_MODE_DIE);
|
||||
}
|
||||
|
||||
# This is used only by Bugzilla::Error to throw errors.
|
||||
sub _json_server {
|
||||
my ($class, $newval) = @_;
|
||||
if (defined $newval) {
|
||||
$class->request_cache->{_json_server} = $newval;
|
||||
}
|
||||
return $class->request_cache->{_json_server};
|
||||
}
|
||||
|
||||
sub usage_mode {
|
||||
my ($class, $newval) = @_;
|
||||
if (defined $newval) {
|
||||
@ -369,9 +378,12 @@ sub usage_mode {
|
||||
elsif ($newval == USAGE_MODE_CMDLINE) {
|
||||
$class->error_mode(ERROR_MODE_DIE);
|
||||
}
|
||||
elsif ($newval == USAGE_MODE_WEBSERVICE) {
|
||||
elsif ($newval == USAGE_MODE_XMLRPC) {
|
||||
$class->error_mode(ERROR_MODE_DIE_SOAP_FAULT);
|
||||
}
|
||||
elsif ($newval == USAGE_MODE_JSON) {
|
||||
$class->error_mode(ERROR_MODE_JSON_RPC);
|
||||
}
|
||||
elsif ($newval == USAGE_MODE_EMAIL) {
|
||||
$class->error_mode(ERROR_MODE_DIE);
|
||||
}
|
||||
@ -667,10 +679,11 @@ usage mode changes.
|
||||
=item C<usage_mode>
|
||||
|
||||
Call either C<Bugzilla->usage_mode(Bugzilla::Constants::USAGE_MODE_CMDLINE)>
|
||||
or C<Bugzilla->usage_mode(Bugzilla::Constants::USAGE_MODE_WEBSERVICE)> near the
|
||||
or C<Bugzilla->usage_mode(Bugzilla::Constants::USAGE_MODE_XMLRPC)> near the
|
||||
beginning of your script to change this flag's default of
|
||||
C<Bugzilla::Constants::USAGE_MODE_BROWSER> and to indicate that Bugzilla is
|
||||
being called in a non-interactive manner.
|
||||
|
||||
This influences error handling because on usage mode changes, C<usage_mode>
|
||||
calls C<Bugzilla->error_mode> to set an error mode which makes sense for the
|
||||
usage mode.
|
||||
|
||||
@ -347,10 +347,10 @@ sub require_https {
|
||||
my ($self, $url) = @_;
|
||||
# Do not create query string if data submitted via XMLRPC
|
||||
# since we want the data to be resubmitted over POST method.
|
||||
my $query = Bugzilla->usage_mode == USAGE_MODE_WEBSERVICE ? 0 : 1;
|
||||
my $query = Bugzilla->usage_mode == USAGE_MODE_XMLRPC ? 0 : 1;
|
||||
# XMLRPC clients (SOAP::Lite at least) requires 301 to redirect properly
|
||||
# and do not work with 302.
|
||||
my $status = Bugzilla->usage_mode == USAGE_MODE_WEBSERVICE ? 301 : 302;
|
||||
my $status = Bugzilla->usage_mode == USAGE_MODE_XMLRPC ? 301 : 302;
|
||||
if (defined $url) {
|
||||
$url .= $self->url('-path_info' => 1, '-query' => $query, '-relative' => 1);
|
||||
} else {
|
||||
|
||||
@ -128,12 +128,14 @@ use File::Basename;
|
||||
|
||||
USAGE_MODE_BROWSER
|
||||
USAGE_MODE_CMDLINE
|
||||
USAGE_MODE_WEBSERVICE
|
||||
USAGE_MODE_XMLRPC
|
||||
USAGE_MODE_EMAIL
|
||||
USAGE_MODE_JSON
|
||||
|
||||
ERROR_MODE_WEBPAGE
|
||||
ERROR_MODE_DIE
|
||||
ERROR_MODE_DIE_SOAP_FAULT
|
||||
ERROR_MODE_JSON_RPC
|
||||
|
||||
INSTALLATION_MODE_INTERACTIVE
|
||||
INSTALLATION_MODE_NON_INTERACTIVE
|
||||
@ -378,14 +380,16 @@ use constant SAFE_PROTOCOLS => ('afs', 'cid', 'ftp', 'gopher', 'http', 'https',
|
||||
# Usage modes. Default USAGE_MODE_BROWSER. Use with Bugzilla->usage_mode.
|
||||
use constant USAGE_MODE_BROWSER => 0;
|
||||
use constant USAGE_MODE_CMDLINE => 1;
|
||||
use constant USAGE_MODE_WEBSERVICE => 2;
|
||||
use constant USAGE_MODE_XMLRPC => 2;
|
||||
use constant USAGE_MODE_EMAIL => 3;
|
||||
use constant USAGE_MODE_JSON => 4;
|
||||
|
||||
# Error modes. Default set by Bugzilla->usage_mode (so ERROR_MODE_WEBPAGE
|
||||
# usually). Use with Bugzilla->error_mode.
|
||||
use constant ERROR_MODE_WEBPAGE => 0;
|
||||
use constant ERROR_MODE_DIE => 1;
|
||||
use constant ERROR_MODE_DIE_SOAP_FAULT => 2;
|
||||
use constant ERROR_MODE_JSON_RPC => 3;
|
||||
|
||||
# The various modes that checksetup.pl can run in.
|
||||
use constant INSTALLATION_MODE_INTERACTIVE => 0;
|
||||
|
||||
@ -101,7 +101,9 @@ sub _throw_error {
|
||||
if (Bugzilla->error_mode == ERROR_MODE_DIE) {
|
||||
die("$message\n");
|
||||
}
|
||||
elsif (Bugzilla->error_mode == ERROR_MODE_DIE_SOAP_FAULT) {
|
||||
elsif (Bugzilla->error_mode == ERROR_MODE_DIE_SOAP_FAULT
|
||||
|| Bugzilla->error_mode == ERROR_MODE_JSON_RPC)
|
||||
{
|
||||
# Clone the hash so we aren't modifying the constant.
|
||||
my %error_map = %{ WS_ERROR_CODE() };
|
||||
require Bugzilla::Hook;
|
||||
@ -112,7 +114,19 @@ sub _throw_error {
|
||||
$code = ERROR_UNKNOWN_FATAL if $name =~ /code/i;
|
||||
$code = ERROR_UNKNOWN_TRANSIENT if $name =~ /user/i;
|
||||
}
|
||||
die SOAP::Fault->faultcode($code)->faultstring($message);
|
||||
|
||||
if (Bugzilla->error_mode == ERROR_MODE_DIE_SOAP_FAULT) {
|
||||
die SOAP::Fault->faultcode($code)->faultstring($message);
|
||||
}
|
||||
else {
|
||||
my $server = Bugzilla->_json_server;
|
||||
# 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);
|
||||
$server->response($server->error_response_header);
|
||||
}
|
||||
}
|
||||
}
|
||||
exit;
|
||||
|
||||
@ -225,6 +225,12 @@ sub OPTIONAL_MODULES {
|
||||
blacklist => ['^0\.70', '^0\.710?\.0[1-5]$'],
|
||||
feature => 'XML-RPC Interface'
|
||||
},
|
||||
{
|
||||
package => 'JSON-RPC',
|
||||
module => 'JSON::RPC',
|
||||
version => 0,
|
||||
feature => 'JSON-RPC Interface'
|
||||
},
|
||||
{
|
||||
# We need the 'utf8_mode' method of HTML::Parser, for HTML::Scrubber.
|
||||
package => 'HTML-Parser',
|
||||
|
||||
@ -219,7 +219,12 @@ sub _do_list_select {
|
||||
$sql .= " $postamble" if $postamble;
|
||||
|
||||
my $dbh = Bugzilla->dbh;
|
||||
my $objects = $dbh->selectall_arrayref($sql, {Slice=>{}}, @$values);
|
||||
# Sometimes the values are tainted, but we don't want to untaint them
|
||||
# for the caller. So we copy the array. It's safe to untaint because
|
||||
# they're only used in placeholders here.
|
||||
my @untainted = @{ $values || [] };
|
||||
trick_taint($_) foreach @untainted;
|
||||
my $objects = $dbh->selectall_arrayref($sql, {Slice=>{}}, @untainted);
|
||||
bless ($_, $class) foreach @$objects;
|
||||
return $objects
|
||||
}
|
||||
|
||||
@ -22,17 +22,8 @@ use strict;
|
||||
use Date::Parse;
|
||||
use XMLRPC::Lite;
|
||||
|
||||
sub datetime_format {
|
||||
my ($self, $date_string) = @_;
|
||||
|
||||
my $time = str2time($date_string);
|
||||
my ($sec, $min, $hour, $mday, $mon, $year) = localtime $time;
|
||||
# This format string was stolen from SOAP::Utils->format_datetime,
|
||||
# which doesn't work but which has almost the right format string.
|
||||
my $iso_datetime = sprintf('%d%02d%02dT%02d:%02d:%02d',
|
||||
$year + 1900, $mon + 1, $mday, $hour, $min, $sec);
|
||||
return $iso_datetime;
|
||||
}
|
||||
# Used by the JSON-RPC server to convert incoming date fields apprpriately.
|
||||
use constant DATE_FIELDS => {};
|
||||
|
||||
# For some methods, we shouldn't call Bugzilla->login before we call them
|
||||
use constant LOGIN_EXEMPT => { };
|
||||
@ -63,85 +54,84 @@ Bugzilla::WebService - The Web Service interface to Bugzilla
|
||||
This is the standard API for external programs that want to interact
|
||||
with Bugzilla. It provides various methods in various modules.
|
||||
|
||||
Currently the only method of accessing the API is via XML-RPC. The XML-RPC
|
||||
standard is described here: L<http://www.xmlrpc.com/spec>
|
||||
|
||||
The endpoint for Bugzilla WebServices is the C<xmlrpc.cgi> script in
|
||||
your Bugzilla installation. For example, if your Bugzilla is at
|
||||
C<bugzilla.yourdomain.com>, then your XML-RPC client would access the
|
||||
API via: C<http://bugzilla.yourdomain.com/xmlrpc.cgi>
|
||||
You can interact with this API via
|
||||
L<XML-RPC|Bugzilla::WebService::Server::XMLRPC> or
|
||||
L<JSON-RPC|Bugzilla::WebService::Server::JSONRPC>.
|
||||
|
||||
=head1 CALLING METHODS
|
||||
|
||||
Methods are called in the normal XML-RPC fashion. Bugzilla does not currently
|
||||
implement any extensions to the standard method of XML-RPC method calling.
|
||||
|
||||
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> in XML-RPC.
|
||||
L<Bugzilla::WebService::Bug/get>, is called as C<Bug.get>.
|
||||
|
||||
=head1 PARAMETERS
|
||||
|
||||
In addition to the standard parameter types like C<int>, C<string>, etc.,
|
||||
XML-RPC has two data structures, a C<< <struct> >> and an C<< <array> >>.
|
||||
The Bugzilla API takes the following various types of parameters:
|
||||
|
||||
=head2 Structs
|
||||
=over
|
||||
|
||||
In Perl, we call a C<< <struct> >> a "hash" or a "hashref". You may see
|
||||
us refer to it that way in the API documentation.
|
||||
=item C<int>
|
||||
|
||||
In example code, you will see the characters C<{> and C<}> used to represent
|
||||
the beginning and end of structs.
|
||||
Integer. May be null.
|
||||
|
||||
For example, here's a struct in XML-RPC:
|
||||
=item C<double>
|
||||
|
||||
<struct>
|
||||
<member>
|
||||
<name>fruit</name>
|
||||
<value><string>oranges</string></value>
|
||||
</member>
|
||||
<member>
|
||||
<name>vegetable</name>
|
||||
<value><string>lettuce</string></value>
|
||||
</member>
|
||||
</struct>
|
||||
A floating-point number. May be null.
|
||||
|
||||
In our example code in these API docs, that would look like:
|
||||
=item C<string>
|
||||
|
||||
{ fruit => 'oranges', vegetable => 'lettuce' }
|
||||
A string. May be null.
|
||||
|
||||
=head2 Arrays
|
||||
=item C<dateTime>
|
||||
|
||||
A date/time. Represented differently in different interfaces to this API.
|
||||
May be null.
|
||||
|
||||
=item C<boolean>
|
||||
|
||||
True or false.
|
||||
|
||||
=item C<array>
|
||||
|
||||
An array. There may be mixed types in an array.
|
||||
|
||||
In example code, you will see the characters C<[> and C<]> used to
|
||||
represent the beginning and end of arrays.
|
||||
|
||||
For example, here's an array in XML-RPC:
|
||||
|
||||
<array>
|
||||
<data>
|
||||
<value><i4>1</i4></value>
|
||||
<value><i4>2</i4></value>
|
||||
<value><i4>3</i4></value>
|
||||
</data>
|
||||
</array>
|
||||
|
||||
In our example code in these API docs, that would look like:
|
||||
In our example code in these API docs, an array that contains the numbers
|
||||
1, 2, and 3 would look like:
|
||||
|
||||
[1, 2, 3]
|
||||
|
||||
=item C<struct>
|
||||
|
||||
A mapping of keys to values. Called a "hash", "dict", or "map" in some
|
||||
other programming languages. We sometimes call this a "hash" in the API
|
||||
documentation.
|
||||
|
||||
The keys are strings, and the values can be any type.
|
||||
|
||||
In example code, you will see the characters C<{> and C<}> used to represent
|
||||
the beginning and end of structs.
|
||||
|
||||
For example, a struct with an "fruit" key whose value is "oranges",
|
||||
and a "vegetable" key whose value is "lettuce" would look like:
|
||||
|
||||
{ fruit => 'oranges', vegetable => 'lettuce' }
|
||||
|
||||
=back
|
||||
|
||||
=head2 How Bugzilla WebService Methods Take Parameters
|
||||
|
||||
B<All> Bugzilla WebServices functions take their parameters in
|
||||
a C<< <struct> >>. Another way of saying this would be: All functions
|
||||
take a single argument, a C<< <struct> >> that contains all parameters.
|
||||
The names of the parameters listed in the API docs for each function are
|
||||
the C<name> element for the struct C<member>s.
|
||||
B<All> Bugzilla WebService functions use I<named> parameters.
|
||||
The individual C<Bugzilla::WebService::Server> modules explain
|
||||
how this is implemented for those frontends.
|
||||
|
||||
=head1 LOGGING IN
|
||||
|
||||
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 XML-RPC client must be capable of receiving and transmitting
|
||||
calls, so your client must be capable of receiving and transmitting
|
||||
cookies.
|
||||
|
||||
=head1 STABLE, EXPERIMENTAL, and UNSTABLE
|
||||
@ -164,18 +154,17 @@ Bugzilla versions.
|
||||
|
||||
=head1 ERRORS
|
||||
|
||||
If a particular webservice call fails, it will throw a standard XML-RPC
|
||||
error. There will be a numeric error code, and then the description
|
||||
field will contain descriptive text of the error. Each error that Bugzilla
|
||||
can throw has a specific code that will not change between versions of
|
||||
Bugzilla.
|
||||
If a particular webservice call fails, it will throw an error in the
|
||||
appropriate format for the frontend that you are using. For all frontends,
|
||||
there is at least a numeric error code and descriptive text for the error.
|
||||
|
||||
The various errors that functions can throw are specified by the
|
||||
documentation of those functions.
|
||||
|
||||
If your code needs to know what error Bugzilla threw, use the numeric
|
||||
code. Don't try to parse the description, because that may change
|
||||
from version to version of Bugzilla.
|
||||
Each error that Bugzilla can throw has a specific numeric code that will
|
||||
not change between versions of Bugzilla. If your code needs to know what
|
||||
error Bugzilla threw, use the numeric code. Don't try to parse the
|
||||
description, because that may change from version to version of Bugzilla.
|
||||
|
||||
Note that if you display the error to the user in an HTML program, make
|
||||
sure that you properly escape the error, as it will not be HTML-escaped.
|
||||
@ -259,21 +248,3 @@ would return something like:
|
||||
{ users => [{ id => 1, real_name => 'John Smith' }] }
|
||||
|
||||
=back
|
||||
|
||||
|
||||
=head1 EXTENSIONS TO THE XML-RPC STANDARD
|
||||
|
||||
=head2 Undefined Values
|
||||
|
||||
Normally, XML-RPC does not allow empty values for C<int>, C<double>, or
|
||||
C<dateTime.iso8601> fields. Bugzilla does--it treats empty values as
|
||||
C<undef> (called C<NULL> or C<None> in some programming languages).
|
||||
|
||||
Bugzilla also accepts a type called C<< <nil> >>, which is always considered
|
||||
to be C<undef>, no matter what it contains.
|
||||
|
||||
=begin private
|
||||
|
||||
nil is implemented by XMLRPC::Lite, in XMLRPC::Deserializer::decode_value.
|
||||
|
||||
=end private
|
||||
|
||||
@ -56,6 +56,11 @@ use constant FIELD_MAP => {
|
||||
|
||||
use constant PRODUCT_SPECIFIC_FIELDS => qw(version target_milestone component);
|
||||
|
||||
use constant DATE_FIELDS => {
|
||||
comments => ['new_since'],
|
||||
search => ['last_change_time', 'creation_time'],
|
||||
};
|
||||
|
||||
######################################################
|
||||
# Add aliases here for old method name compatibility #
|
||||
######################################################
|
||||
|
||||
@ -133,5 +133,4 @@ sub WS_DISPATCH {
|
||||
return $dispatch;
|
||||
};
|
||||
|
||||
|
||||
1;
|
||||
|
||||
18
mozilla/webtools/bugzilla/Bugzilla/WebService/README
Normal file
18
mozilla/webtools/bugzilla/Bugzilla/WebService/README
Normal file
@ -0,0 +1,18 @@
|
||||
The class structure of these files is a little strange, and this README
|
||||
explains it.
|
||||
|
||||
Our goal is to make JSON::RPC and XMLRPC::Lite both work with the same code.
|
||||
(That is, we want to have one WebService API, and have two frontends for it.)
|
||||
|
||||
The problem is that these both pass different things for $self to WebService
|
||||
methods.
|
||||
|
||||
When XMLRPC::Lite calls a method, $self is the name of the *class* the
|
||||
method is in. For example, if we call Bugzilla.version(), the first argument
|
||||
is Bugzilla::WebService::Bugzilla. So in order to have $self
|
||||
(our first argument) act correctly in XML-RPC, we make all WebService
|
||||
classes use base qw(Bugzilla::WebService).
|
||||
|
||||
When JSON::RPC calls a method, $self is the JSON-RPC *server object*. In other
|
||||
words, it's an instance of Bugzilla::WebService::Server::JSONRPC. So we have
|
||||
Bugzilla::WebService::Server::JSONRPC inherit from Bugzilla::WebService.
|
||||
266
mozilla/webtools/bugzilla/Bugzilla/WebService/Server/JSONRPC.pm
Normal file
266
mozilla/webtools/bugzilla/Bugzilla/WebService/Server/JSONRPC.pm
Normal file
@ -0,0 +1,266 @@
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla JSON Webservices Interface.
|
||||
#
|
||||
# The Initial Developer of the Original Code is the San Jose State
|
||||
# University Foundation. Portions created by the Initial Developer
|
||||
# are Copyright (C) 2008 the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
package Bugzilla::WebService::Server::JSONRPC;
|
||||
|
||||
use strict;
|
||||
use base qw(JSON::RPC::Server::CGI Bugzilla::WebService::Server);
|
||||
|
||||
use Bugzilla::Error;
|
||||
use Bugzilla::WebService::Constants;
|
||||
use Date::Parse;
|
||||
use DateTime;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $self = $class->SUPER::new(@_);
|
||||
Bugzilla->_json_server($self);
|
||||
$self->dispatch(WS_DISPATCH);
|
||||
$self->return_die_message(1);
|
||||
$self->json->allow_blessed(1);
|
||||
$self->json->convert_blessed(1);
|
||||
# Default to JSON-RPC 1.0
|
||||
$self->version(0);
|
||||
return $self;
|
||||
}
|
||||
|
||||
# Override the JSON::RPC method to return our CGI object instead of theirs.
|
||||
sub cgi { return Bugzilla->cgi; }
|
||||
|
||||
sub type {
|
||||
my ($self, $type, $value) = @_;
|
||||
|
||||
# This is the only type that does something special with undef.
|
||||
if ($type eq 'boolean') {
|
||||
return $value ? JSON::true : JSON::false;
|
||||
}
|
||||
|
||||
return JSON::null if !defined $value;
|
||||
|
||||
my $retval = $value;
|
||||
|
||||
if ($type eq 'int') {
|
||||
$retval = int($value);
|
||||
}
|
||||
if ($type eq 'double') {
|
||||
$retval = 0.0 + $value;
|
||||
}
|
||||
elsif ($type eq 'string') {
|
||||
# Forces string context, so that JSON will make it a string.
|
||||
$retval = "$value";
|
||||
}
|
||||
elsif ($type eq 'dateTime') {
|
||||
# str2time uses Time::Local internally, so I believe it should
|
||||
# always return seconds based on the *Unix* epoch, even if the
|
||||
# system doesn't use the Unix epoch.
|
||||
$retval = str2time($value) * 1000;
|
||||
}
|
||||
# XXX Will have to implement base64 if Bugzilla starts using it.
|
||||
|
||||
return $retval;
|
||||
}
|
||||
|
||||
##################
|
||||
# Login Handling #
|
||||
##################
|
||||
|
||||
# This handles dispatching our calls to the appropriate class based on
|
||||
# the name of the method.
|
||||
sub _find_procedure {
|
||||
my $self = shift;
|
||||
|
||||
# This is also a good place to deny GET requests, since we can
|
||||
# safely call ThrowUserError at this point.
|
||||
if ($self->request->method ne 'POST') {
|
||||
ThrowUserError('json_rpc_post_only');
|
||||
}
|
||||
|
||||
my $method = shift;
|
||||
$self->{_bz_method_name} = $method;
|
||||
|
||||
# This tricks SUPER::_find_procedure into finding the right class.
|
||||
$method =~ /^(\S+)\.(\S+)$/;
|
||||
$self->path_info($1);
|
||||
unshift(@_, $2);
|
||||
|
||||
return $self->SUPER::_find_procedure(@_);
|
||||
}
|
||||
|
||||
# This is a hacky way to do something right before methods are called.
|
||||
# This is the last thing that JSON::RPC::Server::_handle calls right before
|
||||
# the method is actually called.
|
||||
sub _argument_type_check {
|
||||
my $self = shift;
|
||||
my $params = $self->SUPER::_argument_type_check(@_);
|
||||
|
||||
# This is the best time to do login checks.
|
||||
$self->handle_login();
|
||||
|
||||
# If there are no parameters, we don't need to parse them.
|
||||
return $params if !ref $params;
|
||||
|
||||
# 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.
|
||||
if (ref $params eq 'ARRAY') {
|
||||
$params = $params->[0];
|
||||
}
|
||||
|
||||
# Now, convert dateTime fields on input.
|
||||
$self->_bz_method_name =~ /^(\S+)\.(\S+)$/;
|
||||
my ($class, $method) = ($1, $2);
|
||||
my $pkg = $self->{dispatch_path}->{$class};
|
||||
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->_bz_convert_datetime($_) } @$value ];
|
||||
}
|
||||
else {
|
||||
$params->{$field} = $self->_bz_convert_datetime($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 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;
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
sub _bz_convert_datetime {
|
||||
my ($self, $time) = @_;
|
||||
my $dt = DateTime->from_epoch(epoch => ($time / 1000),
|
||||
time_zone => Bugzilla->local_timezone);
|
||||
return $dt->strftime('%Y-%m-%d %T');
|
||||
}
|
||||
|
||||
sub handle_login {
|
||||
my $self = shift;
|
||||
|
||||
my $path = $self->path_info;
|
||||
my $class = $self->{dispatch_path}->{$path};
|
||||
my $full_method = $self->_bz_method_name;
|
||||
$full_method =~ /^\S+\.(\S+)/;
|
||||
my $method = $1;
|
||||
$self->SUPER::handle_login($class, $method, $full_method);
|
||||
}
|
||||
|
||||
# _bz_method_name is stored by _find_procedure for later use.
|
||||
sub _bz_method_name {
|
||||
return $_[0]->{_bz_method_name};
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::WebService::Server::JSONRPC - The JSON-RPC Interface to Bugzilla
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This documentation describes things about the Bugzilla WebService that
|
||||
are specific to JSON-RPC. For a general overview of the Bugzilla WebServices,
|
||||
see L<Bugzilla::WebService>.
|
||||
|
||||
Please note that I<everything> about this JSON-RPC interface is
|
||||
B<UNSTABLE>. If you want a stable API, please use the
|
||||
C<XML-RPC|Bugzilla::WebService::Server::XMLRPC> interface.
|
||||
|
||||
=head1 JSON-RPC
|
||||
|
||||
Bugzilla supports both JSON-RPC 1.0 and 1.1. We recommend that you use
|
||||
JSON-RPC 1.0 instead of 1.1, though, because 1.1 is deprecated.
|
||||
|
||||
At some point in the future, Bugzilla may also support JSON-RPC 2.0.
|
||||
|
||||
The JSON-RPC standards are described at L<http://json-rpc.org/>.
|
||||
|
||||
=head1 CONNECTING
|
||||
|
||||
The endpoint for the JSON-RPC interface is the C<jsonrpc.cgi> script in
|
||||
your Bugzilla installation. For example, if your Bugzilla is at
|
||||
C<bugzilla.yourdomain.com>, then your JSON-RPC client would access the
|
||||
API via: C<http://bugzilla.yourdomain.com/jsonrpc.cgi>
|
||||
|
||||
Bugzilla only allows JSON-RPC requests over C<POST>. C<GET> requests
|
||||
(or any other type of request, such as C<HEAD>) will be denied.
|
||||
|
||||
=head1 PARAMETERS
|
||||
|
||||
For JSON-RPC 1.0, the very first parameter should be an object containing
|
||||
the named parameters. For example, if you were passing two named parameters,
|
||||
one called C<foo> and the other called C<bar>, the C<params> element of
|
||||
your JSON-RPC call would look like:
|
||||
|
||||
"params": [{ "foo": 1, "bar": "something" }]
|
||||
|
||||
For JSON-RPC 1.1, you can pass parameters either in the above fashion
|
||||
or using the standard named-parameters mechanism of JSON-RPC 1.1.
|
||||
|
||||
C<dateTime> fields are represented as an integer number of microseconds
|
||||
since the Unix epoch (January 1, 1970 UTC). They are always in the UTC
|
||||
timezone.
|
||||
|
||||
All other types are standard JSON types.
|
||||
|
||||
=head1 ERRORS
|
||||
|
||||
All errors thrown by Bugzilla itself have 100000 added to their numeric
|
||||
code. So, if the documentation says that an error is C<302>, then
|
||||
it will be C<100302> when it is thrown via JSON-RPC.
|
||||
|
||||
Errors less than 100000 are errors thrown by the JSON-RPC library that
|
||||
Bugzilla uses, not by Bugzilla.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
=head2 Server Types
|
||||
|
||||
=over
|
||||
|
||||
=item L<Bugzilla::WebService::Server::XMLRPC>
|
||||
|
||||
=item L<Bugzilla::WebService::Server::JSONRPC>
|
||||
|
||||
=back
|
||||
|
||||
=head2 WebService Methods
|
||||
|
||||
=over
|
||||
|
||||
=item L<Bugzilla::WebService::Bug>
|
||||
|
||||
=item L<Bugzilla::WebService::Bugzilla>
|
||||
|
||||
=item L<Bugzilla::WebService::Product>
|
||||
|
||||
=item L<Bugzilla::WebService::User>
|
||||
|
||||
=back
|
||||
@ -168,3 +168,56 @@ sub as_string {
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Bugzilla::WebService::Server::XMLRPC - The XML-RPC Interface to Bugzilla
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This documentation describes things about the Bugzilla WebService that
|
||||
are specific to XML-RPC. For a general overview of the Bugzilla WebServices,
|
||||
see L<Bugzilla::WebService>.
|
||||
|
||||
=head1 XML-RPC
|
||||
|
||||
The XML-RPC standard is described here: L<http://www.xmlrpc.com/spec>
|
||||
|
||||
=head1 CONNECTING
|
||||
|
||||
The endpoint for the XML-RPC interface is the C<xmlrpc.cgi> script in
|
||||
your Bugzilla installation. For example, if your Bugzilla is at
|
||||
C<bugzilla.yourdomain.com>, then your XML-RPC client would access the
|
||||
API via: C<http://bugzilla.yourdomain.com/xmlrpc.cgi>
|
||||
|
||||
=head1 PARAMETERS
|
||||
|
||||
C<dateTime> fields are the standard C<dateTime.iso8601> XML-RPC field. They
|
||||
should be in C<YYYY-MM-DDTHH:MM:SS> format (where C<T> is a literal T).
|
||||
|
||||
All other fields are standard XML-RPC types.
|
||||
|
||||
=head2 How XML-RPC WebService Methods Take Parameters
|
||||
|
||||
All functions take a single argument, a C<< <struct> >> that contains all parameters.
|
||||
The names of the parameters listed in the API docs for each function are the
|
||||
C<< <name> >> element for the struct C<< <member> >>s.
|
||||
|
||||
=head1 EXTENSIONS TO THE XML-RPC STANDARD
|
||||
|
||||
=head2 Undefined Values
|
||||
|
||||
Normally, XML-RPC does not allow empty values for C<int>, C<double>, or
|
||||
C<dateTime.iso8601> fields. Bugzilla does--it treats empty values as
|
||||
C<undef> (called C<NULL> or C<None> in some programming languages).
|
||||
|
||||
Bugzilla also accepts a type called C<< <nil> >>, which is always considered
|
||||
to be C<undef>, no matter what it contains.
|
||||
|
||||
=begin private
|
||||
|
||||
nil is implemented by XMLRPC::Lite, in XMLRPC::Deserializer::decode_value.
|
||||
|
||||
=end private
|
||||
@ -24,7 +24,7 @@ use warnings;
|
||||
use base qw(Bugzilla::WebService);
|
||||
use Bugzilla::Error;
|
||||
|
||||
# This can be called as Example.hello() from XML-RPC.
|
||||
# This can be called as Example.hello() from the WebService.
|
||||
sub hello { return 'Hello!'; }
|
||||
|
||||
sub throw_an_error { ThrowUserError('example_my_error') }
|
||||
|
||||
38
mozilla/webtools/bugzilla/jsonrpc.cgi
Executable file
38
mozilla/webtools/bugzilla/jsonrpc.cgi
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/perl -wT
|
||||
# -*- Mode: perl; indent-tabs-mode: nil -*-
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is the Bugzilla JSON Webservices Interface.
|
||||
#
|
||||
# The Initial Developer of the Original Code is the San Jose State
|
||||
# University Foundation. Portions created by the Initial Developer
|
||||
# are Copyright (C) 2008 the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Max Kanat-Alexander <mkanat@bugzilla.org>
|
||||
|
||||
use strict;
|
||||
use lib qw(. lib);
|
||||
|
||||
use Bugzilla;
|
||||
use Bugzilla::Constants;
|
||||
use Bugzilla::WebService::Constants;
|
||||
# This eval allows runtests to pass even if JSON::RPC isn't
|
||||
# installed.
|
||||
eval { require Bugzilla::WebService::Server::JSONRPC; };
|
||||
$@ && ThrowCodeError('json_rpc_not_installed');
|
||||
|
||||
Bugzilla->usage_mode(USAGE_MODE_JSON);
|
||||
|
||||
local @INC = (bz_locations()->{extensionsdir}, @INC);
|
||||
my $server = new Bugzilla::WebService::Server::JSONRPC;
|
||||
$server->dispatch(WS_DISPATCH)->handle();
|
||||
@ -292,6 +292,13 @@
|
||||
the job "[% job FILTER html %]". You need to add this job type
|
||||
to the <code>JOB_MAP</code> constant in <code>Bugzilla::JobQueue</code>.
|
||||
|
||||
[% ELSIF error == "json_rpc_not_installed" %]
|
||||
[% admindocslinks = { 'installation.html#install-perlmodules'
|
||||
=> 'Installing Perl modules' } %]
|
||||
The JSON-RPC interface will not work without the <code>JSON::RPC</code>
|
||||
Perl module being installed. Run <code>checksetup.pl</code> for
|
||||
installation instructions.
|
||||
|
||||
[% ELSIF error == "ldap_bind_failed" %]
|
||||
Failed to bind to the LDAP server. The error message was:
|
||||
<code>[% errstr FILTER html %]</code>
|
||||
|
||||
@ -949,6 +949,10 @@
|
||||
[% title = "Invalid Username Or Password" %]
|
||||
The username or password you entered is not valid.
|
||||
|
||||
[% ELSIF error == "json_rpc_post_only" %]
|
||||
For security reasons, you may only use JSON-RPC with the POST
|
||||
HTTP method.
|
||||
|
||||
[% ELSIF error == "keyword_already_exists" %]
|
||||
[% title = "Keyword Already Exists" %]
|
||||
A keyword with the name [% name FILTER html %] already exists.
|
||||
|
||||
@ -28,7 +28,7 @@ use Bugzilla::WebService::Constants;
|
||||
eval { require Bugzilla::WebService::Server::XMLRPC; };
|
||||
$@ && ThrowCodeError('soap_not_installed');
|
||||
|
||||
Bugzilla->usage_mode(USAGE_MODE_WEBSERVICE);
|
||||
Bugzilla->usage_mode(USAGE_MODE_XMLRPC);
|
||||
|
||||
# Fix the error code that SOAP::Lite uses for Perl errors.
|
||||
local $SOAP::Constants::FAULT_SERVER;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user