From d403dca90ecda9fb290dbbc9507bdda79dae7b59 Mon Sep 17 00:00:00 2001 From: Max Kanat-Alexander Date: Mon, 12 Jul 2010 18:41:12 -0700 Subject: Bug 415813: Implement Bug.update() as an API for WebServices r=dkl, a=mkanat --- Bugzilla/Bug.pm | 15 +- Bugzilla/WebService/Bug.pm | 480 +++++++++++++++++++++++++++++++++++++++ Bugzilla/WebService/Constants.pm | 1 + 3 files changed, 494 insertions(+), 2 deletions(-) (limited to 'Bugzilla') diff --git a/Bugzilla/Bug.pm b/Bugzilla/Bug.pm index efee14826..e28c752df 100644 --- a/Bugzilla/Bug.pm +++ b/Bugzilla/Bug.pm @@ -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}; } diff --git a/Bugzilla/WebService/Bug.pm b/Bugzilla/WebService/Bug.pm index dd46d870e..c083bd491 100644 --- a/Bugzilla/WebService/Bug.pm +++ b/Bugzilla/WebService/Bug.pm @@ -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 + +B + +=over + +=item B + +Allows you to update the fields of a bug. Automatically sends emails +out about the changes. + +=item B + +=over + +=item C + +Array of Cs or Cs. The ids or aliases of the bugs that +you want to modify. + +=back + +B: All following fields specify the values you want to set on the +bugs you are updating. + +=over + +=item C + +(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, passing in +a value for C will cause an error to be thrown. + +=item C + +C The full login name of the user this bug is assigned to. + +=item C + +=item C + +C 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 An array of Cs. Bug ids to add to this field. + +=item C An array of Cs. Bug ids to remove from this field. +If the bug ids are not already in the field, they will be ignored. + +=item C An array of Cs. An exact set of bug ids to set this +field to, overriding the current value. If you specify C, then C +and C will be ignored. + +=back + +=item C + +C The users on the cc list. To modify this field, pass a hash, which +may have the following fields: + +=over + +=item C Array of Cs. 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 Array of Cs. 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 + +C 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 + +C. A comment on the change. The hash may contain the following fields: + +=over + +=item C C The actual text of the comment. +B: For compatibility with the parameters to L, +you can also call this field C, if you want. + +=item C C 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 + +C 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 id of comments (not +their count on a bug, like #1, #2, #3, but their globally-unique id, +as returned by L) and the value is a C which specifies +whether that comment should become private (C) or public (C). + +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 + +C The Component the bug is in. + +=item C + +C 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 + +C 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 set the C or C fields. They will +automatically be set by Bugzilla to the appropriate values for +duplicate bugs. + +=item C + +C The total estimate of time required to fix the bug, in hours. +This is the I estimate, not the amount of time remaining to fix it. + +=item C + +C The groups a bug is in. To modify this field, pass a hash, which +may have the following fields: + +=over + +=item C Array of Cs. 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 Array of Cs. 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 + +C Keywords on the bug. To modify this field, pass a hash, which +may have the following fields: + +=over + +=item C An array of Cs. 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 An array of Cs. 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 An array of Cs. 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 overrides C and +C. + +=back + +=item C + +C The Operating System ("OS") field on the bug. + +=item C + +C The Platform or "Hardware" field on the bug. + +=item C + +C The Priority field on the bug. + +=item C + +C The name of the product that the bug is in. If you change +this, you will probably also want to change C, +C, and C, since those have different legal +values in every product. + +If you cannot change the C 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 + +C The full login name of the bug's QA Contact. + +=item C + +C 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 + +C How much work time is remaining to fix the bug, in hours. +If you set C but don't explicitly set C, +then the C will be deducted from the bug's C. + +=item C + +C If true, the C 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 + +C If true, the C 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 + +C 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 value (even an empty or null string) on an +open bug will cause an error to be thrown. + +If you change the C field to an open status, the resolution +field will automatically be cleared, so you don't have to clear it +manually. + +=item C + +C 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 An array of Cs. 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 An array of Cs. URLs to remove from the field. +Invalid URLs will be ignored. + +=back + +=item C + +C The Severity field of a bug. + +=item C + +C 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. + +=item C + +C The Summary field of the bug. + +=item C + +C The bug's Target Milestone. + +=item C + +C The "URL" field of a bug. + +=item C + +C The bug's Version field. + +=item C + +C The Status Whiteboard field of a bug. + +=item C + +C The number of hours worked on this bug as part of this change. +If you set C but don't explicitly set C, +then the C will be deducted from the bug's C. + +=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 + +A C with a single field, "bugs". This points to an array of hashes +with the following fields: + +=over + +=item C + +C The id of the bug that was updated. + +=item C + +C The alias of the bug that was updated, if aliases are enabled and +this bug has an alias. + +=item C + +C 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 + +C 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 (C) The values that were added to this field, +possibly a comma-and-space-separated list if multiple values were added. + +=item C (C) 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 + +This function can throw all of the errors that L 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 + +=over + +=item Added in Bugzilla B<4.0>. + +=back + +=back + + =item C B diff --git a/Bugzilla/WebService/Constants.pm b/Bugzilla/WebService/Constants.pm index 0ada98ba4..44b7bb4e0 100644 --- a/Bugzilla/WebService/Constants.pm +++ b/Bugzilla/WebService/Constants.pm @@ -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, -- cgit v1.2.3-24-g4f1b