Skip to content

Commit 7397576

Browse files
committed
Add Jira Client, JiraShowIssueCommand middleware
1 parent 12beab3 commit 7397576

File tree

6 files changed

+171
-6
lines changed

6 files changed

+171
-6
lines changed

config/autoload/dependencies.global.php

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
<?php
2+
use App\Client\JiraClient;
23
use App\Client\SlackClient;
34
use App\Pipeline\SlackJiraPipeline;
5+
use App\Service\Jira\JiraShowIssueCommand;
6+
use App\Service\Jira\JiraShowIssueCommandFactory;
47
use App\Validator\Slack\ParseSlackJiraInput;
58
use App\Validator\Slack\ParseSlackJiraInputFactory;
69
use App\Validator\Slack\ValidateSlackToken;
@@ -25,12 +28,14 @@
2528
],
2629
// Use 'factories' for services provided by callbacks/factory classes.
2730
'factories' => [
28-
Application::class => ApplicationFactory::class,
29-
Helper\UrlHelper::class => Helper\UrlHelperFactory::class,
30-
SlackJiraPipeline::class => SlackJiraPipeline::class,
31-
ValidateSlackToken::class => ValidateSlackTokenFactory::class,
32-
ParseSlackJiraInput::class => ParseSlackJiraInputFactory::class,
33-
SlackClient::class => SlackClient::class,
31+
Application::class => ApplicationFactory::class,
32+
Helper\UrlHelper::class => Helper\UrlHelperFactory::class,
33+
SlackJiraPipeline::class => SlackJiraPipeline::class,
34+
ValidateSlackToken::class => ValidateSlackTokenFactory::class,
35+
ParseSlackJiraInput::class => ParseSlackJiraInputFactory::class,
36+
SlackClient::class => SlackClient::class,
37+
JiraClient::class => JiraClient::class,
38+
JiraShowIssueCommand::class => JiraShowIssueCommandFactory::class,
3439
],
3540
],
3641
];
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
return [
4+
'jira_config' => [
5+
'auth' => ['user' => 'your JIRA username (not email)', 'password' => 'password'],
6+
'base_uri' => 'https://$yourJiraInstance.atlassian.net/',
7+
],
8+
];

src/App/Client/JiraClient.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace App\Client;
4+
5+
use GuzzleHttp\Client;
6+
use Interop\Container\ContainerInterface;
7+
8+
class JiraClient
9+
{
10+
public function __invoke(ContainerInterface $container)
11+
{
12+
$config = $container->get('config')['jira_config'] ?? [];
13+
$auth = $config['auth'] ?? [];
14+
$baseUri = $config['base_uri'] ?? '';
15+
16+
return new Client(
17+
[
18+
'base_uri' => $baseUri,
19+
'auth' => [($auth['user'] ?? ''), ($auth['password'] ?? '')],
20+
'headers' => [
21+
'Content-Type' => 'application/json',
22+
],
23+
]
24+
);
25+
}
26+
}

src/App/Pipeline/SlackJiraPipeline.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace App\Pipeline;
44

5+
use App\Service\Jira\JiraShowIssueCommand;
56
use App\Validator\Slack\ParseSlackJiraInput;
67
use App\Validator\Slack\ValidateSlackToken;
78
use App\Validator\ValidateBody;
@@ -16,6 +17,7 @@ public function __invoke(ContainerInterface $container)
1617
$pipeline->pipe($container->get(ValidateBody::class));
1718
$pipeline->pipe($container->get(ValidateSlackToken::class));
1819
$pipeline->pipe($container->get(ParseSlackJiraInput::class));
20+
$pipeline->pipe($container->get(JiraShowIssueCommand::class));
1921

