Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
7004f30
feat: add queue handling for GitHub comments
tanhongit Feb 15, 2025
b7312ec
feat: create new jobs to handle comment
tanhongit Feb 15, 2025
90b9b23
feat: update webhook job to handle save event to cache
tanhongit Feb 15, 2025
9e8f958
feat: update cache handling in webhook processing
tanhongit Feb 15, 2025
0986a13
Fix styling
tanhongit Feb 15, 2025
c214816
fix: analyse
tanhongit Feb 15, 2025
33a9118
feat: implement GithubService for handling comments and integrate wit…
tanhongit Feb 15, 2025
b8845aa
Merge remote-tracking branch 'origin/queue-event' into queue-event
tanhongit Feb 15, 2025
fccb450
Fix styling
tanhongit Feb 15, 2025
4cad255
fix: update workflow and aggregate event
tanhongit Feb 16, 2025
c218e9e
Fix styling
tanhongit Feb 16, 2025
809e675
Merge branch 'main' into queue-event
tanhongit Feb 16, 2025
da28cc9
Merge branch 'main' into queue-event
tanhongit Feb 18, 2025
3233a6e
feat: refactor comment handling and introduce content view
tanhongit Feb 18, 2025
9e518ad
feat: handle aggregate event job
tanhongit Feb 18, 2025
c1a3179
fix: update author display in comment view
tanhongit Feb 18, 2025
d51b384
Fix styling
tanhongit Feb 18, 2025
6193670
feat: inject client dependency into GithubService
tanhongit Feb 18, 2025
983f32c
fix: update cache key construction for comment aggregation
tanhongit Feb 18, 2025
77b6432
Fix styling
tanhongit Feb 18, 2025
736da00
fix: improve type handling and update comment handling logic
tanhongit Feb 18, 2025
d69de1d
refactor: update phpstan baseline and add type hints for event data
tanhongit Feb 18, 2025
a62daa9
feat: add logging for event processing in ProcessAggregatedEvents and…
tanhongit Feb 18, 2025
7891040
feat: inject GithubService into ProcessAggregatedEvents for commentin…
tanhongit Feb 18, 2025
815b5cd
fix: simplify author logging in ProcessAggregatedEvents
tanhongit Feb 18, 2025
ed065ab
refactor: simplify ProcessAggregatedEvents and ProcessWebhookEvent cl…
tanhongit Feb 19, 2025
d7c6c76
refactor: remove client instantiation from service provider and move …
tanhongit Feb 19, 2025
d331202
Fix styling
tanhongit Feb 19, 2025
f1075cf
refactor: remove unused Github\Client dependency from WebhookService
tanhongit Feb 19, 2025
d0d5283
Merge remote-tracking branch 'origin/queue-event-test' into queue-eve…
tanhongit Feb 19, 2025
1b59d08
refactor: update eventData property documentation in ProcessWebhookEvent
tanhongit Feb 19, 2025
9acab85
fix: cast content_node_id to string in ProcessWebhookEvent
tanhongit Feb 19, 2025
e8d9c25
Fix styling
tanhongit Feb 19, 2025
a61d25d
feat: enhance author display in comments with GitHub link
tanhongit Feb 19, 2025
2e49ce0
Merge remote-tracking branch 'origin/queue-event' into queue-event
tanhongit Feb 19, 2025
ffc5c5e
fix: update formatting for author change notification
tanhongit Feb 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions config/github-project.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@
],

'enable_status_comment' => env('GITHUB_PROJECT_ENABLE_STATUS_COMMENT', false),

