From 14b6abd542c40aa4b5aeebe4575c68458ae514c7 Mon Sep 17 00:00:00 2001 From: Rasha Moumneh Date: Fri, 14 Jan 2022 13:30:55 -0500 Subject: [PATCH 1/2] Update Zendesk when Linear issue changes When a support blocker linear issue is updated with a new comment, an internal comment is posted on its associated zendesk ticket and the ticket status is changed to "Open". --- lib/Synergy/Reactor/LinearNotification.pm | 56 +++++++ .../Reactor/LinearZendeskIntegration.pm | 138 ++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 lib/Synergy/Reactor/LinearZendeskIntegration.pm diff --git a/lib/Synergy/Reactor/LinearNotification.pm b/lib/Synergy/Reactor/LinearNotification.pm index 0ade6ab0..25a6e6ad 100644 --- a/lib/Synergy/Reactor/LinearNotification.pm +++ b/lib/Synergy/Reactor/LinearNotification.pm @@ -80,6 +80,53 @@ has name => ( default => 'lin', ); +has zendesk => ( + is => 'ro', + isa => 'Zendesk::Client', + default => sub ($self) { + return Synergy::Reactor::Zendesk->zendesk_client; + }, + lazy => 1, +); + +sub linear_update_zendesk ($payload) { + my $issue_id = $payload->{data}{issueId}; + my $issue = $self->linear->do_query( + q[ query Issue { + issue(id: $issue_id) { + attachments { nodes { url } } + labels { nodes { name } } + } + }])->get; + + my $has_escalation_label = grep { + lc $_{name} eq lc $self->escalation_label_name + } $issue->{data}{issue}{labels}{nodes}->@*; + + my @zendesk_url = grep { + $_{url} =~ /.*fastmail\.help\/agent\/tickets\/\d*/ + } $issue->{data}{issue}{attachments}{nodes}->@*; + + if ($has_escalation_label && @zendesk_url) { + $zendesk_url[0] =~ /.*\/(\d*)/; + + $self->zendesk->ticket_api->add_comment_to_ticket_f($1, { + body => "Issue updated with a comment: $payload->{url}", + public => \0, + })->else(sub ($err, @) { + $Logger->log([ "something went wrong posting to Zendesk: %s", $err ]); + return Future->done; + })->retain; + + $self->zendesk->ticket_api->update_by_zendesk_id_f($1, { + status => "open", + })->else(sub ($err, @) { + $Logger->log([ "something went wrong changing the zendesk ticket status: %s", $err ]); + return Future->done; + })->retain; + } +} + sub http_app ($self, $env) { my $req = Plack::Request->new($env); @@ -104,6 +151,7 @@ sub http_app ($self, $env) { return [ "200", [], [ '{"bad":"json"}' ] ]; } + # Notify in slack if issue is escalated if ($self->escalation_channel_name && $self->escalation_address) { if (my $channel = $self->hub->channel_named($self->escalation_channel_name)) { @@ -171,6 +219,14 @@ sub http_app ($self, $env) { } } + # Action Zendesk ticket if comments are made on linear issues + my $made_comment = $payload->{type} eq 'Issue comments' + && $payload->{action} eq 'create'; + + if ($made_comment) { + linear_update_zendesk($payload); + } + return [ "200", [], [ '{"o":"k"}' ] ]; } diff --git a/lib/Synergy/Reactor/LinearZendeskIntegration.pm b/lib/Synergy/Reactor/LinearZendeskIntegration.pm new file mode 100644 index 00000000..307c1d39 --- /dev/null +++ b/lib/Synergy/Reactor/LinearZendeskIntegration.pm @@ -0,0 +1,138 @@ +use v.5.28.0; +use warnings; +package Synergy::Reactor::LinearZendeskIntegration; + +use Moose; + +with 'Synergy::Role::Reactor::EasyListening'; +with 'Synergy::Role::HTTPEndpoint'; + +use Synergy::Reactor::Linear; +use Synergy::Reactor::Zendesk; +use Synergy::Logger '$Logger'; +use experimental qw(signatures postderef); + +has +http_path => ( + default => '/linearzendeskintegration', +); + +has escalation_label_name => ( + is => 'ro', + isa => 'Str', + default => 'support blocker', +); + +my %allowed = ( + '35.231.147.226' => 1, + '35.243.134.228' => 1, +); + +has confirm_remote_ips => ( + is => 'ro', + isa => 'Bool', + default => 1, +); + +has linear_token => ( + is => 'ro', + required => 1, +); + +has linear => ( + is => 'ro', + isa => 'Linear::Client', + default => sub ($self) { + Linear::Client->new({ + auth_token => $self->linear_token, + helper => Synergy::Reactor::Linear::LinearHelper->new_for_reactor($self), + }); + }, + lazy => 1, +); + +has zendesk => ( + is => 'ro', + isa => 'Zendesk::Client', + default => sub ($self) { + return Synergy::Reactor::Zendesk->zendesk_client; + }, + lazy => 1, +); + +sub http_app ($self, $env) { + my $req = Plack::Request->new($env); + + if ($self->confirm_remote_ips) { + unless ($allowed{$req->address}) { + warn "ADDRESS " . $req->address . " not allowed\n"; + return [ "200", [], ['{"go":"away"}'] ]; + } + } + + my $err; + + my $payload = try { + decode_json( $req->raw_body ); + } catch { + $err = $_; + }; + + if ($err) { + warn "Failed to parse json: $err\n"; + + return [ "200", [], [ '{"bad":"json"}' ] ]; + } + + my $made_comment = $payload->{type} eq 'Issue comments' + && $payload->{action} eq 'create'; + + if ($made_comment) { + my $issue_id = $payload->{data}{issueId}; + my $issue = $self->linear->do_query( + q[ query Issue { + issue(id: $issue_id) { + attachments { nodes { url } } + labels { nodes { name } } + } + }])->get; + + my $has_escalation_label = grep { + lc $_{name} eq lc $self->escalation_label_name + } $issue->{data}{issue}{labels}{nodes}->@*; + + my @zendesk_url = grep { + $_{url} =~ /.*fastmail\.help\/agent\/tickets\/\d*/ + } $issue->{data}{issue}{attachments}{nodes}->@*; + + if ($has_escalation_label && @zendesk_url) { + $zendesk_url[0] =~ /.*\/(\d*)/; + + $self->zendesk->ticket_api->add_comment_to_ticket_f($1, { + body => "Issue updated with a comment: $payload->{url}", + public => \0, + })->else(sub ($err, @) { + $Logger->log([ "something went wrong posting to Zendesk: %s", $err ]); + return Future->done; + })->retain; + + $self->zendesk->ticket_api->update_by_zendesk_id_f($1, { + status => "open", + })->else(sub ($err, @) { + $Logger->log([ "something went wrong changing the zendesk ticket status: %s", $err ]); + return Future->done; + })->retain; + } + } + return [ "200", [], [ '{"o":"k"}' ] ]; +} + +sub listener_specs { + return { + name => 'linear_zendesk_notification', + method => 'linear_zendesk_notification', + predicate => sub ($, $e) { 0 }, + }; +} + +no Moose; +1; From 4b1ec1812235cdd3baec9d34e90416f67efcf492 Mon Sep 17 00:00:00 2001 From: Rasha Moumneh Date: Fri, 4 Feb 2022 11:26:27 -0500 Subject: [PATCH 2/2] Delete LinearZendeskIntegration.pm Its functionality was moved to LinearNotification.pm --- .../Reactor/LinearZendeskIntegration.pm | 138 ------------------ 1 file changed, 138 deletions(-) delete mode 100644 lib/Synergy/Reactor/LinearZendeskIntegration.pm diff --git a/lib/Synergy/Reactor/LinearZendeskIntegration.pm b/lib/Synergy/Reactor/LinearZendeskIntegration.pm deleted file mode 100644 index 307c1d39..00000000 --- a/lib/Synergy/Reactor/LinearZendeskIntegration.pm +++ /dev/null @@ -1,138 +0,0 @@ -use v.5.28.0; -use warnings; -package Synergy::Reactor::LinearZendeskIntegration; - -use Moose; - -with 'Synergy::Role::Reactor::EasyListening'; -with 'Synergy::Role::HTTPEndpoint'; - -use Synergy::Reactor::Linear; -use Synergy::Reactor::Zendesk; -use Synergy::Logger '$Logger'; -use experimental qw(signatures postderef); - -has +http_path => ( - default => '/linearzendeskintegration', -); - -has escalation_label_name => ( - is => 'ro', - isa => 'Str', - default => 'support blocker', -); - -my %allowed = ( - '35.231.147.226' => 1, - '35.243.134.228' => 1, -); - -has confirm_remote_ips => ( - is => 'ro', - isa => 'Bool', - default => 1, -); - -has linear_token => ( - is => 'ro', - required => 1, -); - -has linear => ( - is => 'ro', - isa => 'Linear::Client', - default => sub ($self) { - Linear::Client->new({ - auth_token => $self->linear_token, - helper => Synergy::Reactor::Linear::LinearHelper->new_for_reactor($self), - }); - }, - lazy => 1, -); - -has zendesk => ( - is => 'ro', - isa => 'Zendesk::Client', - default => sub ($self) { - return Synergy::Reactor::Zendesk->zendesk_client; - }, - lazy => 1, -); - -sub http_app ($self, $env) { - my $req = Plack::Request->new($env); - - if ($self->confirm_remote_ips) { - unless ($allowed{$req->address}) { - warn "ADDRESS " . $req->address . " not allowed\n"; - return [ "200", [], ['{"go":"away"}'] ]; - } - } - - my $err; - - my $payload = try { - decode_json( $req->raw_body ); - } catch { - $err = $_; - }; - - if ($err) { - warn "Failed to parse json: $err\n"; - - return [ "200", [], [ '{"bad":"json"}' ] ]; - } - - my $made_comment = $payload->{type} eq 'Issue comments' - && $payload->{action} eq 'create'; - - if ($made_comment) { - my $issue_id = $payload->{data}{issueId}; - my $issue = $self->linear->do_query( - q[ query Issue { - issue(id: $issue_id) { - attachments { nodes { url } } - labels { nodes { name } } - } - }])->get; - - my $has_escalation_label = grep { - lc $_{name} eq lc $self->escalation_label_name - } $issue->{data}{issue}{labels}{nodes}->@*; - - my @zendesk_url = grep { - $_{url} =~ /.*fastmail\.help\/agent\/tickets\/\d*/ - } $issue->{data}{issue}{attachments}{nodes}->@*; - - if ($has_escalation_label && @zendesk_url) { - $zendesk_url[0] =~ /.*\/(\d*)/; - - $self->zendesk->ticket_api->add_comment_to_ticket_f($1, { - body => "Issue updated with a comment: $payload->{url}", - public => \0, - })->else(sub ($err, @) { - $Logger->log([ "something went wrong posting to Zendesk: %s", $err ]); - return Future->done; - })->retain; - - $self->zendesk->ticket_api->update_by_zendesk_id_f($1, { - status => "open", - })->else(sub ($err, @) { - $Logger->log([ "something went wrong changing the zendesk ticket status: %s", $err ]); - return Future->done; - })->retain; - } - } - return [ "200", [], [ '{"o":"k"}' ] ]; -} - -sub listener_specs { - return { - name => 'linear_zendesk_notification', - method => 'linear_zendesk_notification', - predicate => sub ($, $e) { 0 }, - }; -} - -no Moose; -1;