summaryrefslogtreecommitdiffstats
path: root/Bugzilla/WebService
diff options
context:
space:
mode:
authorSimon Green <sgreen@redhat.com>2014-05-22 01:17:46 +0200
committerSimon Green <sgreen@redhat.com>2014-05-22 01:17:46 +0200
commit9bd3f08dce23acc052819d97d1f082666c354b20 (patch)
tree6fee7c37bb9bff84a681593eff2e565e7bdb5f3e /Bugzilla/WebService
parentabf6ec5cff89b9507aa978d5345559239886c014 (diff)
downloadbugzilla-9bd3f08dce23acc052819d97d1f082666c354b20.tar.gz
bugzilla-9bd3f08dce23acc052819d97d1f082666c354b20.tar.xz
Bug 1008764: Add a web service to create and update Flag types
r=glob, a=justdave
Diffstat (limited to 'Bugzilla/WebService')
-rw-r--r--Bugzilla/WebService/Constants.pm13
-rw-r--r--Bugzilla/WebService/FlagType.pm647
-rw-r--r--Bugzilla/WebService/Server/REST/Resources/FlagType.pm56
3 files changed, 714 insertions, 2 deletions
diff --git a/Bugzilla/WebService/Constants.pm b/Bugzilla/WebService/Constants.pm
index e18e2b8ec..5164ec0c1 100644
--- a/Bugzilla/WebService/Constants.pm
+++ b/Bugzilla/WebService/Constants.pm
@@ -81,8 +81,9 @@ use constant WS_ERROR_CODE => {
illegal_field => 104,
freetext_too_long => 104,
# Component errors
- require_component => 105,
- component_name_too_long => 105,
+ require_component => 105,
+ component_name_too_long => 105,
+ product_unknown_component => 105,
# Invalid Product
no_products => 106,
entry_access_denied => 106,
@@ -191,6 +192,13 @@ use constant WS_ERROR_CODE => {
# Search errors are 1000-1100
buglist_parameters_required => 1000,
+ # Flag type errors are 1100-1200
+ flag_type_name_invalid => 1101,
+ flag_type_description_invalid => 1102,
+ flag_type_cc_list_invalid => 1103,
+ flag_type_sortkey_invalid => 1104,
+ flag_type_not_editable => 1105,
+
# Errors thrown by the WebService itself. The ones that are negative
# conform to http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
xmlrpc_invalid_value => -32600,
@@ -269,6 +277,7 @@ sub WS_DISPATCH {
'Bugzilla' => 'Bugzilla::WebService::Bugzilla',
'Bug' => 'Bugzilla::WebService::Bug',
'Classification' => 'Bugzilla::WebService::Classification',
+ 'FlagType' => 'Bugzilla::WebService::FlagType',
'Group' => 'Bugzilla::WebService::Group',
'Product' => 'Bugzilla::WebService::Product',
'User' => 'Bugzilla::WebService::User',
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<Bugzilla::WebService> for a description of what B<STABLE>, B<UNSTABLE>,
+and B<EXPERIMENTAL> mean, and for more description about error codes.
+
+=head2 Create Flag
+
+=over
+
+=item C<create> B<UNSTABLE>
+
+=item B<Description>
+
+Creates a new FlagType
+
+=item B<REST>
+
+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<Params>
+
+At a minimum the following two arguments must be supplied:
+
+=over
+
+=item C<name> (string) - The name of the new Flag Type.
+
+=item C<description> (string) - A description for the Flag Type object.
+
+=back
+
+=item B<Returns>
+
+C<int> flag_id
+
+The ID of the new FlagType object is returned.
+
+=item B<Params>
+
+=over
+
+=item name B<required>
+
+C<string> A short name identifying this type.
+
+=item description B<required>
+
+C<string> A comprehensive description of this type.
+
+=item inclusions B<optional>
+
+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<All> components of I<FooProduct>,
+components C1 and C3 of I<BarProduct>, and C7 of I<BazProduct>.
+
+=item exclusions B<optional>
+
+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<optional>
+
+C<int> 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<optional>
+
+C<boolean> Flag of this type appear in the UI and can be set. Default is B<true>.
+
+=item is_requestable B<optional>
+
+C<boolean> Users can ask for flags of this type to be set. Default is B<true>.
+
+=item cc_list B<optional>
+
+C<array> 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<optional>
+
+C<boolean> Users can ask specific other users to set flags of this type as
+opposed to just asking the wind. Default is B<true>.
+
+=item is_multiplicable B<optional>
+
+C<boolean> Multiple flags of this type can be set on the same bug. Default is B<true>.
+
+=item grant_group B<optional>
+
+C<string> 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<no group>.
+
+=item request_group B<optional>
+
+C<string> 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<no group>.
+
+=back
+
+=item B<Errors>
+
+=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<History>
+
+=over
+
+=item Added in Bugzilla B<5.0>.
+
+=back
+
+=back
+
+=head2 update
+
+B<EXPERIMENTAL>
+
+=over
+
+=item B<Description>
+
+This allows you to update a flag type in Bugzilla.
+
+=item B<REST>
+
+PUT /rest/flagtype/<product_id_or_name>
+
+The params to include in the PUT body as well as the returned data format,
+are the same as below. The C<ids> and C<names> params will be overridden as
+it is pulled from the URL path.
+
+=item B<Params>
+
+B<Note:> The following parameters specify which products you are updating.
+You must set one or both of these parameters.
+
+=over
+
+=item C<ids>
+
+C<array> of C<int>s. Numeric ids of the flag types that you wish to update.
+
+=item C<names>
+
+C<array> of C<string>s. 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<Note:> The following parameters specify the new values you want to set for
+the products you are updating.
+
+=over
+
+=item name
+
+C<string> A short name identifying this type.
+
+=item description
+
+C<string> A comprehensive description of this type.
+
+=item inclusions B<optional>
+
+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<All> components of I<FooProduct>,
+components C1 and C3 of I<BarProduct>, and C7 of I<BazProduct>.
+
+=item exclusions B<optional>
+
+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<int> 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<boolean> Flag of this type appear in the UI and can be set.
+
+=item is_requestable
+
+C<boolean> Users can ask for flags of this type to be set.
+
+=item cc_list
+
+C<array> 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<boolean> Users can ask specific other users to set flags of this type as
+opposed to just asking the wind.
+
+=item is_multiplicable
+
+C<boolean> Multiple flags of this type can be set on the same bug.
+
+=item grant_group
+
+C<string> 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<string> 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<Returns>
+
+A C<hash> with a single field "flagtypes". This points to an array of hashes
+with the following fields:
+
+=over
+
+=item C<id>
+
+C<int> The id of the product that was updated.
+
+=item C<name>
+
+C<string> The name of the product that was updated.
+
+=item C<changes>
+
+C<hash> 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<added>
+
+C<string> The value that this field was changed to.
+
+=item C<removed>
+
+C<string> 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<Errors>
+
+=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<History>
+
+=over
+
+=item Added in Bugzilla B<5.0>.
+
+=back
+
+=back
diff --git a/Bugzilla/WebService/Server/REST/Resources/FlagType.pm b/Bugzilla/WebService/Server/REST/Resources/FlagType.pm
new file mode 100644
index 000000000..745785838
--- /dev/null
+++ b/Bugzilla/WebService/Server/REST/Resources/FlagType.pm
@@ -0,0 +1,56 @@
+# 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::Server::REST::Resources::FlagType;
+
+use 5.10.1;
+use strict;
+
+use Bugzilla::WebService::Constants;
+use Bugzilla::WebService::FlagType;
+
+use Bugzilla::Error;
+
+BEGIN {
+ *Bugzilla::WebService::FlagType::rest_resources = \&_rest_resources;
+};
+
+sub _rest_resources {
+ my $rest_resources = [
+ qr{^/flagtype$}, {
+ POST => {
+ method => 'create',
+ success_code => STATUS_CREATED
+ }
+ },
+ qr{^/flagtype/([^/]+)$}, {
+ PUT => {
+ method => 'update',
+ params => sub {
+ my $param = $_[0] =~ /^\d+$/ ? 'ids' : 'names';
+ return { $param => [ $_[0] ] };
+ }
+ }
+ },
+ ];
+ return $rest_resources;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Bugzilla::Webservice::Server::REST::Resources::FlagType - The Flag Type REST API
+
+=head1 DESCRIPTION
+
+This part of the Bugzilla REST API allows you to create and update Flag types.
+
+See L<Bugzilla::WebService::FlagType> for more details on how to use this
+part of the REST API.