Skip to content

Commit

Permalink
Update Zendesk when Linear issue changes
Browse files Browse the repository at this point in the history
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".
  • Loading branch information
Rasha Moumneh committed Feb 4, 2022
1 parent 47772de commit 14b6abd
Show file tree
Hide file tree
Showing 2 changed files with 194 additions and 0 deletions.
56 changes: 56 additions & 0 deletions lib/Synergy/Reactor/LinearNotification.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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)) {

Expand Down Expand Up @@ -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"}' ] ];
}

Expand Down
138 changes: 138 additions & 0 deletions lib/Synergy/Reactor/LinearZendeskIntegration.pm
Original file line number Diff line number Diff line change
@@ -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;

0 comments on commit 14b6abd

Please sign in to comment.