'is_queue_enabled' => env('GITHUB_PROJECT_QUEUE_ENABLED', false),
'comment_aggregation_cache_key' => env('GITHUB_PROJECT_COMMENT_AGGREGATION_CACHE_KEY', 'github-project-comment-aggregation'),
'comment_aggregation_time' => env('GITHUB_PROJECT_COMMENT_AGGREGATION_TIME', 20),
];
3 changes: 3 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@ parameters:
- message: '#Cannot cast mixed to string#'
path: src/*.php

- message: '#Cannot cast mixed to int#'
path: src/*.php

- message: '#Cannot access offset .+ on mixed#'
path: src/*.php
24 changes: 5 additions & 19 deletions resources/views/md/comment.blade.php
Original file line number Diff line number Diff line change
@@ -1,19 +1,5 @@
@php
$fieldData = $payload['changes']['field_value'] ?? null;

$fieldName = $fieldData['field_name'] ?? 'Unknown Field';
$fromValue = $fieldData['from']['name']
?? $fieldData['from']['title']
?? $fieldData['from']
?? null;
$toValue = $fieldData['to']['name']
?? $fieldData['to']['title']
?? $fieldData['to']
?? null;
@endphp
@include(
'github-project::md.field_types.' . $fieldData['field_type'],
compact('fieldName', 'fromValue', 'toValue', 'fieldData')
)

@include('github-project::md.shared.author', compact('payload'))
@include('github-project::md.shared.content', compact('payload'))
@include('github-project::md.shared.author', [
'name' => $payload['sender']['login'] ?? 'Unknown',
'html_url' => $payload['sender']['html_url'] ?? '#'
])
3 changes: 2 additions & 1 deletion resources/views/md/shared/author.blade.php
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Made changes by: [{{ $payload['sender']['login'] ?? 'Unknown' }}]({{ $payload['sender']['html_url'] ?? '#' }})

> Made changes by: [{{ $name }}]({{ $html_url }})
17 changes: 17 additions & 0 deletions resources/views/md/shared/content.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@php
$fieldData = $payload['changes']['field_value'] ?? null;

$fieldName = $fieldData['field_name'] ?? 'Unknown Field';
$fromValue = $fieldData['from']['name']
?? $fieldData['from']['title']
?? $fieldData['from']
?? null;
$toValue = $fieldData['to']['name']
?? $fieldData['to']['title']
?? $fieldData['to']
?? null;
@endphp
@include(
'github-project::md.field_types.' . $fieldData['field_type'],
compact('fieldName', 'fromValue', 'toValue', 'fieldData')
)
10 changes: 6 additions & 4 deletions src/Actions/WebhookAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace CSlant\GitHubProject\Actions;

use CSlant\GitHubProject\Services\GithubService;
use CSlant\GitHubProject\Services\WebhookService;
use Illuminate\Http\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
Expand All @@ -11,9 +12,12 @@ class WebhookAction
{
protected WebhookService $webhookService;

public function __construct(WebhookService $webhookService)
protected GithubService $githubService;

public function __construct(WebhookService $webhookService, GithubService $githubService)
{
$this->webhookService = $webhookService;
$this->githubService = $githubService;
}

/**
Expand All @@ -35,9 +39,7 @@ public function __invoke(): JsonResponse
return $validationResponse;
}

$message = view('github-project::md.comment', compact('payload'))->render();

$this->webhookService->commentOnNode((string) $payload['projects_v2_item']['content_node_id'], $message);
$this->githubService->handleComment($payload);

return response()->json(['message' => __('github-project::github-project.success.message')]);
}
Expand Down
70 changes: 70 additions & 0 deletions src/Jobs/ProcessAggregatedEvents.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace CSlant\GitHubProject\Jobs;

use CSlant\GitHubProject\Services\GithubService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Cache;

class ProcessAggregatedEvents implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;

protected string $nodeId;

/**
* Create a new job instance.
*/
public function __construct(string $nodeId)
{
$this->nodeId = $nodeId;
}

/**
* Execute the job.
*/
public function handle(): void
{
$commentAggregationCacheKey = config('github-project.comment_aggregation_cache_key')."_{$this->nodeId}";

/** @var array<string, mixed> $eventMessages */
$eventMessages = Cache::pull($commentAggregationCacheKey, []);

$message = $this->aggregateMessages($eventMessages);
$author = Cache::pull($commentAggregationCacheKey.'_author', []);

Cache::forget($commentAggregationCacheKey);
Cache::forget($commentAggregationCacheKey.'_author');

$message .= view(
'github-project::md.shared.author',
['name' => $author['name'], 'html_url' => $author['html_url']]
)->render();

$githubService = new GithubService;
$githubService->commentOnNode($this->nodeId, $message);
}

/**
* Aggregate messages from events.
*
* @param array<string, mixed> $eventMessages
*
* @return string
*/
protected function aggregateMessages(array $eventMessages): string
{
$messages = array_map(function ($message) {
return $message;
}, $eventMessages);

return implode("\n", $messages);
}
}
61 changes: 61 additions & 0 deletions src/Jobs/ProcessWebhookEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

namespace CSlant\GitHubProject\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Cache;

class ProcessWebhookEvent implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;

