From 9bd3f08dce23acc052819d97d1f082666c354b20 Mon Sep 17 00:00:00 2001 From: Simon Green Date: Thu, 22 May 2014 09:17:46 +1000 Subject: Bug 1008764: Add a web service to create and update Flag types r=glob, a=justdave --- Bugzilla/WebService/FlagType.pm | 647 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 647 insertions(+) create mode 100644 Bugzilla/WebService/FlagType.pm (limited to 'Bugzilla/WebService/FlagType.pm') diff --git a/Bugzilla/WebService/FlagType.pm b/Bugzilla/WebService/FlagType.pm new file mode 100644 index 000000000..d755b8885 --- /dev/null +++ b/Bugzilla/WebService/FlagType.pm @@ -0,0 +1,647 @@ +# 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::FlagType; + +use 5.10.1; +use strict; + +use parent qw(Bugzilla::WebService); +use Bugzilla::Component; +use Bugzilla::Constants; +use Bugzilla::Error; +use Bugzilla::FlagType; +use Bugzilla::Product; +use Bugzilla::Util qw(trim); + +use List::MoreUtils qw(uniq); + +sub create { + my ($self, $params) = @_; + + my $dbh = Bugzilla->dbh; + my $user = Bugzilla->user; + + Bugzilla->user->in_group('editcomponents') + || scalar(@{$user->get_products_by_permission('editcomponents')}) + || ThrowUserError("auth_failure", { group => "editcomponents", + action => "add", + object => "flagtypes" }); + + $params->{name} || ThrowCodeError('param_required', { param => 'name' }); + $params->{description} || ThrowCodeError('param_required', { param => 'description' }); + + my %args = ( + sortkey => 1, + name => undef, + inclusions => ['0:0'], # Default to __ALL__:__ALL__ + cc_list => '', + description => undef, + is_requestable => 'on', + exclusions => [], + is_multiplicable => 'on', + request_group => '', + is_active => 'on', + is_specifically_requestable => 'on', + target_type => 'bug', + grant_group => '', + ); + + foreach my $key (keys %args) { + $args{$key} = $params->{$key} if defined($params->{$key}); + } + + $args{name} = trim($params->{name}); + $args{description} = trim($params->{description}); + + # Is specifically requestable is actually is_requesteeable + if (exists $args{is_specifically_requestable}) { + $args{is_requesteeble} = delete $args{is_specifically_requestable}; + } + + # Default is on for the tickbox flags. + # If the user has set them to 'off' then undefine them so the flags are not ticked + foreach my $arg_name (qw(is_requestable is_multiplicable is_active is_requesteeble)) { + if (defined($args{$arg_name}) && ($args{$arg_name} eq '0')) { + $args{$arg_name} = undef; + } + } + + # Process group inclusions and exclusions + $args{inclusions} = _process_lists($params->{inclusions}) if defined $params->{inclusions}; + $args{exclusions} = _process_lists($params->{exclusions}) if defined $params->{exclusions}; + + my $flagtype = Bugzilla::FlagType->create(\%args); + + return { id => $self->type('int', $flagtype->id) }; +} + +sub update { + my ($self, $params) = @_; + + my $dbh = Bugzilla->dbh; + my $user = Bugzilla->user; + + Bugzilla->login(LOGIN_REQUIRED); + $user->in_group('editcomponents') + || scalar(@{$user->get_products_by_permission('editcomponents')}) + || ThrowUserError("auth_failure", { group => "editcomponents", + action => "edit", + object => "flagtypes" }); + + defined($params->{names}) || defined($params->{ids}) + || ThrowCodeError('params_required', + { function => 'FlagType.update', params => ['ids', 'names'] }); + + # Get the list of unique flag type ids we are updating + my @flag_type_ids = defined($params->{ids}) ? @{$params->{ids}} : (); + if (defined $params->{names}) { + push @flag_type_ids, map { $_->id } + @{ Bugzilla::FlagType::match({ name => $params->{names} }) }; + } + @flag_type_ids = uniq @flag_type_ids; + + # We delete names and ids to keep only new values to set. + delete $params->{names}; + delete $params->{ids}; + + # Process group inclusions and exclusions + # We removed them from $params because these are handled differently + my $inclusions = _process_lists(delete $params->{inclusions}) if defined $params->{inclusions}; + my $exclusions = _process_lists(delete $params->{exclusions}) if defined $params->{exclusions}; + + $dbh->bz_start_transaction(); + my %changes = (); + + foreach my $flag_type_id (@flag_type_ids) { + my ($flagtype, $can_fully_edit) = $user->check_can_admin_flagtype($flag_type_id); + + if ($can_fully_edit) { + $flagtype->set_all($params); + } + elsif (scalar keys %$params) { + ThrowUserError('flag_type_not_editable', { flagtype => $flagtype }); + } + + # Process the clusions + foreach my $type ('inclusions', 'exclusions') { + my $clusions = $type eq 'inclusions' ? $inclusions : $exclusions; + next if not defined $clusions; + + my @extra_clusions = (); + if (!$user->in_group('editcomponents')) { + my $products = $user->get_products_by_permission('editcomponents'); + # Bring back the products the user cannot edit. + foreach my $item (values %{$flagtype->$type}) { + my ($prod_id, $comp_id) = split(':', $item); + push(@extra_clusions, $item) unless grep { $_->id == $prod_id } @$products; + } + } + + $flagtype->set_clusions({ + $type => [@$clusions, @extra_clusions], + }); + } + + my $returned_changes = $flagtype->update(); + $changes{$flagtype->id} = { + name => $flagtype->name, + changes => $returned_changes, + }; + } + $dbh->bz_commit_transaction(); + + my @result; + foreach my $flag_type_id (keys %changes) { + my %hash = ( + id => $self->type('int', $flag_type_id), + name => $self->type('string', $changes{$flag_type_id}{name}), + changes => {}, + ); + + foreach my $field (keys %{ $changes{$flag_type_id}{changes} }) { + my $change = $changes{$flag_type_id}{changes}{$field}; + $hash{changes}{$field} = { + removed => $self->type('string', $change->[0]), + added => $self->type('string', $change->[1]) + }; + } + + push(@result, \%hash); + } + + return { flagtypes => \@result }; +} + +sub _process_lists { + my $list = shift; + my $user = Bugzilla->user; + + my @products; + if ($user->in_group('editcomponents')) { + @products = Bugzilla::Product->get_all; + } + else { + @products = @{$user->get_products_by_permission('editcomponents')}; + } + + my @component_list; + + foreach my $item (@$list) { + # A hash with products as the key and component names as the values + if(ref($item) eq 'HASH') { + while (my ($product_name, $component_names) = each %$item) { + my $product = Bugzilla::Product->check({name => $product_name}); + unless (grep { $product->name eq $_->name } @products) { + ThrowUserError('product_access_denied', { name => $product_name }); + } + my @component_ids; + + foreach my $comp_name (@$component_names) { + my $component = Bugzilla::Component->check({product => $product, name => $comp_name}); + ThrowCodeError('param_invalid', { param => $comp_name}) unless defined $component; + push @component_list, $product->id . ':' . $component->id; + } + } + } + elsif(!ref($item)) { + # These are whole products + my $product = Bugzilla::Product->check({name => $item}); + unless (grep { $product->name eq $_->name } @products) { + ThrowUserError('product_access_denied', { name => $item }); + } + push @component_list, $product->id . ':0'; + } + else { + # The user has passed something invalid + ThrowCodeError('param_invalid', { param => $item }); + } + } + + return \@component_list; +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::WebService::FlagType - API for creating flags. + +=head1 DESCRIPTION + +This part of the Bugzilla API allows you to create new flags + +=head1 METHODS + +See L for a description of what B, B, +and B mean, and for more description about error codes. + +=head2 Create Flag + +=over + +=item C B + +=item B + +Creates a new FlagType + +=item B + +POST /rest/flagtype + +The params to include in the POST body as well as the returned data format, +are the same as below. + +=item B + +At a minimum the following two arguments must be supplied: + +=over + +=item C (string) - The name of the new Flag Type. + +=item C (string) - A description for the Flag Type object. + +=back + +=item B + +C flag_id + +The ID of the new FlagType object is returned. + +=item B + +=over + +=item name B + +C A short name identifying this type. + +=item description B + +C A comprehensive description of this type. + +=item inclusions B + +An array of strings or a hash containing product names, and optionally +component names. If you provide a string, the flag type will be shown on +all bugs in that product. If you provide a hash, the key represents the +product name, and the value is the components of the product to be included. + +For example: + + [ 'FooProduct', + { + BarProduct => [ 'C1', 'C3' ], + BazProduct => [ 'C7' ] + } + ] + +This flag will be added to B components of I, +components C1 and C3 of I, and C7 of I. + +=item exclusions B + +An array of strings or hashes containing product names. This uses the same +fromat as inclusions. + +This will exclude the flag from all products and components specified. + +=item sortkey B + +C A number between 1 and 32767 by which this type will be sorted when +displayed to users in a list; ignore if you don't care what order the types +appear in or if you want them to appear in alphabetical order. + +=item is_active B + +C Flag of this type appear in the UI and can be set. Default is B. + +=item is_requestable B + +C Users can ask for flags of this type to be set. Default is B. + +=item cc_list B + +C An array of strings. If the flag type is requestable, who should +receive e-mail notification of requests. This is an array of e-mail addresses +which do not need to be Bugzilla logins. + +=item is_specifically_requestable B + +C Users can ask specific other users to set flags of this type as +opposed to just asking the wind. Default is B. + +=item is_multiplicable B + +C Multiple flags of this type can be set on the same bug. Default is B. + +=item grant_group B + +C The group allowed to grant/deny flags of this type (to allow all +users to grant/deny these flags, select no group). Default is B. + +=item request_group B + +C If flags of this type are requestable, the group allowed to request +them (to allow all users to request these flags, select no group). Note that +the request group alone has no effect if the grant group is not defined! +Default is B. + +=back + +=item B + +=over + +=item 51 (Group Does Not Exist) + +The group name you entered does not exist, or you do not have access to it. + +=item 105 (Unknown component) + +The component does not exist for this product. + +=item 106 (Product Access Denied) + +Either the product does not exist or you don't have editcomponents privileges +to it. + +=item 501 (Illegal Email Address) + +One of the e-mail address in the CC list is invalid. An e-mail in the CC +list does NOT need to be a valid Bugzilla user. + +=item 1101 (Flag Type Name invalid) + +You must specify a non-blank name for this flag type. It must +no contain spaces or commas, and must be 50 characters or less. + +=item 1102 (Flag type must have description) + +You must specify a description for this flag type. + +=item 1103 (Flag type CC list is invalid + +The CC list must be 200 characters or less. + +=item 1104 (Flag Type Sort Key Not Valid) + +The sort key is not a valid number. + +=item 1105 (Flag Type Not Editable) + +This flag type is not available for the products you can administer. Therefore +you can not edit attributes of the flag type, other than the inclusion and +exclusion list. + +=back + +=item B + +=over + +=item Added in Bugzilla B<5.0>. + +=back + +=back + +=head2 update + +B + +=over + +=item B + +This allows you to update a flag type in Bugzilla. + +=item B + +PUT /rest/flagtype/ + +The params to include in the PUT body as well as the returned data format, +are the same as below. The C and C params will be overridden as +it is pulled from the URL path. + +=item B + +B The following parameters specify which products you are updating. +You must set one or both of these parameters. + +=over + +=item C + +C of Cs. Numeric ids of the flag types that you wish to update. + +=item C + +C of Cs. Names of the flag types that you wish to update. If +many flag types have the same name, this will change ALL of them. + +=back + +B The following parameters specify the new values you want to set for +the products you are updating. + +=over + +=item name + +C A short name identifying this type. + +=item description + +C A comprehensive description of this type. + +=item inclusions B + +An array of strings or a hash containing product names, and optionally +component names. If you provide a string, the flag type will be shown on +all bugs in that product. If you provide a hash, the key represents the +product name, and the value is the components of the product to be included. + +for example + + [ 'FooProduct', + { + BarProduct => [ 'C1', 'C3' ], + BazProduct => [ 'C7' ] + } + ] + +This flag will be added to B components of I, +components C1 and C3 of I, and C7 of I. + +=item exclusions B + +An array of strings or hashes containing product names. +This uses the same fromat as inclusions. + +This will exclude the flag from all products and components specified. + +=item sortkey + +C A number between 1 and 32767 by which this type will be sorted when +displayed to users in a list; ignore if you don't care what order the types +appear in or if you want them to appear in alphabetical order. + +=item is_active + +C Flag of this type appear in the UI and can be set. + +=item is_requestable + +C Users can ask for flags of this type to be set. + +=item cc_list + +C An array of strings. If the flag type is requestable, who should +receive e-mail notification of requests. This is an array of e-mail addresses +which do not need to be Bugzilla logins. + +=item is_specifically_requestable + +C Users can ask specific other users to set flags of this type as +opposed to just asking the wind. + +=item is_multiplicable + +C Multiple flags of this type can be set on the same bug. + +=item grant_group + +C The group allowed to grant/deny flags of this type (to allow all +users to grant/deny these flags, select no group). + +=item request_group + +C If flags of this type are requestable, the group allowed to request +them (to allow all users to request these flags, select no group). Note that +the request group alone has no effect if the grant group is not defined! + +=back + +=item B + +A C with a single field "flagtypes". This points to an array of hashes +with the following fields: + +=over + +=item C + +C The id of the product that was updated. + +=item C + +C The name of the product that was updated. + +=item C + +C The changes that were actually done on this product. 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 value that this field was changed to. + +=item C + +C The value that was previously set in this field. + +=back + +Note that booleans will be represented with the strings '1' and '0'. + +Here's an example of what a return value might look like: + + { + products => [ + { + id => 123, + changes => { + name => { + removed => 'FooFlagType', + added => 'BarFlagType' + }, + is_requestable => { + removed => '1', + added => '0', + } + } + } + ] + } + +=back + +=item B + +=over + +=item 51 (Group Does Not Exist) + +The group name you entered does not exist, or you do not have access to it. + +=item 105 (Unknown component) + +The component does not exist for this product. + +=item 106 (Product Access Denied) + +Either the product does not exist or you don't have editcomponents privileges +to it. + +=item 501 (Illegal Email Address) + +One of the e-mail address in the CC list is invalid. An e-mail in the CC +list does NOT need to be a valid Bugzilla user. + +=item 1101 (Flag Type Name invalid) + +You must specify a non-blank name for this flag type. It must +no contain spaces or commas, and must be 50 characters or less. + +=item 1102 (Flag type must have description) + +You must specify a description for this flag type. + +=item 1103 (Flag type CC list is invalid + +The CC list must be 200 characters or less. + +=item 1104 (Flag Type Sort Key Not Valid) + +The sort key is not a valid number. + +=item 1105 (Flag Type Not Editable) + +This flag type is not available for the products you can administer. Therefore +you can not edit attributes of the flag type, other than the inclusion and +exclusion list. + +=back + +=item B + +=over + +=item Added in Bugzilla B<5.0>. + +=back + +=back -- cgit v1.2.3-24-g4f1b