2022
return $pipeline;
2123
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
3+
namespace App\Service\Jira;
4+
5+
use GuzzleHttp\Client;
6+
use GuzzleHttp\Exception\ClientException;
7+
use GuzzleHttp\UriTemplate;
8+
use Psr\Http\Message\ResponseInterface;
9+
use Psr\Http\Message\ServerRequestInterface;
10+
11+
class JiraShowIssueCommand
12+
{
13+
/**
14+
* @var Client
15+
*/
16+
private $jiraClient;
17+
/**
18+
* @var Client
19+
*/
20+
private $slackClient;
21+
/**
22+
* @var string
23+
*/
24+
private $jiraUrl;
25+
26+
public function __construct(Client $jiraClient, Client $slackClient, string $jiraUrl)
27+
{
28+
$this->jiraClient = $jiraClient;
29+
$this->slackClient = $slackClient;
30+
$this->jiraUrl = $jiraUrl;
31+
}
32+
33+
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
34+
{
35+
$body = $request->getParsedBody();
36+
if ($body['args'][0] !== "show") {
37+
return $next($request, $response);
38+
}
39+
$jobs = array_reverse(array_slice($body['args'], 1));
40+
$uriTemplate = new UriTemplate();
41+
$uri = $uriTemplate->expand(
42+
'rest/api/2/search?jql=key in ({issueKeys*})&expand=editmeta&fields=customfield_10024&fields=summary'
43+
. '&fields=creator&fields=assignee&fields=issuetype&fields=priority&fields=status&fields=resolution',
44+
[
45+
'issueKeys' => $jobs,
46+
]
47+
);
48+
try {
49+
$json = json_decode($this->jiraClient->get($uri)->getBody()->getContents(), true);
50+
$attachments = $this->prepareSlackAttachments($json);
51+
$responseBody = [
52+
'text' => "Search Results for `{$body['command']} {$body['text']}`",
53+
'response_type' => $body['response_type'] ?? 'ephemeral',
54+
'attachments' => $attachments,
55+
];
56+
$this->slackClient->post($body['response_url'], ['body' => json_encode($responseBody)]);
57+
} catch (ClientException $e) {
58+
$error = $e->getMessage();
59+
$this->slackClient->post(
60+
$body['response_url'],
61+
[
62+
'body' => json_encode(
63+
[
64+
'text' => "Running `{$body['command']} {$body['text']}` didn't work. Got "
65+
. $e->getCode() . " for an HTTP response",
66+
]
67+
),
68+
]
69+
);
70+
}
71+
72+
return $next($request, $response, $error ?? null);
73+
}
74+
75+
private function prepareSlackAttachments($json)
76+
{
77+
$results = [];
78+
$jobs = $json['issues'] ?? [];
79+
$colorMap = [
80+
'Blocker' => '#ec0909', // bright red
81+
'Critical' => '#d9534f', // red
82+
'Major' => '#f0ad4e', // orange
83+
'Medium' => '#5bc0de', // blue
84+
'Trivial' => '#c7ead4', // light-green
85+
];
86+
foreach ($jobs as $index => $job) {
87+
$priority = $job['fields']['priority']['name'] ?? '¯\_(ツ)_/¯';
88+
$status = $job['fields']['status']['name'] ?? '¯\_(ツ)_/¯';
89+
$type = $job['fields']['issuetype']['name'] ?? '¯\_(ツ)_/¯';
90+
$creator = $job['fields']['creator']['displayName'] ?? 'no one';
91+
$assignee = $job['fields']['assignee']['displayName'] ?? 'no one';
92+
$blocked = ($job['fields']['customfield_10024']['value'] == "Yes" ? " that is currently *Blocked*" : '');
93+
94+
$results[$index]['title'] = "{$job['key']} - " . ($job['fields']['summary'] ?? '');
95+
$results[$index]['title_link'] = "{$this->jiraUrl}/browse/{$job['key']}";
96+
$results[$index]['fallback'] = $results[$index]['title'];
97+
$results[$index]['color'] = $colorMap[$job['fields']['priority']['name'] ?? null] ?? '#205081';
98+
$results[$index]['text'] = "A *{$priority}* *{$type}* marked as *'{$status}'*,"
99+
. " created by *{$creator}* assigned to *{$assignee}* {$blocked}";
100+
$results[$index]['mrkdwn_in'] = ['text'];
101+
}
102+
103+
return $results;
104+
}
105+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace App\Service\Jira;
4+
5+
use App\Client\JiraClient;
6+
use App\Client\SlackClient;
7+
use Interop\Container\ContainerInterface;
8+
9+
class JiraShowIssueCommandFactory
10+
{
11+
public function __invoke(ContainerInterface $container)
12+
{
13+
$jiraClient = $container->get(JiraClient::class);
14+
$slackClient = $container->get(SlackClient::class);
15+
$jiraUrl = $container->get('config')['jira_config']['base_uri'] ?? '';
16+
17+
return new JiraShowIssueCommand($jiraClient, $slackClient, $jiraUrl);
18+
}
19+
}

0 commit comments

Comments
 (0)