Skip to content

Commit 74775d3

Browse files
authoredAug 20, 2023
migrate webhooks (#1858)
1 parent 071afbe commit 74775d3

22 files changed

+360
-334
lines changed
 

‎.env.testing

+1
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ DB_CONNECTION=sqlite
77
DB_DATABASE=:memory:
88

99
CACHE_DRIVER=file
10+
WEBHOOKS_ACTIVE=true

‎.idea/php.xml

+1-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎.idea/trwl.iml

+1-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎.vscode/settings.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"php.format.codeStyle": "K&R"
3+
}

‎app/Enum/WebhookEvent.php

+6-22
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,10 @@
44

55
namespace App\Enum;
66

7-
enum WebhookEvent: int {
8-
case CHECKIN_CREATE = 1 << 0;
9-
case CHECKIN_UPDATE = 1 << 1;
10-
case CHECKIN_DELETE = 1 << 2;
11-
case NOTIFICATION = 1 << 3;
12-
13-
public function name(): string {
14-
return match ($this) {
15-
WebhookEvent::CHECKIN_CREATE => 'checkin_create',
16-
WebhookEvent::CHECKIN_UPDATE => 'checkin_update',
17-
WebhookEvent::CHECKIN_DELETE => 'checkin_delete',
18-
WebhookEvent::NOTIFICATION => 'notification',
19-
};
20-
}
21-
22-
public static function fromNames(array $names): int {
23-
return array_reduce(array_filter(WebhookEvent::cases(), function ($event) use ($names) {
24-
return in_array($event->name(), $names);
25-
}), function ($acc, $value) {
26-
return $acc | $value->value;
27-
}, 0);
28-
}
7+
// Note: Webhook Event strings must not be longer than 32 characters.
8+
enum WebhookEvent: string {
9+
case CHECKIN_CREATE = "checkin_create";
10+
case CHECKIN_UPDATE = "checkin_update";
11+
case CHECKIN_DELETE = "checkin_delete";
12+
case NOTIFICATION = "notification";
2913
}

‎app/Http/Controllers/Backend/Auth/AccessTokenController.php

+2-7
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
namespace App\Http\Controllers\Backend\Auth;
44

55
use App\Http\Controllers\Backend\WebhookController;
6-
use Illuminate\Http\Response;
76
use Laravel\Passport\Exceptions\OAuthServerException;
87
use Laravel\Passport\Http\Controllers\AccessTokenController as PassportAccessTokenController;
98
use Nyholm\Psr7\Response as Psr7Response;
9+
use Nyholm\Psr7\Stream;
1010
use Psr\Http\Message\ServerRequestInterface;
1111
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
1212

@@ -48,11 +48,6 @@ protected function extendResponseWithWebhookData(ServerRequestInterface $request
4848
'secret' => $webhook->secret,
4949
'url' => $webhook->url,
5050
];
51-
52-
return new Response(
53-
$data,
54-
$response->getStatusCode(),
55-
$response->getHeaders()
56-
);
51+
return $response->withBody(Stream::create(json_encode($data)));
5752
}
5853
}

‎app/Http/Controllers/Backend/Auth/ApproveAuthorizationController.php

+3-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace App\Http\Controllers\Backend\Auth;
44

