Skip to content

Commit a78b7af

Browse files
committed
Get labels from github
1 parent 5c78ccc commit a78b7af

File tree

8 files changed

+132
-43
lines changed

8 files changed

+132
-43
lines changed

config/github.yml

Whitespace-only changes.

config/packages/cache.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ framework:
1515
#app: cache.adapter.apcu
1616

1717
# Namespaced pools use the above "app" backend by default
18-
#pools:
18+
pools:
1919
#my.dedicated.cache: null

config/services_test.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
serivices:
2+
App\Issues\GitHub\CachedLabelsApi:
3+
class: App\Tests\Service\Issues\Github\FakedCachedLabelApi

src/Issues/GitHub/CachedLabelsApi.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
use App\Repository\Repository;
66
use Github\Api\Issue\Labels;
7+
use Symfony\Contracts\Cache\CacheInterface;
8+
use Symfony\Contracts\Cache\ItemInterface;
79

810
/**
911
* @author Bernhard Schussek <bschussek@gmail.com>
@@ -16,13 +18,20 @@ class CachedLabelsApi
1618
private $labelsApi;
1719

1820
/**
21+
* In memory cache for specific issues
1922
* @var array<array-key, array<array-key, bool>>
2023
*/
2124
private $labelCache = [];
2225

23-
public function __construct(Labels $labelsApi)
26+
/**
27+
* @var CacheInterface
28+
*/
29+
private $cache;
30+
31+
public function __construct(Labels $labelsApi, CacheInterface $cache)
2432
{
2533
$this->labelsApi = $labelsApi;
34+
$this->cache = $cache;
2635
}
2736

2837
public function getIssueLabels($issueNumber, Repository $repository)
@@ -94,6 +103,21 @@ public function addIssueLabels($issueNumber, array $labels, Repository $reposito
94103
}
95104
}
96105

