PHP code example of openapi-tools / generator-psr-15-webhook-middleware

1. Go to this page and download the library: Download openapi-tools/generator-psr-15-webhook-middleware library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

openapi-tools / generator-psr-15-webhook-middleware example snippets


 declare (strict_types=1);
namespace ApiClients\Client\Example\Internal\WebHook;

final class Hydrator
{
    public function hydrate(string $className, array $data): object
    {
        return match ($className) {
            \ApiClients\Client\Example\Schema\HealthCheck::class => $this->hydrateSchemaHealthCheck($data),
            \ApiClients\Client\Example\Schema\InventoryUpdate::class => $this->hydrateSchemaInventoryUpdate($data),
            \ApiClients\Client\Example\Schema\AdoptedPet::class => $this->hydrateSchemaAdoptedPet($data),
            \ApiClients\Client\Example\Schema\SurrenderedPet::class => $this->hydrateSchemaSurrenderedPet($data),
            \ApiClients\Client\Example\Schema\WebhookStorePolicyCreated::class => $this->hydrateSchemaWebhookStorePolicyCreated($data),
            \ApiClients\Client\Example\Schema\WebhookStorePolicyDeleted::class => $this->hydrateSchemaWebhookStorePolicyDeleted($data),
            default => throw new \RuntimeException('Unknown webhook payload class'),
        };
    }
    private function hydrateSchemaHealthCheck(array $data): \ApiClients\Client\Example\Schema\HealthCheck
    {
        return new \ApiClients\Client\Example\Schema\HealthCheck(message: $data['message']);
    }
    private function hydrateSchemaInventoryUpdate(array $data): \ApiClients\Client\Example\Schema\InventoryUpdate
    {
        return new \ApiClients\Client\Example\Schema\InventoryUpdate(sku: $data['sku'], quantity: $data['quantity']);
    }
    private function hydrateSchemaAdoptedPet(array $data): \ApiClients\Client\Example\Schema\AdoptedPet
    {
        return new \ApiClients\Client\Example\Schema\AdoptedPet(eventType: $data['eventType'], name: $data['name']);
    }
    private function hydrateSchemaSurrenderedPet(array $data): \ApiClients\Client\Example\Schema\SurrenderedPet
    {
        return new \ApiClients\Client\Example\Schema\SurrenderedPet(eventType: $data['eventType'], name: $data['name']);
    }
    private function hydrateSchemaWebhookStorePolicyCreated(array $data): \ApiClients\Client\Example\Schema\WebhookStorePolicyCreated
    {
        return new \ApiClients\Client\Example\Schema\WebhookStorePolicyCreated(action: $data['action'], franchise: $data['franchise'] === null ? null : $this->hydrate(\ApiClients\Client\Example\Schema\FranchiseWebhooks::class, $data['franchise']), registration: $data['registration'] === null ? null : $this->hydrate(\ApiClients\Client\Example\Schema\StoreRegistration::class, $data['registration']), chain: $data['chain'] === null ? null : $this->hydrate(\ApiClients\Client\Example\Schema\ChainWebhooks::class, $data['chain']), store: $this->hydrate(\ApiClients\Client\Example\Schema\StoreWebhooks::class, $data['store']), policy: $this->hydrate(\ApiClients\Client\Example\Schema\PolicyRule::class, $data['policy']), customer: $data['customer'] === null ? null : $this->hydrate(\ApiClients\Client\Example\Schema\Customer::class, $data['customer']));
    }
    private function hydrateSchemaWebhookStorePolicyDeleted(array $data): \ApiClients\Client\Example\Schema\WebhookStorePolicyDeleted
    {
        return new \ApiClients\Client\Example\Schema\WebhookStorePolicyDeleted(action: $data['action'], franchise: $data['franchise'] === null ? null : $this->hydrate(\ApiClients\Client\Example\Schema\FranchiseWebhooks::class, $data['franchise']), registration: $data['registration'] === null ? null : $this->hydrate(\ApiClients\Client\Example\Schema\StoreRegistration::class, $data['registration']), chain: $data['chain'] === null ? null : $this->hydrate(\ApiClients\Client\Example\Schema\ChainWebhooks::class, $data['chain']), store: $this->hydrate(\ApiClients\Client\Example\Schema\StoreWebhooks::class, $data['store']), policy: $this->hydrate(\ApiClients\Client\Example\Schema\PolicyRule::class, $data['policy']), customer: $data['customer'] === null ? null : $this->hydrate(\ApiClients\Client\Example\Schema\Customer::class, $data['customer']));
    }
}

 declare (strict_types=1);
namespace ApiClients\Client\Example\Internal\WebHook;

enum Event
{
    case HealthCheck;
    case InventoryUpdate;
    case AdoptedPet;
    case SurrenderedPet;
    case WebhookStorePolicyCreated;
    case WebhookStorePolicyDeleted;
}

 declare (strict_types=1);
namespace ApiClients\Client\Example\Internal\WebHook;