5-
use App\Enum\WebhookEvent;
65
use App\Http\Controllers\Backend\WebhookController;
76
use Illuminate\Http\Request;
87
use Illuminate\Support\Facades\Log;
@@ -17,11 +16,11 @@ public function approve(Request $request) {
1716
$code = $query['code'];
1817
$user = $webhook['user'];
1918
$client = $webhook['client'];
20-
$events = WebhookEvent::fromNames($webhook['events']);
19+
$events = $webhook['events'];
2120
Log::debug("Creating a new webhook creation request", [
2221
'client_id' => $client->id,
23-
'user_id' => $user->id,
24-
'events' => $webhook['events'],
22+
'user_id' => $user->id,
23+
'events' => $events,
2524
]);
2625
WebhookController::createWebhookRequest(
2726
$user,

‎app/Http/Controllers/Backend/Auth/AuthorizationController.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace App\Http\Controllers\Backend\Auth;
44

5+
use App\Enum\WebhookEvent;
56
use App\Models\OAuthClient;
67
use App\Models\Webhook;
78
use App\Repositories\OAuthClientRepository;
@@ -11,6 +12,7 @@
1112
use Illuminate\Http\Response;
1213
use Illuminate\Support\Facades\Validator;
1314
use Illuminate\Support\Str;
15+
use Illuminate\Validation\Rules\Enum;
1416
use Laravel\Passport\ClientRepository;
1517
use Laravel\Passport\Exceptions\AuthenticationException;
1618
use Laravel\Passport\Http\Controllers\AuthorizationController as PassportAuthorizationController;
@@ -131,7 +133,7 @@ public function parseWebhookExtensions(Request $request, OAuthClient $client): ?
131133
}
132134

133135
$validator = Validator::make($request->all(), [
134-
'trwl_webhook_events' => ['required', new Delimited(new StringifiedWebhookEvents())],
136+
'trwl_webhook_events' => ['required', new Delimited(new Enum(WebhookEvent::class))],
135137
'trwl_webhook_url' => ['required', 'string', new AuthorizedWebhookURL($client)]
136138
]);
137139

‎app/Http/Controllers/Backend/WebhookController.php

+22-11
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace App\Http\Controllers\Backend;
44

5-
use App\Enum\WebhookEvent;
5+
use App\Enum\WebhookEvent as WebhookEventEnum;
66
use App\Exceptions\PermissionException;
77
use App\Http\Controllers\Controller;
88
use App\Http\Resources\StatusResource;
@@ -12,12 +12,13 @@
1212
use App\Models\User;
1313
use App\Models\Webhook;
1414
use App\Models\WebhookCreationRequest;
15+
use App\Models\WebhookEvent;
1516
use Carbon\Carbon;
17+
use Illuminate\Database\Eloquent\Builder;
1618
use Illuminate\Notifications\DatabaseNotification;
1719
use Illuminate\Support\Facades\DB;
1820
use Illuminate\Support\Facades\Gate;
1921
use Illuminate\Support\Facades\Log;
20-
use Laravel\Passport\Client;
2122
use Spatie\WebhookServer\WebhookCall;
2223

2324
abstract class WebhookController extends Controller {
@@ -33,14 +34,21 @@ public static function createWebhook(
3334
$secret = bin2hex(random_bytes(32));
3435
$client = $request->client()->first();
3536
$user = $request->user()->first();
36-
$events = $request->events;
37+
3738
$webhook = Webhook::create([
3839
'oauth_client_id' => $client->id,
3940
'url' => $request->url,
4041
'secret' => $secret,
41-
'events' => $events,
4242
'user_id' => $user->id
4343
]);
44+
45+
foreach (explode(",", $request->events) as $event) {
46+
WebhookEvent::create([
47+
'webhook_id' => $webhook->id,
48+
'event' => $event,
49+
]);
50+
}
51+
4452
DB::commit();
4553

4654
Log::debug("Created a new webhook.", ['webhook' => $webhook]);
@@ -67,27 +75,30 @@ public static function deleteWebhook(
6775
return true;
6876
}
6977

70-
public static function sendStatusWebhook(Status $status, WebhookEvent $event): void {
78+
public static function sendStatusWebhook(Status $status, WebhookEventEnum $event): void {
7179
self::dispatchWebhook($status->user, $event, [
7280
'status' => new StatusResource($status)
7381
]);
7482
}
7583

7684
public static function sendNotificationWebhook(User $user, DatabaseNotification $notification): void {
77-
self::dispatchWebhook($user, WebhookEvent::NOTIFICATION, [
85+
self::dispatchWebhook($user, WebhookEventEnum::NOTIFICATION, [
7886
'notification' => new UserNotificationResource($notification)
7987
]);
8088
}
8189

82-
public static function dispatchWebhook(User $user, WebhookEvent $event, array $data): void {
90+
public static function dispatchWebhook(User $user, WebhookEventEnum $event, array $data): void {
8391
if (!config("trwl.webhooks_active")) {
8492
return;
8593
}
8694

8795
$webhooks = $user->webhooks()
88-
->whereBitflag('events', $event->value)
96+
->withWhereHas('events', function ($builder) use ($event) {
97+
$builder->where('event', '=', $event);
98+
})
8999
->where('user_id', $user->id)
90100
->get();
101+
91102
foreach ($webhooks as $webhook) {
92103
Log::debug("Sending webhook", [
93104
'webhook_id' => $webhook->id,
@@ -100,7 +111,7 @@ public static function dispatchWebhook(User $user, WebhookEvent $event, array $d
100111
'X-Trwl-Webhook-Id' => $webhook->id,
101112
])
102113
->payload([
103-
'event' => $event->name(),
114+
'event' => $event->value,
104115
...$data
105116
])
106117
->useSecret($webhook->secret)
@@ -116,14 +127,14 @@ public static function createWebhookRequest(
116127
OAuthClient $client,
117128
string $oauthCode,
118129
string $url,
119-
int $events,
130+
array $events,
120131
): WebhookCreationRequest {
121132
return WebhookCreationRequest::create([
122133
'id' => hash('sha256', $oauthCode),
123134
'user_id' => $user->id,
124135
'oauth_client_id' => $client->id,
125136
'expires_at' => Carbon::now()->addHour(),
126-
'events' => $events,
137+
'events' => implode(",", $events),
127138
'url' => $url,
128139
]);
129140
}

‎app/Models/Webhook.php

+6-3
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
namespace App\Models;
44

5-
use AwStudio\Bitflags\Casts\Bitflags;
65
use Illuminate\Database\Eloquent\Builder;
76
use Illuminate\Database\Eloquent\Factories\HasFactory;
87
use Illuminate\Database\Eloquent\Model;
98
use Illuminate\Database\Eloquent\Relations\BelongsTo;
9+
use Illuminate\Database\Eloquent\Relations\HasMany;
1010
use Laravel\Passport\Passport;
1111

1212
/**
@@ -15,15 +15,14 @@
1515
class Webhook extends Model {
1616
use HasFactory;
1717

18-
protected $fillable = ['user_id', 'oauth_client_id', 'url', 'secret', 'events'];
18+
protected $fillable = ['user_id', 'oauth_client_id', 'url', 'secret'];
1919
protected $hidden = ['oauth_client_id', 'secret', 'created_at', 'updated_at'];
2020
protected $casts = [
2121
'id' => 'integer',
2222
'oauth_client_id' => 'string',
2323
'url' => 'string',
2424
'secret' => 'string',
2525
'user_id' => 'integer',
26-
'events' => Bitflags::class,
2726
];
2827

2928
public function client(): BelongsTo {
@@ -33,4 +32,8 @@ public function client(): BelongsTo {
3332
public function user(): BelongsTo {
3433
return $this->belongsTo(User::class);
3534
}
35+
36+
public function events(): HasMany {
37+
return $this->hasMany(WebhookEvent::class, 'webhook_id', 'id');
38+
}
3639
}

‎app/Models/WebhookCreationRequest.php

+1-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace App\Models;
44

5-
use AwStudio\Bitflags\Casts\Bitflags;
65
use Carbon\Carbon;
76
use Illuminate\Database\Eloquent\Builder;
87
use Illuminate\Database\Eloquent\Model;
@@ -21,7 +20,7 @@ class WebhookCreationRequest extends Model {
2120
'oauth_client_id' => 'integer',
2221
'revoked' => 'boolean',
2322
'expires_at' => 'datetime',
24-
'events' => Bitflags::class,
23+
'events' => 'string',
2524
'url' => 'string',
2625
];
2726

‎app/Models/WebhookEvent.php

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace App\Models;
4+
5+
use Illuminate\Database\Eloquent\Factories\HasFactory;
6+
use Illuminate\Database\Eloquent\Model;
7+
use App\Enum\WebhookEvent as WebhookEventEnum;
8+
use Illuminate\Database\Eloquent\Relations\HasOne;
9+
use Illuminate\Database\Eloquent\Builder;
10+
11+
/**
12+
* @mixin Builder
13+
*/
14+
class WebhookEvent extends Model
15+
{
16+
use HasFactory;
17+
18+
public $timestamps = false;
19+
20+
protected $fillable = ['webhook_id', 'event'];
21+
protected $casts = [
22+
'webhook_id' => 'integer',
23+
'event' => WebhookEventEnum::class,
24+
];
25+
26+
public function webhook(): HasOne {
27+
return $this->hasOne(Webhook::class, 'id', 'webhook_id');
28+
}
29+
}

‎app/Rules/StringifiedWebhookEvents.php

-25
This file was deleted.

‎composer.json

+2-9
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@
3838
"spatie/laravel-sitemap": "^6.0",
3939
"spatie/laravel-validation-rules": "^3.2",
4040
"spatie/laravel-webhook-server": "^3.4",
41-
"trwl/socialite-mastodon": "^1.2",
42-
"uwumarie/laravel-bitflags": "dev-main"
41+
"trwl/socialite-mastodon": "^1.2"
4342
},
4443
"require-dev": {
4544
"ext-pdo_sqlite": "*",
@@ -92,11 +91,5 @@
9291
"post-create-project-cmd": [
9392
"@php artisan key:generate --ansi"
9493
]
95-
},
96-
"repositories": [
97-
{
98-
"type": "vcs",
99-
"url": "https://github.com/NyCodeGHG/laravel-bitflags"
100-
}
101-
]
94+
}
10295
}

0 commit comments

Comments
 (0)