summaryrefslogtreecommitdiffstats
path: root/extensions/PhabBugz/lib/WebService.pm
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/PhabBugz/lib/WebService.pm')
-rw-r--r--extensions/PhabBugz/lib/WebService.pm389
1 files changed, 140 insertions, 249 deletions
diff --git a/extensions/PhabBugz/lib/WebService.pm b/extensions/PhabBugz/lib/WebService.pm
index 5b6310de6..0239ccf74 100644
--- a/extensions/PhabBugz/lib/WebService.pm
+++ b/extensions/PhabBugz/lib/WebService.pm
@@ -13,115 +13,44 @@ use warnings;
use base qw(Bugzilla::WebService);
-use Bugzilla::Attachment;
-use Bugzilla::Bug;
-use Bugzilla::BugMail;
use Bugzilla::Constants;
-use Bugzilla::Error;
-use Bugzilla::Extension::Push::Util qw(is_public);
use Bugzilla::User;
-use Bugzilla::Util qw(detaint_natural);
+use Bugzilla::Util qw(detaint_natural datetime_from time_ago trick_taint);
use Bugzilla::WebService::Constants;
use Bugzilla::Extension::PhabBugz::Constants;
use Bugzilla::Extension::PhabBugz::Util qw(
- add_security_sync_comments
- create_revision_attachment
- create_private_revision_policy
- edit_revision_policy
- get_bug_role_phids
- get_project_phid
- get_revisions_by_ids
- intersect
- is_attachment_phab_revision
- make_revision_public
- request
- get_security_sync_groups
+ get_needs_review
);
-use List::Util qw(first);
+use DateTime ();
+use List::Util qw(first uniq);
use List::MoreUtils qw(any);
use MIME::Base64 qw(decode_base64);
-use constant PUBLIC_METHODS => qw(
+use constant READ_ONLY => qw(
check_user_permission_for_bug
- obsolete_attachments
- revision
- update_reviewer_statuses
+ needs_review
);
-sub revision {
- 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;
-
- my $user = Bugzilla->login(LOGIN_REQUIRED);
-
- # Prechecks
- _phabricator_precheck($user);
-
- unless (defined $params->{revision} && detaint_natural($params->{revision})) {
- ThrowCodeError('param_required', { param => 'revision' })
- }
-
- # Obtain more information about the revision from Phabricator
- my $revision_id = $params->{revision};
- my $revisions = get_revisions_by_ids([$revision_id]);
- my $revision = $revisions->[0];
-
- my $revision_phid = $revision->{phid};
- my $revision_title = $revision->{fields}{title} || 'Unknown Description';
- my $bug_id = $revision->{fields}{'bugzilla.bug-id'};
-
- my $bug = Bugzilla::Bug->new($bug_id);
-
- # If bug is public then remove privacy policy
- my $result;
- if (is_public($bug)) {
- $result = make_revision_public($revision_id);
- }
- # else bug is private
- else {
- my @set_groups = get_security_sync_groups($bug);
-
- # If bug privacy groups do not have any matching synchronized groups,
- # then leave revision private and it will have be dealt with manually.
- if (!@set_groups) {
- add_security_sync_comments($revisions, $bug);
- }
-
- my $policy_phid = create_private_revision_policy($bug, \@set_groups);
- my $subscribers = get_bug_role_phids($bug);
- $result = edit_revision_policy($revision_phid, $policy_phid, $subscribers);
- }
-
- my $attachment = create_revision_attachment($bug, $revision_id, $revision_title);
-
- Bugzilla::BugMail::Send($bug_id, { changer => $user });
-
- return {
- result => $result,
- attachment_id => $attachment->id,
- attachment_link => Bugzilla->localconfig->{urlbase} . "attachment.cgi?id=" . $attachment->id
- };
-}
+use constant PUBLIC_METHODS => qw(
+ check_user_permission_for_bug
+ needs_review
+ set_build_target
+);
sub check_user_permission_for_bug {
my ($self, $params) = @_;
my $user = Bugzilla->login(LOGIN_REQUIRED);
- # Prechecks
- _phabricator_precheck($user);
+ # Ensure PhabBugz is on
+ ThrowUserError('phabricator_not_enabled')
+ unless Bugzilla->params->{phabricator_enabled};
+
+ # Validate that the requesting user's email matches phab-bot
+ ThrowUserError('phabricator_unauthorized_user')
+ unless $user->login eq PHAB_AUTOMATION_USER;
# Validate that a bug id and user id are provided
ThrowUserError('phabricator_invalid_request_params')
@@ -136,184 +65,152 @@ sub check_user_permission_for_bug {
};
}
-sub update_reviewer_statuses {
+sub needs_review {
my ($self, $params) = @_;
-
+ ThrowUserError('phabricator_not_enabled')
+ unless Bugzilla->params->{phabricator_enabled};
my $user = Bugzilla->login(LOGIN_REQUIRED);
-
- # Prechecks
- _phabricator_precheck($user);
-
- my $revision_id = $params->{revision_id};
- unless (defined $revision_id && detaint_natural($revision_id)) {
- ThrowCodeError('param_required', { param => 'revision_id' })
- }
-
- my $bug_id = $params->{bug_id};
- unless (defined $bug_id && detaint_natural($bug_id)) {
- ThrowCodeError('param_required', { param => 'bug_id' })
+ my $dbh = Bugzilla->dbh;
+
+ my $reviews = get_needs_review();
+
+ my $authors = Bugzilla::Extension::PhabBugz::User->match({
+ phids => [
+ uniq
+ grep { defined }
+ map { $_->{fields}{authorPHID} }
+ @$reviews
+ ]
+ });
+
+ my %author_phab_to_id = map { $_->phid => $_->bugzilla_user->id } @$authors;
+ my %author_id_to_user = map { $_->bugzilla_user->id => $_->bugzilla_user } @$authors;
+
+ # bug data
+ my $visible_bugs = $user->visible_bugs([
+ uniq
+ grep { $_ }
+ map { $_->{fields}{'bugzilla.bug-id'} }
+ @$reviews
+ ]);
+
+ # get all bug statuses and summaries in a single query to avoid creation of
+ # many bug objects
+ my %bugs;
+ if (@$visible_bugs) {
+ #<<<
+ my $bug_rows =$dbh->selectall_arrayref(
+ 'SELECT bug_id, bug_status, short_desc ' .
+ ' FROM bugs ' .
+ ' WHERE bug_id IN (' . join(',', ('?') x @$visible_bugs) . ')',
+ { Slice => {} },
+ @$visible_bugs
+ );
+ #>>>
+ %bugs = map { $_->{bug_id} => $_ } @$bug_rows;
}
- my $accepted_user_ids = $params->{accepted_users};
- defined $accepted_user_ids
- || ThrowCodeError('param_required', { param => 'accepted_users' });
- $accepted_user_ids = [ split(':', $accepted_user_ids) ];
-
- my $denied_user_ids = $params->{denied_users};
- defined $denied_user_ids
- || ThrowCodeError('param_required', { param => 'denied_users' });
- $denied_user_ids = [ split(':', $denied_user_ids) ];
-
- my $bug = Bugzilla::Bug->check($bug_id);
-
- my @attachments =
- grep { is_attachment_phab_revision($_) } @{ $bug->attachments() };
-
- return { result => [] } if !@attachments;
-
- my $dbh = Bugzilla->dbh;
- my ($timestamp) = $dbh->selectrow_array("SELECT NOW()");
-
- my @updated_attach_ids;
- foreach my $attachment (@attachments) {
- my ($curr_revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN);
- next if $revision_id != $curr_revision_id;
-
- # Clear old flags if no longer accepted
- my (@denied_flags, @new_flags, @removed_flags, %accepted_done, $flag_type);
- foreach my $flag (@{ $attachment->flags }) {
- next if $flag->type->name ne 'review';
- $flag_type = $flag->type if $flag->type->is_active;
- if (any { $flag->setter->id == $_ } @$denied_user_ids) {
- push(@denied_flags, { id => $flag->id, setter => $flag->setter, status => 'X' });
- }
- if (any { $flag->setter->id == $_ } @$accepted_user_ids) {
- $accepted_done{$flag->setter->id}++;
- }
- if ($flag->status eq '+'
- && !any { $flag->setter->id == $_ } (@$accepted_user_ids, @$denied_user_ids)) {
- push(@removed_flags, { id => $flag->id, setter => $flag->setter, status => 'X' });
- }
- }
-
- $flag_type ||= first { $_->name eq 'review' && $_->is_active } @{ $attachment->flag_types };
-
- # Create new flags
- foreach my $user_id (@$accepted_user_ids) {
- next if $accepted_done{$user_id};
- my $user = Bugzilla::User->check({ id => $user_id, cache => 1 });
- push(@new_flags, { type_id => $flag_type->id, setter => $user, status => '+' });
+ # build result
+ my $datetime_now = DateTime->now(time_zone => $user->timezone);
+ my @result;
+ foreach my $review (@$reviews) {
+ my $review_flat = {
+ id => $review->{id},
+ title => $review->{fields}{title},
+ url => Bugzilla->params->{phabricator_base_uri} . 'D' . $review->{id},
+ };
+
+ # show date in user's timezone
+ my $datetime = DateTime->from_epoch(
+ epoch => $review->{fields}{dateModified},
+ time_zone => 'UTC'
+ );
+ $datetime->set_time_zone($user->timezone);
+ $review_flat->{updated} = $datetime->strftime('%Y-%m-%d %T %Z');
+ $review_flat->{updated_fancy} = time_ago($datetime, $datetime_now);
+
+ # review requester
+ if (my $author = $author_id_to_user{$author_phab_to_id{ $review->{fields}{authorPHID} }}) {
+ $review_flat->{author_name} = $author->name;
+ $review_flat->{author_email} = $author->email;
}
-
- # Also add comment to for attachment update showing the user's name
- # that changed the revision.
- my $comment;
- foreach my $flag_data (@new_flags) {
- $comment .= $flag_data->{setter}->name . " has approved the revision.\n";
- }
- foreach my $flag_data (@denied_flags) {
- $comment .= $flag_data->{setter}->name . " has requested changes to the revision.\n";
- }
- foreach my $flag_data (@removed_flags) {
- $comment .= $flag_data->{setter}->name . " has been removed from the revision.\n";
+ else {
+ $review_flat->{author_name} = 'anonymous';
+ $review_flat->{author_email} = 'anonymous';
}
- if ($comment) {
- $comment .= "\n" . Bugzilla->params->{phabricator_base_uri} . "D" . $revision_id;
- # Add transaction_id as anchor if one present
- $comment .= "#" . $params->{transaction_id} if $params->{transaction_id};
- $bug->add_comment($comment, {
- isprivate => $attachment->isprivate,
- type => CMT_ATTACHMENT_UPDATED,
- extra_data => $attachment->id
- });
+ # referenced bug
+ if (my $bug_id = $review->{fields}{'bugzilla.bug-id'}) {
+ my $bug = $bugs{$bug_id};
+ $review_flat->{bug_id} = $bug_id;
+ $review_flat->{bug_status} = $bug->{bug_status};
+ $review_flat->{bug_summary} = $bug->{short_desc};
}
- $attachment->set_flags([ @denied_flags, @removed_flags ], \@new_flags);
- $attachment->update($timestamp);
- $bug->update($timestamp) if $comment;
-
- push(@updated_attach_ids, $attachment->id);
+ push @result, $review_flat;
}
- Bugzilla::BugMail::Send($bug_id, { changer => $user }) if @updated_attach_ids;
-
- return { result => \@updated_attach_ids };
+ return { result => \@result };
}
-sub obsolete_attachments {
- my ($self, $params) = @_;
+sub set_build_target {
+ my ( $self, $params ) = @_;
- my $user = Bugzilla->login(LOGIN_REQUIRED);
-
- # Prechecks
- _phabricator_precheck($user);
-
- my $revision_id = $params->{revision_id};
- unless (defined $revision_id && detaint_natural($revision_id)) {
- ThrowCodeError('param_required', { param => 'revision' })
- }
-
- my $bug_id= $params->{bug_id};
- unless (defined $bug_id && detaint_natural($bug_id)) {
- ThrowCodeError('param_required', { param => 'bug_id' })
- }
-
- my $make_obsolete = $params->{make_obsolete};
- unless (defined $make_obsolete) {
- ThrowCodeError('param_required', { param => 'make_obsolete' })
- }
- $make_obsolete = $make_obsolete ? 1 : 0;
-
- my $bug = Bugzilla::Bug->check($bug_id);
-
- my @attachments =
- grep { is_attachment_phab_revision($_) } @{ $bug->attachments() };
-
- return { result => [] } if !@attachments;
-
- my $dbh = Bugzilla->dbh;
- my ($timestamp) = $dbh->selectrow_array("SELECT NOW()");
-
- my @updated_attach_ids;
- foreach my $attachment (@attachments) {
- my ($curr_revision_id) = ($attachment->filename =~ PHAB_ATTACHMENT_PATTERN);
- next if $revision_id != $curr_revision_id;
+ # 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;
- $attachment->set_is_obsolete($make_obsolete);
- $attachment->update($timestamp);
+ my $user = Bugzilla->login(LOGIN_REQUIRED);
- push(@updated_attach_ids, $attachment->id);
- }
+ # Ensure PhabBugz is on
+ ThrowUserError('phabricator_not_enabled')
+ unless Bugzilla->params->{phabricator_enabled};
+
+ # Validate that the requesting user's email matches phab-bot
+ ThrowUserError('phabricator_unauthorized_user')
+ unless $user->login eq PHAB_AUTOMATION_USER;
- Bugzilla::BugMail::Send($bug_id, { changer => $user }) if @updated_attach_ids;
+ my $revision_id = $params->{revision_id};
+ my $build_target = $params->{build_target};
- return { result => \@updated_attach_ids };
-}
+ ThrowUserError('invalid_phabricator_revision_id')
+ unless detaint_natural($revision_id);
-sub _phabricator_precheck {
- my ($user) = @_;
+ ThrowUserError('invalid_phabricator_build_target')
+ unless $build_target =~ /^PHID-HMBT-[a-zA-Z0-9]+$/;
+ trick_taint($build_target);
- # Ensure PhabBugz is on
- ThrowUserError('phabricator_not_enabled')
- unless Bugzilla->params->{phabricator_enabled};
+ Bugzilla->dbh->do(
+ "INSERT INTO phabbugz (name, value) VALUES (?, ?)",
+ undef,
+ 'build_target_' . $revision_id,
+ $build_target
+ );
- # Validate that the requesting user's email matches phab-bot
- ThrowUserError('phabricator_unauthorized_user')
- unless $user->login eq PHAB_AUTOMATION_USER;
+ return { result => 1 };
}
sub rest_resources {
return [
- # Revision creation
- qr{^/phabbugz/revision/([^/]+)$}, {
+ # Set build target in Phabricator
+ qr{^/phabbugz/build_target/(\d+)/(PHID-HMBT-.*)$}, {
POST => {
- method => 'revision',
+ method => 'set_build_target',
params => sub {
- return { revision => $_[0] };
+ return {
+ revision_id => $_[0],
+ build_target => $_[1]
+ };
}
}
- },
+ },
# Bug permission checks
qr{^/phabbugz/check_bug/(\d+)/(\d+)$}, {
GET => {
@@ -323,17 +220,11 @@ sub rest_resources {
}
}
},
- # Update reviewer statuses
- qr{^/phabbugz/update_reviewer_statuses$}, {
- PUT => {
- method => 'update_reviewer_statuses',
- }
- },
- # Obsolete attachments
- qr{^/phabbugz/obsolete$}, {
- PUT => {
- method => 'obsolete_attachments',
- }
+ # Review requests
+ qw{^/phabbugz/needs_review$}, {
+ GET => {
+ method => 'needs_review',
+ },
}
];
}