Commit d403dca9 authored by Max Kanat-Alexander's avatar Max Kanat-Alexander

Bug 415813: Implement Bug.update() as an API for WebServices

r=dkl, a=mkanat
parent 33e1f44e
......@@ -265,9 +265,14 @@ use constant MAX_LINE_LENGTH => 254;
# of Bugzilla. (These are the field names that the WebService and email_in.pl
# use.)
use constant FIELD_MAP => {
blocks => 'blocked',
is_confirmed => 'everconfirmed',
cc_accessible => 'cclist_accessible',
creation_time => 'creation_ts',
creator => 'reporter',
description => 'comment',
depends_on => 'dependson',
dupe_of => 'dup_id',
id => 'bug_id',
last_change_time => 'delta_ts',
platform => 'rep_platform',
......@@ -3706,11 +3711,17 @@ sub LogActivityEntry {
# Convert WebService API and email_in.pl field names to internal DB field
# names.
sub map_fields {
my ($params) = @_;
my ($params, $except) = @_;
my %field_values;
foreach my $field (keys %$params) {
my $field_name = FIELD_MAP->{$field} || $field;
my $field_name;
if ($except->{$field}) {
$field_name = $field;
}
else {
$field_name = FIELD_MAP->{$field} || $field;
}
$field_values{$field_name} = $params->{$field};
}
......
......@@ -47,6 +47,7 @@ use constant PRODUCT_SPECIFIC_FIELDS => qw(version target_milestone component);
use constant DATE_FIELDS => {
comments => ['new_since'],
search => ['last_change_time', 'creation_time'],
update => ['deadline'],
};
use constant READ_ONLY => qw(
......@@ -448,6 +449,93 @@ sub possible_duplicates {
return { bugs => \@hashes };
}
sub update {
my ($self, $params) = validate(@_, 'ids');
my $user = Bugzilla->login(LOGIN_REQUIRED);
my $dbh = Bugzilla->dbh;
$params = Bugzilla::Bug::map_fields($params, { summary => 1 });
my $ids = delete $params->{ids};
defined $ids || ThrowCodeError('param_required', { param => 'ids' });
my @bugs = map { Bugzilla::Bug->check($_) } @$ids;
my %values = %$params;
$values{other_bugs} = \@bugs;
if (exists $values{comment} and exists $values{comment}{comment}) {
$values{comment}{body} = delete $values{comment}{comment};
}
# Prevent bugs that could be triggered by specifying fields that
# have valid "set_" functions in Bugzilla::Bug, but shouldn't be
# called using those field names.
delete $values{dependencies};
delete $values{flags};
foreach my $bug (@bugs) {
if (!$user->can_edit_product($bug->product_obj->id) ) {
ThrowUserError("product_edit_denied",
{ product => $bug->product });
}
$bug->set_all(\%values);
}
my %all_changes;
$dbh->bz_start_transaction();
foreach my $bug (@bugs) {
$all_changes{$bug->id} = $bug->update();
}
$dbh->bz_commit_transaction();
foreach my $bug (@bugs) {
$bug->send_changes($all_changes{$bug->id});
}
my %api_name = reverse %{ Bugzilla::Bug::FIELD_MAP() };
# This doesn't normally belong in FIELD_MAP, but we do want to translate
# "bug_group" back into "groups".
$api_name{'bug_group'} = 'groups';
my @result;
foreach my $bug (@bugs) {
my %hash = (
id => $self->type('int', $bug->id),
last_change_time => $self->type('dateTime', $bug->delta_ts),
changes => {},
);
# alias is returned in case users pass a mixture of ids and aliases,
# so that they can know which set of changes relates to which value
# they passed.
if (Bugzilla->params->{'usebugaliases'}) {
$hash{alias} = $self->type('string', $bug->alias);
}
else {
# For API reasons, we always want the alias field to appear, we
# just don't want it to have a value if aliases are turned off.
$hash{alias} = $self->type('string', '');
}
my %changes = %{ $all_changes{$bug->id} };
foreach my $field (keys %changes) {
my $change = $changes{$field};
my $api_field = $api_name{$field} || $field;
$hash{changes}->{$api_field} = {
removed => $self->type('string', $change->[0]),
added => $self->type('string', $change->[1])
};
}
push(@result, \%hash);
}
return { bugs => \@result };
}
sub create {
my ($self, $params) = @_;
Bugzilla->login(LOGIN_REQUIRED);
......@@ -2046,6 +2134,398 @@ code of 32000.
=back
=item C<update>
B<UNSTABLE>
=over
=item B<Description>
Allows you to update the fields of a bug. Automatically sends emails
out about the changes.
=item B<Params>
=over
=item C<ids>
Array of C<int>s or C<string>s. The ids or aliases of the bugs that
you want to modify.
=back
B<Note>: All following fields specify the values you want to set on the
bugs you are updating.
=over
=item C<alias>
(string) The alias of the bug. You can only set this if you are modifying
a single bug. If there is more than one bug specified in C<ids>, passing in
a value for C<alias> will cause an error to be thrown.
=item C<assigned_to>
C<string> The full login name of the user this bug is assigned to.
=item C<blocks>
=item C<depends_on>
C<hash> These specify the bugs that this bug blocks or depends on,
respectively. To set these, you should pass a hash as the value. The hash
may contain the following fields:
=over
=item C<add> An array of C<int>s. Bug ids to add to this field.
=item C<remove> An array of C<int>s. Bug ids to remove from this field.
If the bug ids are not already in the field, they will be ignored.
=item C<set> An array of C<int>s. An exact set of bug ids to set this
field to, overriding the current value. If you specify C<set>, then C<add>
and C<remove> will be ignored.
=back
=item C<cc>
C<hash> The users on the cc list. To modify this field, pass a hash, which
may have the following fields:
=over
=item C<add> Array of C<string>s. User names to add to the CC list.
They must be full user names, and an error will be thrown if you pass
in an invalid user name.
=item C<remove> Array of C<string>s. User names to remove from the CC
list. They must be full user names, and an error will be thrown if you
pass in an invalid user name.
=back
=item C<cc_accessible>
C<boolean> Whether or not users in the CC list are allowed to access
the bug, even if they aren't in a group that can normally access the bug.
=item C<comment>
C<hash>. A comment on the change. The hash may contain the following fields:
=over
=item C<body> C<string> The actual text of the comment.
B<Note>: For compatibility with the parameters to L</add_comment>,
you can also call this field C<comment>, if you want.
=item C<is_private> C<boolean> Whether the comment is private or not.
If you try to make a comment private and you don't have the permission
to, an error will be thrown.
=back
=item C<comment_is_private>
C<hash> This is how you update the privacy of comments that are already
on a bug. This is a hash, where the keys are the C<int> id of comments (not
their count on a bug, like #1, #2, #3, but their globally-unique id,
as returned by L</comments>) and the value is a C<boolean> which specifies
whether that comment should become private (C<true>) or public (C<false>).
The comment ids must be valid for the bug being updated. Thus, it is not
practical to use this while updating multiple bugs at once, as a single
comment id will never be valid on multiple bugs.
=item C<component>
C<string> The Component the bug is in.
=item C<deadline>
C<dateTime> The Deadline field--a date specifying when the bug must
be completed by. The time specified is ignored--only the date is
significant.
=item C<dupe_of>
C<int> The bug that this bug is a duplicate of. If you want to mark
a bug as a duplicate, the safest thing to do is to set this value
and I<not> set the C<status> or C<resolution> fields. They will
automatically be set by Bugzilla to the appropriate values for
duplicate bugs.
=item C<estimated_time>
C<double> The total estimate of time required to fix the bug, in hours.
This is the I<total> estimate, not the amount of time remaining to fix it.
=item C<groups>
C<hash> The groups a bug is in. To modify this field, pass a hash, which
may have the following fields:
=over
=item C<add> Array of C<string>s. The names of groups to add. Passing
in an invalid group name or a group that you cannot add to this bug will
cause an error to be thrown.
=item C<remove> Array of C<string>s. The names of groups to remove. Passing
in an invalid group name or a group that you cannot remove from this bug
will cause an error to be thrown.
=back
=item C<keywords>
C<hash> Keywords on the bug. To modify this field, pass a hash, which
may have the following fields:
=over
=item C<add> An array of C<strings>s. The names of keywords to add to
the field on the bug. Passing something that isn't a valid keyword name
will cause an error to be thrown.
=item C<remove> An array of C<string>s. The names of keywords to remove
from the field on the bug. Passing something that isn't a valid keyword
name will cause an error to be thrown.
=item C<set> An array of C<strings>s. An exact set of keywords to set the
field to, on the bug. Passing something that isn't a valid keyword name
will cause an error to be thrown. Specifying C<set> overrides C<add> and
C<remove>.
=back
=item C<op_sys>
C<string> The Operating System ("OS") field on the bug.
=item C<platform>
C<string> The Platform or "Hardware" field on the bug.
=item C<priority>
C<string> The Priority field on the bug.
=item C<product>
C<string> The name of the product that the bug is in. If you change
this, you will probably also want to change C<target_milestone>,
C<version>, and C<component>, since those have different legal
values in every product.
If you cannot change the C<target_milestone> field, it will be reset to
the default for the product, when you move a bug to a new product.
You may also wish to add or remove groups, as which groups are
valid on a bug depends on the product. Groups that are not valid
in the new product will be automatically removed, and groups which
are mandatory in the new product will be automaticaly added, but no
other automatic group changes will be done.
Note that users can only move a bug into a product if they would
normally have permission to file new bugs in that product.
=item C<qa_contact>
C<string> The full login name of the bug's QA Contact.
=item C<reporter_accessible>
C<boolean> Whether or not the bug's reporter is allowed to access
the bug, even if he or she isn't in a group that can normally access
the bug.
=item C<remaining_time>
C<double> How much work time is remaining to fix the bug, in hours.
If you set C<work_time> but don't explicitly set C<remaining_time>,
then the C<work_time> will be deducted from the bug's C<remaining_time>.
=item C<reset_assigned_to>
C<boolean> If true, the C<assigned_to> field will be reset to the
default for the component that the bug is in. (If you have set the
component at the same time as using this, then the component used
will be the new component, not the old one.)
=item C<reset_qa_contact>
C<boolean> If true, the C<qa_contact> field will be reset to the
default for the component that the bug is in. (If you have set the
component at the same time as using this, then the component used
will be the new component, not the old one.)
=item C<resolution>
C<string> The current resolution. May only be set if you are closing
a bug or if you are modifying an already-closed bug. Attempting to set
the resolution to I<any> value (even an empty or null string) on an
open bug will cause an error to be thrown.
If you change the C<status> field to an open status, the resolution
field will automatically be cleared, so you don't have to clear it
manually.
=item C<see_also>
C<hash> The See Also field on a bug, specifying URLs to bugs in other
bug trackers. To modify this field, pass a hash, which may have the
following fields:
=over
=item C<add> An array of C<strings>s. URLs to add to the field.
Each URL must be a valid URL to a bug-tracker, or an error will
be thrown.
=item C<remove> An array of C<string>s. URLs to remove from the field.
Invalid URLs will be ignored.
=back
=item C<severity>
C<string> The Severity field of a bug.
=item C<status>
C<string> The status you want to change the bug to. Note that if
a bug is changing from open to closed, you should also specify
a C<resolution>.
=item C<summary>
C<string> The Summary field of the bug.
=item C<target_milestone>
C<string> The bug's Target Milestone.
=item C<url>
C<string> The "URL" field of a bug.
=item C<version>
C<string> The bug's Version field.
=item C<whiteboard>
C<string> The Status Whiteboard field of a bug.
=item C<work_time>
C<double> The number of hours worked on this bug as part of this change.
If you set C<work_time> but don't explicitly set C<remaining_time>,
then the C<work_time> will be deducted from the bug's C<remaining_time>.
=back
You can also set the value of any custom field by passing its name as
a parameter, and the value to set the field to. For multiple-selection
fields, the value should be an array of strings.
=item B<Returns>
A C<hash> with a single field, "bugs". This points to an array of hashes
with the following fields:
=over
=item C<id>
C<int> The id of the bug that was updated.
=item C<alias>
C<string> The alias of the bug that was updated, if aliases are enabled and
this bug has an alias.
=item C<last_change_time>
C<dateTime> The exact time that this update was done at, for this bug.
If no update was done (that is, no fields had their values changed and
no comment was added) then this will instead be the last time the bug
was updated.
=item C<changes>
C<hash> The changes that were actually done on this bug. The keys are
the names of the fields that were changed, and the values are a hash
with two keys:
=over
=item C<added> (C<string>) The values that were added to this field,
possibly a comma-and-space-separated list if multiple values were added.
=item C<removed> (C<string>) The values that were removed from this
field, possibly a comma-and-space-separated list if multiple values were
removed.
=back
=back
Here's an example of what a return value might look like:
{
bugs => [
{
id => 123,
alias => 'foo',
last_change_time => '2010-01-01T12:34:56',
changes => {
status => {
removed => 'NEW',
added => 'ASSIGNED'
},
keywords => {
removed => 'bar',
added => 'qux, quo, qui',
}
},
}
]
}
=item B<Errors>
This function can throw all of the errors that L</get> can throw, plus:
=over
=item 103 (Invalid Alias)
Either you tried to set an alias when changing multiple bugs at once,
or the alias you specified is invalid for some reason.
=back
FIXME: Plus a whole load of other errors that we haven't documented yet,
which we won't even know about until after we do QA for 4.0.
=item B<History>
=over
=item Added in Bugzilla B<4.0>.
=back
=back
=item C<update_see_also>
B<EXPERIMENTAL>
......
......@@ -66,6 +66,7 @@ use constant WS_ERROR_CODE => {
alias_in_use => 103,
alias_is_numeric => 103,
alias_has_comma_or_space => 103,
alias_not_allowed => 103,
# Misc. bug field errors
illegal_field => 104,
freetext_too_long => 104,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment