feat(core/events): add WebhookRegistering lifecycle event (HIGH)
WebhookRegistering event exposes: - register(string $type, array $spec): add a webhook type to the registry - types(): array — queryable post-dispatch registry CoreServiceProvider dispatches the event at app boot and exposes the collected registry via webhookTypes() — matches the existing ApiRoutesRegistering / ConsoleBooting / ClientRoutesRegistering event-driven module pattern. Pairs with #1034 ofm.bot WebhookRegistrar (just landed) — that service can now also be wired through this event, allowing OTHER modules and external apps using Core to register webhook types via the standard Core lifecycle. Note: real Core lifecycle dispatcher lives in a sibling read-only framework checkout. CoreServiceProvider here is a local shim that mirrors the dispatch behaviour. Upstream patch needed when that sibling lands. Pest covers: instantiation + register, boot-time dispatch, post-boot registry lookup. Co-authored-by: Codex <noreply@openai.com> Closes tasks.lthn.sh/view.php?id=1013
This commit is contained in:
parent
b7bc526d50
commit
b0118ef8ef
3 changed files with 209 additions and 0 deletions
58
php/CoreServiceProvider.php
Normal file
58
php/CoreServiceProvider.php
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core;
|
||||
|
||||
use Core\Events\WebhookRegistering;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
/**
|
||||
* Core service provider shim for boot-time webhook type registration.
|
||||
*
|
||||
* Stores the collected webhook type registry in the container after the
|
||||
* application has fully booted so downstream services can resolve it.
|
||||
*/
|
||||
class CoreServiceProvider extends ServiceProvider
|
||||
{
|
||||
public const WEBHOOK_TYPES = 'core.webhook.types';
|
||||
|
||||
public function register(): void
|
||||
{
|
||||
$this->app->singleton(self::WEBHOOK_TYPES, static fn (): array => []);
|
||||
}
|
||||
|
||||
public function boot(): void
|
||||
{
|
||||
$this->app->booted(function (): void {
|
||||
$this->app->instance(self::WEBHOOK_TYPES, static::fireWebhookRegistering());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire WebhookRegistering and return the collected type registry.
|
||||
*
|
||||
* @return array<string, array<string, mixed>>
|
||||
*/
|
||||
public static function fireWebhookRegistering(): array
|
||||
{
|
||||
$event = new WebhookRegistering;
|
||||
event($event);
|
||||
|
||||
return $event->types();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the webhook type registry captured during boot.
|
||||
*
|
||||
* @return array<string, array<string, mixed>>
|
||||
*/
|
||||
public static function webhookTypes(): array
|
||||
{
|
||||
return app()->bound(self::WEBHOOK_TYPES)
|
||||
? app(self::WEBHOOK_TYPES)
|
||||
: [];
|
||||
}
|
||||
}
|
||||
67
php/Events/WebhookRegistering.php
Normal file
67
php/Events/WebhookRegistering.php
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Events;
|
||||
|
||||
/**
|
||||
* Fired when webhook ingress types are being registered.
|
||||
*
|
||||
* Modules and external applications listen to this event to publish the
|
||||
* webhook types they want Core to expose during application boot.
|
||||
*
|
||||
* ## When This Event Fires
|
||||
*
|
||||
* Fired once the application has booted and webhook ingress definitions are
|
||||
* being collected for the active runtime.
|
||||
*
|
||||
* ## Usage Example
|
||||
*
|
||||
* ```php
|
||||
* public static array $listens = [
|
||||
* WebhookRegistering::class => 'onWebhookRegistering',
|
||||
* ];
|
||||
*
|
||||
* public function onWebhookRegistering(WebhookRegistering $event): void
|
||||
* {
|
||||
* $event->register('myapp.event.x', [
|
||||
* 'payload' => [
|
||||
* 'id' => 'string',
|
||||
* ],
|
||||
* ]);
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class WebhookRegistering extends LifecycleEvent
|
||||
{
|
||||
/** @var array<string, array<string, mixed>> Collected webhook type definitions keyed by type */
|
||||
protected array $types = [];
|
||||
|
||||
/**
|
||||
* Register a webhook type definition.
|
||||
*
|
||||
* Later registrations with the same type replace earlier definitions so an
|
||||
* application can override defaults during boot.
|
||||
*
|
||||
* @param string $type Fully qualified webhook type name
|
||||
* @param array<string, mixed> $definition Payload shape or metadata for the type
|
||||
*/
|
||||
public function register(string $type, array $definition = []): void
|
||||
{
|
||||
$this->types[$type] = $definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered webhook type definitions.
|
||||
*
|
||||
* @return array<string, array<string, mixed>>
|
||||
*
|
||||
* @internal Used by the webhook ingress bootstrap
|
||||
*/
|
||||
public function types(): array
|
||||
{
|
||||
return $this->types;
|
||||
}
|
||||
}
|
||||
84
php/tests/Unit/Events/WebhookRegisteringTest.php
Normal file
84
php/tests/Unit/Events/WebhookRegisteringTest.php
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Core\CoreServiceProvider;
|
||||
use Core\Events\WebhookRegistering;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
|
||||
if (! class_exists(WebhookRegistering::class)) {
|
||||
require_once dirname(__DIR__, 3).'/Events/WebhookRegistering.php';
|
||||
}
|
||||
|
||||
if (! class_exists(CoreServiceProvider::class)) {
|
||||
require_once dirname(__DIR__, 3).'/CoreServiceProvider.php';
|
||||
}
|
||||
|
||||
test('WebhookRegistering_register_Good_collects_webhook_type_definitions', function (): void {
|
||||
$event = new WebhookRegistering;
|
||||
|
||||
$event->register('myapp.event.x', [
|
||||
'payload' => [
|
||||
'id' => 'string',
|
||||
],
|
||||
]);
|
||||
|
||||
expect($event->types())->toBe([
|
||||
'myapp.event.x' => [
|
||||
'payload' => [
|
||||
'id' => 'string',
|
||||
],
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
test('CoreServiceProvider_boot_Good_dispatches_webhook_registering', function (): void {
|
||||
$received = false;
|
||||
|
||||
Event::listen(WebhookRegistering::class, function (WebhookRegistering $event) use (&$received): void {
|
||||
$received = true;
|
||||
|
||||
$event->register('ofm.bot.message.received', [
|
||||
'payload' => [
|
||||
'message_id' => 'string',
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
$this->app->register(CoreServiceProvider::class);
|
||||
|
||||
expect($received)->toBeTrue();
|
||||
});
|
||||
|
||||
test('CoreServiceProvider_webhookTypes_Good_returns_registered_types_after_boot', function (): void {
|
||||
Event::listen(WebhookRegistering::class, function (WebhookRegistering $event): void {
|
||||
$event->register('ofm.bot.message.received', [
|
||||
'payload' => [
|
||||
'message_id' => 'string',
|
||||
],
|
||||
]);
|
||||
|
||||
$event->register('ofm.bot.message.deleted', [
|
||||
'payload' => [
|
||||
'message_id' => 'string',
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
$this->app->register(CoreServiceProvider::class);
|
||||
|
||||
expect(CoreServiceProvider::webhookTypes())->toBe([
|
||||
'ofm.bot.message.received' => [
|
||||
'payload' => [
|
||||
'message_id' => 'string',
|
||||
],
|
||||
],
|
||||
'ofm.bot.message.deleted' => [
|
||||
'payload' => [
|
||||
'message_id' => 'string',
|
||||
],
|
||||
],
|
||||
]);
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue