Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 0 additions & 27 deletions app/Http/Controllers/WebhookController.php

This file was deleted.

32 changes: 0 additions & 32 deletions app/Http/Requests/GateWebhookRequest.php

This file was deleted.

30 changes: 30 additions & 0 deletions app/Webhooks/GateSignatureValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace App\Webhooks;

use Illuminate\Http\Request;
use Spatie\WebhookClient\SignatureValidator\SignatureValidator;
use Spatie\WebhookClient\WebhookConfig;

class GateSignatureValidator implements SignatureValidator
{
public function isValid(Request $request, WebhookConfig $config): bool
{
$signature = $request->header($config->signatureHeaderName);
$secret = $config->signingSecret;

if (empty($secret)) {
return true;
}

if (empty($signature)) {
return false;
}

$computedSignature = hash_hmac('sha256', $request->getContent(), $secret);

return hash_equals($computedSignature, $signature);
}
}
30 changes: 30 additions & 0 deletions app/Webhooks/GateWebhookProfile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace App\Webhooks;

use Illuminate\Http\Request;
use Spatie\WebhookClient\WebhookProfile\WebhookProfile;

class GateWebhookProfile implements WebhookProfile
{
public function shouldProcess(Request $request): bool
{
$payload = $request->all();

if (empty($payload['repository']) || ! is_string($payload['repository'])) {
return false;
}

if (empty($payload['sha']) || ! is_string($payload['sha'])) {
return false;
}

if (empty($payload['verdict']) || ! in_array($payload['verdict'], ['approved', 'rejected', 'escalate'], true)) {
return false;
}

return true;
}
}
26 changes: 26 additions & 0 deletions app/Webhooks/ProcessGateWebhookJob.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace App\Webhooks;

use App\Events\CertificationCompleted;
use Spatie\WebhookClient\Jobs\ProcessWebhookJob;

class ProcessGateWebhookJob extends ProcessWebhookJob
{
public function handle(): void
{
$payload = $this->webhookCall->payload;

CertificationCompleted::fire(
repository: $payload['repository'],
sha: $payload['sha'],
verdict: $payload['verdict'],
reason: $payload['reason'] ?? null,
checks: $payload['checks'] ?? null,
triggeredBy: $payload['triggered_by'] ?? null,
prNumber: isset($payload['pr_number']) ? (int) $payload['pr_number'] : null,
);
}
}
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"php": "^8.2",
"hirethunk/verbs": "^0.7.0",
"laravel/framework": "^12.0",
"laravel/tinker": "^2.10.1"
"laravel/tinker": "^2.10.1",
"spatie/laravel-webhook-client": "^3.4"
},
"require-dev": {
"fakerphp/faker": "^1.23",
Expand Down
80 changes: 79 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

77 changes: 77 additions & 0 deletions config/webhook-client.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

return [
'configs' => [
[
/*
* This package supports multiple webhook receiving endpoints. If you only have
* one endpoint receiving webhooks, you can use 'default'.
*/
'name' => 'gate',

/*
* We expect that every webhook call will be signed using a secret. This secret
* is used to verify that the payload has not been tampered with.
*/
'signing_secret' => env('GATE_WEBHOOK_SECRET'),

/*
* The name of the header containing the signature.
*/
'signature_header_name' => 'X-Gate-Signature',

/*
* This class will verify that the content of the signature header is valid.
*
* It should implement \Spatie\WebhookClient\SignatureValidator\SignatureValidator
*/
'signature_validator' => \App\Webhooks\GateSignatureValidator::class,

/*
* This class determines if the webhook call should be stored and processed.
*/
'webhook_profile' => \App\Webhooks\GateWebhookProfile::class,

/*
* This class determines the response on a valid webhook call.
*/
'webhook_response' => \Spatie\WebhookClient\WebhookResponse\DefaultRespondsTo::class,

/*
* The classname of the model to be used to store webhook calls. The class should
* be equal or extend Spatie\WebhookClient\Models\WebhookCall.
*/
'webhook_model' => \Spatie\WebhookClient\Models\WebhookCall::class,

/*
* In this array, you can pass the headers that should be stored on
* the webhook call model when a webhook comes in.
*
* To store all headers, set this value to `*`.
*/
'store_headers' => [
'X-Gate-Signature',
'Content-Type',
],

/*
* The class name of the job that will process the webhook request.
*
* This should be set to a class that extends \Spatie\WebhookClient\Jobs\ProcessWebhookJob.
*/
'process_webhook_job' => \App\Webhooks\ProcessGateWebhookJob::class,
],
],

/*
* The integer amount of days after which models should be deleted.
*
* It deletes all records after 30 days. Set to null if no models should be deleted.
*/
'delete_after_days' => 30,

/*
* Should a unique token be added to the route name
*/
'add_unique_token_to_route_name' => false,
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
public function up()
{
Schema::create('webhook_calls', function (Blueprint $table) {
$table->bigIncrements('id');

$table->string('name');
$table->string('url', 512);
$table->json('headers')->nullable();
$table->json('payload')->nullable();
$table->text('exception')->nullable();

$table->timestamps();
});
}
};
4 changes: 1 addition & 3 deletions routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

declare(strict_types=1);

use App\Http\Controllers\WebhookController;
use Illuminate\Support\Facades\Route;

Route::post('/webhooks/gate', [WebhookController::class, 'gate'])
->name('webhooks.gate');
Route::webhooks('webhooks/gate', 'gate');
Loading