106+
107+
/**
108+
* @return string[]
109+
*/
110+
public function getAllLabelsForRepository(Repository $repository): array
111+
{
112+
$key = 'labels'.sha1($repository->getFullName());
113+
return $this->cache->get($key, function (ItemInterface $item) use ($repository) {
114+
$labels = $this->labelsApi->all($repository->getVendor(), $repository->getName());
115+
$item->expiresAfter(36000);
116+
117+
return array_column($labels, 'name');
118+
});
119+
}
120+
97121
private function getCacheKey($issueNumber, Repository $repository)
98122
{
99123
return sprintf('%s_%s_%s', $issueNumber, $repository->getVendor(), $repository->getName());

src/Subscriber/AutoLabelPRFromContentSubscriber.php renamed to src/Subscriber/AutoLabelFromContentSubscriber.php

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
use App\Event\GitHubEvent;
66
use App\GitHubEvents;
77
use App\Issues\GitHub\CachedLabelsApi;
8+
use App\Repository\Repository;
89
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
910

1011
/**
1112
* Looks at new pull requests and auto-labels based on text.
1213
*/
13-
class AutoLabelPRFromContentSubscriber implements EventSubscriberInterface
14+
class AutoLabelFromContentSubscriber implements EventSubscriberInterface
1415
{
1516
private $labelsApi;
1617

@@ -35,14 +36,15 @@ public function onPullRequest(GitHubEvent $event)
3536
if ('opened' !== $action = $data['action']) {
3637
return;
3738
}
39+
$repository = $event->getRepository();
3840

3941
$prNumber = $data['pull_request']['number'];
4042
$prTitle = $data['pull_request']['title'];
4143
$prBody = $data['pull_request']['body'];
4244
$prLabels = [];
4345

4446
// the PR title usually contains one or more labels
45-
foreach ($this->extractLabels($prTitle) as $label) {
47+
foreach ($this->extractLabels($prTitle, $repository) as $label) {
4648
$prLabels[] = $label;
4749
}
4850

@@ -60,26 +62,55 @@ public function onPullRequest(GitHubEvent $event)
6062
$prLabels[] = 'Deprecation';
6163
}
6264

63-
$this->labelsApi->addIssueLabels($prNumber, $prLabels, $event->getRepository());
65+
$this->labelsApi->addIssueLabels($prNumber, $prLabels, $repository);
6466

6567
$event->setResponseData([
6668
'pull_request' => $prNumber,
6769
'pr_labels' => $prLabels,
6870
]);
6971
}
7072

71-
private function extractLabels($prTitle)
73+
/**
74+
* @param GitHubEvent $event
75+
*/
76+
public function onIssue(GitHubEvent $event)
77+
{
78+
$data = $event->getData();
79+
if ('opened' !== $action = $data['action']) {
80+
return;
81+
}
82+
$repository = $event->getRepository();
83+
84+
$issueNumber = $data['issue']['number'];
85+
$prTitle = $data['issue']['title'];
86+
$labels = array();
87+
88+
// the issue title usually contains one or more labels
89+
foreach ($this->extractLabels($prTitle, $repository) as $label) {
90+
$labels[] = $label;
91+
}
92+
93+
$this->labelsApi->addIssueLabels($issueNumber, $labels, $repository);
94+
95+
$event->setResponseData(array(
96+
'issue' => $issueNumber,
97+
'issue_labels' => $labels,
98+
));
99+
}
100+
101+
private function extractLabels($title, Repository $repository)
72102
{
73103
$labels = [];
74104

75105
// e.g. "[PropertyAccess] [RFC] [WIP] Allow custom methods on property accesses"
76-
if (preg_match_all('/\[(?P<labels>.+)\]/U', $prTitle, $matches)) {
106+
if (preg_match_all('/\[(?P<labels>.+)\]/U', $title, $matches)) {
77107
// creates a key=>val array, but the key is lowercased
108+
$allLabels = $this->labelsApi->getAllLabelsForRepository($repository);
78109
$validLabels = array_combine(
79110
array_map(function ($s) {
80111
return strtolower($s);
81-
}, $this->getValidLabels()),
82-
$this->getValidLabels()
112+
}, $allLabels),
113+
$allLabels
83114
);
84115

85116
foreach ($matches['labels'] as $label) {
@@ -95,34 +126,6 @@ private function extractLabels($prTitle)
95126
return $labels;
96127
}
97128

98-
/**
99-
* TODO: get valid labels from the repository via GitHub API.
100-
*/
101-
private function getValidLabels()
102-
{
103-
$realLabels = [
104-
'Asset', 'BC Break', 'BrowserKit', 'Bug', 'Cache', 'Config', 'Console',
105-
'Contracts', 'Critical', 'CssSelector', 'Debug', 'DebugBundle', 'DependencyInjection',
106-
'Deprecation', 'Doctrine', 'DoctrineBridge', 'DomCrawler', 'Dotenv',
107-
'DX', 'Enhancement', 'ErrorHandler', 'EventDispatcher', 'ExpressionLanguage',
108-
'Feature', 'Filesystem', 'Finder', 'Form', 'FrameworkBundle', 'Hack Day',
109-
'HttpClient', 'HttpFoundation', 'HttpKernel', 'Inflector', 'Intl', 'Ldap',
110-
'Locale', 'Lock', 'Mailer', 'Messenger', 'Mime', 'MonologBridge', 'Notifier',
111-
'OptionsResolver', 'Performance', 'PhpUnitBridge', 'Process', 'PropertyAccess',
112-
'PropertyInfo', 'ProxyManagerBridge', 'RFC', 'Routing', 'Security',
113-
'SecurityBundle', 'Serializer', 'Stopwatch', 'String', 'Templating',
114-
'Translator', 'TwigBridge', 'TwigBundle', 'Uid', 'Validator', 'VarDumper',
115-
'VarExporter', 'WebLink', 'WebProfilerBundle', 'WebServerBundle', 'Workflow',
116-
'Yaml',
117-
];
118-
119-
return array_merge(
120-
$realLabels,
121-
// also consider the "aliases" as valid, so they are used
122-
array_keys(self::$labelAliases)
123-
);
124-
}
125-
126129
/**
127130
* It fixes common misspellings and aliases commonly used for label names
128131
* (e.g. DI -> DependencyInjection).
@@ -142,6 +145,7 @@ public static function getSubscribedEvents()
142145
{
143146
return [
144147
GitHubEvents::PULL_REQUEST => 'onPullRequest',
148+
GitHubEvents::ISSUES => 'onIssue',
145149
];
146150
}
147151
}

tests/Issues/GitHub/CachedLabelsApiTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use Github\Api\Issue\Labels;
88
use PHPUnit\Framework\MockObject\MockObject;
99
use PHPUnit\Framework\TestCase;
10+
use Symfony\Component\Cache\Adapter\NullAdapter;
1011

1112
/**
1213
* @author Bernhard Schussek <bschussek@gmail.com>
@@ -34,7 +35,7 @@ protected function setUp()
3435
$this->backendApi = $this->getMockBuilder(Labels::class)
3536
->disableOriginalConstructor()
3637
->getMock();
37-
$this->api = new CachedLabelsApi($this->backendApi);
38+
$this->api = new CachedLabelsApi($this->backendApi, new NullAdapter());
3839
$this->repository = new Repository(
3940
self::USER_NAME,
4041
self::REPO_NAME,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Tests\Service\Issues\Github;
6+
7+
use App\Issues\GitHub\CachedLabelsApi;
8+
use App\Repository\Repository;
9+
10+
class FakedCachedLabelApi extends CachedLabelsApi
11+
{
12+
public function getAllLabelsForRepository(Repository $repository): array
13+
{
14+
return array(
15+
'Asset', 'BC Break', 'BrowserKit', 'Bug', 'Cache', 'Config', 'Console',
16+
'Contracts', 'Critical', 'CssSelector', 'Debug', 'DebugBundle', 'DependencyInjection',
17+
'Deprecation', 'Doctrine', 'DoctrineBridge', 'DomCrawler', 'Dotenv',
18+
'DX', 'Enhancement', 'ErrorHandler', 'EventDispatcher', 'ExpressionLanguage',
19+
'Feature', 'Filesystem', 'Finder', 'Form', 'FrameworkBundle', 'Hack Day',
20+
'HttpClient', 'HttpFoundation', 'HttpKernel', 'Inflector', 'Intl', 'Ldap',
21+
'Locale', 'Lock', 'Mailer', 'Messenger', 'Mime', 'MonologBridge', 'Notifier',
22+
'OptionsResolver', 'Performance', 'PhpUnitBridge', 'Process', 'PropertyAccess',
23+
'PropertyInfo', 'ProxyManagerBridge', 'RFC', 'Routing', 'Security',
24+
'SecurityBundle', 'Serializer', 'Stopwatch', 'String', 'Templating',
25+
'Translator', 'TwigBridge', 'TwigBundle', 'Uid', 'Validator', 'VarDumper',
26+
'VarExporter', 'WebLink', 'WebProfilerBundle', 'WebServerBundle', 'Workflow',
27+
'Yaml',
28+
);
29+
}
30+
}

tests/Subscriber/AutoLabelPRFromContentSubscriberTest.php renamed to tests/Subscriber/AutoLabelFromContentSubscriberTest.php

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
use App\GitHubEvents;
77
use App\Issues\GitHub\CachedLabelsApi;
88
use App\Repository\Repository;
9-
use App\Subscriber\AutoLabelPRFromContentSubscriber;
9+
use App\Subscriber\AutoLabelFromContentSubscriber;
10+
use App\Tests\Service\Issues\Github\FakedCachedLabelApi;
1011
use PHPUnit\Framework\TestCase;
1112
use Symfony\Component\EventDispatcher\EventDispatcher;
1213

13-
class AutoLabelPRFromContentSubscriberTest extends TestCase
14+
class AutoLabelFromContentSubscriberTest extends TestCase
1415
{
1516
private $autoLabelSubscriber;
1617

@@ -25,20 +26,46 @@ class AutoLabelPRFromContentSubscriberTest extends TestCase
2526

2627
protected function setUp()
2728
{
28-
$this->labelsApi = $this->getMockBuilder(CachedLabelsApi::class)
29+
$this->labelsApi = $this->getMockBuilder(FakedCachedLabelApi::class)
2930
->disableOriginalConstructor()
31+
->setMethods(['addIssueLabels'])
3032
->getMock();
31-
$this->autoLabelSubscriber = new AutoLabelPRFromContentSubscriber($this->labelsApi);
33+
$this->autoLabelSubscriber = new AutoLabelFromContentSubscriber($this->labelsApi);
3234
$this->repository = new Repository('weaverryan', 'symfony', null);
3335

3436
$this->dispatcher = new EventDispatcher();
3537
$this->dispatcher->addSubscriber($this->autoLabelSubscriber);
3638
}
3739

40+
public function testAutoLabelIssue()
41+
{
42+
$this->labelsApi->expects($this->once())
43+
->method('addIssueLabels')
44+
->with(1234, ['Messenger'], $this->repository)
45+
->willReturn(null);
46+
47+
$event = new GitHubEvent([
48+
'action' => 'opened',
49+
'issue' => [
50+
'number' => 1234,
51+
'title' => '[Messenger] Foobar',
52+
'body' => 'Some content',
53+
],
54+
], $this->repository);
55+
56+
$this->dispatcher->dispatch($event, GitHubEvents::ISSUES);
57+
58+
$responseData = $event->getResponseData();
59+
60+
$this->assertCount(2, $responseData);
61+
$this->assertSame(1234, $responseData['issue']);
62+
$this->assertSame(['Messenger'], $responseData['issue_labels']);
63+
}
64+
3865
/**
3966
* @dataProvider getPRTests
4067
*/
41-
public function testAutoLabel($prTitle, $prBody, array $expectedNewLabels)
68+
public function testAutoLabelPR($prTitle, $prBody, array $expectedNewLabels)
4269
{
4370
$this->labelsApi->expects($this->once())
4471
->method('addIssueLabels')

0 commit comments

Comments
 (0)