final class WebHooks
{
    public function __construct(private readonly \League\OpenAPIValidation\Schema\SchemaValidator $requestSchemaValidator, private readonly \ApiClients\Client\Example\Internal\WebHook\Hydrator $hydrator)
    {
    }
    public function resolve(array $headers, array $data): \ApiClients\Client\Example\Schema\HealthCheck|\ApiClients\Client\Example\Schema\InventoryUpdate|\ApiClients\Client\Example\Schema\AdoptedPet|\ApiClients\Client\Example\Schema\SurrenderedPet|\ApiClients\Client\Example\Schema\WebhookStorePolicyCreated|\ApiClients\Client\Example\Schema\WebhookStorePolicyDeleted
    {
        $headers = (static function (array $headers): array {
            $loweredHeaders = [];
            foreach ($headers as $key => $value) {
                $loweredHeaders[strtolower($key)] = $value;
            }
            return $loweredHeaders;
        })($headers);
        return match ($this->detectDiscriminatedEvent($data)) {
            Event::AdoptedPet => $this->validatedHydrate(\ApiClients\Client\Example\Schema\AdoptedPet::class, $data),
            Event::SurrenderedPet => $this->validatedHydrate(\ApiClients\Client\Example\Schema\SurrenderedPet::class, $data),
            Event::WebhookStorePolicyCreated => $this->validatedHydrate(\ApiClients\Client\Example\Schema\WebhookStorePolicyCreated::class, $data),
            Event::WebhookStorePolicyDeleted => $this->validatedHydrate(\ApiClients\Client\Example\Schema\WebhookStorePolicyDeleted::class, $data),
            default => $this->resolveByHeaders($headers, $data),
        };
    }
    private function detectDiscriminatedEvent(array $data): ?Event
    {
        if (array_key_exists('eventType', $data)) {
            $event = match ($data['eventType']) {
                'adopted' => Event::AdoptedPet,
                'surrendered' => Event::SurrenderedPet,
                default => null,
            };
            if ($event instanceof Event) {
                return $event;
            }
        }
        if (array_key_exists('action', $data)) {
            $event = match ($data['action']) {
                'created' => Event::WebhookStorePolicyCreated,
                'deleted' => Event::WebhookStorePolicyDeleted,
                default => null,
            };
            if ($event instanceof Event) {
                return $event;
            }
        }
        return null;
    }
    private function resolveByHeaders(array $headers, array $data): \ApiClients\Client\Example\Schema\HealthCheck|\ApiClients\Client\Example\Schema\InventoryUpdate
    {
        $error = new \RuntimeException('No webhook matching given headers and data');
        try {
            if ($headers['x-petstore-event'] === 'healthCheck' && array_key_exists('message', $data)) {
                return $this->validatedHydrate(\ApiClients\Client\Example\Schema\HealthCheck::class, $data);
            }
        } catch (\Throwable $throwable) {
            $error = $throwable;
        }
        try {
            if ($headers['x-petstore-event'] === 'inventoryUpdate' && array_key_exists('sku', $data) && array_key_exists('quantity', $data)) {
                return $this->validatedHydrate(\ApiClients\Client\Example\Schema\InventoryUpdate::class, $data);
            }
        } catch (\Throwable $throwable) {
            $error = $throwable;
        }
        throw $error;
    }
    private function validatedHydrate(string $className, array $data): object
    {
        $this->requestSchemaValidator->validate($data, \cebe\openapi\Reader::readFromJson($className::SCHEMA_JSON, \cebe\openapi\spec\Schema::class));
        return $this->hydrator->hydrate(className::class, $data);
    }
}

 declare (strict_types=1);
namespace ApiClients\Client\Example;

use OpenAPITools\Contract\WebHookHandlerInterface;
use \ApiClients\Client\Example\Internal\WebHook\InvalidWebHookRequestException;
use \ApiClients\Client\Example\Internal\WebHook\WebHooks;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
final readonly class WebHookMiddleware implements Psr\Http\Server\MiddlewareInterface
{
    public function __construct(private readonly \ApiClients\Client\Example\Internal\WebHook\WebHooks $webHooks, private readonly \OpenAPITools\Contract\WebHookHandlerInterface $handler, private readonly array $paths = ['/webhook'])
    {
    }
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        if ($this->paths !== [] && !in_array($request->getUri()->getPath(), $this->paths, true)) {
            return $handler->handle($request);
        }
        $body = (string) $request->getBody();
        if ($body === '') {
            throw new InvalidWebHookRequestException('Missing webhook request body');
        }
        $data = json_decode($body, true);
        if (is_array($data) !== true) {
            throw new InvalidWebHookRequestException('Invalid webhook request body');
        }
        $headers = [];
        foreach ($request->getHeaders() as $name => $values) {
            $headers[strtolower($name)] = $values[0];
        }
        try {
            $payload = $this->webHooks->resolve($headers, $data);
        } catch (\Throwable $throwable) {
            throw new InvalidWebHookRequestException('Failed to resolve webhook', 0, $throwable);
        }
        return $this->handler->handle($payload);
    }
}

final readonly class MyWebHookHandler implements WebHookHandlerInterface
{
    public function handle(object $payload): ResponseInterface
    {
        return match ($payload::class) {
            Ping::class => $this->ping($payload),
            default => new EmptyResponse(404),
        };
    }
}

$middleware = new WebHookMiddleware(
    new Internal\WebHook\WebHooks($requestSchemaValidator, new Internal\WebHook\Hydrator()),
    new MyWebHookHandler(),
    paths: ['/webhook'],
);

use OpenAPITools\Generator\PSR15\WebHook\WebHookMiddleware;
use PhpParser\BuilderFactory;

$generator = new WebHookMiddleware(new BuilderFactory(), defaultPaths: ['/webhook']);

foreach ($generator->generate($package, $representation->namespace($package->namespace)) as $file) {
    // $file->pathPrefix  — e.g. "src"
    // $file->fqcn        — e.g. "WebHookMiddleware"
    // $file->contents    — PhpParser Node
}