diff options
Diffstat (limited to 'extensions/PhabBugz/lib')
-rw-r--r-- | extensions/PhabBugz/lib/Config.pm | 76 | ||||
-rw-r--r-- | extensions/PhabBugz/lib/Constants.pm | 14 | ||||
-rw-r--r-- | extensions/PhabBugz/lib/Daemon.pm | 89 | ||||
-rw-r--r-- | extensions/PhabBugz/lib/Feed.pm | 1206 | ||||
-rw-r--r-- | extensions/PhabBugz/lib/Policy.pm | 133 | ||||
-rw-r--r-- | extensions/PhabBugz/lib/Project.pm | 355 | ||||
-rw-r--r-- | extensions/PhabBugz/lib/Revision.pm | 544 | ||||
-rw-r--r-- | extensions/PhabBugz/lib/Types.pm | 17 | ||||
-rw-r--r-- | extensions/PhabBugz/lib/User.pm | 202 | ||||
-rw-r--r-- | extensions/PhabBugz/lib/Util.pm | 251 | ||||
-rw-r--r-- | extensions/PhabBugz/lib/WebService.pm | 187 |
11 files changed, 1470 insertions, 1604 deletions
diff --git a/extensions/PhabBugz/lib/Config.pm b/extensions/PhabBugz/lib/Config.pm index d4b71430b..d808b607a 100644 --- a/extensions/PhabBugz/lib/Config.pm +++ b/extensions/PhabBugz/lib/Config.pm @@ -16,48 +16,40 @@ use Bugzilla::Config::Common; our $sortkey = 1300; sub get_param_list { - my ($class) = @_; - - my @params = ( - { - name => 'phabricator_enabled', - type => 'b', - default => 0 - }, - { - name => 'phabricator_base_uri', - type => 't', - default => '', - checker => \&check_urlbase - }, - { - name => 'phabricator_api_key', - type => 't', - default => '', - }, - { - name => 'phabricator_auth_callback_url', - type => 't', - default => '', - checker => sub { - my ($url) = (@_); - return 'must be an HTTP/HTTPS absolute URL' unless $url =~ m{^https?://}; - return ''; - } - }, - { - name => 'phabricator_app_id', - type => 't', - default => '', - checker => sub { - my ($app_id) = (@_); - return 'must be a hex number' unless $app_id =~ /^[[:xdigit:]]+$/; - return ''; - } - } - ); - - return @params; + my ($class) = @_; + + my @params = ( + {name => 'phabricator_enabled', type => 'b', default => 0}, + { + name => 'phabricator_base_uri', + type => 't', + default => '', + checker => \&check_urlbase + }, + {name => 'phabricator_api_key', type => 't', default => '',}, + { + name => 'phabricator_auth_callback_url', + type => 't', + default => '', + checker => sub { + my ($url) = (@_); + return 'must be an HTTP/HTTPS absolute URL' unless $url =~ m{^https?://}; + return ''; + } + }, + { + name => 'phabricator_app_id', + type => 't', + default => '', + checker => sub { + my ($app_id) = (@_); + return 'must be a hex number' unless $app_id =~ /^[[:xdigit:]]+$/; + return ''; + } + } + ); + + return @params; } 1; diff --git a/extensions/PhabBugz/lib/Constants.pm b/extensions/PhabBugz/lib/Constants.pm index 19987de25..642c1962b 100644 --- a/extensions/PhabBugz/lib/Constants.pm +++ b/extensions/PhabBugz/lib/Constants.pm @@ -13,13 +13,13 @@ use warnings; use base qw(Exporter); our @EXPORT = qw( - PHAB_AUTOMATION_USER - PHAB_ATTACHMENT_PATTERN - PHAB_CONTENT_TYPE - PHAB_FEED_POLL_SECONDS - PHAB_USER_POLL_SECONDS - PHAB_GROUP_POLL_SECONDS - PHAB_TIMEOUT + PHAB_AUTOMATION_USER + PHAB_ATTACHMENT_PATTERN + PHAB_CONTENT_TYPE + PHAB_FEED_POLL_SECONDS + PHAB_USER_POLL_SECONDS + PHAB_GROUP_POLL_SECONDS + PHAB_TIMEOUT ); use constant PHAB_ATTACHMENT_PATTERN => qr/^phabricator-D(\d+)/; diff --git a/extensions/PhabBugz/lib/Daemon.pm b/extensions/PhabBugz/lib/Daemon.pm index ef4a00534..9f995553f 100644 --- a/extensions/PhabBugz/lib/Daemon.pm +++ b/extensions/PhabBugz/lib/Daemon.pm @@ -21,7 +21,7 @@ use File::Spec; use Pod::Usage; sub start { - newdaemon(); + newdaemon(); } # @@ -29,69 +29,72 @@ sub start { # sub gd_preconfig { - my $self = shift; - my $pidfile = $self->{gd_args}{pidfile}; - if (!$pidfile) { - $pidfile = File::Spec->catfile(bz_locations()->{datadir}, $self->{gd_progname} . ".pid"); - } - return (pidfile => $pidfile); + my $self = shift; + my $pidfile = $self->{gd_args}{pidfile}; + if (!$pidfile) { + $pidfile = File::Spec->catfile(bz_locations()->{datadir}, + $self->{gd_progname} . ".pid"); + } + return (pidfile => $pidfile); } sub gd_getopt { - my $self = shift; - $self->SUPER::gd_getopt(); - if ($self->{gd_args}{progname}) { - $self->{gd_progname} = $self->{gd_args}{progname}; - } else { - $self->{gd_progname} = basename($0); - } - $self->{_original_zero} = $0; - $0 = $self->{gd_progname}; + my $self = shift; + $self->SUPER::gd_getopt(); + if ($self->{gd_args}{progname}) { + $self->{gd_progname} = $self->{gd_args}{progname}; + } + else { + $self->{gd_progname} = basename($0); + } + $self->{_original_zero} = $0; + $0 = $self->{gd_progname}; } sub gd_postconfig { - my $self = shift; - $0 = delete $self->{_original_zero}; + my $self = shift; + $0 = delete $self->{_original_zero}; } sub gd_more_opt { - my $self = shift; - return ( - 'pidfile=s' => \$self->{gd_args}{pidfile}, - 'n=s' => \$self->{gd_args}{progname}, - ); + my $self = shift; + return ( + 'pidfile=s' => \$self->{gd_args}{pidfile}, + 'n=s' => \$self->{gd_args}{progname}, + ); } sub gd_usage { - pod2usage({ -verbose => 0, -exitval => 'NOEXIT' }); - return 0; -}; + pod2usage({-verbose => 0, -exitval => 'NOEXIT'}); + return 0; +} sub gd_redirect_output { - my $self = shift; - - my $filename = File::Spec->catfile(bz_locations()->{datadir}, $self->{gd_progname} . ".log"); + my $self = shift; + + my $filename = File::Spec->catfile(bz_locations()->{datadir}, + $self->{gd_progname} . ".log"); + open(STDERR, ">>", $filename) or (print "could not open stderr: $!" && exit(1)); + close(STDOUT); + open(STDOUT, ">&", STDERR) or die "redirect STDOUT -> STDERR: $!"; + $SIG{HUP} = sub { + close(STDERR); open(STDERR, ">>", $filename) or (print "could not open stderr: $!" && exit(1)); - close(STDOUT); - open(STDOUT, ">&", STDERR) or die "redirect STDOUT -> STDERR: $!"; - $SIG{HUP} = sub { - close(STDERR); - open(STDERR, ">>", $filename) or (print "could not open stderr: $!" && exit(1)); - }; + }; } sub gd_setup_signals { - my $self = shift; - $self->SUPER::gd_setup_signals(); - $SIG{TERM} = sub { $self->gd_quit_event(); } + my $self = shift; + $self->SUPER::gd_setup_signals(); + $SIG{TERM} = sub { $self->gd_quit_event(); } } sub gd_run { - my $self = shift; - $SIG{__DIE__} = \&Carp::confess if $self->{debug}; - my $phabbugz = Bugzilla::Extension::PhabBugz::Feed->new(); - $phabbugz->is_daemon(1); - $phabbugz->start(); + my $self = shift; + $SIG{__DIE__} = \&Carp::confess if $self->{debug}; + my $phabbugz = Bugzilla::Extension::PhabBugz::Feed->new(); + $phabbugz->is_daemon(1); + $phabbugz->start(); } 1; diff --git a/extensions/PhabBugz/lib/Feed.pm b/extensions/PhabBugz/lib/Feed.pm index 71e6aa827..f199a96aa 100644 --- a/extensions/PhabBugz/lib/Feed.pm +++ b/extensions/PhabBugz/lib/Feed.pm @@ -26,7 +26,8 @@ use Bugzilla::Field; use Bugzilla::Logging; use Bugzilla::Mailer; use Bugzilla::Search; -use Bugzilla::Util qw(diff_arrays format_time with_writable_database with_readonly_database); +use Bugzilla::Util + qw(diff_arrays format_time with_writable_database with_readonly_database); use Bugzilla::Types qw(:types); use Bugzilla::Extension::PhabBugz::Types qw(:types); use Bugzilla::Extension::PhabBugz::Constants; @@ -34,663 +35,661 @@ use Bugzilla::Extension::PhabBugz::Policy; use Bugzilla::Extension::PhabBugz::Revision; use Bugzilla::Extension::PhabBugz::User; use Bugzilla::Extension::PhabBugz::Util qw( - create_revision_attachment - get_bug_role_phids - is_attachment_phab_revision - request - set_phab_user + create_revision_attachment + get_bug_role_phids + is_attachment_phab_revision + request + set_phab_user ); -has 'is_daemon' => ( is => 'rw', default => 0 ); +has 'is_daemon' => (is => 'rw', default => 0); -my $Invocant = class_type { class => __PACKAGE__ }; +my $Invocant = class_type {class => __PACKAGE__}; sub start { - my ($self) = @_; - - my $sig_alarm = IO::Async::Signal->new( - name => 'ALRM', - on_receipt => sub { - FATAL("Timeout reached"); - exit; - }, - ); - # Query for new revisions or changes - my $feed_timer = IO::Async::Timer::Periodic->new( - first_interval => 0, - interval => PHAB_FEED_POLL_SECONDS, - reschedule => 'drift', - on_tick => sub { - try { - with_writable_database { - alarm(PHAB_TIMEOUT); - $self->feed_query(); - }; - } - catch { - FATAL($_); - } - finally { - alarm(0); - Bugzilla->_cleanup(); - }; - }, - ); - - # Query for new users - my $user_timer = IO::Async::Timer::Periodic->new( - first_interval => 0, - interval => PHAB_USER_POLL_SECONDS, - reschedule => 'drift', - on_tick => sub { - try { - with_writable_database { - alarm(PHAB_TIMEOUT); - $self->user_query(); - }; - } - catch { - FATAL($_); - } - finally { - alarm(0); - Bugzilla->_cleanup(); - }; - }, - ); - - # Update project membership in Phabricator based on Bugzilla groups - my $group_timer = IO::Async::Timer::Periodic->new( - first_interval => 0, - interval => PHAB_GROUP_POLL_SECONDS, - reschedule => 'drift', - on_tick => sub { - try { - with_writable_database { - alarm(PHAB_TIMEOUT); - $self->group_query(); - }; - } - catch { - FATAL($_); - } - finally { - alarm(0); - Bugzilla->_cleanup(); - }; - - }, - ); - - my $loop = IO::Async::Loop->new; - $loop->add($feed_timer); - $loop->add($user_timer); - $loop->add($group_timer); - $loop->add($sig_alarm); - $feed_timer->start; - $user_timer->start; - $group_timer->start; - $loop->run; + my ($self) = @_; + + my $sig_alarm = IO::Async::Signal->new( + name => 'ALRM', + on_receipt => sub { + FATAL("Timeout reached"); + exit; + }, + ); + + # Query for new revisions or changes + my $feed_timer = IO::Async::Timer::Periodic->new( + first_interval => 0, + interval => PHAB_FEED_POLL_SECONDS, + reschedule => 'drift', + on_tick => sub { + try { + with_writable_database { + alarm(PHAB_TIMEOUT); + $self->feed_query(); + }; + } + catch { + FATAL($_); + } + finally { + alarm(0); + Bugzilla->_cleanup(); + }; + }, + ); + + # Query for new users + my $user_timer = IO::Async::Timer::Periodic->new( + first_interval => 0, + interval => PHAB_USER_POLL_SECONDS, + reschedule => 'drift', + on_tick => sub { + try { + with_writable_database { + alarm(PHAB_TIMEOUT); + $self->user_query(); + }; + } + catch { + FATAL($_); + } + finally { + alarm(0); + Bugzilla->_cleanup(); + }; + }, + ); + + # Update project membership in Phabricator based on Bugzilla groups + my $group_timer = IO::Async::Timer::Periodic->new( + first_interval => 0, + interval => PHAB_GROUP_POLL_SECONDS, + reschedule => 'drift', + on_tick => sub { + try { + with_writable_database { + alarm(PHAB_TIMEOUT); + $self->group_query(); + }; + } + catch { + FATAL($_); + } + finally { + alarm(0); + Bugzilla->_cleanup(); + }; + + }, + ); + + my $loop = IO::Async::Loop->new; + $loop->add($feed_timer); + $loop->add($user_timer); + $loop->add($group_timer); + $loop->add($sig_alarm); + $feed_timer->start; + $user_timer->start; + $group_timer->start; + $loop->run; } sub feed_query { - my ($self) = @_; + my ($self) = @_; + + local Bugzilla::Logging->fields->{type} = 'FEED'; + + # Ensure Phabricator syncing is enabled + if (!Bugzilla->params->{phabricator_enabled}) { + WARN("PHABRICATOR SYNC DISABLED"); + return; + } + + # PROCESS NEW FEED TRANSACTIONS + + INFO("Fetching new stories"); + + my $story_last_id = $self->get_last_id('feed'); + + # Check for new transctions (stories) + my $new_stories = $self->new_stories($story_last_id); + INFO("No new stories") unless @$new_stories; + + # Process each story + foreach my $story_data (@$new_stories) { + my $story_id = $story_data->{id}; + my $story_phid = $story_data->{phid}; + my $author_phid = $story_data->{authorPHID}; + my $object_phid = $story_data->{objectPHID}; + my $story_text = $story_data->{text}; - local Bugzilla::Logging->fields->{type} = 'FEED'; + TRACE("STORY ID: $story_id"); + TRACE("STORY PHID: $story_phid"); + TRACE("AUTHOR PHID: $author_phid"); + TRACE("OBJECT PHID: $object_phid"); + INFO("STORY TEXT: $story_text"); - # Ensure Phabricator syncing is enabled - if (!Bugzilla->params->{phabricator_enabled}) { - WARN("PHABRICATOR SYNC DISABLED"); - return; + # Only interested in changes to revisions for now. + if ($object_phid !~ /^PHID-DREV/) { + INFO("SKIPPING: Not a revision change"); + $self->save_last_id($story_id, 'feed'); + next; } - # PROCESS NEW FEED TRANSACTIONS - - INFO("Fetching new stories"); - - my $story_last_id = $self->get_last_id('feed'); - - # Check for new transctions (stories) - my $new_stories = $self->new_stories($story_last_id); - INFO("No new stories") unless @$new_stories; - - # Process each story - foreach my $story_data (@$new_stories) { - my $story_id = $story_data->{id}; - my $story_phid = $story_data->{phid}; - my $author_phid = $story_data->{authorPHID}; - my $object_phid = $story_data->{objectPHID}; - my $story_text = $story_data->{text}; - - TRACE("STORY ID: $story_id"); - TRACE("STORY PHID: $story_phid"); - TRACE("AUTHOR PHID: $author_phid"); - TRACE("OBJECT PHID: $object_phid"); - INFO("STORY TEXT: $story_text"); - - # Only interested in changes to revisions for now. - if ($object_phid !~ /^PHID-DREV/) { - INFO("SKIPPING: Not a revision change"); - $self->save_last_id($story_id, 'feed'); - next; - } - - # Skip changes done by phab-bot user - # If changer does not exist in bugzilla database - # we use the phab-bot account as the changer - my $author = Bugzilla::Extension::PhabBugz::User->new_from_query( - { phids => [ $author_phid ] } - ); - - if ($author && $author->bugzilla_id) { - if ($author->bugzilla_user->login eq PHAB_AUTOMATION_USER) { - INFO("SKIPPING: Change made by phabricator user"); - $self->save_last_id($story_id, 'feed'); - next; - } - } - else { - my $phab_user = Bugzilla::User->new( { name => PHAB_AUTOMATION_USER } ); - $author = Bugzilla::Extension::PhabBugz::User->new_from_query( - { - ids => [ $phab_user->id ] - } - ); - } - # Load the revision from Phabricator - my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query({ phids => [ $object_phid ] }); - $self->process_revision_change($revision, $author, $story_text); + # Skip changes done by phab-bot user + # If changer does not exist in bugzilla database + # we use the phab-bot account as the changer + my $author = Bugzilla::Extension::PhabBugz::User->new_from_query( + {phids => [$author_phid]}); + + if ($author && $author->bugzilla_id) { + if ($author->bugzilla_user->login eq PHAB_AUTOMATION_USER) { + INFO("SKIPPING: Change made by phabricator user"); $self->save_last_id($story_id, 'feed'); + next; + } + } + else { + my $phab_user = Bugzilla::User->new({name => PHAB_AUTOMATION_USER}); + $author + = Bugzilla::Extension::PhabBugz::User->new_from_query({ids => [$phab_user->id] + }); } - # Process any build targets as well. - my $dbh = Bugzilla->dbh; + # Load the revision from Phabricator + my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query( + {phids => [$object_phid]}); + $self->process_revision_change($revision, $author, $story_text); + $self->save_last_id($story_id, 'feed'); + } - INFO("Checking for revisions in draft mode"); - my $build_targets = $dbh->selectall_arrayref( - "SELECT name, value FROM phabbugz WHERE name LIKE 'build_target_%'", - { Slice => {} } - ); + # Process any build targets as well. + my $dbh = Bugzilla->dbh; - my $delete_build_target = $dbh->prepare( - "DELETE FROM phabbugz WHERE name = ? AND VALUE = ?" - ); + INFO("Checking for revisions in draft mode"); + my $build_targets + = $dbh->selectall_arrayref( + "SELECT name, value FROM phabbugz WHERE name LIKE 'build_target_%'", + {Slice => {}}); - foreach my $target (@$build_targets) { - my ($revision_id) = ($target->{name} =~ /^build_target_(\d+)$/); - my $build_target = $target->{value}; + my $delete_build_target + = $dbh->prepare("DELETE FROM phabbugz WHERE name = ? AND VALUE = ?"); - next unless $revision_id && $build_target; + foreach my $target (@$build_targets) { + my ($revision_id) = ($target->{name} =~ /^build_target_(\d+)$/); + my $build_target = $target->{value}; - INFO("Processing revision $revision_id with build target $build_target"); + next unless $revision_id && $build_target; - my $revision = - Bugzilla::Extension::PhabBugz::Revision->new_from_query( - { - ids => [ int($revision_id) ] - } - ); + INFO("Processing revision $revision_id with build target $build_target"); - $self->process_revision_change( $revision, $revision->author, " created D" . $revision->id ); + my $revision + = Bugzilla::Extension::PhabBugz::Revision->new_from_query({ + ids => [int($revision_id)] + }); - # Set the build target to a passing status to - # allow the revision to exit draft state - request( 'harbormaster.sendmessage', { - buildTargetPHID => $build_target, - type => 'pass' - } ); + $self->process_revision_change($revision, $revision->author, + " created D" . $revision->id); - $delete_build_target->execute($target->{name}, $target->{value}); - } + # Set the build target to a passing status to + # allow the revision to exit draft state + request('harbormaster.sendmessage', + {buildTargetPHID => $build_target, type => 'pass'}); - if (Bugzilla->datadog) { - my $dd = Bugzilla->datadog(); - $dd->increment('bugzilla.phabbugz.feed_query_count'); - } + $delete_build_target->execute($target->{name}, $target->{value}); + } + + if (Bugzilla->datadog) { + my $dd = Bugzilla->datadog(); + $dd->increment('bugzilla.phabbugz.feed_query_count'); + } } sub user_query { - my ( $self ) = @_; + my ($self) = @_; - local Bugzilla::Logging->fields->{type} = 'USERS'; + local Bugzilla::Logging->fields->{type} = 'USERS'; - # Ensure Phabricator syncing is enabled - if (!Bugzilla->params->{phabricator_enabled}) { - WARN("PHABRICATOR SYNC DISABLED"); - return; - } + # Ensure Phabricator syncing is enabled + if (!Bugzilla->params->{phabricator_enabled}) { + WARN("PHABRICATOR SYNC DISABLED"); + return; + } - # PROCESS NEW USERS + # PROCESS NEW USERS - INFO("Fetching new users"); + INFO("Fetching new users"); - my $user_last_id = $self->get_last_id('user'); + my $user_last_id = $self->get_last_id('user'); - # Check for new users - my $new_users = $self->new_users($user_last_id); - INFO("No new users") unless @$new_users; + # Check for new users + my $new_users = $self->new_users($user_last_id); + INFO("No new users") unless @$new_users; - # Process each new user - foreach my $user_data (@$new_users) { - my $user_id = $user_data->{id}; - my $user_login = $user_data->{fields}{username}; - my $user_realname = $user_data->{fields}{realName}; - my $object_phid = $user_data->{phid}; + # Process each new user + foreach my $user_data (@$new_users) { + my $user_id = $user_data->{id}; + my $user_login = $user_data->{fields}{username}; + my $user_realname = $user_data->{fields}{realName}; + my $object_phid = $user_data->{phid}; - TRACE("ID: $user_id"); - TRACE("LOGIN: $user_login"); - TRACE("REALNAME: $user_realname"); - TRACE("OBJECT PHID: $object_phid"); + TRACE("ID: $user_id"); + TRACE("LOGIN: $user_login"); + TRACE("REALNAME: $user_realname"); + TRACE("OBJECT PHID: $object_phid"); - with_readonly_database { - $self->process_new_user($user_data); - }; - $self->save_last_id($user_id, 'user'); - } + with_readonly_database { + $self->process_new_user($user_data); + }; + $self->save_last_id($user_id, 'user'); + } - if (Bugzilla->datadog) { - my $dd = Bugzilla->datadog(); - $dd->increment('bugzilla.phabbugz.user_query_count'); - } + if (Bugzilla->datadog) { + my $dd = Bugzilla->datadog(); + $dd->increment('bugzilla.phabbugz.user_query_count'); + } } sub group_query { - my ($self) = @_; + my ($self) = @_; - local Bugzilla::Logging->fields->{type} = 'GROUPS'; + local Bugzilla::Logging->fields->{type} = 'GROUPS'; - # Ensure Phabricator syncing is enabled - if ( !Bugzilla->params->{phabricator_enabled} ) { - WARN("PHABRICATOR SYNC DISABLED"); - return; - } + # Ensure Phabricator syncing is enabled + if (!Bugzilla->params->{phabricator_enabled}) { + WARN("PHABRICATOR SYNC DISABLED"); + return; + } - # PROCESS SECURITY GROUPS - - INFO("Updating group memberships"); - - # Loop through each group and perform the following: - # - # 1. Load flattened list of group members - # 2. Check to see if Phab project exists for 'bmo-<group_name>' - # 3. Create if does not exist with locked down policy. - # 4. Set project members to exact list including phab-bot user - # 5. Profit - - my $sync_groups = Bugzilla::Group->match( { isactive => 1, isbuggroup => 1 } ); - - # Load phab-bot Phabricator user to add as a member of each project group later - my $phab_bmo_user = Bugzilla::User->new( { name => PHAB_AUTOMATION_USER, cache => 1 } ); - my $phab_user = - Bugzilla::Extension::PhabBugz::User->new_from_query( - { - ids => [ $phab_bmo_user->id ] - } - ); - - # secure-revision project that will be used for bmo group projects - my $secure_revision = - Bugzilla::Extension::PhabBugz::Project->new_from_query( - { - name => 'secure-revision' - } - ); - - foreach my $group (@$sync_groups) { - # Create group project if one does not yet exist - my $phab_project_name = 'bmo-' . $group->name; - my $project = - Bugzilla::Extension::PhabBugz::Project->new_from_query( - { - name => $phab_project_name - } - ); - - if ( !$project ) { - INFO("Project $phab_project_name not found. Creating."); - $project = Bugzilla::Extension::PhabBugz::Project->create( - { - name => $phab_project_name, - description => 'BMO Security Group for ' . $group->name, - view_policy => $secure_revision->phid, - edit_policy => $secure_revision->phid, - join_policy => $secure_revision->phid - } - ); - } - else { - # Make sure that the group project permissions are set properly - INFO("Updating permissions on $phab_project_name"); - $project->set_policy( 'view', $secure_revision->phid ); - $project->set_policy( 'edit', $secure_revision->phid ); - $project->set_policy( 'join', $secure_revision->phid ); - } - - # Make sure phab-bot also a member of the new project group so that it can - # make policy changes to the private revisions - INFO( "Checking project members for " . $project->name ); - my $set_members = $self->get_group_members($group); - my @set_member_phids = uniq map { $_->phid } ( @$set_members, $phab_user ); - my @current_member_phids = uniq map { $_->phid } @{ $project->members }; - my ( $removed, $added ) = diff_arrays( \@current_member_phids, \@set_member_phids ); - - if (@$added) { - INFO( 'Adding project members: ' . join( ',', @$added ) ); - $project->add_member($_) foreach @$added; - } - - if (@$removed) { - INFO( 'Removing project members: ' . join( ',', @$removed ) ); - $project->remove_member($_) foreach @$removed; - } - - if (@$added || @$removed) { - my $result = $project->update(); - local Bugzilla::Logging->fields->{api_result} = $result; - INFO( "Project " . $project->name . " updated" ); - } - } + # PROCESS SECURITY GROUPS + + INFO("Updating group memberships"); + + # Loop through each group and perform the following: + # + # 1. Load flattened list of group members + # 2. Check to see if Phab project exists for 'bmo-<group_name>' + # 3. Create if does not exist with locked down policy. + # 4. Set project members to exact list including phab-bot user + # 5. Profit + + my $sync_groups = Bugzilla::Group->match({isactive => 1, isbuggroup => 1}); - if (Bugzilla->datadog) { - my $dd = Bugzilla->datadog(); - $dd->increment('bugzilla.phabbugz.group_query_count'); + # Load phab-bot Phabricator user to add as a member of each project group later + my $phab_bmo_user + = Bugzilla::User->new({name => PHAB_AUTOMATION_USER, cache => 1}); + my $phab_user + = Bugzilla::Extension::PhabBugz::User->new_from_query({ + ids => [$phab_bmo_user->id] + }); + + # secure-revision project that will be used for bmo group projects + my $secure_revision + = Bugzilla::Extension::PhabBugz::Project->new_from_query({ + name => 'secure-revision' + }); + + foreach my $group (@$sync_groups) { + + # Create group project if one does not yet exist + my $phab_project_name = 'bmo-' . $group->name; + my $project + = Bugzilla::Extension::PhabBugz::Project->new_from_query({ + name => $phab_project_name + }); + + if (!$project) { + INFO("Project $phab_project_name not found. Creating."); + $project = Bugzilla::Extension::PhabBugz::Project->create({ + name => $phab_project_name, + description => 'BMO Security Group for ' . $group->name, + view_policy => $secure_revision->phid, + edit_policy => $secure_revision->phid, + join_policy => $secure_revision->phid + }); + } + else { + # Make sure that the group project permissions are set properly + INFO("Updating permissions on $phab_project_name"); + $project->set_policy('view', $secure_revision->phid); + $project->set_policy('edit', $secure_revision->phid); + $project->set_policy('join', $secure_revision->phid); } -} -sub process_revision_change { - state $check = compile($Invocant, Revision, LinkedPhabUser, Str); - my ($self, $revision, $changer, $story_text) = $check->(@_); - - # NO BUG ID - if (!$revision->bug_id) { - if ($story_text =~ /\s+created\s+D\d+/) { - # If new revision and bug id was omitted, make revision public - INFO("No bug associated with new revision. Marking public."); - $revision->make_public(); - $revision->update(); - INFO("SUCCESS"); - return; - } - else { - INFO("SKIPPING: No bug associated with revision change"); - return; - } + # Make sure phab-bot also a member of the new project group so that it can + # make policy changes to the private revisions + INFO("Checking project members for " . $project->name); + my $set_members = $self->get_group_members($group); + my @set_member_phids = uniq map { $_->phid } (@$set_members, $phab_user); + my @current_member_phids = uniq map { $_->phid } @{$project->members}; + my ($removed, $added) = diff_arrays(\@current_member_phids, \@set_member_phids); + + if (@$added) { + INFO('Adding project members: ' . join(',', @$added)); + $project->add_member($_) foreach @$added; } + if (@$removed) { + INFO('Removing project members: ' . join(',', @$removed)); + $project->remove_member($_) foreach @$removed; + } - my $log_message = sprintf( - "REVISION CHANGE FOUND: D%d: %s | bug: %d | %s | %s", - $revision->id, - $revision->title, - $revision->bug_id, - $changer->name, - $story_text); - INFO($log_message); - - # change to the phabricator user, which returns a guard that restores the previous user. - my $restore_prev_user = set_phab_user(); - my $bug = $revision->bug; - - # Check to make sure bug id is valid and author can see it - if ($bug->{error} - ||!$revision->author->bugzilla_user->can_see_bug($revision->bug_id)) - { - if ($story_text =~ /\s+created\s+D\d+/) { - INFO('Invalid bug ID or author does not have access to the bug. ' . - 'Waiting til next revision update to notify author.'); - return; - } - - INFO('Invalid bug ID or author does not have access to the bug'); - my $phab_error_message = ""; - Bugzilla->template->process('revision/comments.html.tmpl', - { message => 'invalid_bug_id' }, - \$phab_error_message); - $revision->add_comment($phab_error_message); - $revision->update(); - return; + if (@$added || @$removed) { + my $result = $project->update(); + local Bugzilla::Logging->fields->{api_result} = $result; + INFO("Project " . $project->name . " updated"); } + } - # REVISION SECURITY POLICY + if (Bugzilla->datadog) { + my $dd = Bugzilla->datadog(); + $dd->increment('bugzilla.phabbugz.group_query_count'); + } +} - # If bug is public then remove privacy policy - if (!@{ $bug->groups_in }) { - INFO('Bug is public so setting view/edit public'); - $revision->make_public(); +sub process_revision_change { + state $check = compile($Invocant, Revision, LinkedPhabUser, Str); + my ($self, $revision, $changer, $story_text) = $check->(@_); + + # NO BUG ID + if (!$revision->bug_id) { + if ($story_text =~ /\s+created\s+D\d+/) { + + # If new revision and bug id was omitted, make revision public + INFO("No bug associated with new revision. Marking public."); + $revision->make_public(); + $revision->update(); + INFO("SUCCESS"); + return; } - # else bug is private. else { - # Here we create a new custom policy containing the project - # groups that are mapped to bugzilla groups. - my $set_project_names = [ map { "bmo-" . $_->name } @{ $bug->groups_in } ]; - - # If current policy projects matches what we want to set, then - # we leave the current policy alone. - my $current_policy; - if ($revision->view_policy =~ /^PHID-PLCY/) { - INFO("Loading current policy: " . $revision->view_policy); - $current_policy - = Bugzilla::Extension::PhabBugz::Policy->new_from_query({ phids => [ $revision->view_policy ]}); - my $current_project_names = [ map { $_->name } @{ $current_policy->rule_projects } ]; - INFO("Current policy projects: " . join(", ", @$current_project_names)); - my ($added, $removed) = diff_arrays($current_project_names, $set_project_names); - if (@$added || @$removed) { - INFO('Project groups do not match. Need new custom policy'); - $current_policy = undef; - } - else { - INFO('Project groups match. Leaving current policy as-is'); - } - } - - if (!$current_policy) { - INFO("Creating new custom policy: " . join(", ", @$set_project_names)); - $revision->make_private($set_project_names); - } - - # Subscriber list of the private revision should always match - # the bug roles such as assignee, qa contact, and cc members. - my $subscribers = get_bug_role_phids($bug); - $revision->set_subscribers($subscribers); + INFO("SKIPPING: No bug associated with revision change"); + return; + } + } + + + my $log_message = sprintf( + "REVISION CHANGE FOUND: D%d: %s | bug: %d | %s | %s", + $revision->id, $revision->title, $revision->bug_id, + $changer->name, $story_text + ); + INFO($log_message); + +# change to the phabricator user, which returns a guard that restores the previous user. + my $restore_prev_user = set_phab_user(); + my $bug = $revision->bug; + + # Check to make sure bug id is valid and author can see it + if ($bug->{error} + || !$revision->author->bugzilla_user->can_see_bug($revision->bug_id)) + { + if ($story_text =~ /\s+created\s+D\d+/) { + INFO( 'Invalid bug ID or author does not have access to the bug. ' + . 'Waiting til next revision update to notify author.'); + return; } - my ($timestamp) = Bugzilla->dbh->selectrow_array("SELECT NOW()"); - - INFO('Checking for revision attachment'); - my $rev_attachment = create_revision_attachment($bug, $revision, $timestamp, $revision->author->bugzilla_user); - INFO('Attachment ' . $rev_attachment->id . ' created or already exists.'); - - # ATTACHMENT OBSOLETES + INFO('Invalid bug ID or author does not have access to the bug'); + my $phab_error_message = ""; + Bugzilla->template->process('revision/comments.html.tmpl', + {message => 'invalid_bug_id'}, + \$phab_error_message); + $revision->add_comment($phab_error_message); + $revision->update(); + return; + } + + # REVISION SECURITY POLICY + + # If bug is public then remove privacy policy + if (!@{$bug->groups_in}) { + INFO('Bug is public so setting view/edit public'); + $revision->make_public(); + } + + # else bug is private. + else { + # Here we create a new custom policy containing the project + # groups that are mapped to bugzilla groups. + my $set_project_names = [map { "bmo-" . $_->name } @{$bug->groups_in}]; + + # If current policy projects matches what we want to set, then + # we leave the current policy alone. + my $current_policy; + if ($revision->view_policy =~ /^PHID-PLCY/) { + INFO("Loading current policy: " . $revision->view_policy); + $current_policy = Bugzilla::Extension::PhabBugz::Policy->new_from_query( + {phids => [$revision->view_policy]}); + my $current_project_names + = [map { $_->name } @{$current_policy->rule_projects}]; + INFO("Current policy projects: " . join(", ", @$current_project_names)); + my ($added, $removed) = diff_arrays($current_project_names, $set_project_names); + if (@$added || @$removed) { + INFO('Project groups do not match. Need new custom policy'); + $current_policy = undef; + } + else { + INFO('Project groups match. Leaving current policy as-is'); + } + } - # fixup attachments on current bug - my @attachments = - grep { is_attachment_phab_revision($_) } @{ $bug->attachments() }; + if (!$current_policy) { + INFO("Creating new custom policy: " . join(", ", @$set_project_names)); + $revision->make_private($set_project_names); + } - foreach my $attachment (@attachments) { - my ($attach_revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN); - next if $attach_revision_id != $revision->id; + # Subscriber list of the private revision should always match + # the bug roles such as assignee, qa contact, and cc members. + my $subscribers = get_bug_role_phids($bug); + $revision->set_subscribers($subscribers); + } - my $make_obsolete = $revision->status eq 'abandoned' ? 1 : 0; - INFO('Updating obsolete status on attachmment ' . $attachment->id); - $attachment->set_is_obsolete($make_obsolete); + my ($timestamp) = Bugzilla->dbh->selectrow_array("SELECT NOW()"); - if ($revision->title ne $attachment->description) { - INFO('Updating description on attachment ' . $attachment->id); - $attachment->set_description($revision->title); - } + INFO('Checking for revision attachment'); + my $rev_attachment = create_revision_attachment($bug, $revision, $timestamp, + $revision->author->bugzilla_user); + INFO('Attachment ' . $rev_attachment->id . ' created or already exists.'); - $attachment->update($timestamp); - } + # ATTACHMENT OBSOLETES - # fixup attachments with same revision id but on different bugs - my %other_bugs; - my $other_attachments = Bugzilla::Attachment->match({ - mimetype => PHAB_CONTENT_TYPE, - filename => 'phabricator-D' . $revision->id . '-url.txt', - WHERE => { 'bug_id != ? AND NOT isobsolete' => $bug->id } - }); - foreach my $attachment (@$other_attachments) { - $other_bugs{$attachment->bug_id}++; - INFO('Updating obsolete status on attachment ' . - $attachment->id . " for bug " . $attachment->bug_id); - $attachment->set_is_obsolete(1); - $attachment->update($timestamp); - } + # fixup attachments on current bug + my @attachments + = grep { is_attachment_phab_revision($_) } @{$bug->attachments()}; - # FINISH UP + foreach my $attachment (@attachments) { + my ($attach_revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN); + next if $attach_revision_id != $revision->id; - $bug->update($timestamp); - $revision->update(); + my $make_obsolete = $revision->status eq 'abandoned' ? 1 : 0; + INFO('Updating obsolete status on attachmment ' . $attachment->id); + $attachment->set_is_obsolete($make_obsolete); - # Email changes for this revisions bug and also for any other - # bugs that previously had these revision attachments - foreach my $bug_id ($revision->bug_id, keys %other_bugs) { - Bugzilla::BugMail::Send($bug_id, { changer => $changer->bugzilla_user }); + if ($revision->title ne $attachment->description) { + INFO('Updating description on attachment ' . $attachment->id); + $attachment->set_description($revision->title); } - INFO('SUCCESS: Revision D' . $revision->id . ' processed'); + $attachment->update($timestamp); + } + + # fixup attachments with same revision id but on different bugs + my %other_bugs; + my $other_attachments = Bugzilla::Attachment->match({ + mimetype => PHAB_CONTENT_TYPE, + filename => 'phabricator-D' . $revision->id . '-url.txt', + WHERE => {'bug_id != ? AND NOT isobsolete' => $bug->id} + }); + foreach my $attachment (@$other_attachments) { + $other_bugs{$attachment->bug_id}++; + INFO( 'Updating obsolete status on attachment ' + . $attachment->id + . " for bug " + . $attachment->bug_id); + $attachment->set_is_obsolete(1); + $attachment->update($timestamp); + } + + # FINISH UP + + $bug->update($timestamp); + $revision->update(); + + # Email changes for this revisions bug and also for any other + # bugs that previously had these revision attachments + foreach my $bug_id ($revision->bug_id, keys %other_bugs) { + Bugzilla::BugMail::Send($bug_id, {changer => $changer->bugzilla_user}); + } + + INFO('SUCCESS: Revision D' . $revision->id . ' processed'); } sub process_new_user { - state $check = compile($Invocant, HashRef); - my ( $self, $user_data ) = $check->(@_); + state $check = compile($Invocant, HashRef); + my ($self, $user_data) = $check->(@_); - # Load the user data into a proper object - my $phab_user = Bugzilla::Extension::PhabBugz::User->new($user_data); + # Load the user data into a proper object + my $phab_user = Bugzilla::Extension::PhabBugz::User->new($user_data); - if (!$phab_user->bugzilla_id) { - WARN("SKIPPING: No bugzilla id associated with user"); - return; - } + if (!$phab_user->bugzilla_id) { + WARN("SKIPPING: No bugzilla id associated with user"); + return; + } - my $bug_user = $phab_user->bugzilla_user; + my $bug_user = $phab_user->bugzilla_user; - # Pre setup before querying DB - my $restore_prev_user = set_phab_user(); + # Pre setup before querying DB + my $restore_prev_user = set_phab_user(); - # CHECK AND WARN FOR POSSIBLE USERNAME SQUATTING - INFO("Checking for username squatters"); - my $dbh = Bugzilla->dbh; - my $regexp = $dbh->quote( ":?:" . quotemeta($phab_user->name) . "[[:>:]]" ); - my $results = $dbh->selectall_arrayref( " + # CHECK AND WARN FOR POSSIBLE USERNAME SQUATTING + INFO("Checking for username squatters"); + my $dbh = Bugzilla->dbh; + my $regexp = $dbh->quote(":?:" . quotemeta($phab_user->name) . "[[:>:]]"); + my $results = $dbh->selectall_arrayref(" SELECT userid, login_name, realname FROM profiles - WHERE userid != ? AND " . $dbh->sql_regexp( 'realname', $regexp ), - { Slice => {} }, - $bug_user->id ); - if (@$results) { - # The email client will display the Date: header in the desired timezone, - # so we can always use UTC here. - my $timestamp = Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); - $timestamp = format_time($timestamp, '%a, %d %b %Y %T %z', 'UTC'); - - foreach my $row (@$results) { - WARN( - 'Possible username squatter: ', - 'phab user login: ' . $phab_user->name, - ' phab user realname: ' . $phab_user->realname, - ' bugzilla user id: ' . $row->{userid}, - ' bugzilla login: ' . $row->{login_name}, - ' bugzilla realname: ' . $row->{realname} - ); - - my $vars = { - date => $timestamp, - phab_user_login => $phab_user->name, - phab_user_realname => $phab_user->realname, - bugzilla_userid => $bug_user->id, - bugzilla_login => $bug_user->login, - bugzilla_realname => $bug_user->name, - squat_userid => $row->{userid}, - squat_login => $row->{login_name}, - squat_realname => $row->{realname} - }; - - my $message; - my $template = Bugzilla->template; - $template->process("admin/email/squatter-alert.txt.tmpl", $vars, \$message) - || ThrowTemplateError($template->error()); - - MessageToMTA($message); - } + WHERE userid != ? AND " . $dbh->sql_regexp('realname', $regexp), + {Slice => {}}, $bug_user->id); + if (@$results) { + + # The email client will display the Date: header in the desired timezone, + # so we can always use UTC here. + my $timestamp = Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); + $timestamp = format_time($timestamp, '%a, %d %b %Y %T %z', 'UTC'); + + foreach my $row (@$results) { + WARN( + 'Possible username squatter: ', + 'phab user login: ' . $phab_user->name, + ' phab user realname: ' . $phab_user->realname, + ' bugzilla user id: ' . $row->{userid}, + ' bugzilla login: ' . $row->{login_name}, + ' bugzilla realname: ' . $row->{realname} + ); + + my $vars = { + date => $timestamp, + phab_user_login => $phab_user->name, + phab_user_realname => $phab_user->realname, + bugzilla_userid => $bug_user->id, + bugzilla_login => $bug_user->login, + bugzilla_realname => $bug_user->name, + squat_userid => $row->{userid}, + squat_login => $row->{login_name}, + squat_realname => $row->{realname} + }; + + my $message; + my $template = Bugzilla->template; + $template->process("admin/email/squatter-alert.txt.tmpl", $vars, \$message) + || ThrowTemplateError($template->error()); + + MessageToMTA($message); } + } - # ADD SUBSCRIBERS TO REVSISIONS FOR CURRENT PRIVATE BUGS + # ADD SUBSCRIBERS TO REVSISIONS FOR CURRENT PRIVATE BUGS - my $params = { - f3 => 'OP', - j3 => 'OR', + my $params = { + f3 => 'OP', + j3 => 'OR', - # User must be either reporter, assignee, qa_contact - # or on the cc list of the bug - f4 => 'cc', - o4 => 'equals', - v4 => $bug_user->login, + # User must be either reporter, assignee, qa_contact + # or on the cc list of the bug + f4 => 'cc', + o4 => 'equals', + v4 => $bug_user->login, - f5 => 'assigned_to', - o5 => 'equals', - v5 => $bug_user->login, + f5 => 'assigned_to', + o5 => 'equals', + v5 => $bug_user->login, - f6 => 'qa_contact', - o6 => 'equals', - v6 => $bug_user->login, + f6 => 'qa_contact', + o6 => 'equals', + v6 => $bug_user->login, - f7 => 'reporter', - o7 => 'equals', - v7 => $bug_user->login, + f7 => 'reporter', + o7 => 'equals', + v7 => $bug_user->login, - f9 => 'CP', + f9 => 'CP', - # The bug needs to be private - f10 => 'bug_group', - o10 => 'isnotempty', + # The bug needs to be private + f10 => 'bug_group', + o10 => 'isnotempty', - # And the bug must have one or more attachments - # that are connected to revisions - f11 => 'attachments.filename', - o11 => 'regexp', - v11 => '^phabricator-D[[:digit:]]+-url.txt$', - }; + # And the bug must have one or more attachments + # that are connected to revisions + f11 => 'attachments.filename', + o11 => 'regexp', + v11 => '^phabricator-D[[:digit:]]+-url.txt$', + }; - my $search = Bugzilla::Search->new( fields => [ 'bug_id' ], - params => $params, - order => [ 'bug_id' ] ); - my $data = $search->data; + my $search = Bugzilla::Search->new( + fields => ['bug_id'], + params => $params, + order => ['bug_id'] + ); + my $data = $search->data; - # the first value of each row should be the bug id - my @bug_ids = map { shift @$_ } @$data; + # the first value of each row should be the bug id + my @bug_ids = map { shift @$_ } @$data; - INFO("Updating subscriber values for old private bugs"); + INFO("Updating subscriber values for old private bugs"); - foreach my $bug_id (@bug_ids) { - INFO("Processing bug $bug_id"); + foreach my $bug_id (@bug_ids) { + INFO("Processing bug $bug_id"); - my $bug = Bugzilla::Bug->new({ id => $bug_id, cache => 1 }); + my $bug = Bugzilla::Bug->new({id => $bug_id, cache => 1}); - my @attachments = - grep { is_attachment_phab_revision($_) } @{ $bug->attachments() }; + my @attachments + = grep { is_attachment_phab_revision($_) } @{$bug->attachments()}; - foreach my $attachment (@attachments) { - my ($revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN); + foreach my $attachment (@attachments) { + my ($revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN); - if (!$revision_id) { - WARN("Skipping " . $attachment->filename . " on bug $bug_id. Filename should be fixed."); - next; - } + if (!$revision_id) { + WARN( "Skipping " + . $attachment->filename + . " on bug $bug_id. Filename should be fixed."); + next; + } - INFO("Processing revision D$revision_id"); + INFO("Processing revision D$revision_id"); - my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query( - { ids => [ int($revision_id) ] }); + my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query( + {ids => [int($revision_id)]}); - $revision->add_subscriber($phab_user->phid); - $revision->update(); + $revision->add_subscriber($phab_user->phid); + $revision->update(); - INFO("Revision $revision_id updated"); - } + INFO("Revision $revision_id updated"); } + } - INFO('SUCCESS: User ' . $phab_user->id . ' processed'); + INFO('SUCCESS: User ' . $phab_user->id . ' processed'); } ################## @@ -698,87 +697,74 @@ sub process_new_user { ################## sub new_stories { - my ( $self, $after ) = @_; - my $data = { view => 'text' }; - $data->{after} = $after if $after; + my ($self, $after) = @_; + my $data = {view => 'text'}; + $data->{after} = $after if $after; - my $result = request( 'feed.query_id', $data ); + my $result = request('feed.query_id', $data); - unless ( ref $result->{result}{data} eq 'ARRAY' - && @{ $result->{result}{data} } ) - { - return []; - } + unless (ref $result->{result}{data} eq 'ARRAY' && @{$result->{result}{data}}) { + return []; + } - # Guarantee that the data is in ascending ID order - return [ sort { $a->{id} <=> $b->{id} } @{ $result->{result}{data} } ]; + # Guarantee that the data is in ascending ID order + return [sort { $a->{id} <=> $b->{id} } @{$result->{result}{data}}]; } sub new_users { - my ( $self, $after ) = @_; - my $data = { - order => [ "id" ], - attachments => { - 'external-accounts' => 1 - } - }; - $data->{before} = $after if $after; + my ($self, $after) = @_; + my $data = {order => ["id"], attachments => {'external-accounts' => 1}}; + $data->{before} = $after if $after; - my $result = request( 'user.search', $data ); + my $result = request('user.search', $data); - unless ( ref $result->{result}{data} eq 'ARRAY' - && @{ $result->{result}{data} } ) - { - return []; - } + unless (ref $result->{result}{data} eq 'ARRAY' && @{$result->{result}{data}}) { + return []; + } - # Guarantee that the data is in ascending ID order - return [ sort { $a->{id} <=> $b->{id} } @{ $result->{result}{data} } ]; + # Guarantee that the data is in ascending ID order + return [sort { $a->{id} <=> $b->{id} } @{$result->{result}{data}}]; } sub get_last_id { - my ( $self, $type ) = @_; - my $type_full = $type . "_last_id"; - my $last_id = Bugzilla->dbh->selectrow_array( " - SELECT value FROM phabbugz WHERE name = ?", undef, $type_full ); - $last_id ||= 0; - TRACE(uc($type_full) . ": $last_id" ); - return $last_id; + my ($self, $type) = @_; + my $type_full = $type . "_last_id"; + my $last_id = Bugzilla->dbh->selectrow_array(" + SELECT value FROM phabbugz WHERE name = ?", undef, $type_full); + $last_id ||= 0; + TRACE(uc($type_full) . ": $last_id"); + return $last_id; } sub save_last_id { - my ( $self, $last_id, $type ) = @_; + my ($self, $last_id, $type) = @_; - # Store the largest last key so we can start from there in the next session - my $type_full = $type . "_last_id"; - TRACE("UPDATING " . uc($type_full) . ": $last_id" ); - Bugzilla->dbh->do( "REPLACE INTO phabbugz (name, value) VALUES (?, ?)", - undef, $type_full, $last_id ); + # Store the largest last key so we can start from there in the next session + my $type_full = $type . "_last_id"; + TRACE("UPDATING " . uc($type_full) . ": $last_id"); + Bugzilla->dbh->do("REPLACE INTO phabbugz (name, value) VALUES (?, ?)", + undef, $type_full, $last_id); } sub get_group_members { - state $check = compile( $Invocant, Group | Str ); - my ( $self, $group ) = $check->(@_); - my $group_obj = - ref $group ? $group : Bugzilla::Group->check( { name => $group, cache => 1 } ); + state $check = compile($Invocant, Group | Str); + my ($self, $group) = $check->(@_); + my $group_obj + = ref $group ? $group : Bugzilla::Group->check({name => $group, cache => 1}); - my $flat_list = join(',', - @{ Bugzilla::Group->flatten_group_membership( $group_obj->id ) } ); + my $flat_list + = join(',', @{Bugzilla::Group->flatten_group_membership($group_obj->id)}); - my $user_query = " + my $user_query = " SELECT DISTINCT profiles.userid FROM profiles, user_group_map AS ugm WHERE ugm.user_id = profiles.userid AND ugm.isbless = 0 AND ugm.group_id IN($flat_list)"; - my $user_ids = Bugzilla->dbh->selectcol_arrayref($user_query); + my $user_ids = Bugzilla->dbh->selectcol_arrayref($user_query); - # Return matching users in Phabricator - return Bugzilla::Extension::PhabBugz::User->match( - { - ids => $user_ids - } - ); + # Return matching users in Phabricator + return Bugzilla::Extension::PhabBugz::User->match({ids => $user_ids}); } 1; diff --git a/extensions/PhabBugz/lib/Policy.pm b/extensions/PhabBugz/lib/Policy.pm index 415ea20fb..658d0ddde 100644 --- a/extensions/PhabBugz/lib/Policy.pm +++ b/extensions/PhabBugz/lib/Policy.pm @@ -21,30 +21,22 @@ use Types::Standard -all; use Type::Utils; use Type::Params qw( compile ); -has 'phid' => ( is => 'ro', isa => Str ); -has 'type' => ( is => 'ro', isa => Str ); -has 'name' => ( is => 'ro', isa => Str ); -has 'shortName' => ( is => 'ro', isa => Str ); -has 'fullName' => ( is => 'ro', isa => Str ); -has 'href' => ( is => 'ro', isa => Maybe[Str] ); -has 'workflow' => ( is => 'ro', isa => Maybe[Str] ); -has 'icon' => ( is => 'ro', isa => Str ); -has 'default' => ( is => 'ro', isa => Str ); +has 'phid' => (is => 'ro', isa => Str); +has 'type' => (is => 'ro', isa => Str); +has 'name' => (is => 'ro', isa => Str); +has 'shortName' => (is => 'ro', isa => Str); +has 'fullName' => (is => 'ro', isa => Str); +has 'href' => (is => 'ro', isa => Maybe [Str]); +has 'workflow' => (is => 'ro', isa => Maybe [Str]); +has 'icon' => (is => 'ro', isa => Str); +has 'default' => (is => 'ro', isa => Str); has 'rules' => ( - is => 'ro', - isa => ArrayRef[ - Dict[ - action => Str, - rule => Str, - value => Maybe[ArrayRef[Str]] - ] - ] + is => 'ro', + isa => + ArrayRef [Dict [action => Str, rule => Str, value => Maybe [ArrayRef [Str]]]] ); -has 'rule_projects' => ( - is => 'lazy', - isa => ArrayRef[Project], -); +has 'rule_projects' => (is => 'lazy', isa => ArrayRef [Project],); # { # "data": [ @@ -81,64 +73,61 @@ has 'rule_projects' => ( # } # } -my $Invocant = class_type { class => __PACKAGE__ }; +my $Invocant = class_type {class => __PACKAGE__}; sub new_from_query { - state $check = compile($Invocant | ClassName, Dict[phids => ArrayRef[Str]]); - my ($class, $params) = $check->(@_); - my $result = request('policy.query', $params); - if (exists $result->{result}{data} && @{ $result->{result}{data} }) { - return $class->new($result->{result}->{data}->[0]); - } + state $check = compile($Invocant | ClassName, Dict [phids => ArrayRef [Str]]); + my ($class, $params) = $check->(@_); + my $result = request('policy.query', $params); + if (exists $result->{result}{data} && @{$result->{result}{data}}) { + return $class->new($result->{result}->{data}->[0]); + } } sub create { - state $check = compile($Invocant | ClassName, ArrayRef[Project]); - my ($class, $projects) = $check->(@_); - - my $data = { - objectType => 'DREV', - default => 'deny', - policy => [ - { - action => 'allow', - rule => 'PhabricatorSubscriptionsSubscribersPolicyRule', - }, - { - action => 'allow', - rule => 'PhabricatorDifferentialReviewersPolicyRule' - } - ] - }; - - if (@$projects) { - push @{ $data->{policy} }, { - action => 'allow', - rule => 'PhabricatorProjectsAllPolicyRule', - value => [ map { $_->phid } @$projects ], - }; - } - else { - my $secure_revision = Bugzilla::Extension::PhabBugz::Project->new_from_query({ - name => 'secure-revision' - }); - push @{ $data->{policy} }, { action => 'allow', value => $secure_revision->phid }; - } - - my $result = request('policy.create', $data); - return $class->new_from_query({ phids => [ $result->{result}{phid} ] }); + state $check = compile($Invocant | ClassName, ArrayRef [Project]); + my ($class, $projects) = $check->(@_); + + my $data = { + objectType => 'DREV', + default => 'deny', + policy => [ + {action => 'allow', rule => 'PhabricatorSubscriptionsSubscribersPolicyRule',}, + {action => 'allow', rule => 'PhabricatorDifferentialReviewersPolicyRule'} + ] + }; + + if (@$projects) { + push @{$data->{policy}}, + { + action => 'allow', + rule => 'PhabricatorProjectsAllPolicyRule', + value => [map { $_->phid } @$projects], + }; + } + else { + my $secure_revision + = Bugzilla::Extension::PhabBugz::Project->new_from_query({ + name => 'secure-revision' + }); + push @{$data->{policy}}, {action => 'allow', value => $secure_revision->phid}; + } + + my $result = request('policy.create', $data); + return $class->new_from_query({phids => [$result->{result}{phid}]}); } sub _build_rule_projects { - my ($self) = @_; - - return [] unless $self->rules; - my $rule = first { $_->{rule} =~ /PhabricatorProjects(?:All)?PolicyRule/ } @{ $self->rules }; - return [] unless $rule; - return [ - map { Bugzilla::Extension::PhabBugz::Project->new_from_query( { phids => [$_] } ) } - @{ $rule->{value} } - ]; + my ($self) = @_; + + return [] unless $self->rules; + my $rule = first { $_->{rule} =~ /PhabricatorProjects(?:All)?PolicyRule/ } + @{$self->rules}; + return [] unless $rule; + return [ + map { Bugzilla::Extension::PhabBugz::Project->new_from_query({phids => [$_]}) } + @{$rule->{value}} + ]; } -1;
\ No newline at end of file +1; diff --git a/extensions/PhabBugz/lib/Project.pm b/extensions/PhabBugz/lib/Project.pm index c18708887..8af01f74e 100644 --- a/extensions/PhabBugz/lib/Project.pm +++ b/extensions/PhabBugz/lib/Project.pm @@ -24,63 +24,61 @@ use Bugzilla::Extension::PhabBugz::Util qw(request); # Initialization # ######################### -has id => ( is => 'ro', isa => Int ); -has phid => ( is => 'ro', isa => Str ); -has type => ( is => 'ro', isa => Str ); -has name => ( is => 'ro', isa => Str ); -has description => ( is => 'ro', isa => Maybe[Str] ); -has creation_ts => ( is => 'ro', isa => Str ); -has modification_ts => ( is => 'ro', isa => Str ); -has view_policy => ( is => 'ro', isa => Str ); -has edit_policy => ( is => 'ro', isa => Str ); -has join_policy => ( is => 'ro', isa => Str ); -has members_raw => ( is => 'ro', isa => ArrayRef [ Dict [ phid => Str ] ] ); -has members => ( is => 'lazy', isa => ArrayRef[PhabUser] ); - -my $Invocant = class_type { class => __PACKAGE__ }; +has id => (is => 'ro', isa => Int); +has phid => (is => 'ro', isa => Str); +has type => (is => 'ro', isa => Str); +has name => (is => 'ro', isa => Str); +has description => (is => 'ro', isa => Maybe [Str]); +has creation_ts => (is => 'ro', isa => Str); +has modification_ts => (is => 'ro', isa => Str); +has view_policy => (is => 'ro', isa => Str); +has edit_policy => (is => 'ro', isa => Str); +has join_policy => (is => 'ro', isa => Str); +has members_raw => (is => 'ro', isa => ArrayRef [Dict [phid => Str]]); +has members => (is => 'lazy', isa => ArrayRef [PhabUser]); + +my $Invocant = class_type {class => __PACKAGE__}; sub new_from_query { - my ( $class, $params ) = @_; - - my $data = { - queryKey => 'all', - attachments => { members => 1 }, - constraints => $params - }; - - my $result = request( 'project.search', $data ); - if ( exists $result->{result}{data} && @{ $result->{result}{data} } ) { - # If name is used as a query param, we need to loop through and look - # for exact match as Conduit will tokenize the name instead of doing - # exact string match :( If name is not used, then return first one. - if ( exists $params->{name} ) { - foreach my $item ( @{ $result->{result}{data} } ) { - next if $item->{fields}{name} ne $params->{name}; - return $class->new($item); - } - } - else { - return $class->new( $result->{result}{data}[0] ); - } + my ($class, $params) = @_; + + my $data + = {queryKey => 'all', attachments => {members => 1}, constraints => $params}; + + my $result = request('project.search', $data); + if (exists $result->{result}{data} && @{$result->{result}{data}}) { + + # If name is used as a query param, we need to loop through and look + # for exact match as Conduit will tokenize the name instead of doing + # exact string match :( If name is not used, then return first one. + if (exists $params->{name}) { + foreach my $item (@{$result->{result}{data}}) { + next if $item->{fields}{name} ne $params->{name}; + return $class->new($item); + } + } + else { + return $class->new($result->{result}{data}[0]); } + } } sub BUILDARGS { - my ( $class, $params ) = @_; + my ($class, $params) = @_; - $params->{name} = $params->{fields}->{name}; - $params->{description} = $params->{fields}->{description}; - $params->{creation_ts} = $params->{fields}->{dateCreated}; - $params->{modification_ts} = $params->{fields}->{dateModified}; - $params->{view_policy} = $params->{fields}->{policy}->{view}; - $params->{edit_policy} = $params->{fields}->{policy}->{edit}; - $params->{join_policy} = $params->{fields}->{policy}->{join}; - $params->{members_raw} = $params->{attachments}->{members}->{members}; + $params->{name} = $params->{fields}->{name}; + $params->{description} = $params->{fields}->{description}; + $params->{creation_ts} = $params->{fields}->{dateCreated}; + $params->{modification_ts} = $params->{fields}->{dateModified}; + $params->{view_policy} = $params->{fields}->{policy}->{view}; + $params->{edit_policy} = $params->{fields}->{policy}->{edit}; + $params->{join_policy} = $params->{fields}->{policy}->{join}; + $params->{members_raw} = $params->{attachments}->{members}->{members}; - delete $params->{fields}; - delete $params->{attachments}; + delete $params->{fields}; + delete $params->{attachments}; - return $params; + return $params; } # { @@ -146,131 +144,106 @@ sub BUILDARGS { ######################### sub create { - state $check = compile( - $Invocant | ClassName, - Dict[ - name => Str, - description => Str, - view_policy => Str, - edit_policy => Str, - join_policy => Str, - ] - ); - my ( $class, $params ) = $check->(@_); - - my $name = trim($params->{name}); - my $description = $params->{description}; - my $view_policy = $params->{view_policy}; - my $edit_policy = $params->{edit_policy}; - my $join_policy = $params->{join_policy}; - - my $data = { - transactions => [ - { type => 'name', value => $name }, - { type => 'description', value => $description }, - { type => 'edit', value => $edit_policy }, - { type => 'join', value => $join_policy }, - { type => 'view', value => $view_policy }, - { type => 'icon', value => 'group' }, - { type => 'color', value => 'red' } - ] - }; - - my $result = request( 'project.edit', $data ); - - return $class->new_from_query( - { phids => [ $result->{result}{object}{phid} ] } ); + state $check = compile( + $Invocant | ClassName, + Dict [ + name => Str, + description => Str, + view_policy => Str, + edit_policy => Str, + join_policy => Str, + ] + ); + my ($class, $params) = $check->(@_); + + my $name = trim($params->{name}); + my $description = $params->{description}; + my $view_policy = $params->{view_policy}; + my $edit_policy = $params->{edit_policy}; + my $join_policy = $params->{join_policy}; + + my $data = { + transactions => [ + {type => 'name', value => $name}, + {type => 'description', value => $description}, + {type => 'edit', value => $edit_policy}, + {type => 'join', value => $join_policy}, + {type => 'view', value => $view_policy}, + {type => 'icon', value => 'group'}, + {type => 'color', value => 'red'} + ] + }; + + my $result = request('project.edit', $data); + + return $class->new_from_query({phids => [$result->{result}{object}{phid}]}); } sub update { - my ($self) = @_; - - my $data = { - objectIdentifier => $self->phid, - transactions => [] - }; - - if ( $self->{set_name} ) { - push( - @{ $data->{transactions} }, - { - type => 'name', - value => $self->{set_name} - } - ); - } + my ($self) = @_; - if ( $self->{set_description} ) { - push( - @{ $data->{transactions} }, - { - type => 'description', - value => $self->{set_description} - } - ); - } + my $data = {objectIdentifier => $self->phid, transactions => []}; - if ( $self->{set_members} ) { - push( - @{ $data->{transactions} }, - { - type => 'members.set', - value => $self->{set_members} - } - ); - } - else { - if ( $self->{add_members} ) { - push( - @{ $data->{transactions} }, - { - type => 'members.add', - value => $self->{add_members} - } - ); - } - - if ( $self->{remove_members} ) { - push( - @{ $data->{transactions} }, - { - type => 'members.remove', - value => $self->{remove_members} - } - ); - } - } + if ($self->{set_name}) { + push(@{$data->{transactions}}, {type => 'name', value => $self->{set_name}}); + } - if ( $self->{set_policy} ) { - foreach my $name ( "view", "edit" ) { - next unless $self->{set_policy}->{$name}; - push( - @{ $data->{transactions} }, - { - type => $name, - value => $self->{set_policy}->{$name} - } - ); - } - } + if ($self->{set_description}) { + push( + @{$data->{transactions}}, + {type => 'description', value => $self->{set_description}} + ); + } - if ($self->{add_projects}) { - push(@{ $data->{transactions} }, { - type => 'projects.add', - value => $self->{add_projects} - }); + if ($self->{set_members}) { + push( + @{$data->{transactions}}, + {type => 'members.set', value => $self->{set_members}} + ); + } + else { + if ($self->{add_members}) { + push( + @{$data->{transactions}}, + {type => 'members.add', value => $self->{add_members}} + ); } - if ($self->{remove_projects}) { - push(@{ $data->{transactions} }, { - type => 'projects.remove', - value => $self->{remove_projects} - }); + if ($self->{remove_members}) { + push( + @{$data->{transactions}}, + {type => 'members.remove', value => $self->{remove_members}} + ); } + } + + if ($self->{set_policy}) { + foreach my $name ("view", "edit") { + next unless $self->{set_policy}->{$name}; + push( + @{$data->{transactions}}, + {type => $name, value => $self->{set_policy}->{$name}} + ); + } + } - my $result = request( 'project.edit', $data ); + if ($self->{add_projects}) { + push( + @{$data->{transactions}}, + {type => 'projects.add', value => $self->{add_projects}} + ); + } - return $result; + if ($self->{remove_projects}) { + push( + @{$data->{transactions}}, + {type => 'projects.remove', value => $self->{remove_projects}} + ); + } + + my $result = request('project.edit', $data); + + return $result; } ######################### @@ -278,40 +251,40 @@ sub update { ######################### sub set_name { - my ( $self, $name ) = @_; - $name = trim($name); - $self->{set_name} = $name; + my ($self, $name) = @_; + $name = trim($name); + $self->{set_name} = $name; } sub set_description { - my ( $self, $description ) = @_; - $description = trim($description); - $self->{set_description} = $description; + my ($self, $description) = @_; + $description = trim($description); + $self->{set_description} = $description; } sub add_member { - my ( $self, $member ) = @_; - $self->{add_members} ||= []; - my $member_phid = blessed $member ? $member->phid : $member; - push( @{ $self->{add_members} }, $member_phid ); + my ($self, $member) = @_; + $self->{add_members} ||= []; + my $member_phid = blessed $member ? $member->phid : $member; + push(@{$self->{add_members}}, $member_phid); } sub remove_member { - my ( $self, $member ) = @_; - $self->{remove_members} ||= []; - my $member_phid = blessed $member ? $member->phid : $member; - push( @{ $self->{remove_members} }, $member_phid ); + my ($self, $member) = @_; + $self->{remove_members} ||= []; + my $member_phid = blessed $member ? $member->phid : $member; + push(@{$self->{remove_members}}, $member_phid); } sub set_members { - my ( $self, $members ) = @_; - $self->{set_members} = [ map { blessed $_ ? $_->phid : $_ } @$members ]; + my ($self, $members) = @_; + $self->{set_members} = [map { blessed $_ ? $_->phid : $_ } @$members]; } sub set_policy { - my ( $self, $name, $policy ) = @_; - $self->{set_policy} ||= {}; - $self->{set_policy}->{$name} = $policy; + my ($self, $name, $policy) = @_; + $self->{set_policy} ||= {}; + $self->{set_policy}->{$name} = $policy; } ############ @@ -319,21 +292,17 @@ sub set_policy { ############ sub _build_members { - my ( $self ) = @_; - return [] unless $self->members_raw; + my ($self) = @_; + return [] unless $self->members_raw; - my @phids; - foreach my $member ( @{ $self->members_raw } ) { - push( @phids, $member->{phid} ); - } + my @phids; + foreach my $member (@{$self->members_raw}) { + push(@phids, $member->{phid}); + } - return [] if !@phids; + return [] if !@phids; - return Bugzilla::Extension::PhabBugz::User->match( - { - phids => \@phids - } - ); + return Bugzilla::Extension::PhabBugz::User->match({phids => \@phids}); } -1;
\ No newline at end of file +1; diff --git a/extensions/PhabBugz/lib/Revision.pm b/extensions/PhabBugz/lib/Revision.pm index 6ad906829..d529c581d 100644 --- a/extensions/PhabBugz/lib/Revision.pm +++ b/extensions/PhabBugz/lib/Revision.pm @@ -27,100 +27,93 @@ use Bugzilla::Extension::PhabBugz::Util qw(request); # Initialization # ######################### -has id => ( is => 'ro', isa => Int ); -has phid => ( is => 'ro', isa => Str ); -has title => ( is => 'ro', isa => Str ); -has summary => ( is => 'ro', isa => Str ); -has status => ( is => 'ro', isa => Str ); -has creation_ts => ( is => 'ro', isa => Str ); -has modification_ts => ( is => 'ro', isa => Str ); -has author_phid => ( is => 'ro', isa => Str ); -has bug_id => ( is => 'ro', isa => Str ); -has view_policy => ( is => 'ro', isa => Str ); -has edit_policy => ( is => 'ro', isa => Str ); -has subscriber_count => ( is => 'ro', isa => Int ); -has bug => ( is => 'lazy', isa => Object ); -has author => ( is => 'lazy', isa => Object ); -has reviews => ( is => 'lazy', isa => ArrayRef [ Dict [ user => PhabUser, status => Str ] ] ); -has subscribers => ( is => 'lazy', isa => ArrayRef [PhabUser] ); -has projects => ( is => 'lazy', isa => ArrayRef [Project] ); +has id => (is => 'ro', isa => Int); +has phid => (is => 'ro', isa => Str); +has title => (is => 'ro', isa => Str); +has summary => (is => 'ro', isa => Str); +has status => (is => 'ro', isa => Str); +has creation_ts => (is => 'ro', isa => Str); +has modification_ts => (is => 'ro', isa => Str); +has author_phid => (is => 'ro', isa => Str); +has bug_id => (is => 'ro', isa => Str); +has view_policy => (is => 'ro', isa => Str); +has edit_policy => (is => 'ro', isa => Str); +has subscriber_count => (is => 'ro', isa => Int); +has bug => (is => 'lazy', isa => Object); +has author => (is => 'lazy', isa => Object); +has reviews => + (is => 'lazy', isa => ArrayRef [Dict [user => PhabUser, status => Str]]); +has subscribers => (is => 'lazy', isa => ArrayRef [PhabUser]); +has projects => (is => 'lazy', isa => ArrayRef [Project]); has reviewers_raw => ( - is => 'ro', - isa => ArrayRef [ - Dict [ - reviewerPHID => Str, - status => Str, - isBlocking => Bool | JSONBool, - actorPHID => Maybe [Str], - ], - ] + is => 'ro', + isa => ArrayRef [ + Dict [ + reviewerPHID => Str, + status => Str, + isBlocking => Bool | JSONBool, + actorPHID => Maybe [Str], + ], + ] ); has subscribers_raw => ( - is => 'ro', - isa => Dict [ - subscriberPHIDs => ArrayRef [Str], - subscriberCount => Int, - viewerIsSubscribed => Bool | JSONBool, - ] -); -has projects_raw => ( - is => 'ro', - isa => Dict [ - projectPHIDs => ArrayRef [Str] - ] + is => 'ro', + isa => Dict [ + subscriberPHIDs => ArrayRef [Str], + subscriberCount => Int, + viewerIsSubscribed => Bool | JSONBool, + ] ); +has projects_raw => (is => 'ro', isa => Dict [projectPHIDs => ArrayRef [Str]]); sub new_from_query { - my ( $class, $params ) = @_; - - my $data = { - queryKey => 'all', - attachments => { - projects => 1, - reviewers => 1, - subscribers => 1 - }, - constraints => $params - }; - - my $result = request( 'differential.revision.search', $data ); - if ( exists $result->{result}{data} && @{ $result->{result}{data} } ) { - $result = $result->{result}{data}[0]; - - # Some values in Phabricator for bug ids may have been saved - # white whitespace so we remove any here just in case. - $result->{fields}->{'bugzilla.bug-id'} = - $result->{fields}->{'bugzilla.bug-id'} - ? trim( $result->{fields}->{'bugzilla.bug-id'} ) - : ""; - return $class->new($result); - } - - return undef; + my ($class, $params) = @_; + + my $data = { + queryKey => 'all', + attachments => {projects => 1, reviewers => 1, subscribers => 1}, + constraints => $params + }; + + my $result = request('differential.revision.search', $data); + if (exists $result->{result}{data} && @{$result->{result}{data}}) { + $result = $result->{result}{data}[0]; + + # Some values in Phabricator for bug ids may have been saved + # white whitespace so we remove any here just in case. + $result->{fields}->{'bugzilla.bug-id'} + = $result->{fields}->{'bugzilla.bug-id'} + ? trim($result->{fields}->{'bugzilla.bug-id'}) + : ""; + return $class->new($result); + } + + return undef; } sub BUILDARGS { - my ( $class, $params ) = @_; - - $params->{title} = $params->{fields}->{title}; - $params->{summary} = $params->{fields}->{summary}; - $params->{status} = $params->{fields}->{status}->{value}; - $params->{creation_ts} = $params->{fields}->{dateCreated}; - $params->{modification_ts} = $params->{fields}->{dateModified}; - $params->{author_phid} = $params->{fields}->{authorPHID}; - $params->{bug_id} = $params->{fields}->{'bugzilla.bug-id'}; - $params->{view_policy} = $params->{fields}->{policy}->{view}; - $params->{edit_policy} = $params->{fields}->{policy}->{edit}; - $params->{reviewers_raw} = $params->{attachments}->{reviewers}->{reviewers} // []; - $params->{subscribers_raw} = $params->{attachments}->{subscribers}; - $params->{projects_raw} = $params->{attachments}->{projects}; - $params->{subscriber_count} = - $params->{attachments}->{subscribers}->{subscriberCount}; - - delete $params->{fields}; - delete $params->{attachments}; - - return $params; + my ($class, $params) = @_; + + $params->{title} = $params->{fields}->{title}; + $params->{summary} = $params->{fields}->{summary}; + $params->{status} = $params->{fields}->{status}->{value}; + $params->{creation_ts} = $params->{fields}->{dateCreated}; + $params->{modification_ts} = $params->{fields}->{dateModified}; + $params->{author_phid} = $params->{fields}->{authorPHID}; + $params->{bug_id} = $params->{fields}->{'bugzilla.bug-id'}; + $params->{view_policy} = $params->{fields}->{policy}->{view}; + $params->{edit_policy} = $params->{fields}->{policy}->{edit}; + $params->{reviewers_raw} = $params->{attachments}->{reviewers}->{reviewers} + // []; + $params->{subscribers_raw} = $params->{attachments}->{subscribers}; + $params->{projects_raw} = $params->{attachments}->{projects}; + $params->{subscriber_count} + = $params->{attachments}->{subscribers}->{subscriberCount}; + + delete $params->{fields}; + delete $params->{attachments}; + + return $params; } # { @@ -185,99 +178,71 @@ sub BUILDARGS { ######################### sub update { - my ($self) = @_; - - my $data = { - objectIdentifier => $self->phid, - transactions => [] - }; - - if ( $self->{added_comments} ) { - foreach my $comment ( @{ $self->{added_comments} } ) { - push @{ $data->{transactions} }, - { - type => 'comment', - value => $comment - }; - } - } - - if ( $self->{set_subscribers} ) { - push @{ $data->{transactions} }, - { - type => 'subscribers.set', - value => $self->{set_subscribers} - }; - } - - if ( $self->{add_subscribers} ) { - push @{ $data->{transactions} }, - { - type => 'subscribers.add', - value => $self->{add_subscribers} - }; - } + my ($self) = @_; - if ( $self->{remove_subscribers} ) { - push @{ $data->{transactions} }, - { - type => 'subscribers.remove', - value => $self->{remove_subscribers} - }; - } - - if ( $self->{set_reviewers} ) { - push @{ $data->{transactions} }, - { - type => 'reviewers.set', - value => $self->{set_reviewers} - }; - } - - if ( $self->{add_reviewers} ) { - push @{ $data->{transactions} }, - { - type => 'reviewers.add', - value => $self->{add_reviewers} - }; - } + my $data = {objectIdentifier => $self->phid, transactions => []}; - if ( $self->{remove_reviewers} ) { - push @{ $data->{transactions} }, - { - type => 'reviewers.remove', - value => $self->{remove_reviewers} - }; + if ($self->{added_comments}) { + foreach my $comment (@{$self->{added_comments}}) { + push @{$data->{transactions}}, {type => 'comment', value => $comment}; } - - if ( $self->{set_policy} ) { - foreach my $name ( "view", "edit" ) { - next unless $self->{set_policy}->{$name}; - push @{ $data->{transactions} }, - { - type => $name, - value => $self->{set_policy}->{$name} - }; - } + } + + if ($self->{set_subscribers}) { + push @{$data->{transactions}}, + {type => 'subscribers.set', value => $self->{set_subscribers}}; + } + + if ($self->{add_subscribers}) { + push @{$data->{transactions}}, + {type => 'subscribers.add', value => $self->{add_subscribers}}; + } + + if ($self->{remove_subscribers}) { + push @{$data->{transactions}}, + {type => 'subscribers.remove', value => $self->{remove_subscribers}}; + } + + if ($self->{set_reviewers}) { + push @{$data->{transactions}}, + {type => 'reviewers.set', value => $self->{set_reviewers}}; + } + + if ($self->{add_reviewers}) { + push @{$data->{transactions}}, + {type => 'reviewers.add', value => $self->{add_reviewers}}; + } + + if ($self->{remove_reviewers}) { + push @{$data->{transactions}}, + {type => 'reviewers.remove', value => $self->{remove_reviewers}}; + } + + if ($self->{set_policy}) { + foreach my $name ("view", "edit") { + next unless $self->{set_policy}->{$name}; + push @{$data->{transactions}}, + {type => $name, value => $self->{set_policy}->{$name}}; } + } - if ($self->{add_projects}) { - push(@{ $data->{transactions} }, { - type => 'projects.add', - value => $self->{add_projects} - }); - } + if ($self->{add_projects}) { + push( + @{$data->{transactions}}, + {type => 'projects.add', value => $self->{add_projects}} + ); + } - if ($self->{remove_projects}) { - push(@{ $data->{transactions} }, { - type => 'projects.remove', - value => $self->{remove_projects} - }); - } + if ($self->{remove_projects}) { + push( + @{$data->{transactions}}, + {type => 'projects.remove', value => $self->{remove_projects}} + ); + } - my $result = request( 'differential.revision.edit', $data ); + my $result = request('differential.revision.edit', $data); - return $result; + return $result; } ######################### @@ -285,80 +250,61 @@ sub update { ######################### sub _build_bug { - my ($self) = @_; - return $self->{bug} ||= - Bugzilla::Bug->new( { id => $self->bug_id, cache => 1 } ); + my ($self) = @_; + return $self->{bug} ||= Bugzilla::Bug->new({id => $self->bug_id, cache => 1}); } sub _build_author { - my ($self) = @_; - return $self->{author} if $self->{author}; - my $phab_user = Bugzilla::Extension::PhabBugz::User->new_from_query( - { - phids => [ $self->author_phid ] - } - ); - if ($phab_user) { - return $self->{author} = $phab_user; - } + my ($self) = @_; + return $self->{author} if $self->{author}; + my $phab_user + = Bugzilla::Extension::PhabBugz::User->new_from_query({ + phids => [$self->author_phid] + }); + if ($phab_user) { + return $self->{author} = $phab_user; + } } sub _build_reviews { - my ($self) = @_; + my ($self) = @_; - my %by_phid = map { $_->{reviewerPHID} => $_ } @{ $self->reviewers_raw }; - my $users = Bugzilla::Extension::PhabBugz::User->match( - { - phids => [keys %by_phid] - } - ); + my %by_phid = map { $_->{reviewerPHID} => $_ } @{$self->reviewers_raw}; + my $users + = Bugzilla::Extension::PhabBugz::User->match({phids => [keys %by_phid]}); - return [ - map { - { - user => $_, - status => $by_phid{ $_->phid }{status}, - } - } @$users - ]; + return [map { {user => $_, status => $by_phid{$_->phid}{status},} } @$users]; } sub _build_subscribers { - my ($self) = @_; + my ($self) = @_; - return $self->{subscribers} if $self->{subscribers}; - return [] unless $self->subscribers_raw->{subscriberPHIDs}; + return $self->{subscribers} if $self->{subscribers}; + return [] unless $self->subscribers_raw->{subscriberPHIDs}; - my @phids; - foreach my $phid ( @{ $self->subscribers_raw->{subscriberPHIDs} } ) { - push @phids, $phid; - } + my @phids; + foreach my $phid (@{$self->subscribers_raw->{subscriberPHIDs}}) { + push @phids, $phid; + } - my $users = Bugzilla::Extension::PhabBugz::User->match( - { - phids => \@phids - } - ); + my $users = Bugzilla::Extension::PhabBugz::User->match({phids => \@phids}); - return $self->{subscribers} = $users; + return $self->{subscribers} = $users; } sub _build_projects { - my ($self) = @_; - - return $self->{projects} if $self->{projects}; - return [] unless $self->projects_raw->{projectPHIDs}; - - my @projects; - foreach my $phid ( @{ $self->projects_raw->{projectPHIDs} } ) { - push @projects, Bugzilla::Extension::PhabBugz::Project->new_from_query( - { - phids => [ $phid ] - } - ); - } + my ($self) = @_; - return $self->{projects} = \@projects; + return $self->{projects} if $self->{projects}; + return [] unless $self->projects_raw->{projectPHIDs}; + + my @projects; + foreach my $phid (@{$self->projects_raw->{projectPHIDs}}) { + push @projects, + Bugzilla::Extension::PhabBugz::Project->new_from_query({phids => [$phid]}); + } + + return $self->{projects} = \@projects; } ######################### @@ -366,124 +312,116 @@ sub _build_projects { ######################### sub add_comment { - my ( $self, $comment ) = @_; - $comment = trim($comment); - $self->{added_comments} ||= []; - push @{ $self->{added_comments} }, $comment; + my ($self, $comment) = @_; + $comment = trim($comment); + $self->{added_comments} ||= []; + push @{$self->{added_comments}}, $comment; } sub add_reviewer { - my ( $self, $reviewer ) = @_; - $self->{add_reviewers} ||= []; - my $reviewer_phid = blessed $reviewer ? $reviewer->phid : $reviewer; - push @{ $self->{add_reviewers} }, $reviewer_phid; + my ($self, $reviewer) = @_; + $self->{add_reviewers} ||= []; + my $reviewer_phid = blessed $reviewer ? $reviewer->phid : $reviewer; + push @{$self->{add_reviewers}}, $reviewer_phid; } sub remove_reviewer { - my ( $self, $reviewer ) = @_; - $self->{remove_reviewers} ||= []; - my $reviewer_phid = blessed $reviewer ? $reviewer->phid : $reviewer; - push @{ $self->{remove_reviewers} }, $reviewer_phid; + my ($self, $reviewer) = @_; + $self->{remove_reviewers} ||= []; + my $reviewer_phid = blessed $reviewer ? $reviewer->phid : $reviewer; + push @{$self->{remove_reviewers}}, $reviewer_phid; } sub set_reviewers { - my ( $self, $reviewers ) = @_; - $self->{set_reviewers} = [ map { $_->phid } @$reviewers ]; + my ($self, $reviewers) = @_; + $self->{set_reviewers} = [map { $_->phid } @$reviewers]; } sub add_subscriber { - my ( $self, $subscriber ) = @_; - $self->{add_subscribers} ||= []; - my $subscriber_phid = - blessed $subscriber ? $subscriber->phid : $subscriber; - push @{ $self->{add_subscribers} }, $subscriber_phid; + my ($self, $subscriber) = @_; + $self->{add_subscribers} ||= []; + my $subscriber_phid = blessed $subscriber ? $subscriber->phid : $subscriber; + push @{$self->{add_subscribers}}, $subscriber_phid; } sub remove_subscriber { - my ( $self, $subscriber ) = @_; - $self->{remove_subscribers} ||= []; - my $subscriber_phid = - blessed $subscriber ? $subscriber->phid : $subscriber; - push @{ $self->{remove_subscribers} }, $subscriber_phid; + my ($self, $subscriber) = @_; + $self->{remove_subscribers} ||= []; + my $subscriber_phid = blessed $subscriber ? $subscriber->phid : $subscriber; + push @{$self->{remove_subscribers}}, $subscriber_phid; } sub set_subscribers { - my ( $self, $subscribers ) = @_; - $self->{set_subscribers} = $subscribers; + my ($self, $subscribers) = @_; + $self->{set_subscribers} = $subscribers; } sub set_policy { - my ( $self, $name, $policy ) = @_; - $self->{set_policy} ||= {}; - $self->{set_policy}->{$name} = $policy; + my ($self, $name, $policy) = @_; + $self->{set_policy} ||= {}; + $self->{set_policy}->{$name} = $policy; } sub add_project { - my ( $self, $project ) = @_; - $self->{add_projects} ||= []; - my $project_phid = blessed $project ? $project->phid : $project; - return undef unless $project_phid; - push @{ $self->{add_projects} }, $project_phid; + my ($self, $project) = @_; + $self->{add_projects} ||= []; + my $project_phid = blessed $project ? $project->phid : $project; + return undef unless $project_phid; + push @{$self->{add_projects}}, $project_phid; } sub remove_project { - my ( $self, $project ) = @_; - $self->{remove_projects} ||= []; - my $project_phid = blessed $project ? $project->phid : $project; - return undef unless $project_phid; - push @{ $self->{remove_projects} }, $project_phid; + my ($self, $project) = @_; + $self->{remove_projects} ||= []; + my $project_phid = blessed $project ? $project->phid : $project; + return undef unless $project_phid; + push @{$self->{remove_projects}}, $project_phid; } sub make_private { - my ( $self, $project_names ) = @_; - - my $secure_revision_project = - Bugzilla::Extension::PhabBugz::Project->new_from_query( - { - name => 'secure-revision' - } - ); - - my @set_projects; - foreach my $name (@$project_names) { - my $set_project = - Bugzilla::Extension::PhabBugz::Project->new_from_query( - { - name => $name - } - ); - push @set_projects, $set_project; - } + my ($self, $project_names) = @_; - my $new_policy = Bugzilla::Extension::PhabBugz::Policy->create(\@set_projects); - $self->set_policy('view', $new_policy->phid); - $self->set_policy('edit', $new_policy->phid); + my $secure_revision_project + = Bugzilla::Extension::PhabBugz::Project->new_from_query({ + name => 'secure-revision' + }); - foreach my $project ($secure_revision_project, @set_projects) { - $self->add_project($project->phid); - } + my @set_projects; + foreach my $name (@$project_names) { + my $set_project + = Bugzilla::Extension::PhabBugz::Project->new_from_query({name => $name}); + push @set_projects, $set_project; + } - return $self; + my $new_policy = Bugzilla::Extension::PhabBugz::Policy->create(\@set_projects); + $self->set_policy('view', $new_policy->phid); + $self->set_policy('edit', $new_policy->phid); + + foreach my $project ($secure_revision_project, @set_projects) { + $self->add_project($project->phid); + } + + return $self; } sub make_public { - my ( $self ) = @_; + my ($self) = @_; - my $editbugs = Bugzilla::Extension::PhabBugz::Project->new_from_query( - { - name => 'bmo-editbugs-team' - } - ); + my $editbugs + = Bugzilla::Extension::PhabBugz::Project->new_from_query({ + name => 'bmo-editbugs-team' + }); - $self->set_policy( 'view', 'public' ); - $self->set_policy( 'edit', ( $editbugs ? $editbugs->phid : 'users' ) ); + $self->set_policy('view', 'public'); + $self->set_policy('edit', ($editbugs ? $editbugs->phid : 'users')); - my @current_group_projects = grep { $_->name =~ /^(bmo-.*|secure-revision)$/ } @{ $self->projects }; - foreach my $project (@current_group_projects) { - $self->remove_project($project->phid); - } + my @current_group_projects + = grep { $_->name =~ /^(bmo-.*|secure-revision)$/ } @{$self->projects}; + foreach my $project (@current_group_projects) { + $self->remove_project($project->phid); + } - return $self; + return $self; } 1; diff --git a/extensions/PhabBugz/lib/Types.pm b/extensions/PhabBugz/lib/Types.pm index 493e97fbc..267b8c26a 100644 --- a/extensions/PhabBugz/lib/Types.pm +++ b/extensions/PhabBugz/lib/Types.pm @@ -11,18 +11,15 @@ use 5.10.1; use strict; use warnings; -use Type::Library - -base, - -declare => qw( Revision LinkedPhabUser PhabUser Policy Project ); +use Type::Library -base, + -declare => qw( Revision LinkedPhabUser PhabUser Policy Project ); use Type::Utils -all; use Types::Standard -all; -class_type Revision, { class => 'Bugzilla::Extension::PhabBugz::Revision' }; -class_type Policy, { class => 'Bugzilla::Extension::PhabBugz::Policy' }; -class_type Project, { class => 'Bugzilla::Extension::PhabBugz::Project' }; -class_type PhabUser, { class => 'Bugzilla::Extension::PhabBugz::User' }; -declare LinkedPhabUser, - as PhabUser, - where { is_Int($_->bugzilla_id) }; +class_type Revision, {class => 'Bugzilla::Extension::PhabBugz::Revision'}; +class_type Policy, {class => 'Bugzilla::Extension::PhabBugz::Policy'}; +class_type Project, {class => 'Bugzilla::Extension::PhabBugz::Project'}; +class_type PhabUser, {class => 'Bugzilla::Extension::PhabBugz::User'}; +declare LinkedPhabUser, as PhabUser, where { is_Int($_->bugzilla_id) }; 1; diff --git a/extensions/PhabBugz/lib/User.pm b/extensions/PhabBugz/lib/User.pm index 209425bdf..2bc2ed7dc 100644 --- a/extensions/PhabBugz/lib/User.pm +++ b/extensions/PhabBugz/lib/User.pm @@ -23,44 +23,44 @@ use Type::Params qw(compile); # Initialization # ######################### -has 'id' => ( is => 'ro', isa => Int ); -has 'type' => ( is => 'ro', isa => Str ); -has 'phid' => ( is => 'ro', isa => Str ); -has 'name' => ( is => 'ro', isa => Str ); -has 'realname' => ( is => 'ro', isa => Str ); -has 'creation_ts' => ( is => 'ro', isa => Int ); -has 'modification_ts' => ( is => 'ro', isa => Int ); -has 'roles' => ( is => 'ro', isa => ArrayRef [Str] ); -has 'view_policy' => ( is => 'ro', isa => Str ); -has 'edit_policy' => ( is => 'ro', isa => Str ); -has 'bugzilla_id' => ( is => 'ro', isa => Maybe [Int] ); -has 'bugzilla_user' => ( is => 'lazy', isa => Maybe [User] ); - -my $Invocant = class_type { class => __PACKAGE__ }; +has 'id' => (is => 'ro', isa => Int); +has 'type' => (is => 'ro', isa => Str); +has 'phid' => (is => 'ro', isa => Str); +has 'name' => (is => 'ro', isa => Str); +has 'realname' => (is => 'ro', isa => Str); +has 'creation_ts' => (is => 'ro', isa => Int); +has 'modification_ts' => (is => 'ro', isa => Int); +has 'roles' => (is => 'ro', isa => ArrayRef [Str]); +has 'view_policy' => (is => 'ro', isa => Str); +has 'edit_policy' => (is => 'ro', isa => Str); +has 'bugzilla_id' => (is => 'ro', isa => Maybe [Int]); +has 'bugzilla_user' => (is => 'lazy', isa => Maybe [User]); + +my $Invocant = class_type {class => __PACKAGE__}; sub BUILDARGS { - my ( $class, $params ) = @_; - - $params->{name} = $params->{fields}->{username}; - $params->{realname} = $params->{fields}->{realName}; - $params->{creation_ts} = $params->{fields}->{dateCreated}; - $params->{modification_ts} = $params->{fields}->{dateModified}; - $params->{roles} = $params->{fields}->{roles}; - $params->{view_policy} = $params->{fields}->{policy}->{view}; - $params->{edit_policy} = $params->{fields}->{policy}->{edit}; - - delete $params->{fields}; - - my $external_accounts = - $params->{attachments}{'external-accounts'}{'external-accounts'}; - if ($external_accounts) { - my $bug_user = first { $_->{type} eq 'bmo' } @$external_accounts; - $params->{bugzilla_id} = $bug_user->{id}; - } + my ($class, $params) = @_; + + $params->{name} = $params->{fields}->{username}; + $params->{realname} = $params->{fields}->{realName}; + $params->{creation_ts} = $params->{fields}->{dateCreated}; + $params->{modification_ts} = $params->{fields}->{dateModified}; + $params->{roles} = $params->{fields}->{roles}; + $params->{view_policy} = $params->{fields}->{policy}->{view}; + $params->{edit_policy} = $params->{fields}->{policy}->{edit}; + + delete $params->{fields}; - delete $params->{attachments}; + my $external_accounts + = $params->{attachments}{'external-accounts'}{'external-accounts'}; + if ($external_accounts) { + my $bug_user = first { $_->{type} eq 'bmo' } @$external_accounts; + $params->{bugzilla_id} = $bug_user->{id}; + } - return $params; + delete $params->{attachments}; + + return $params; } # { @@ -110,45 +110,45 @@ sub BUILDARGS { # } sub new_from_query { - my ( $class, $params ) = @_; - my $matches = $class->match($params); - return $matches->[0]; + my ($class, $params) = @_; + my $matches = $class->match($params); + return $matches->[0]; } sub match { - state $check = compile( $Invocant | ClassName, Dict[ ids => ArrayRef[Int] ] | Dict[ phids => ArrayRef[Str] ] ); - my ( $class, $params ) = $check->(@_); - - # BMO id search takes precedence if bugzilla_ids is used. - my $bugzilla_ids = delete $params->{ids}; - if ($bugzilla_ids) { - my $bugzilla_data = - $class->get_phab_bugzilla_ids( { ids => $bugzilla_ids } ); - $params->{phids} = [ map { $_->{phid} } @$bugzilla_data ]; + state $check = compile($Invocant | ClassName, + Dict [ids => ArrayRef [Int]] | Dict [phids => ArrayRef [Str]]); + my ($class, $params) = $check->(@_); + + # BMO id search takes precedence if bugzilla_ids is used. + my $bugzilla_ids = delete $params->{ids}; + if ($bugzilla_ids) { + my $bugzilla_data = $class->get_phab_bugzilla_ids({ids => $bugzilla_ids}); + $params->{phids} = [map { $_->{phid} } @$bugzilla_data]; + } + + return [] if !@{$params->{phids}}; + + # Look for BMO external user id in external-accounts attachment + my $data = { + constraints => {phids => $params->{phids}}, + attachments => {'external-accounts' => 1} + }; + + # We can only fetch 100 users at a time so we need to do this in lumps + my $phab_users = []; + my $result; + do { + $result = request('user.search', $data)->{result}; + if (exists $result->{data} && @{$result->{data}}) { + foreach my $user (@{$result->{data}}) { + push @$phab_users, $class->new($user); + } } + $data->{after} = $result->{cursor}->{after}; + } while ($result->{cursor}->{after}); - return [] if !@{ $params->{phids} }; - - # Look for BMO external user id in external-accounts attachment - my $data = { - constraints => { phids => $params->{phids} }, - attachments => { 'external-accounts' => 1 } - }; - - # We can only fetch 100 users at a time so we need to do this in lumps - my $phab_users = []; - my $result; - do { - $result = request( 'user.search', $data )->{result}; - if ( exists $result->{data} && @{ $result->{data} } ) { - foreach my $user ( @{ $result->{data} } ) { - push @$phab_users, $class->new($user); - } - } - $data->{after} = $result->{cursor}->{after}; - } while ($result->{cursor}->{after}); - - return $phab_users; + return $phab_users; } ################# @@ -156,48 +156,44 @@ sub match { ################# sub _build_bugzilla_user { - my ($self) = @_; - return undef unless $self->bugzilla_id; - return Bugzilla::User->new( { id => $self->bugzilla_id, cache => 1 } ); + my ($self) = @_; + return undef unless $self->bugzilla_id; + return Bugzilla::User->new({id => $self->bugzilla_id, cache => 1}); } sub get_phab_bugzilla_ids { - state $check = compile($Invocant | ClassName, Dict[ids => ArrayRef[Int]]); - my ( $class, $params ) = $check->(@_); - - my $memcache = Bugzilla->memcached; - - # Try to find the values in memcache first - my @results; - my %bugzilla_ids = map { $_ => 1 } @{ $params->{ids} }; - foreach my $bugzilla_id ( keys %bugzilla_ids ) { - my $phid = - $memcache->get( { key => "phab_user_bugzilla_id_" . $bugzilla_id } ); - if ($phid) { - push @results, { id => $bugzilla_id, phid => $phid }; - delete $bugzilla_ids{$bugzilla_id}; - } + state $check = compile($Invocant | ClassName, Dict [ids => ArrayRef [Int]]); + my ($class, $params) = $check->(@_); + + my $memcache = Bugzilla->memcached; + + # Try to find the values in memcache first + my @results; + my %bugzilla_ids = map { $_ => 1 } @{$params->{ids}}; + foreach my $bugzilla_id (keys %bugzilla_ids) { + my $phid = $memcache->get({key => "phab_user_bugzilla_id_" . $bugzilla_id}); + if ($phid) { + push @results, {id => $bugzilla_id, phid => $phid}; + delete $bugzilla_ids{$bugzilla_id}; } + } + + if (%bugzilla_ids) { + $params->{ids} = [keys %bugzilla_ids]; + + my $result = request('bugzilla.account.search', $params); - if (%bugzilla_ids) { - $params->{ids} = [ keys %bugzilla_ids ]; - - my $result = request( 'bugzilla.account.search', $params ); - - # Store new values in memcache for later retrieval - foreach my $user ( @{ $result->{result} } ) { - next if !$user->{phid}; - $memcache->set( - { - key => "phab_user_bugzilla_id_" . $user->{id}, - value => $user->{phid} - } - ); - push @results, $user; - } + # Store new values in memcache for later retrieval + foreach my $user (@{$result->{result}}) { + next if !$user->{phid}; + $memcache->set({ + key => "phab_user_bugzilla_id_" . $user->{id}, value => $user->{phid} + }); + push @results, $user; } + } - return \@results; + return \@results; } 1; diff --git a/extensions/PhabBugz/lib/Util.pm b/extensions/PhabBugz/lib/Util.pm index 32f860413..613fd3466 100644 --- a/extensions/PhabBugz/lib/Util.pm +++ b/extensions/PhabBugz/lib/Util.pm @@ -32,167 +32,166 @@ use Mojo::JSON qw(encode_json); use base qw(Exporter); our @EXPORT = qw( - create_revision_attachment - get_attachment_revisions - get_bug_role_phids - intersect - is_attachment_phab_revision - request - set_phab_user + create_revision_attachment + get_attachment_revisions + get_bug_role_phids + intersect + is_attachment_phab_revision + request + set_phab_user ); sub create_revision_attachment { - state $check = compile(Bug, Revision, Str, User); - my ( $bug, $revision, $timestamp, $submitter ) = $check->(@_); - - my $phab_base_uri = Bugzilla->params->{phabricator_base_uri}; - ThrowUserError('invalid_phabricator_uri') unless $phab_base_uri; - - my $revision_uri = $phab_base_uri . "D" . $revision->id; - - # Check for previous attachment with same revision id. - # If one matches then return it instead. This is fine as - # BMO does not contain actual diff content. - my @review_attachments = grep { is_attachment_phab_revision($_) } @{ $bug->attachments }; - my $review_attachment = first { trim($_->data) eq $revision_uri } @review_attachments; - return $review_attachment if defined $review_attachment; - - # No attachment is present, so we can now create new one - - if (!$timestamp) { - ($timestamp) = Bugzilla->dbh->selectrow_array("SELECT NOW()"); - } - - # If submitter, then switch to that user when creating attachment - local $submitter->{groups} = [ Bugzilla::Group->get_all ]; # We need to always be able to add attachment - my $restore_prev_user = Bugzilla->set_user($submitter, scope_guard => 1); - - my $attachment = Bugzilla::Attachment->create( - { - bug => $bug, - creation_ts => $timestamp, - data => $revision_uri, - description => $revision->title, - filename => 'phabricator-D' . $revision->id . '-url.txt', - ispatch => 0, - isprivate => 0, - mimetype => PHAB_CONTENT_TYPE, - } - ); - - # Insert a comment about the new attachment into the database. - $bug->add_comment($revision->summary, { type => CMT_ATTACHMENT_CREATED, - extra_data => $attachment->id }); - - delete $bug->{attachments}; - - return $attachment; + state $check = compile(Bug, Revision, Str, User); + my ($bug, $revision, $timestamp, $submitter) = $check->(@_); + + my $phab_base_uri = Bugzilla->params->{phabricator_base_uri}; + ThrowUserError('invalid_phabricator_uri') unless $phab_base_uri; + + my $revision_uri = $phab_base_uri . "D" . $revision->id; + + # Check for previous attachment with same revision id. + # If one matches then return it instead. This is fine as + # BMO does not contain actual diff content. + my @review_attachments + = grep { is_attachment_phab_revision($_) } @{$bug->attachments}; + my $review_attachment + = first { trim($_->data) eq $revision_uri } @review_attachments; + return $review_attachment if defined $review_attachment; + + # No attachment is present, so we can now create new one + + if (!$timestamp) { + ($timestamp) = Bugzilla->dbh->selectrow_array("SELECT NOW()"); + } + + # If submitter, then switch to that user when creating attachment + local $submitter->{groups} = [Bugzilla::Group->get_all]; # We need to always be able to add attachment + my $restore_prev_user = Bugzilla->set_user($submitter, scope_guard => 1); + + my $attachment = Bugzilla::Attachment->create({ + bug => $bug, + creation_ts => $timestamp, + data => $revision_uri, + description => $revision->title, + filename => 'phabricator-D' . $revision->id . '-url.txt', + ispatch => 0, + isprivate => 0, + mimetype => PHAB_CONTENT_TYPE, + }); + + # Insert a comment about the new attachment into the database. + $bug->add_comment($revision->summary, + {type => CMT_ATTACHMENT_CREATED, extra_data => $attachment->id}); + + delete $bug->{attachments}; + + return $attachment; } sub intersect { - my ($list1, $list2) = @_; - my %e = map { $_ => undef } @{$list1}; - return grep { exists( $e{$_} ) } @{$list2}; + my ($list1, $list2) = @_; + my %e = map { $_ => undef } @{$list1}; + return grep { exists($e{$_}) } @{$list2}; } sub get_bug_role_phids { - state $check = compile(Bug); - my ($bug) = $check->(@_); - - my @bug_users = ( $bug->reporter ); - push(@bug_users, $bug->assigned_to) - if $bug->assigned_to->email != Bugzilla->params->{'nobody_user'}; - push(@bug_users, $bug->qa_contact) if $bug->qa_contact; - push(@bug_users, @{ $bug->cc_users }) if @{ $bug->cc_users }; - - my $phab_users = - Bugzilla::Extension::PhabBugz::User->match( - { - ids => [ map { $_->id } @bug_users ] - } - ); - - return [ map { $_->phid } @{ $phab_users } ]; + state $check = compile(Bug); + my ($bug) = $check->(@_); + + my @bug_users = ($bug->reporter); + push(@bug_users, $bug->assigned_to) + if $bug->assigned_to->email != Bugzilla->params->{'nobody_user'}; + push(@bug_users, $bug->qa_contact) if $bug->qa_contact; + push(@bug_users, @{$bug->cc_users}) if @{$bug->cc_users}; + + my $phab_users + = Bugzilla::Extension::PhabBugz::User->match({ + ids => [map { $_->id } @bug_users] + }); + + return [map { $_->phid } @{$phab_users}]; } sub is_attachment_phab_revision { - state $check = compile(Attachment); - my ($attachment) = $check->(@_); - return $attachment->contenttype eq PHAB_CONTENT_TYPE; + state $check = compile(Attachment); + my ($attachment) = $check->(@_); + return $attachment->contenttype eq PHAB_CONTENT_TYPE; } sub get_attachment_revisions { - state $check = compile(Bug); - my ($bug) = $check->(@_); + state $check = compile(Bug); + my ($bug) = $check->(@_); - my @attachments = - grep { is_attachment_phab_revision($_) } @{ $bug->attachments() }; + my @attachments + = grep { is_attachment_phab_revision($_) } @{$bug->attachments()}; - return unless @attachments; + return unless @attachments; - my @revision_ids; - foreach my $attachment (@attachments) { - my ($revision_id) = - ( $attachment->filename =~ PHAB_ATTACHMENT_PATTERN ); - next if !$revision_id; - push( @revision_ids, int($revision_id) ); - } + my @revision_ids; + foreach my $attachment (@attachments) { + my ($revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN); + next if !$revision_id; + push(@revision_ids, int($revision_id)); + } - return unless @revision_ids; + return unless @revision_ids; - my @revisions; - foreach my $revision_id (@revision_ids) { - my $revision = Bugzilla::Extension::PhabBugz::Revision->new_from_query({ - ids => [ $revision_id ] - }); - push @revisions, $revision if $revision; - } + my @revisions; + foreach my $revision_id (@revision_ids) { + my $revision + = Bugzilla::Extension::PhabBugz::Revision->new_from_query({ + ids => [$revision_id] + }); + push @revisions, $revision if $revision; + } - return \@revisions; + return \@revisions; } sub request { - state $check = compile(Str, HashRef); - my ($method, $data) = $check->(@_); - my $request_cache = Bugzilla->request_cache; - my $params = Bugzilla->params; - - my $ua = $request_cache->{phabricator_ua}; - unless ($ua) { - $ua = $request_cache->{phabricator_ua} = Mojo::UserAgent->new; - if ($params->{proxy_url}) { - $ua->proxy($params->{proxy_url}); - } + state $check = compile(Str, HashRef); + my ($method, $data) = $check->(@_); + my $request_cache = Bugzilla->request_cache; + my $params = Bugzilla->params; + + my $ua = $request_cache->{phabricator_ua}; + unless ($ua) { + $ua = $request_cache->{phabricator_ua} = Mojo::UserAgent->new; + if ($params->{proxy_url}) { + $ua->proxy($params->{proxy_url}); } + } - my $phab_api_key = $params->{phabricator_api_key}; - ThrowUserError('invalid_phabricator_api_key') unless $phab_api_key; - my $phab_base_uri = $params->{phabricator_base_uri}; - ThrowUserError('invalid_phabricator_uri') unless $phab_base_uri; + my $phab_api_key = $params->{phabricator_api_key}; + ThrowUserError('invalid_phabricator_api_key') unless $phab_api_key; + my $phab_base_uri = $params->{phabricator_base_uri}; + ThrowUserError('invalid_phabricator_uri') unless $phab_base_uri; - my $full_uri = $phab_base_uri . '/api/' . $method; + my $full_uri = $phab_base_uri . '/api/' . $method; - $data->{__conduit__} = { token => $phab_api_key }; + $data->{__conduit__} = {token => $phab_api_key}; - my $response = $ua->post($full_uri => form => { params => encode_json($data) })->result; - ThrowCodeError('phabricator_api_error', { reason => $response->message }) - if $response->is_error; + my $response + = $ua->post($full_uri => form => {params => encode_json($data)})->result; + ThrowCodeError('phabricator_api_error', {reason => $response->message}) + if $response->is_error; - my $result = $response->json; - ThrowCodeError('phabricator_api_error', - { reason => 'JSON decode failure' }) if !defined($result); - ThrowCodeError('phabricator_api_error', - { code => $result->{error_code}, - reason => $result->{error_info} }) if $result->{error_code}; + my $result = $response->json; + ThrowCodeError('phabricator_api_error', {reason => 'JSON decode failure'}) + if !defined($result); + ThrowCodeError('phabricator_api_error', + {code => $result->{error_code}, reason => $result->{error_info}}) + if $result->{error_code}; - return $result; + return $result; } sub set_phab_user { - my $user = Bugzilla::User->new( { name => PHAB_AUTOMATION_USER } ); - $user->{groups} = [ Bugzilla::Group->get_all ]; + my $user = Bugzilla::User->new({name => PHAB_AUTOMATION_USER}); + $user->{groups} = [Bugzilla::Group->get_all]; - return Bugzilla->set_user($user, scope_guard => 1); + return Bugzilla->set_user($user, scope_guard => 1); } 1; diff --git a/extensions/PhabBugz/lib/WebService.pm b/extensions/PhabBugz/lib/WebService.pm index 19a758a70..a9115263a 100644 --- a/extensions/PhabBugz/lib/WebService.pm +++ b/extensions/PhabBugz/lib/WebService.pm @@ -26,143 +26,140 @@ use List::MoreUtils qw(any); use MIME::Base64 qw(decode_base64); use constant READ_ONLY => qw( - check_user_enter_bug_permission - check_user_permission_for_bug + check_user_enter_bug_permission + check_user_permission_for_bug ); use constant PUBLIC_METHODS => qw( - check_user_enter_bug_permission - check_user_permission_for_bug - set_build_target + check_user_enter_bug_permission + check_user_permission_for_bug + set_build_target ); sub _check_phabricator { - # Ensure PhabBugz is on - ThrowUserError('phabricator_not_enabled') - unless Bugzilla->params->{phabricator_enabled}; + + # Ensure PhabBugz is on + ThrowUserError('phabricator_not_enabled') + unless Bugzilla->params->{phabricator_enabled}; } sub _validate_phab_user { - my ($self, $user) = @_; + my ($self, $user) = @_; - $self->_check_phabricator(); + $self->_check_phabricator(); - # Validate that the requesting user's email matches phab-bot - ThrowUserError('phabricator_unauthorized_user') - unless $user->login eq PHAB_AUTOMATION_USER; + # Validate that the requesting user's email matches phab-bot + ThrowUserError('phabricator_unauthorized_user') + unless $user->login eq PHAB_AUTOMATION_USER; } sub check_user_permission_for_bug { - my ($self, $params) = @_; + my ($self, $params) = @_; - my $user = Bugzilla->login(LOGIN_REQUIRED); + my $user = Bugzilla->login(LOGIN_REQUIRED); - $self->_validate_phab_user($user); + $self->_validate_phab_user($user); - # Validate that a bug id and user id are provided - ThrowUserError('phabricator_invalid_request_params') - unless ($params->{bug_id} && $params->{user_id}); + # Validate that a bug id and user id are provided + ThrowUserError('phabricator_invalid_request_params') + unless ($params->{bug_id} && $params->{user_id}); - # Validate that the user exists - my $target_user = Bugzilla::User->check({ id => $params->{user_id}, cache => 1 }); + # Validate that the user exists + my $target_user = Bugzilla::User->check({id => $params->{user_id}, cache => 1}); - # Send back an object which says { "result": 1|0 } - return { - result => $target_user->can_see_bug($params->{bug_id}) - }; + # Send back an object which says { "result": 1|0 } + return {result => $target_user->can_see_bug($params->{bug_id})}; } sub check_user_enter_bug_permission { - my ($self, $params) = @_; + my ($self, $params) = @_; - my $user = Bugzilla->login(LOGIN_REQUIRED); + my $user = Bugzilla->login(LOGIN_REQUIRED); - $self->_validate_phab_user($user); + $self->_validate_phab_user($user); - # Validate that a product name and user id are provided - ThrowUserError('phabricator_invalid_request_params') - unless ($params->{product} && $params->{user_id}); + # Validate that a product name and user id are provided + ThrowUserError('phabricator_invalid_request_params') + unless ($params->{product} && $params->{user_id}); - # Validate that the user exists - my $target_user = Bugzilla::User->check({ id => $params->{user_id}, cache => 1 }); + # Validate that the user exists + my $target_user = Bugzilla::User->check({id => $params->{user_id}, cache => 1}); - # Send back an object with the attribute "result" set to 1 if the user - # can enter bugs into the given product, or 0 if not. - return { - result => $target_user->can_enter_product($params->{product}) ? 1 : 0 - }; + # Send back an object with the attribute "result" set to 1 if the user + # can enter bugs into the given product, or 0 if not. + return {result => $target_user->can_enter_product($params->{product}) ? 1 : 0}; } sub set_build_target { - my ( $self, $params ) = @_; + my ($self, $params) = @_; - # Phabricator only supports sending credentials via HTTP Basic Auth - # so we exploit that function to pass in an API key as the password - # of basic auth. BMO does not support basic auth but does support - # use of API keys. - my $http_auth = Bugzilla->cgi->http('Authorization'); - $http_auth =~ s/^Basic\s+//; - $http_auth = decode_base64($http_auth); - my ($login, $api_key) = split(':', $http_auth); - $params->{'Bugzilla_login'} = $login; - $params->{'Bugzilla_api_key'} = $api_key; + # Phabricator only supports sending credentials via HTTP Basic Auth + # so we exploit that function to pass in an API key as the password + # of basic auth. BMO does not support basic auth but does support + # use of API keys. + my $http_auth = Bugzilla->cgi->http('Authorization'); + $http_auth =~ s/^Basic\s+//; + $http_auth = decode_base64($http_auth); + my ($login, $api_key) = split(':', $http_auth); + $params->{'Bugzilla_login'} = $login; + $params->{'Bugzilla_api_key'} = $api_key; - my $user = Bugzilla->login(LOGIN_REQUIRED); + my $user = Bugzilla->login(LOGIN_REQUIRED); - $self->_validate_phab_user($user); + $self->_validate_phab_user($user); - my $revision_id = $params->{revision_id}; - my $build_target = $params->{build_target}; + my $revision_id = $params->{revision_id}; + my $build_target = $params->{build_target}; - ThrowUserError('invalid_phabricator_revision_id') - unless detaint_natural($revision_id); + ThrowUserError('invalid_phabricator_revision_id') + unless detaint_natural($revision_id); - ThrowUserError('invalid_phabricator_build_target') - unless $build_target =~ /^PHID-HMBT-[a-zA-Z0-9]+$/; - trick_taint($build_target); + ThrowUserError('invalid_phabricator_build_target') + unless $build_target =~ /^PHID-HMBT-[a-zA-Z0-9]+$/; + trick_taint($build_target); - Bugzilla->dbh->do( - "INSERT INTO phabbugz (name, value) VALUES (?, ?)", - undef, - 'build_target_' . $revision_id, - $build_target - ); + Bugzilla->dbh->do( + "INSERT INTO phabbugz (name, value) VALUES (?, ?)", + undef, 'build_target_' . $revision_id, + $build_target + ); - return { result => 1 }; + return {result => 1}; } sub rest_resources { - return [ - # Set build target in Phabricator - qr{^/phabbugz/build_target/(\d+)/(PHID-HMBT-.*)$}, { - POST => { - method => 'set_build_target', - params => sub { - return { - revision_id => $_[0], - build_target => $_[1] - }; - } - } - }, - # Bug permission checks - qr{^/phabbugz/check_bug/(\d+)/(\d+)$}, { - GET => { - method => 'check_user_permission_for_bug', - params => sub { - return { bug_id => $_[0], user_id => $_[1] }; - } - } - }, - qr{^/phabbugz/check_enter_bug/([^/]+)/(\d+)$}, { - GET => { - method => 'check_user_enter_bug_permission', - params => sub { - return { product => $_[0], user_id => $_[1] }; - }, - }, + return [ + # Set build target in Phabricator + qr{^/phabbugz/build_target/(\d+)/(PHID-HMBT-.*)$}, + { + POST => { + method => 'set_build_target', + params => sub { + return {revision_id => $_[0], build_target => $_[1]}; + } + } + }, + + # Bug permission checks + qr{^/phabbugz/check_bug/(\d+)/(\d+)$}, + { + GET => { + method => 'check_user_permission_for_bug', + params => sub { + return {bug_id => $_[0], user_id => $_[1]}; + } + } + }, + qr{^/phabbugz/check_enter_bug/([^/]+)/(\d+)$}, + { + GET => { + method => 'check_user_enter_bug_permission', + params => sub { + return {product => $_[0], user_id => $_[1]}; }, - ]; + }, + }, + ]; } 1; |