Skip to content

Commit 626ad36

Browse files
committed
wip
1 parent 31384ef commit 626ad36

File tree

8 files changed

+108
-66
lines changed

8 files changed

+108
-66
lines changed

.phpunit.cache/test-results

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

README.md

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
# Laravel Azure Event Hub
22

3-
[![PHPStan](https://github.com/codebar-ag/laravel-azure-event-hub/workflows/PHPStan%20Static%20Analysis/badge.svg)](https://github.com/codebar-ag/laravel-azure-event-hub/actions/workflows/phpstan.yml)
4-
[![PHP Version](https://img.shields.io/badge/PHP-8.4%2B-blue.svg)](https://php.net/)
3+
[![Packagist Version](https://img.shields.io/packagist/v/codebar-ag/laravel-azure-event-hub.svg)](https://packagist.org/packages/codebar-ag/laravel-azure-event-hub)
4+
[![Downloads](https://img.shields.io/packagist/dt/codebar-ag/laravel-azure-event-hub.svg)](https://packagist.org/packages/codebar-ag/laravel-azure-event-hub/stats)
5+
[![License](https://img.shields.io/packagist/l/codebar-ag/laravel-azure-event-hub.svg)](https://github.com/codebar-ag/laravel-azure-event-hub/blob/main/LICENSE)
6+
[![PHP Version](https://img.shields.io/badge/PHP-8.4%2B-blue.svg)](https://www.php.net/)
57
[![Laravel Version](https://img.shields.io/badge/Laravel-12.0%2B-red.svg)](https://laravel.com/)
68
[![Pest Tests](https://github.com/codebar-ag/laravel-azure-event-hub/workflows/Pest%20Tests/badge.svg)](https://github.com/codebar-ag/laravel-azure-event-hub/actions/workflows/pest.yml)
9+
[![PHPStan](https://github.com/codebar-ag/laravel-azure-event-hub/workflows/PHPStan%20Static%20Analysis/badge.svg)](https://github.com/codebar-ag/laravel-azure-event-hub/actions/workflows/phpstan.yml)
710

811
This package primarily provides an event_log functionality for logging HTTP requests and model events. It also provides a connector to send those events to Azure Event Hub, but you need to process them by yourself in the packages.
912

@@ -37,7 +40,7 @@ composer require codebar-ag/laravel-azure-event-hub
3740
php artisan vendor:publish --provider="CodebarAg\LaravelAzureEventHub\LaravelAzureEventHubServiceProvider"
3841
```
3942

40-
This will publish the configuration file to `config/azure-event-hub.php` where you can customize the package settings.
43+
This will publish the configuration file to `config/laravel-azure-event-hub.php` where you can customize the package settings.
4144

4245
## Usage
4346

@@ -48,7 +51,7 @@ The `EventLogMiddleware` automatically logs all HTTP requests. Add it to your La
4851
#### Configuration
4952

5053
```php
51-
// config/azure-event-hub.php
54+
// config/laravel-azure-event-hub.php
5255
return [
5356
'exclude_routes' => [
5457
'livewire.update',
@@ -125,12 +128,12 @@ The package provides an action to send event logs to Azure Event Hub. You can pr
125128
Add your Azure Event Hub credentials to the published config file:
126129

127130
```php
128-
// config/azure-event-hub.php
131+
// config/laravel-azure-event-hub.php
129132
return [
130-
'enabled' => env('AZURE_EVENT_HUB_ENABLED', true),
133+
'enabled' => env('AZURE_EVENT_HUB_ENABLED', false),
131134
'endpoint' => env('AZURE_EVENT_HUB_ENDPOINT'),
132135
'path' => env('AZURE_EVENT_HUB_PATH'),
133-
'policy_name' => env('AZURE_EVENT_HUB_POLICY_NAME'),
136+
'policy_name' => env('AZURE_EVENT_HUB_POLICY'),
134137
'primary_key' => env('AZURE_EVENT_HUB_PRIMARY_KEY'),
135138
// ... other config options
136139
];
@@ -142,10 +145,24 @@ return [
142145
AZURE_EVENT_HUB_ENABLED=true
143146
AZURE_EVENT_HUB_ENDPOINT=https://your-namespace.servicebus.windows.net
144147
AZURE_EVENT_HUB_PATH=your-event-hub-name
145-
AZURE_EVENT_HUB_POLICY_NAME=RootManageSharedAccessKey
148+
AZURE_EVENT_HUB_POLICY=RootManageSharedAccessKey
146149
AZURE_EVENT_HUB_PRIMARY_KEY=your-primary-key
147150
```
148151

152+
#### Available Methods
153+
154+
- **`(new AzureEventHubAction())->send(EventLog $eventLog): void`**: Sends a single `CodebarAg\LaravelAzureEventHub\Models\EventLog` to Azure Event Hub using the REST API. The payload is the model's `toArray()` encoded as JSON and sent to `.../messages?api-version=2014-01` with a SAS token in the `Authorization` header. Returns `void`. Recommended to guard calls with `config('laravel-azure-event-hub.enabled')` and to handle idempotency (e.g., `synced_at`) in your job.
155+
156+
Minimal usage example:
157+
158+
```php
159+
use CodebarAg\LaravelAzureEventHub\Actions\AzureEventHubAction;
160+
use CodebarAg\LaravelAzureEventHub\Models\EventLog;
161+
162+
// Send one event (instance API)
163+
(new AzureEventHubAction())->sendEvent($eventLog); // $eventLog is an instance of EventLog
164+
```
165+
149166
#### Example Implementation
150167

151168
Create a job to process and send event logs to Azure Event Hub:
@@ -170,7 +187,7 @@ class ProcessAzureEventJob implements ShouldQueue
170187

171188
public function handle(): void
172189
{
173-
$enabled = config('azure-event-hub.enabled');
190+
$enabled = config('laravel-azure-event-hub.enabled');
174191

175192
if (!$enabled) {
176193
return;
@@ -180,8 +197,8 @@ class ProcessAzureEventJob implements ShouldQueue
180197
return;
181198
}
182199

183-
// Send to Azure Event Hub using the DTO
184-
AzureEventHubAction::send(eventLog: $this->eventLog);
200+
// Send to Azure Event Hub
201+
(new AzureEventHubAction())->sendEvent(eventLog: $this->eventLog);
185202

186203
// Mark as synced
187204
$this->eventLog->update([

phpstan.neon

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,8 @@ parameters:
22
level: 9
33
paths:
44
- src
5-
excludePaths:
6-
- tests
75
treatPhpDocTypesAsCertain: false
86
ignoreErrors:
9-
- '#Class.*uses generic trait Illuminate\\Database\\Eloquent\\Factories\\HasFactory but does not specify its types#'
10-
- '#Function config not found#'
11-
- '#Function config_path not found#'
12-
- '#Call to method getName\(\) on an unknown class Illuminate\\Routing\\Route#'
13-
- '#Call to an undefined static method CodebarAg\\LaravelAzureEventHub\\Models\\EventLog::create\(\)#'
14-
- '#Access to an undefined property Illuminate\\Contracts\\Auth\\Authenticatable::\$id#'
15-
- '#Trait CodebarAg\\LaravelAzureEventHub\\Traits\\HasEventLogTrait is used zero times#'
7+
-
8+
identifier: trait.unused
9+
path: src/Traits/HasEventLogTrait.php

src/Actions/AzureEventHubAction.php

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,43 +7,25 @@
77

88
class AzureEventHubAction
99
{
10-
/**
11-
* Send one event to Azure Event Hubs via REST.
12-
*
13-
* @param EventLog $event
14-
*/
15-
public static function send(EventLog $eventLog): void
16-
{
17-
$endpoint = rtrim((string) config('laravel-azure-event-hub.endpoint'), '/');
18-
$hub = trim((string) config('laravel-azure-event-hub.path'), '/');
10+
private string $endpoint;
1911

20-
$resource = self::resource($endpoint, $hub);
21-
$postUrl = "{$resource}/messages?api-version=2014-01";
12+
private string $hub;
2213

23-
$token = self::token($resource);
14+
private string $policy;
2415

25-
Http::retry(3, 500)
26-
->withHeaders([
27-
'Authorization' => $token,
28-
'Content-Type' => 'application/json',
29-
])
30-
->withBody(json_encode($eventLog->toArray(), JSON_UNESCAPED_SLASHES) ?: '{}', 'application/json')
31-
->post($postUrl);
32-
}
16+
private string $primaryKey;
3317

34-
/**
35-
* Build (cached) SAS token for the given Event Hub resource.
36-
*/
37-
public static function token(string $resource): string
18+
public function __construct()
3819
{
39-
$policy = (string) config('laravel-azure-event-hub.policy_name');
40-
$key = (string) config('laravel-azure-event-hub.primary_key');
41-
$encodedResource = rawurlencode($resource);
42-
$expiry = time() + 7200; // 1 hour
43-
$stringToSign = $encodedResource."\n".$expiry;
44-
$signature = rawurlencode(base64_encode(hash_hmac('sha256', $stringToSign, $key, true)));
20+
$endpointConfig = config('laravel-azure-event-hub.endpoint');
21+
$hubConfig = config('laravel-azure-event-hub.path');
22+
$policyConfig = config('laravel-azure-event-hub.policy_name');
23+
$keyConfig = config('laravel-azure-event-hub.primary_key');
4524

46-
return "SharedAccessSignature sr={$encodedResource}&sig={$signature}&se={$expiry}&skn={$policy}";
25+
$this->endpoint = is_string($endpointConfig) ? rtrim($endpointConfig, '/') : '';
26+
$this->hub = is_string($hubConfig) ? trim($hubConfig, '/') : '';
27+
$this->policy = is_string($policyConfig) ? $policyConfig : '';
28+
$this->primaryKey = is_string($keyConfig) ? $keyConfig : '';
4729
}
4830

4931
private static function resource(string $endpoint, string $hub): string
@@ -55,4 +37,34 @@ private static function resource(string $endpoint, string $hub): string
5537

5638
return "{$scheme}://{$host}/{$hub}";
5739
}
40+
41+
public function resourceUrl(): string
42+
{
43+
return self::resource($this->endpoint, $this->hub);
44+
}
45+
46+
public function buildToken(): string
47+
{
48+
$resource = $this->resourceUrl();
49+
$encodedResource = rawurlencode($resource);
50+
$expiry = time() + 7200; // 1 hour
51+
$stringToSign = $encodedResource."\n".$expiry;
52+
$signature = rawurlencode(base64_encode(hash_hmac('sha256', $stringToSign, $this->primaryKey, true)));
53+
54+
return "SharedAccessSignature sr={$encodedResource}&sig={$signature}&se={$expiry}&skn={$this->policy}";
55+
}
56+
57+
public function send(EventLog $eventLog): void
58+
{
59+
$resource = $this->resourceUrl();
60+
$postUrl = "{$resource}/messages?api-version=2014-01";
61+
62+
Http::retry(3, 500)
63+
->withHeaders([
64+
'Authorization' => $this->buildToken(),
65+
'Content-Type' => 'application/json',
66+
])
67+
->withBody(json_encode($eventLog->toArray(), JSON_UNESCAPED_SLASHES) ?: '{}', 'application/json')
68+
->post($postUrl);
69+
}
5870
}

src/DTO/AzureEventHubDTO.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public static function fromEventLog(EventLog $eventLog): self
4949
event: $eventLog->event instanceof EventLogEventEnum ? $eventLog->event : null,
5050
event_data: $eventLog->event_data,
5151
context: $eventLog->context,
52-
created_at: $eventLog->created_at ? $eventLog->created_at->toDateTimeString() : null,
52+
created_at: $eventLog->created_at->toDateTimeString(),
5353
);
5454
}
5555

src/Middleware/EventLogMiddleware.php

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,34 @@ public function handle(Request $request, Closure $next): Response
2323
$user = Auth::user();
2424

2525
// Allow disabling via config; default true in package tests
26-
$enabled = config('laravel-azure-event-hub.enabled', true);
26+
$enabled = (bool) config('laravel-azure-event-hub.enabled', true);
2727
if (! $enabled) {
2828
return $next($request);
2929
}
3030

31-
$excludeRoutes = config('laravel-azure-event-hub.exclude_routes', []);
31+
$excludeRoutesConfig = config('laravel-azure-event-hub.exclude_routes', []);
32+
$excludeRoutes = is_array($excludeRoutesConfig)
33+
? array_values(array_filter($excludeRoutesConfig, static fn ($v): bool => is_string($v)))
34+
: [];
3235
$route = $request->route();
33-
$currentRouteName = $route ? $route->getName() : null;
36+
$currentRouteNameRaw = $route ? $route->getName() : null;
37+
$currentRouteName = is_string($currentRouteNameRaw) ? $currentRouteNameRaw : null;
3438

35-
if ($currentRouteName && in_array($currentRouteName, $excludeRoutes)) {
39+
if ($currentRouteName !== null && in_array($currentRouteName, $excludeRoutes, true)) {
3640
return $next($request);
3741
}
3842

39-
$requestHeadersToRemove = config('laravel-azure-event-hub.sanitize.request_headers_exclude', []);
40-
$requestDataToRemove = config('laravel-azure-event-hub.sanitize.request_data_exclude', []);
43+
$headersExcludeConfig = config('laravel-azure-event-hub.sanitize.request_headers_exclude', []);
44+
$dataExcludeConfig = config('laravel-azure-event-hub.sanitize.request_data_exclude', []);
45+
46+
/** @var array<int, string> $requestHeadersToRemove */
47+
$requestHeadersToRemove = is_array($headersExcludeConfig)
48+
? array_values(array_filter($headersExcludeConfig, static fn ($v): bool => is_string($v)))
49+
: [];
50+
/** @var array<int, string> $requestDataToRemove */
51+
$requestDataToRemove = is_array($dataExcludeConfig)
52+
? array_values(array_filter($dataExcludeConfig, static fn ($v): bool => is_string($v)))
53+
: [];
4154

4255
EventLog::create([
4356
'type' => EventLogTypeEnum::HTTP,

src/Models/EventLog.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
/**
1414
* @property int $id
1515
* @property string $uuid
16-
* @property EventLogTypeEnum $type
16+
* @property EventLogTypeEnum|null $type
1717
* @property string|null $subject_type
1818
* @property int|null $subject_id
1919
* @property string|null $user_type
@@ -30,9 +30,14 @@
3030
* @property \Carbon\Carbon|null $synced_at
3131
* @property \Carbon\Carbon $created_at
3232
* @property \Carbon\Carbon $updated_at
33+
*
34+
* @use HasFactory<EventLogFactory>
3335
*/
3436
class EventLog extends Model
3537
{
38+
/**
39+
* @use HasFactory<EventLogFactory>
40+
*/
3641
use HasFactory;
3742

3843
protected $fillable = [
@@ -54,7 +59,7 @@ class EventLog extends Model
5459
'synced_at',
5560
];
5661

57-
/** @var array<string, mixed> */
62+
/** @var array<string, string> */
5863
protected $casts = [
5964
'request_headers' => 'array',
6065
'request_data' => 'array',
@@ -83,7 +88,7 @@ public function toArray(): array
8388
/**
8489
* Create a new factory instance for the model.
8590
*/
86-
protected static function newFactory()
91+
protected static function newFactory(): EventLogFactory
8792
{
8893
return EventLogFactory::new();
8994
}

tests/Unit/Actions/AzureEventHubActionTest.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
use Illuminate\Support\Facades\Http;
88

99
test('azure event hub action can generate token', function () {
10-
$resource = 'https://test-namespace.servicebus.windows.net/test-event-hub';
11-
$token = AzureEventHubAction::token($resource);
10+
$token = (new AzureEventHubAction)->buildToken();
1211

1312
expect($token)->toContain('SharedAccessSignature');
1413
expect($token)->toContain('sr=');
@@ -33,7 +32,7 @@
3332
]);
3433

3534
// This should not throw an exception
36-
expect(fn () => AzureEventHubAction::send($eventLog))->not->toThrow(\Throwable::class);
35+
expect(fn () => (new AzureEventHubAction)->send($eventLog))->not->toThrow(\Throwable::class);
3736
});
3837

3938
test('azure event hub action handles malformed data gracefully', function () {
@@ -42,10 +41,12 @@
4241
'https://test-namespace.servicebus.windows.net/test-event-hub/messages*' => Http::response('OK', 200),
4342
]);
4443

44+
/** @var mixed $malformedData */
4545
$malformedData = null;
4646

4747
// Should handle null data gracefully
48-
expect(fn () => AzureEventHubAction::send($malformedData))->toThrow(\TypeError::class);
48+
expect(fn () => (new AzureEventHubAction)->send($malformedData)) // @phpstan-ignore-line
49+
->toThrow(\TypeError::class);
4950
});
5051

5152
test('azure event hub configuration is properly loaded', function () {

0 commit comments

Comments
 (0)