Skip to content

Commit 95a9c7e

Browse files
authored
Merge pull request #3 from cslant/queue-event
Queue event
2 parents 339faed + ffc5c5e commit 95a9c7e

File tree

11 files changed

+237
-72
lines changed

11 files changed

+237
-72
lines changed

config/github-project.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,8 @@
1313
],
1414

1515
'enable_status_comment' => env('GITHUB_PROJECT_ENABLE_STATUS_COMMENT', false),
16+
17+
'is_queue_enabled' => env('GITHUB_PROJECT_QUEUE_ENABLED', false),
18+
'comment_aggregation_cache_key' => env('GITHUB_PROJECT_COMMENT_AGGREGATION_CACHE_KEY', 'github-project-comment-aggregation'),
19+
'comment_aggregation_time' => env('GITHUB_PROJECT_COMMENT_AGGREGATION_TIME', 20),
1620
];

phpstan-baseline.neon

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,8 @@ parameters:
99
- message: '#Cannot cast mixed to string#'
1010
path: src/*.php
1111

12+
- message: '#Cannot cast mixed to int#'
13+
path: src/*.php
14+
1215
- message: '#Cannot access offset .+ on mixed#'
1316
path: src/*.php
Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,5 @@
1-
@php
2-
$fieldData = $payload['changes']['field_value'] ?? null;
3-
4-
$fieldName = $fieldData['field_name'] ?? 'Unknown Field';
5-
$fromValue = $fieldData['from']['name']
6-
?? $fieldData['from']['title']
7-
?? $fieldData['from']
8-
?? null;
9-
$toValue = $fieldData['to']['name']
10-
?? $fieldData['to']['title']
11-
?? $fieldData['to']
12-
?? null;
13-
@endphp
14-
@include(
15-
'github-project::md.field_types.' . $fieldData['field_type'],
16-
compact('fieldName', 'fromValue', 'toValue', 'fieldData')
17-
)
18-
19-
@include('github-project::md.shared.author', compact('payload'))
1+
@include('github-project::md.shared.content', compact('payload'))
2+
@include('github-project::md.shared.author', [
3+
'name' => $payload['sender']['login'] ?? 'Unknown',
4+
'html_url' => $payload['sender']['html_url'] ?? '#'
5+
])
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
Made changes by: [{{ $payload['sender']['login'] ?? 'Unknown' }}]({{ $payload['sender']['html_url'] ?? '#' }})
1+
2+
> Made changes by: [{{ $name }}]({{ $html_url }})
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
@php
2+
$fieldData = $payload['changes']['field_value'] ?? null;
3+
4+
$fieldName = $fieldData['field_name'] ?? 'Unknown Field';
5+
$fromValue = $fieldData['from']['name']
6+
?? $fieldData['from']['title']
7+
?? $fieldData['from']
8+
?? null;
9+
$toValue = $fieldData['to']['name']
10+
?? $fieldData['to']['title']
11+
?? $fieldData['to']
12+
?? null;
13+
@endphp
14+
@include(
15+
'github-project::md.field_types.' . $fieldData['field_type'],
16+
compact('fieldName', 'fromValue', 'toValue', 'fieldData')
17+
)

src/Actions/WebhookAction.php

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

33
namespace CSlant\GitHubProject\Actions;
44

5+
use CSlant\GitHubProject\Services\GithubService;
56
use CSlant\GitHubProject\Services\WebhookService;
67
use Illuminate\Http\JsonResponse;
78
use Symfony\Component\HttpFoundation\Request;
@@ -11,9 +12,12 @@ class WebhookAction
1112
{
1213
protected WebhookService $webhookService;
1314

14-
public function __construct(WebhookService $webhookService)
15+
protected GithubService $githubService;
16+
17+
public function __construct(WebhookService $webhookService, GithubService $githubService)
1518
{
1619
$this->webhookService = $webhookService;
20+
$this->githubService = $githubService;
1721
}
1822

1923
/**
@@ -35,9 +39,7 @@ public function __invoke(): JsonResponse
3539
return $validationResponse;
3640
}
3741

38-
$message = view('github-project::md.comment', compact('payload'))->render();
39-
40-
$this->webhookService->commentOnNode((string) $payload['projects_v2_item']['content_node_id'], $message);
42+
$this->githubService->handleComment($payload);
4143

4244
return response()->json(['message' => __('github-project::github-project.success.message')]);
4345
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
namespace CSlant\GitHubProject\Jobs;
4+
5+
use CSlant\GitHubProject\Services\GithubService;
6+
use Illuminate\Bus\Queueable;
7+
use Illuminate\Contracts\Queue\ShouldQueue;
8+
use Illuminate\Foundation\Bus\Dispatchable;
9+
use Illuminate\Queue\InteractsWithQueue;
10+
use Illuminate\Queue\SerializesModels;
11+
use Illuminate\Support\Facades\Cache;
12+
13+
class ProcessAggregatedEvents implements ShouldQueue
14+
{
15+
use Dispatchable;
16+
use InteractsWithQueue;
17+
use Queueable;
18+
use SerializesModels;
19+
20+
protected string $nodeId;
21+
22+
/**
23+
* Create a new job instance.
24+
*/
25+
public function __construct(string $nodeId)
26+
{
27+
$this->nodeId = $nodeId;
28+
}
29+
30+
/**
31+
* Execute the job.
32+
*/
33+
public function handle(): void
34+
{
35+
$commentAggregationCacheKey = config('github-project.comment_aggregation_cache_key')."_{$this->nodeId}";
36+
37+
/** @var array<string, mixed> $eventMessages */
38+
$eventMessages = Cache::pull($commentAggregationCacheKey, []);
39+
40+
$message = $this->aggregateMessages($eventMessages);
41+
$author = Cache::pull($commentAggregationCacheKey.'_author', []);
42+
43+
Cache::forget($commentAggregationCacheKey);
44+
Cache::forget($commentAggregationCacheKey.'_author');
45+
46+
$message .= view(
47+
'github-project::md.shared.author',
48+
['name' => $author['name'], 'html_url' => $author['html_url']]
49+
)->render();
50+
51+
$githubService = new GithubService;
52+
$githubService->commentOnNode($this->nodeId, $message);
53+
}
54+
55+
/**
56+
* Aggregate messages from events.
57+
*
58+
* @param array<string, mixed> $eventMessages
59+
*
60+
* @return string
61+
*/
62+
protected function aggregateMessages(array $eventMessages): string
63+
{
64+
$messages = array_map(function ($message) {
65+
return $message;
66+
}, $eventMessages);
67+
68+
return implode("\n", $messages);
69+
}
70+
}