/** @var array<string, mixed> */
protected array $eventData;

/**
* Create a new job instance.
*
* @param array<string, mixed> $eventData
*/
public function __construct(array $eventData)
{
$this->eventData = $eventData;
}

/**
* Execute the job.
*/
public function handle(): void
{
$nodeId = (string) $this->eventData['projects_v2_item']['content_node_id'];
$commentAggregationCacheKey = config('github-project.comment_aggregation_cache_key')."_{$nodeId}";
$commentAggregationTime = (int) config('github-project.comment_aggregation_time');

$eventMessages = (array) Cache::get($commentAggregationCacheKey, []);
$eventMessages[] = view('github-project::md.shared.content', ['payload' => $this->eventData])->render();

Cache::put($commentAggregationCacheKey, $eventMessages, now()->addSeconds($commentAggregationTime + 3));

if (!Cache::has($commentAggregationCacheKey.'_author')) {
Cache::put(
$commentAggregationCacheKey.'_author',
[
'name' => $this->eventData['sender']['login'],
'html_url' => $this->eventData['sender']['html_url'],
],
now()->addSeconds($commentAggregationTime + 3)
);
}

if (count($eventMessages) === 1) {
ProcessAggregatedEvents::dispatch($nodeId)->delay(now()->addSeconds($commentAggregationTime));
}
}
}
9 changes: 0 additions & 9 deletions src/Providers/GithubProjectServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

namespace CSlant\GithubProject\Providers;

use Github\AuthMethod;
use Github\Client;
use Illuminate\Support\ServiceProvider;

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

$this->registerCommands();

$this->app->singleton(Client::class, function () {
$client = new Client;
$client->authenticate((string) config('github-project.github.access_token'), null, AuthMethod::ACCESS_TOKEN);

return $client;
});
}

/**
Expand Down
69 changes: 69 additions & 0 deletions src/Services/GithubService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

namespace CSlant\GitHubProject\Services;

use CSlant\GitHubProject\Jobs\ProcessWebhookEvent;
use Github\AuthMethod;
use Github\Client;

class GithubService
{
protected Client $client;

public function __construct(?Client $client = null)
{
$this->client = $client ?? new Client;
}

/**
* @param string $contentNodeId
* @param string $message
*
* @return array<string, mixed>
*/
public function commentOnNode(string $contentNodeId, string $message): array
{
$query = <<<'GRAPHQL'
mutation($input: AddCommentInput!) {
addComment(input: $input) {
commentEdge {
node {
id
body
}
}
}
}
GRAPHQL;

$variables = [
'input' => [
'subjectId' => $contentNodeId,
'body' => $message,
],
];

$this->client->authenticate((string) config('github-project.github.access_token'), null, AuthMethod::ACCESS_TOKEN);

return $this->client->graphql()->execute($query, $variables);
}

/**
* @param array<string, mixed> $payload
*
* @throws \Throwable
*/
public function handleComment(array $payload): void
{
if (config('github-project.is_queue_enabled')) {
ProcessWebhookEvent::dispatch($payload);

return;
}

$this->commentOnNode(
(string) $payload['projects_v2_item']['content_node_id'],
view('github-project::md.comment', compact('payload'))->render()
);
}
}
39 changes: 0 additions & 39 deletions src/Services/WebhookService.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,11 @@

namespace CSlant\GitHubProject\Services;

use Github\Client;
use Illuminate\Http\JsonResponse;
use Symfony\Component\HttpFoundation\Request;

class WebhookService
{
protected Client $client;

public function __construct(Client $client)
{
$this->client = $client;
}

public function eventRequestApproved(Request $request): bool
{
$event = $request->server->get('HTTP_X_GITHUB_EVENT');
Expand Down Expand Up @@ -103,35 +95,4 @@ protected function isStatusCommentEnabled(array $payload): bool

return true;
}

/**
* @param string $contentNodeId
* @param string $message
*
* @return array<string, mixed>
*/
public function commentOnNode(string $contentNodeId, string $message): array
{
$query = <<<'GRAPHQL'
mutation($input: AddCommentInput!) {
addComment(input: $input) {
commentEdge {
node {
id
body
}
}
}
}
GRAPHQL;

$variables = [
'input' => [
'subjectId' => $contentNodeId,
'body' => $message,
],
];

return $this->client->graphql()->execute($query, $variables);
}
}