diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ef5d1bf..5b30049 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,8 +17,6 @@ jobs: - "lowest" - "highest" php-version: - - "8.1" - - "8.2" - "8.3" operating-system: - "ubuntu-latest" diff --git a/composer.json b/composer.json index 5a00005..be9da1a 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,6 @@ "require": { "php": "^8.3", "juststeveking/php-sdk": "^3.0", - "treblle/cloudevent-php": "dev-main", "thecodingmachine/safe": "^2.5" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 5a094ab..b2030bb 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c99bc21a1fc4450bdbde269c56b6fbce", + "content-hash": "aa3cd8f317d10bfee25600d6f23eba05", "packages": [ { "name": "clue/stream-filter", @@ -1845,61 +1845,6 @@ "source": "https://github.com/thecodingmachine/safe/tree/v2.5.0" }, "time": "2023-04-05T11:54:14+00:00" - }, - { - "name": "treblle/cloudevent-php", - "version": "dev-main", - "source": { - "type": "git", - "url": "https://github.com/Treblle/cloudevent-php.git", - "reference": "83fe1b2d0c936ff7276b108bb0220a3134b39413" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Treblle/cloudevent-php/zipball/83fe1b2d0c936ff7276b108bb0220a3134b39413", - "reference": "83fe1b2d0c936ff7276b108bb0220a3134b39413", - "shasum": "" - }, - "require": { - "php": "^8.2", - "php-http/discovery": "^1.19", - "psr/http-factory": "^1.1", - "psr/http-message": "^2.0" - }, - "require-dev": { - "laravel/pint": "^1.15", - "phpstan/phpstan": "^1.10", - "phpstan/phpstan-strict-rules": "^1.5", - "phpunit/phpunit": "^11.1", - "roave/security-advisories": "dev-latest" - }, - "default-branch": true, - "type": "package", - "autoload": { - "psr-4": { - "Treblle\\CloudEvent\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Steve McDougall", - "email": "juststevemcd@gmail.com", - "homepage": "https://www.juststeveking.uk/", - "role": "Developer" - } - ], - "description": "Create Cloud Events in PHP.", - "homepage": "https://docs.treblle.com/", - "support": { - "email": "support@treblle.com", - "issues": "https://github.com/treblle/runtime-php/issues", - "source": "https://github.com/treblle/runtime-php" - }, - "time": "2024-05-15T12:09:35+00:00" } ], "packages-dev": [ @@ -4732,7 +4677,6 @@ "aliases": [], "minimum-stability": "dev", "stability-flags": { - "treblle/cloudevent-php": 20, "roave/security-advisories": 20 }, "prefer-stable": true, diff --git a/phpunit.xml b/phpunit.xml index 0c12bb9..8b001db 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -3,6 +3,7 @@ xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.3/phpunit.xsd" bootstrap="vendor/autoload.php" colors="true" + cacheResult="false" > diff --git a/pint.json b/pint.json index 0941efc..4a66495 100644 --- a/pint.json +++ b/pint.json @@ -14,7 +14,7 @@ "declare_parentheses": true, "declare_strict_types": true, "explicit_string_variable": true, - "final_class": true, + "final_class": false, "fully_qualified_strict_types": true, "global_namespace_import": { "import_classes": true, diff --git a/src/Contracts/MaskingEngineContract.php b/src/Contracts/MaskingEngineContract.php index 5eef7b6..8da86dd 100644 --- a/src/Contracts/MaskingEngineContract.php +++ b/src/Contracts/MaskingEngineContract.php @@ -6,5 +6,5 @@ interface MaskingEngineContract { - public function mask(array $payload): array; + public function mask(array $payload, string $type = 'body'): array; } diff --git a/src/DataObjects/Config.php b/src/DataObjects/Config.php index 99ffbca..321b4e5 100644 --- a/src/DataObjects/Config.php +++ b/src/DataObjects/Config.php @@ -4,6 +4,10 @@ namespace Treblle\Runtime\DataObjects; +/** + * @package Treblle Runtime + * @author Steve McDougall + */ final readonly class Config { /** @@ -11,11 +15,13 @@ * @param string $project_id * @param array $ignored_environments * @param array $masking + * @param array $headers */ public function __construct( public string $api_key, public string $project_id, public array $ignored_environments, public array $masking, + public array $headers, ) {} } diff --git a/src/Factories/ConfigFactory.php b/src/Factories/ConfigFactory.php index 98b3049..9299dad 100644 --- a/src/Factories/ConfigFactory.php +++ b/src/Factories/ConfigFactory.php @@ -14,6 +14,7 @@ final class ConfigFactory * project_id:string, * ignored_environments:array, * masking:array, + * headers:array, * } $config * @return Config */ @@ -24,6 +25,7 @@ public static function make(array $config): Config project_id: $config['project_id'], ignored_environments: $config['ignored_environments'], masking: $config['masking'], + headers: $config['headers'], ); } } diff --git a/src/Masking/MaskingEngine.php b/src/Masking/MaskingEngine.php index 1e3208b..0dda8c9 100644 --- a/src/Masking/MaskingEngine.php +++ b/src/Masking/MaskingEngine.php @@ -15,9 +15,32 @@ public function __construct( private Config $config, ) {} - public function mask(array $payload): array + public function mask(array $payload, string $type = 'body'): array { - return $this->maskArray($payload, []); + if ('body' === $type) { + return $this->maskArray($payload, []); + } + + if('headers' === $type) { + return $this->maskHeaders($payload); + } + + return $payload; + } + + private function maskHeaders(array $payload): array + { + $data = []; + + foreach ($payload as $key => $value) { + if (in_array($key, $this->config->headers)) { + $data[$key] = (new StringMatcher())->input((string) $value)->mask(); + } else { + $data[$key] = $value; + } + } + + return $data; } private function maskArray(array $data, array $path): array @@ -49,18 +72,23 @@ private function shouldMask(string $dotNotationKey): bool return true; } } + return false; } private function getMaskerClass(string $dotNotationKey): string { + /** + * @var string $pattern + * @var string $maskerClass + */ foreach ($this->config->masking as $pattern => $maskerClass) { $regex = $this->convertPatternToRegex($pattern); if (preg_match($regex, $dotNotationKey)) { return $maskerClass; } } - throw new \RuntimeException("No masker class found for key: $dotNotationKey"); + throw new RuntimeException("No masker class found for key: {$dotNotationKey}"); } private function convertPatternToRegex(string $pattern): string diff --git a/src/Masking/StringMatcher.php b/src/Masking/StringMatcher.php index c347d6c..c5bcf8d 100644 --- a/src/Masking/StringMatcher.php +++ b/src/Masking/StringMatcher.php @@ -8,7 +8,7 @@ class StringMatcher implements MasksInput { - private string $pattern; + protected string $pattern; protected string $input; diff --git a/src/Runtime.php b/src/Runtime.php index 96f0dda..eaa13bc 100644 --- a/src/Runtime.php +++ b/src/Runtime.php @@ -7,81 +7,52 @@ use function array_merge; use Fiber; -use Http\Message\Authentication\Header; use Throwable; use Treblle\Runtime\Contracts\MaskingEngineContract; -use Treblle\Runtime\DataObjects\Config; -use Treblle\Runtime\Support\PHP; -use Treblle\Runtime\Support\System; -use Treblle\Runtime\Transport\Payloads\PendingData; -use Treblle\Runtime\Transport\Payloads\PendingPayload; -use Treblle\Runtime\Transport\Payloads\Request; -use Treblle\Runtime\Transport\Payloads\Response; use Treblle\Runtime\Transport\Treblle; -final class Runtime +/** + * @package Treblle Runtime + * @author Steve McDougall + */ +class Runtime { public const string VERSION = '0.0.1'; /** * @param Treblle $transport The HTTP Transport that is being used. - * @param PendingPayload $payload // the Pending Payload for ingress * @param MaskingEngineContract $maskingEngine // the configured masking engine * @param int|float $start // when did this request start */ public function __construct( - private readonly Treblle $transport, - private readonly PendingPayload $payload, - private readonly MaskingEngineContract $maskingEngine, + public readonly Treblle $transport, + public readonly MaskingEngineContract $maskingEngine, public int|float $start = 0, - ) { - } - - /** - * Create a new pending payload - */ - public static function payload(Config $config): PendingPayload - { - return new PendingPayload( - api_key: $config->api_key, - project_id: $config->project_id, - version: self::VERSION, - sdk: 'php', - data: new PendingData( - server: System::server(), - language: PHP::language(), - ), - ); - } + public array $data = [], + ) {} /** * Add the request data to the payload data. * - * @param array{ - * timestamp:string, - * ip:string, - * url:string, - * user_agent:string, - * method:string, - * headers:Header, - * body:array|object - * } $data + * @param array $data * @return $this */ public function request(array $data): Runtime { - if (null !== $this->payload->data) { - $this->payload->data = $this->payload->data?->request( - request: Request::make( - data: array_merge( - $data, - ['body' => $this->maskingEngine->mask( - payload: $data['body'], - )], - ), - ), - ); - } + $this->data['request'] = array_merge( + $this->data['request'] ?? $this->data, + array_merge( + $data, + ['body' => $this->maskingEngine->mask( + payload: (array) $data['body'], + type: 'body', + )], + ['headers' => $this->maskingEngine->mask( + payload: (array) $data['headers'], + type: 'headers', + )], + ), + ); return $this; } @@ -89,30 +60,25 @@ public function request(array $data): Runtime /** * Add the response data to the payload data. * - * @param array{ - * headers:array, - * code:int, - * size:int|string, - * load_time:null|int|float, - * memory_usage:null|int|float, - * body:string|array|object|null, - * } $data + * @param array $data * @return $this */ public function response(array $data): Runtime { - if (null !== $this->payload->data) { - $this->payload->data = $this->payload->data->response( - response: Response::make( - data: array_merge( - $data, - ['body' => $this->maskingEngine->mask( - payload: $data['body'], - )], - ), - ), - ); - } + $this->data['response'] = array_merge( + $this->data['response'] ?? $this->data, + array_merge( + $data, + ['body' => $this->maskingEngine->mask( + payload: (array) $data['body'], + type: 'body', + )], + ['headers' => $this->maskingEngine->mask( + payload: (array) $data['headers'], + type: 'headers', + )], + ), + ); return $this; } @@ -124,7 +90,7 @@ public function process(): void { try { $fiber = new Fiber( - callback: fn () => $this->send(), + callback: fn() => $this->send(), ); $fiber->start(); @@ -140,8 +106,9 @@ public function process(): void public function send(): void { $this->transport->ingress( - payload: $this->payload->build( - loadtime: microtime(true) - $this->start, + payload: array_merge( + $this->data, + ['load_time' => microtime(true) - $this->start], ), ); } diff --git a/src/Support/PHP.php b/src/Support/PHP.php index 44d13be..3702632 100644 --- a/src/Support/PHP.php +++ b/src/Support/PHP.php @@ -8,8 +8,6 @@ use Throwable; -use Treblle\Runtime\Transport\Payloads\Language; - final class PHP { public static function name(): string @@ -36,16 +34,6 @@ public static function displayErrors(): string ); } - public static function language(): Language - { - return new Language( - name: self::name(), - version: self::version(), - expose_php: self::exposePHP(), - display_errors: self::displayErrors(), - ); - } - public static function ini(string $arg): string { try { diff --git a/src/Support/System.php b/src/Support/System.php index d8ee99b..d14c5e0 100644 --- a/src/Support/System.php +++ b/src/Support/System.php @@ -6,9 +6,6 @@ use function php_uname; -use Treblle\Runtime\Transport\Payloads\Os; -use Treblle\Runtime\Transport\Payloads\Server; - final class System { public static function ip(): string @@ -85,23 +82,4 @@ public static function fromGlobal(array $global, string $arg): string { return $global[$arg] ?? ''; } - - public static function server(): Server - { - return new Server( - ip: self::ip(), - timezone: self::timezone(), - os: new Os( - name: self::name(), - host: self::host(), - version: self::version(), - release: self::release(), - architecture: self::architecture(), - ), - software: self::software(), - signature: self::signature(), - protocol: self::protocol(), - encoding: self::encoding(), - ); - } } diff --git a/src/Transport/Payloads/Data.php b/src/Transport/Payloads/Data.php deleted file mode 100644 index 972b63b..0000000 --- a/src/Transport/Payloads/Data.php +++ /dev/null @@ -1,80 +0,0 @@ - $errors - */ - public function __construct( - public Server $server, - public Language $language, - public Request $request, - public Response $response, - public array $errors, - ) {} - - /** - * @param array{ - * server:array, - * language:array, - * request:array, - * response:array, - * errors:null|array - * } $data - * @return Data - */ - public static function make(array $data): Data - { - return new Data( - server: Server::make( - data: $data['server'], - ), - language: Language::make( - data: $data['language'], - ), - request: Request::make( - data: $data['request'], - ), - response: Response::make( - data: $data['response'], - ), - errors: $data['errors'] ? array_map( - callback: static fn(array $error): Error => Error::make( - data: $error, - ), - array: $data['errors'], - ) : [], - ); - } - - /** - * @return array{ - * server:array, - * language:array, - * request:array, - * response:array, - * errors:array - * } - */ - public function toArray(): array - { - return [ - 'server' => $this->server->toArray(), - 'language' => $this->language->toArray(), - 'request' => $this->request->toArray(), - 'response' => $this->response->toArray(), - 'errors' => array_map( - callback: static fn(Error $error): array => $error->toArray(), - array: $this->errors, - ), - ]; - } -} diff --git a/src/Transport/Payloads/Error.php b/src/Transport/Payloads/Error.php deleted file mode 100644 index 646b412..0000000 --- a/src/Transport/Payloads/Error.php +++ /dev/null @@ -1,64 +0,0 @@ - $this->source, - 'type' => $this->type, - 'message' => $this->message, - 'file' => $this->file, - 'line' => $this->line, - ]; - } -} diff --git a/src/Transport/Payloads/Headers.php b/src/Transport/Payloads/Headers.php deleted file mode 100644 index 044786e..0000000 --- a/src/Transport/Payloads/Headers.php +++ /dev/null @@ -1,58 +0,0 @@ - $this->content_type, - 'content-length' => $this->content_length, - 'host' => $this->host, - 'user-agent' => $this->user_agent, - ]; - } -} diff --git a/src/Transport/Payloads/Language.php b/src/Transport/Payloads/Language.php deleted file mode 100644 index 201945b..0000000 --- a/src/Transport/Payloads/Language.php +++ /dev/null @@ -1,58 +0,0 @@ - $this->name, - 'version' => $this->version, - 'expose_php' => $this->expose_php, - 'display_errors' => $this->display_errors, - ]; - } -} diff --git a/src/Transport/Payloads/Os.php b/src/Transport/Payloads/Os.php deleted file mode 100644 index 58e480b..0000000 --- a/src/Transport/Payloads/Os.php +++ /dev/null @@ -1,64 +0,0 @@ - $this->name, - 'host' => $this->host, - 'version' => $this->version, - 'release' => $this->release, - 'architecture' => $this->architecture, - ]; - } -} diff --git a/src/Transport/Payloads/Payload.php b/src/Transport/Payloads/Payload.php deleted file mode 100644 index 98f25ee..0000000 --- a/src/Transport/Payloads/Payload.php +++ /dev/null @@ -1,83 +0,0 @@ -, - * load_time:null|int|float - * } $data - * @return Payload - */ - public static function make(array $data): Payload - { - return new Payload( - api_key: $data['api_key'], - project_id: $data['project_id'], - version: $data['version'], - sdk: $data['sdk'], - data: $data['data'], - load_time: $data['load_time'] ?? 0, - ); - } - - /** - * @return array{ - * api_key: string, - * project_id: string, - * version:string, - * sdk:string, - * data:array, - * load_time:int|float, - * } - */ - public function toArray(): array - { - return [ - 'api_key' => $this->api_key, - 'project_id' => $this->project_id, - 'version' => $this->version, - 'sdk' => $this->sdk, - 'data' => $this->data->toArray(), - 'load_time' => $this->load_time, - ]; - } - - public function toStream(): StreamInterface - { - return Psr17FactoryDiscovery::findStreamFactory()->createStream( - content: (string) json_encode( - value: $this->toArray(), - flags: JSON_THROW_ON_ERROR, - ), - ); - } -} diff --git a/src/Transport/Payloads/PendingData.php b/src/Transport/Payloads/PendingData.php deleted file mode 100644 index 9e9ea76..0000000 --- a/src/Transport/Payloads/PendingData.php +++ /dev/null @@ -1,62 +0,0 @@ -server = $server; - - return $this; - } - - public function language(Language $language): PendingData - { - $this->language = $language; - - return $this; - } - - public function request(Request $request): PendingData - { - $this->request = $request; - - return $this; - } - - public function response(Response $response): PendingData - { - $this->response = $response; - - return $this; - } - - public function errors(array $errors): PendingData - { - $this->errors = $errors; - - return $this; - } - - public function build(): Data - { - return new Data( - server: $this->server, - language: $this->language, - request: $this->request, - response: $this->response, - errors: $this->errors, - ); - } -} diff --git a/src/Transport/Payloads/PendingPayload.php b/src/Transport/Payloads/PendingPayload.php deleted file mode 100644 index d4000ac..0000000 --- a/src/Transport/Payloads/PendingPayload.php +++ /dev/null @@ -1,49 +0,0 @@ -version = $version; - - return $this; - } - - public function sdk(string $sdk): PendingPayload - { - $this->sdk = $sdk; - - return $this; - } - - public function data(PendingData $data): PendingPayload - { - $this->data = $data; - - return $this; - } - - public function build(int|float $loadtime): Payload - { - return new Payload( - api_key: $this->api_key, - project_id: $this->project_id, - version: $this->version, - sdk: $this->sdk, - data: $this->data->build(), - load_time: $loadtime, - ); - } -} diff --git a/src/Transport/Payloads/Request.php b/src/Transport/Payloads/Request.php deleted file mode 100644 index 565873c..0000000 --- a/src/Transport/Payloads/Request.php +++ /dev/null @@ -1,77 +0,0 @@ -|object $body - */ - public function __construct( - public string $timestamp, - public string $ip, - public string $url, - public string $user_agent, - public string $method, - public Headers $headers, - public array|object|null $body, - ) {} - - /** - * @param array{ - * timestamp:string, - * ip:string, - * url:string, - * user_agent:string, - * method:string, - * headers:array, - * body:array|object|null, - * } $data - * @return Request - */ - public static function make(array $data): Request - { - return new Request( - timestamp: $data['timestamp'], - ip: $data['ip'], - url: $data['url'], - user_agent: $data['user_agent'], - method: $data['method'], - headers: Headers::make( - data: $data['headers'], - ), - body: $data['body'] ?? null, - ); - } - - /** - * @return array{ - * timestamp:string, - * ip:string, - * user-agent:string, - * method:string, - * headers:array, - * body:array|object|null - * } - */ - public function toArray(): array - { - return [ - 'timestamp' => $this->timestamp, - 'ip' => $this->ip, - 'url' => $this->url, - 'user-agent' => $this->user_agent, - 'method' => $this->method, - 'headers' => $this->headers->toArray(), - 'body' => $this->body, - ]; - } -} diff --git a/src/Transport/Payloads/Response.php b/src/Transport/Payloads/Response.php deleted file mode 100644 index 51cff05..0000000 --- a/src/Transport/Payloads/Response.php +++ /dev/null @@ -1,70 +0,0 @@ -|object $headers - * @param int $code - * @param int|string $size - * @param null|int|float $load_time - * @param null|int|float $memory_usage - * @param string|array|object|null $body - */ - public function __construct( - public array|object $headers, - public int $code, - public int|string $size, - public null|int|float $load_time, - public null|int|float $memory_usage, - public string|array|object|null $body, - ) {} - - /** - * @param array{ - * headers:array|object, - * code:int, - * size:int|string, - * load_time:null|int|float, - * memory_usage:null|int|float, - * body:string|array|object|null, - * } $data - * @return Response - */ - public static function make(array $data): Response - { - return new Response( - headers: $data['headers'], - code: $data['code'], - size: $data['size'], - load_time: $data['load_time'] ?? null, - memory_usage: $data['memory_usage'] ?? null, - body: $data['body'] ?? null, - ); - } - - /** - * @return array{ - * headers:array|object, - * code:int, - * size:int|string, - * load_time:null|int|float, - * memory_usage:null|int|float, - * body:string|array|object|null - * } - */ - public function toArray(): array - { - return [ - 'headers' => $this->headers, - 'code' => $this->code, - 'size' => $this->size, - 'load_time' => $this->load_time, - 'memory_usage' => $this->memory_usage, - 'body' => $this->body, - ]; - } -} diff --git a/src/Transport/Payloads/Server.php b/src/Transport/Payloads/Server.php deleted file mode 100644 index 98207fc..0000000 --- a/src/Transport/Payloads/Server.php +++ /dev/null @@ -1,82 +0,0 @@ - $this->ip, - 'timezone' => $this->timezone, - 'os' => $this->os->toArray(), - 'software' => $this->software, - 'signature' => $this->signature, - 'protocol' => $this->protocol, - 'encoding' => $this->encoding, - ]; - } -} diff --git a/src/Transport/Treblle.php b/src/Transport/Treblle.php index bc33bd3..e2cd5cf 100644 --- a/src/Transport/Treblle.php +++ b/src/Transport/Treblle.php @@ -4,48 +4,33 @@ namespace Treblle\Runtime\Transport; -use DateTimeImmutable; -use DateTimeInterface; use Http\Discovery\Psr17FactoryDiscovery; use JsonException; use JustSteveKing\Sdk\Client; use JustSteveKing\Sdk\Exceptions\ClientSetupException; use JustSteveKing\Tools\Http\Enums\Method; use Psr\Http\Client\ClientExceptionInterface; -use Treblle\CloudEvent\CloudEvent; -use Treblle\Runtime\Transport\Payloads\Payload; -final class Treblle extends Client +class Treblle extends Client { /** - * @param Payload $payload + * @param array $payload * @return void * @throws JsonException|ClientSetupException|ClientExceptionInterface */ - public function ingress(Payload $payload): void + public function ingress(array $payload): void { $this->setup()->send( request: Psr17FactoryDiscovery::findRequestFactory()->createRequest( method: Method::POST->value, uri: $this->url . '/dumps/13dba067-aaa2-4781-9452-7c58780b9aa3', )->withBody( - body: CloudEvent::make( - data: [ - 'id' => '1234', - 'source' => 'current-url', - 'type' => 'com.treblle.observability.ingress', - 'data' => (string) json_encode( - value: $payload->toArray(), - flags: JSON_THROW_ON_ERROR, - ), - 'data_content_type' => 'application/json', - 'data_schema' => 'create-json-schema', - 'subject' => 'treblle-ingress', - 'time' => (new DateTimeImmutable( - datetime: 'now', - ))->format(DateTimeInterface::RFC3339), - ], - )->toStream(), + body: Psr17FactoryDiscovery::findStreamFactory()->createStream( + content: (string) json_encode( + value: $payload, + flags: JSON_THROW_ON_ERROR, + ), + ), ), ); } diff --git a/test.php b/test.php index a580964..6d33b32 100644 --- a/test.php +++ b/test.php @@ -26,6 +26,10 @@ 'user.payments.cc' => Masking\CreditCardMatcher::class, 'cc' => Masking\CreditCardMatcher::class, ], + headers: [ + 'Authorization', + 'X-API-KEY', + ], ); $runtime = new Runtime( @@ -33,9 +37,6 @@ apiToken: '1234', url: 'https://httpdump.app', ), - payload: Runtime::payload( - config: $config, - ), maskingEngine: new Masking\MaskingEngine( config: $config, ), diff --git a/tests/Masking/MaskingEngineTest.php b/tests/Masking/MaskingEngineTest.php index 3c1ff91..d4aed07 100644 --- a/tests/Masking/MaskingEngineTest.php +++ b/tests/Masking/MaskingEngineTest.php @@ -149,7 +149,7 @@ public function it_can_mask_a_wildcard_value(): void 'name' => '*******', 'user' => [ 'email' => '*****@treblle.com', - ] + ], ], ], actual: $engine->mask( @@ -157,8 +157,8 @@ public function it_can_mask_a_wildcard_value(): void 'account' => [ 'name' => 'Treblle', 'user' => [ - 'email' => 'steve@treblle.com' - ] + 'email' => 'steve@treblle.com', + ], ], ], ), diff --git a/tests/PackageTestCase.php b/tests/PackageTestCase.php index 3819fa6..8d3fe59 100644 --- a/tests/PackageTestCase.php +++ b/tests/PackageTestCase.php @@ -9,12 +9,29 @@ use Treblle\Runtime\Masking\CreditCardMatcher; use Treblle\Runtime\Masking\DateMatcher; use Treblle\Runtime\Masking\EmailMatcher; +use Treblle\Runtime\Masking\MaskingEngine; use Treblle\Runtime\Masking\PostalCodeMatcher; use Treblle\Runtime\Masking\SocialSecurityMatcher; use Treblle\Runtime\Masking\StringMatcher; +use Treblle\Runtime\Runtime; +use Treblle\Runtime\Transport\Treblle; abstract class PackageTestCase extends TestCase { + protected function runtime(): Runtime + { + return new Runtime( + transport: new Treblle( + apiToken: '1234', + url: 'https://www.treblle.com/', + ), + maskingEngine: new MaskingEngine( + config: $this->config(), + ), + start: microtime(true), + ); + } + protected function config(): Config { return new Config( @@ -26,12 +43,17 @@ protected function config(): Config 'cc' => CreditCardMatcher::class, 'password' => StringMatcher::class, 'user.email' => EmailMatcher::class, + 'user.dob' => DateMatcher::class, 'account.*' => StringMatcher::class, 'account.*.email' => EmailMatcher::class, 'ss' => SocialSecurityMatcher::class, 'user.password' => StringMatcher::class, 'postal_code' => PostalCodeMatcher::class, ], + headers: [ + 'Authorization', + 'X-API-KEY', + ], ); } } diff --git a/tests/RuntimeTest.php b/tests/RuntimeTest.php new file mode 100644 index 0000000..02f6eae --- /dev/null +++ b/tests/RuntimeTest.php @@ -0,0 +1,298 @@ +assertInstanceOf( + expected: Runtime::class, + actual: $this->runtime(), + ); + } + + #[Test] + public function it_can_add_a_request(): void + { + $runtime = $this->runtime()->request( + data: [ + 'timestamp' => '2024-05-16:12:12:12', + 'ip' => '127.0.0.1', + 'url' => 'https://api.something.com', + 'user_agent' => 'Some Agent', + 'method' => 'GET', + 'headers' => [ + 'content_type' => 'application/json', + 'content_length' => 123, + 'host' => 'https://api.something.com', + 'user_agent' => 'Some Agent', + ], + 'body' => [ + 'user' => [ + 'name' => 'Steve', + 'email' => 'steve@example.com', + 'dob' => '09/09/1988', + 'password' => 'password', + 'payments' => [ + 'cc' => '1234-1234-1234-1234', + ], + ], + 'ss' => '123-23-2345', + ], + ], + ); + + $this->assertArrayHasKey( + key: 'request', + array: $runtime->data, + ); + + $this->assertEquals( + expected: [ + 'request' => [ + 'timestamp' => '2024-05-16:12:12:12', + 'ip' => '127.0.0.1', + 'url' => 'https://api.something.com', + 'user_agent' => 'Some Agent', + 'method' => 'GET', + 'headers' => [ + 'content_type' => 'application/json', + 'content_length' => 123, + 'host' => 'https://api.something.com', + 'user_agent' => 'Some Agent', + ], + 'body' => [ + 'user' => [ + 'name' => 'Steve', + 'email' => '*****@example.com', + 'dob' => '09/09/****', + 'password' => '********', + 'payments' => [ + 'cc' => '1234-1234-1234-1234', + ], + ], + 'ss' => '***-**-2345', + ], + ], + ], + actual: $runtime->data, + ); + } + + #[Test] + public function it_can_add_a_response(): void + { + $runtime = $this->runtime()->response( + data: [ + 'headers' => [ + 'Accept' => 'application/json', + ], + 'code' => 200, + 'size' => 123, + 'load_time' => 1234, + 'memory_usage' => 1234, + 'body' => [ + 'password' => 'password', + ], + ], + ); + + $this->assertArrayHasKey( + key: 'response', + array: $runtime->data, + ); + + $this->assertEquals( + expected: [ + 'response' => [ + 'headers' => [ + 'Accept' => 'application/json', + ], + 'code' => 200, + 'size' => 123, + 'load_time' => 1234, + 'memory_usage' => 1234, + 'body' => [ + 'password' => '********', + ], + ], + ], + actual: $runtime->data, + ); + } + + #[Test] + public function it_can_mask_request_headers(): void + { + $runtime = $this->runtime()->request( + data: [ + 'timestamp' => '2024-05-16:12:12:12', + 'ip' => '127.0.0.1', + 'url' => 'https://api.something.com', + 'user_agent' => 'Some Agent', + 'method' => 'GET', + 'headers' => [ + 'Authorization' => 'Bearer 12345', + 'content_type' => 'application/json', + 'content_length' => 123, + 'host' => 'https://api.something.com', + 'user_agent' => 'Some Agent', + ], + 'body' => [ + 'user' => [ + 'name' => 'Steve', + 'email' => 'steve@example.com', + 'dob' => '09/09/1988', + 'password' => 'password', + 'payments' => [ + 'cc' => '1234-1234-1234-1234', + ], + ], + 'ss' => '123-23-2345', + ], + ], + ); + + $this->assertArrayHasKey( + key: 'request', + array: $runtime->data, + ); + + $this->assertEquals( + expected: [ + 'request' => [ + 'timestamp' => '2024-05-16:12:12:12', + 'ip' => '127.0.0.1', + 'url' => 'https://api.something.com', + 'user_agent' => 'Some Agent', + 'method' => 'GET', + 'headers' => [ + 'Authorization' => '************', + 'content_type' => 'application/json', + 'content_length' => 123, + 'host' => 'https://api.something.com', + 'user_agent' => 'Some Agent', + ], + 'body' => [ + 'user' => [ + 'name' => 'Steve', + 'email' => '*****@example.com', + 'dob' => '09/09/****', + 'password' => '********', + 'payments' => [ + 'cc' => '1234-1234-1234-1234', + ], + ], + 'ss' => '***-**-2345', + ], + ], + ], + actual: $runtime->data, + ); + } + + #[Test] + public function it_can_mask_response_headers(): void + { + $runtime = $this->runtime()->response( + data: [ + 'headers' => [ + 'Authorization' => 'Basic 12345', + 'X-API-KEY' => '12345', + 'Accept' => 'application/json', + ], + 'code' => 200, + 'size' => 123, + 'load_time' => 1234, + 'memory_usage' => 1234, + 'body' => [ + 'password' => 'password', + ], + ], + ); + + $this->assertArrayHasKey( + key: 'response', + array: $runtime->data, + ); + + $this->assertEquals( + expected: [ + 'response' => [ + 'headers' => [ + 'Authorization' => '***********', + 'X-API-KEY' => '*****', + 'Accept' => 'application/json', + ], + 'code' => 200, + 'size' => 123, + 'load_time' => 1234, + 'memory_usage' => 1234, + 'body' => [ + 'password' => '********', + ], + ], + ], + actual: $runtime->data, + ); + } + + #[Test] + public function process_will_trigger_the_send_method(): void + { + $transport = $this->createMock(Treblle::class); + $maskingEngine = $this->createMock(MaskingEngineContract::class); + + $runtime = $this->getMockBuilder(Runtime::class) + ->setConstructorArgs([$transport, $maskingEngine]) + ->onlyMethods(['send']) + ->getMock(); + + $runtime->expects($this->once())->method('send'); + + $runtime->process(); + } + + #[Test] + public function no_exceptions_are_thrown_on_the_process_method(): void + { + $transport = $this->createMock(Treblle::class); + $maskingEngine = $this->createMock(MaskingEngineContract::class); + + $runtime = $this->getMockBuilder(Runtime::class) + ->setConstructorArgs([$transport, $maskingEngine]) + ->onlyMethods(['send']) + ->getMock(); + + $runtime->method('send')->will($this->throwException(new Exception('Test exception'))); + + $runtime->process(); + + $this->assertTrue(true); + } + + #[Test] + public function it_can_send_the_request(): void + { + $transport = $this->createMock(Treblle::class); + $maskingEngine = $this->createMock(MaskingEngineContract::class); + + $transport->expects($this->once()) + ->method('ingress') + ->with($this->arrayHasKey('load_time')); + + $runtime = new Runtime($transport, $maskingEngine, microtime(true)); + + $runtime->send(); + } +} diff --git a/tests/Support/SupportTest.php b/tests/Support/SupportTest.php index 5346dd8..33b0fed 100644 --- a/tests/Support/SupportTest.php +++ b/tests/Support/SupportTest.php @@ -7,7 +7,6 @@ use PHPUnit\Framework\Attributes\Test; use Treblle\Runtime\Support\PHP; use Treblle\Runtime\Tests\PackageTestCase; -use Treblle\Runtime\Transport\Payloads\Language; final class SupportTest extends PackageTestCase { @@ -46,18 +45,4 @@ public function it_can_get_the_display_errors_ini_value(): void actual: PHP::displayErrors(), ); } - - #[Test] - public function it_can_get_the_language_object(): void - { - $this->assertEquals( - expected: new Language( - name: 'php', - version: '8.3.7', - expose_php: 'On', - display_errors: 'On', - ), - actual: PHP::language(), - ); - } } diff --git a/tests/Transport/TreblleTest.php b/tests/Transport/TreblleTest.php new file mode 100644 index 0000000..b167c0c --- /dev/null +++ b/tests/Transport/TreblleTest.php @@ -0,0 +1,31 @@ +assertInstanceOf( + expected: Client::class, + actual: $this->runtime()->transport, + ); + } + + #[Test] + public function it_will_create_an_interoperable_http_client(): void + { + $this->assertInstanceOf( + expected: ClientContract::class, + actual: $this->runtime()->transport->setup(), + ); + } +}