src/Jobs/ProcessWebhookEvent.php

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
namespace CSlant\GitHubProject\Jobs;
4+
5+
use Illuminate\Bus\Queueable;
6+
use Illuminate\Contracts\Queue\ShouldQueue;
7+
use Illuminate\Foundation\Bus\Dispatchable;
8+
use Illuminate\Queue\InteractsWithQueue;
9+
use Illuminate\Queue\SerializesModels;
10+
use Illuminate\Support\Facades\Cache;
11+
12+
class ProcessWebhookEvent implements ShouldQueue
13+
{
14+
use Dispatchable;
15+
use InteractsWithQueue;
16+
use Queueable;
17+
use SerializesModels;
18+
19+
/** @var array<string, mixed> */
20+
protected array $eventData;
21+
22+
/**
23+
* Create a new job instance.
24+
*
25+
* @param array<string, mixed> $eventData
26+
*/
27+
public function __construct(array $eventData)
28+
{
29+
$this->eventData = $eventData;
30+
}
31+
32+
/**
33+
* Execute the job.
34+
*/
35+
public function handle(): void
36+
{
37+
$nodeId = (string) $this->eventData['projects_v2_item']['content_node_id'];
38+
$commentAggregationCacheKey = config('github-project.comment_aggregation_cache_key')."_{$nodeId}";
39+
$commentAggregationTime = (int) config('github-project.comment_aggregation_time');
40+
41+
$eventMessages = (array) Cache::get($commentAggregationCacheKey, []);
42+
$eventMessages[] = view('github-project::md.shared.content', ['payload' => $this->eventData])->render();
43+
44+
Cache::put($commentAggregationCacheKey, $eventMessages, now()->addSeconds($commentAggregationTime + 3));
45+
46+
if (!Cache::has($commentAggregationCacheKey.'_author')) {
47+
Cache::put(
48+
$commentAggregationCacheKey.'_author',
49+
[
50+
'name' => $this->eventData['sender']['login'],
51+
'html_url' => $this->eventData['sender']['html_url'],
52+
],
53+
now()->addSeconds($commentAggregationTime + 3)
54+
);
55+
}
56+
57+
if (count($eventMessages) === 1) {
58+
ProcessAggregatedEvents::dispatch($nodeId)->delay(now()->addSeconds($commentAggregationTime));
59+
}
60+
}
61+
}

