From 96d709edae39fc961ad96a7e330cd116b35f1408 Mon Sep 17 00:00:00 2001 From: Max Kanat-Alexander Date: Wed, 29 Sep 2010 17:54:55 -0700 Subject: Bug 573195: Make Bug.get return all of a bug's standard and custom field information r=dkl, a=mkanat --- Bugzilla/WebService/Bug.pm | 333 ++++++++++++++++++++++++++++++++++-------- Bugzilla/WebService/Server.pm | 2 + 2 files changed, 271 insertions(+), 64 deletions(-) (limited to 'Bugzilla/WebService') diff --git a/Bugzilla/WebService/Bug.pm b/Bugzilla/WebService/Bug.pm index 953355385..14642bca9 100644 --- a/Bugzilla/WebService/Bug.pm +++ b/Bugzilla/WebService/Bug.pm @@ -780,56 +780,126 @@ sub attachments { # Private Helper Subroutines # ############################## -# A helper for get() and search(). +# A helper for get() and search(). This is done in this fashion in order +# to produce a stable API and to explicitly type return values. +# The internals of Bugzilla::Bug are not stable enough to just +# return them directly. + sub _bug_to_hash { - my ($self, $bug, $filters) = @_; + my ($self, $bug, $params) = @_; + + # All the basic bug attributes are here, in alphabetical order. + # A bug attribute is "basic" if it doesn't require an additional + # database call to get the info. + my %item = ( + alias => $self->type('string', $bug->alias), + classification => $self->type('string', $bug->classification), + component => $self->type('string', $bug->component), + creation_time => $self->type('dateTime', $bug->creation_ts), + id => $self->type('int', $bug->bug_id), + is_confirmed => $self->type('boolean', $bug->everconfirmed), + last_change_time => $self->type('dateTime', $bug->delta_ts), + op_sys => $self->type('string', $bug->op_sys), + platform => $self->type('string', $bug->rep_platform), + priority => $self->type('string', $bug->priority), + product => $self->type('string', $bug->product), + resolution => $self->type('string', $bug->resolution), + severity => $self->type('string', $bug->bug_severity), + status => $self->type('string', $bug->bug_status), + summary => $self->type('string', $bug->short_desc), + target_milestone => $self->type('string', $bug->target_milestone), + url => $self->type('string', $bug->bug_file_loc), + version => $self->type('string', $bug->version), + whiteboard => $self->type('string', $bug->status_whiteboard), + ); + + + # First we handle any fields that require extra SQL calls. + # We don't do the SQL calls at all if the filter would just + # eliminate them anyway. + if (filter_wants $params, 'assigned_to') { + $item{'assigned_to'} = $self->type('string', $bug->assigned_to->login); + } + if (filter_wants $params, 'blocks') { + my @blocks = map { $self->type('int', $_) } @{ $bug->blocked }; + $item{'blocks'} = \@blocks; + } + if (filter_wants $params, 'cc') { + my @cc = map { $self->type('string', $_) } @{ $bug->cc || [] }; + $item{'cc'} = \@cc; + } + if (filter_wants $params, 'creator') { + $item{'creator'} = $self->type('string', $bug->reporter->login); + } + if (filter_wants $params, 'depends_on') { + my @depends_on = map { $self->type('int', $_) } @{ $bug->dependson }; + $item{'depends_on'} = \@depends_on; + } + if (filter_wants $params, 'dupe_of') { + $item{'dupe_of'} = $self->type('int', $bug->dup_id); + } + if (filter_wants $params, 'groups') { + my @groups = map { $self->type('string', $_->name) } + @{ $bug->groups_in }; + $item{'groups'} = \@groups; + } + if (filter_wants $params, 'is_open') { + $item{'is_open'} = $self->type('boolean', $bug->status->is_open); + } + if (filter_wants $params, 'keywords') { + my @keywords = map { $self->type('string', $_->name) } + @{ $bug->keyword_objects }; + $item{'keywords'} = \@keywords; + } + if (filter_wants $params, 'qa_contact') { + my $qa_login = $bug->qa_contact ? $bug->qa_contact->login : ''; + $item{'qa_contact'} = $self->type('string', $qa_login); + } + if (filter_wants $params, 'see_also') { + my @see_also = map { $self->type('string', $_) } @{ $bug->see_also }; + $item{'see_also'} = \@see_also; + } - # Timetracking fields are deleted if the user doesn't belong to - # the corresponding group. - unless (Bugzilla->user->is_timetracker) { - delete $bug->{'estimated_time'}; - delete $bug->{'remaining_time'}; - delete $bug->{'deadline'}; + # And now custom fields + my @custom_fields = Bugzilla->active_custom_fields; + foreach my $field (@custom_fields) { + my $name = $field->name; + next if !filter_wants $params, $name; + if ($field->type == FIELD_TYPE_BUG_ID) { + $item{$name} = $self->type('int', $bug->$name); + } + elsif ($field->type == FIELD_TYPE_DATETIME) { + $item{$name} = $self->type('dateTime', $bug->$name); + } + elsif ($field->type == FIELD_TYPE_MULTI_SELECT) { + my @values = map { $self->type('string', $_) } @{ $bug->$name }; + $item{$name} = \@values; + } + else { + $item{$name} = $self->type('string', $bug->$name); + } } - # This is done in this fashion in order to produce a stable API. - # The internals of Bugzilla::Bug are not stable enough to just - # return them directly. - my %item; - $item{'internals'} = $bug; - $item{'creation_time'} = $self->type('dateTime', $bug->creation_ts); - $item{'last_change_time'} = $self->type('dateTime', $bug->delta_ts); - $item{'id'} = $self->type('int', $bug->bug_id); - $item{'summary'} = $self->type('string', $bug->short_desc); - $item{'assigned_to'} = $self->type('string', $bug->assigned_to->login); - $item{'resolution'} = $self->type('string', $bug->resolution); - $item{'status'} = $self->type('string', $bug->bug_status); - $item{'is_open'} = $self->type('boolean', $bug->status->is_open); - $item{'severity'} = $self->type('string', $bug->bug_severity); - $item{'priority'} = $self->type('string', $bug->priority); - $item{'product'} = $self->type('string', $bug->product); - $item{'component'} = $self->type('string', $bug->component); - $item{'dupe_of'} = $self->type('int', $bug->dup_id); + # Timetracking fields are only sent if the user can see them. + if (Bugzilla->user->is_timetracker) { + $item{'estimated_time'} = $self->type('double', $bug->estimated_time); + $item{'remaining_time'} = $self->type('double', $bug->remaining_time); + $item{'deadline'} = $self->type('dateTime', $bug->deadline); + } if (Bugzilla->user->id) { my $token = issue_hash_token([$bug->id, $bug->delta_ts]); $item{'update_token'} = $self->type('string', $token); } - # if we do not delete this key, additional user info, including their - # real name, etc, will wind up in the 'internals' hashref - delete $item{internals}->{assigned_to_obj}; - - if (Bugzilla->params->{'usebugaliases'}) { - $item{'alias'} = $self->type('string', $bug->alias); - } - else { - # For API reasons, we always want the value to appear, we just - # don't want it to have a value if aliases are turned off. - $item{'alias'} = undef; - } + # The "accessible" bits go here because they have long names and it + # makes the code look nicer to separate them out. + $item{'is_cc_accessible'} = $self->type('boolean', + $bug->cclist_accessible); + $item{'is_creator_accessible'} = $self->type('boolean', + $bug->reporter_accessible); - return filter $filters, \%item; + return filter $params, \%item; } sub _attachment_to_hash { @@ -1462,6 +1532,10 @@ Note: Can also be called as "get_bugs" for compatibilty with Bugzilla 3.0 API. =item B +In addition to the parameters below, this method also accepts the +standard L and +L arguments. + =over =item C @@ -1500,72 +1574,195 @@ the valid ids. Each hash contains the following items: =over -=item alias +=item C -C The alias of this bug. If there is no alias or aliases are -disabled in this Bugzilla, this will be an empty string. +C The unique alias of this bug. -=item assigned_to +=item C C The login name of the user to whom the bug is assigned. -=item component +=item C + +C of Cs. The ids of bugs that are "blocked" by this bug. + +=item C + +C of Cs. The login names of users on the CC list of this +bug. + +=item C + +C The name of the current classification the bug is in. + +=item C C The name of the current component of this bug. -=item creation_time +=item C C When the bug was created. -=item dupe_of +=item C + +C The login name of the person who filed this bug (the reporter). + +=item C + +C The day that this bug is due to be completed. + +If you are not in the time-tracking group, this field will not be included +in the return value. + +=item C + +C of Cs. The ids of bugs that this bug "depends on". + +=item C C The bug ID of the bug that this bug is a duplicate of. If this bug -isn't a duplicate of any bug, this will be an empty int. +isn't a duplicate of any bug, this will be null. -=item id +=item C -C The numeric bug_id of this bug. +C The number of hours that it was estimated that this bug would +take. + +If you are not in the time-tracking group, this field will not be included +in the return value. + +=item C + +C of Cs. The names of all the groups that this bug is in. + +=item C + +C The unique numeric id of this bug. -=item internals B +=item C -A hash. The internals of a L object. This is extremely -unstable, and you should only rely on this if you absolutely have to. The -structure of the hash may even change between point releases of Bugzilla. +C If true, this bug can be accessed by members of the CC list, +even if they are not in the groups the bug is restricted to. -This will be disappearing in a future version of Bugzilla. +=item C -=item is_open +C True if the bug has been confirmed. Usually this means that +the bug has at some point been moved out of the C status +and into another open status. -C Returns true (1) if this bug is open, false (0) if it is closed. +=item C + +C True if this bug is open, false if it is closed. + +=item C + +C If true, this bug can be accessed by the creator (reporter) +of the bug, even if he or she is not a member of the groups the bug +is restricted to. -=item last_change_time +=item C + +C of Cs. Each keyword that is on this bug. + +=item C C When the bug was last changed. -=item priority +=item C + +C The name of the operating system that the bug was filed against. + +=item C + +C The name of the platform (hardware) that the bug was filed against. + +=item C C The priority of the bug. -=item product +=item C C The name of the product this bug is in. -=item resolution +=item C -C The current resolution of the bug, or an empty string if the bug is open. +C The login name of the current QA Contact on the bug. -=item severity +=item C + +C The number of hours of work remaining until work on this bug +is complete. + +If you are not in the time-tracking group, this field will not be included +in the return value. + +=item C + +C The current resolution of the bug, or an empty string if the bug +is open. + +=item C + +B + +C of Cs. The URLs in the See Also field on the bug. + +=item C C The current severity of the bug. -=item status +=item C C The current status of the bug. -=item summary +=item C C The summary of this bug. +=item C + +C The milestone that this bug is supposed to be fixed by, or for +closed bugs, the milestone that it was fixed for. + +=item C + +C The token that you would have to pass to the F +page in order to update this bug. This changes every time the bug is +updated. + +This field is not returned to logged-out users. + +=item C + +B + +C A URL that demonstrates the problem described in +the bug, or is somehow related to the bug report. + +=item C + +C The version the bug was reported against. + +=item C + +C The value of the "status whiteboard" field on the bug. + +=item I + +Every custom field in this installation will also be included in the +return value. Most fields are returned as Cs. However, some +field types have different return values: + +=over + +=item Bug ID Fields - C + +=item Multiple-Selection Fields - C of Cs. + +=item Date/Time Fields - C + +=back + =back =item C B @@ -1653,6 +1850,14 @@ in Bugzilla B<3.4>: =back +=item In Bugzilla B<4.0>, the following items were added to the C +return value: C, C, C, C, +C, C, C, C, +C, C, C, C, +C, C, C, C, C, +C, C, C, C, C, +and all custom fields. + =back diff --git a/Bugzilla/WebService/Server.pm b/Bugzilla/WebService/Server.pm index 71c790e8e..f4b3600d4 100644 --- a/Bugzilla/WebService/Server.pm +++ b/Bugzilla/WebService/Server.pm @@ -43,6 +43,8 @@ sub datetime_format_inbound { sub datetime_format_outbound { my ($self, $date) = @_; + return undef if (!defined $date or $date eq ''); + my $time = $date; if (blessed($date)) { # We expect this to mean we were sent a datetime object -- cgit v1.2.3-24-g4f1b