src/Providers/GithubProjectServiceProvider.php

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

33
namespace CSlant\GithubProject\Providers;
44

5-
use Github\AuthMethod;
6-
use Github\Client;
75
use Illuminate\Support\ServiceProvider;
86

97
class GithubProjectServiceProvider extends ServiceProvider
@@ -30,13 +28,6 @@ public function register(): void
3028
$this->registerConfigs();
3129

3230
$this->registerCommands();
33-
34-
$this->app->singleton(Client::class, function () {
35-
$client = new Client;
36-
$client->authenticate((string) config('github-project.github.access_token'), null, AuthMethod::ACCESS_TOKEN);
37-
38-
return $client;
39-
});
4031
}
4132

4233
/**

src/Services/GithubService.php

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
namespace CSlant\GitHubProject\Services;
4+
5+
use CSlant\GitHubProject\Jobs\ProcessWebhookEvent;
6+
use Github\AuthMethod;
7+
use Github\Client;
8+
9+
class GithubService
10+
{
11+
protected Client $client;
12+
13+
public function __construct(?Client $client = null)
14+
{
15+
$this->client = $client ?? new Client;
16+
}
17+
18+
/**
19+
* @param string $contentNodeId
20+
* @param string $message
21+
*
22+
* @return array<string, mixed>
23+
*/
24+
public function commentOnNode(string $contentNodeId, string $message): array
25+
{
26+
$query = <<<'GRAPHQL'
27+
mutation($input: AddCommentInput!) {
28+
addComment(input: $input) {
29+
commentEdge {
30+
node {
31+
id
32+
body
33+
}
34+
}
35+
}
36+
}
37+
GRAPHQL;
38+
39+
$variables = [
40+
'input' => [
41+
'subjectId' => $contentNodeId,
42+
'body' => $message,
43+
],
44+
];
45+
46+
$this->client->authenticate((string) config('github-project.github.access_token'), null, AuthMethod::ACCESS_TOKEN);
47+
48+
return $this->client->graphql()->execute($query, $variables);
49+
}
50+
51+
/**
52+
* @param array<string, mixed> $payload
53+
*
54+
* @throws \Throwable
55+
*/
56+
public function handleComment(array $payload): void
57+
{
58+
if (config('github-project.is_queue_enabled')) {
59+
ProcessWebhookEvent::dispatch($payload);
60+
61+
return;
62+
}
63+
64+
$this->commentOnNode(
65+
(string) $payload['projects_v2_item']['content_node_id'],
66+
view('github-project::md.comment', compact('payload'))->render()
67+
);
68+
}
69+
}

0 commit comments

Comments
 (0)