From 0c702ba41407af9092b4d43648f7d0558db815b1 Mon Sep 17 00:00:00 2001 From: Syed Sirajul Islam Anik Date: Sat, 5 Aug 2023 21:11:00 +0600 Subject: [PATCH] 3.x (#10) * uses psr/log:^2.0|^3.0, colinodell/psr-testlogger for logger implementation * uses php:^8.0 and phpunit:^9.0|^10.0 * gitignore update * marked two tests as skipped as it depends on user preference and LoggerInterface implementation * abstract test class rename * added guzzlehhtp/promises:^2.0 * github workflows updated * workflow update - remove guzzle promises from matrix * type-hints added, isset removed with null coalescing operator * readme update --- .github/workflows/sniffer.yml | 21 --- .github/workflows/standard-test-coverage.yml | 79 +++++++++++ .github/workflows/test-coverage.yml | 61 -------- .gitignore | 3 +- README.md | 8 +- composer.json | 10 +- phpunit.xml | 43 +++--- src/Formatter/RequestCurlFormatter.php | 19 +-- src/Middleware/LogMiddleware.php | 131 ++++++++---------- .../Request/RequestArrayFormatterTest.php | 4 +- .../Request/RequestCurlFormatterTest.php | 4 +- ...rTest.php => RequestFormatterTestCase.php} | 2 +- tests/Middleware/LogMiddlewareTest.php | 2 + tests/MiddlewareTestCase.php | 2 +- 14 files changed, 188 insertions(+), 201 deletions(-) delete mode 100644 .github/workflows/sniffer.yml create mode 100644 .github/workflows/standard-test-coverage.yml delete mode 100644 .github/workflows/test-coverage.yml rename tests/Formatter/Request/{RequestFormatterTest.php => RequestFormatterTestCase.php} (98%) diff --git a/.github/workflows/sniffer.yml b/.github/workflows/sniffer.yml deleted file mode 100644 index e9a61e6..0000000 --- a/.github/workflows/sniffer.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Code sniffer -on: - pull_request: - push: - branches: - - master - -jobs: - sniff: - name: Sniff codebase - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Install PHP Codesniffer - run: composer global require squizlabs/php_codesniffer - - - name: Check against PSR12 standard - run: | - `composer global config bin-dir --absolute --quiet`/phpcs --standard=PSR12 ./src diff --git a/.github/workflows/standard-test-coverage.yml b/.github/workflows/standard-test-coverage.yml new file mode 100644 index 0000000..3da8ccc --- /dev/null +++ b/.github/workflows/standard-test-coverage.yml @@ -0,0 +1,79 @@ +name: PSR-12 coding standard, Test, Coverage +on: + pull_request: + push: + branches: + - master + - dev + +jobs: + coding-standard: + name: PSR-12 coding standard + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - uses: shivammathur/setup-php@v2 + with: + php-version: 8.2 + + - name: Install PHP code sniffer + run: composer global require squizlabs/php_codesniffer + + - name: Check against PSR12 standard + run: | + `composer global config bin-dir --absolute --quiet`/phpcs --standard=PSR12 ./src + + tests: + needs: coding-standard + name: PHP ${{ matrix.php }} - Guzzle ${{ matrix.guzzle }} + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + php: [ 8.0, 8.1, 8.2 ] + guzzle: [ ^6, ^7 ] + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: json,curl + + - name: Install dependencies + run: composer require guzzlehttp/guzzle:${{ matrix.guzzle }} --no-interaction --prefer-dist + + - name: Run test suite + run: ./vendor/bin/phpunit --testdox + + coverage: + needs: tests + name: Coverage + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - uses: shivammathur/setup-php@v2 + with: + coverage: pcov + php-version: 8.2 + extensions: json,curl + + - name: Install dependencies + run: composer install --no-interaction --prefer-dist + + - name: Run tests for coverage + run: ./vendor/bin/phpunit --coverage-clover=coverage.xml + + - name: Push to Codecov + run: bash <(curl -s https://codecov.io/bash) + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/test-coverage.yml b/.github/workflows/test-coverage.yml deleted file mode 100644 index bf0d94e..0000000 --- a/.github/workflows/test-coverage.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Run tests -on: - pull_request: - push: - branches: - - master - -jobs: - tests: - name: PHP ${{ matrix.php }} with Guzzle ${{ matrix.guzzle }} - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - php: [ 7.1, 7.2, 7.3, 7.4, 8.0 ] - guzzle: [ ^6, ^7 ] - exclude: - - php: 7.1 - guzzle: ^7 - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Install PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: json,curl - - - name: Install dependencies - run: composer require guzzlehttp/guzzle:${{ matrix.guzzle }} --no-interaction --prefer-dist - - - name: Run test suite - run: ./vendor/bin/phpunit --testdox --verbose - - coverage: - needs: tests - name: Coverage - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - uses: shivammathur/setup-php@v2 - with: - coverage: pcov - php-version: 8.0 - - - name: Install dependencies - run: composer require laravel/lumen:"^8" --no-interaction --prefer-dist - - - name: Run tests for coverage - run: ./vendor/bin/phpunit --coverage-clover=coverage.xml - - - name: Push to Codecov - run: bash <(curl -s https://codecov.io/bash) - env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index 298fedb..23d87a0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ vendor/ .idea/ composer.lock .phpunit.result.cache -coverage/ +coverage*/ +.phpunit.cache* diff --git a/README.md b/README.md index 1269510..b144368 100644 --- a/README.md +++ b/README.md @@ -70,8 +70,12 @@ $options = [ * `\Loguzz\Formatter\ExceptionJsonFormatter` - `tag` - **string**. **Empty** by default. When non-empty string, it'll log the formatted data under this tag. Tag can be used to search for specific type of request/response in your log file or your storage. -- `force_json` - **bool**. It is only applicable when **tag** is non-empty string. If enabled, it will then log data as - json string, otherwise it'll log as an array. +- `force_json` - **bool**. **true** by default. It is only applicable when **tag** is non-empty string. If enabled, it + will then log data as + json string, otherwise it'll log as an array. **If set to** `false`, **the code may break due to the type-hint in + psr/log interface. If + your [logger interface supports array](https://github.com/laravel/framework/blob/dd5c5178274e64d0384dc30bf2c8139b00dba098/src/Illuminate/Log/Logger.php#L260), + it will work.** - `separate` - **bool**. It is only applicable when **tag** is non-empty string. If enabled, it will then log data in `{tag}.request`, `{tag}.success`, `{tag}.failure` for request logging, successful response and error response. diff --git a/composer.json b/composer.json index ed46692..3267093 100644 --- a/composer.json +++ b/composer.json @@ -10,14 +10,14 @@ } ], "require": { - "php": ">=7.1", + "php": "^8.0", "guzzlehttp/guzzle": "^6.2|^7.1", - "guzzlehttp/promises": "^1.5", - "psr/log": "^1.0|^2.0|^3.0" + "guzzlehttp/promises": "^1.5|^2.0", + "psr/log": "^2.0|^3.0" }, "require-dev": { - "phpunit/phpunit": "^7.5.15|^8.4|^9.0", - "monolog/monolog": "^1.12|^2.0" + "phpunit/phpunit": "^9.0|^10.0", + "colinodell/psr-testlogger": "^1.2" }, "autoload": { "psr-4": { diff --git a/phpunit.xml b/phpunit.xml index e67f113..b43efb7 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,22 +1,23 @@ - - - - tests/Formatter/Request - - - tests/Formatter/Response - - - tests/Formatter/Exception - - - tests/Middleware - - - - - src - - - \ No newline at end of file + + + + + tests/Formatter/Request + + + tests/Formatter/Response + + + tests/Formatter/Exception + + + tests/Middleware + + + + + src + + + diff --git a/src/Formatter/RequestCurlFormatter.php b/src/Formatter/RequestCurlFormatter.php index 8c55666..68af6c8 100644 --- a/src/Formatter/RequestCurlFormatter.php +++ b/src/Formatter/RequestCurlFormatter.php @@ -6,27 +6,18 @@ class RequestCurlFormatter extends AbstractRequestFormatter { - /** - * @var string - */ - protected $command; + protected string $command; - /** - * @var int - */ - protected $currentLineLength; + protected int $currentLineLength; /** * @var string[] */ - protected $options; + protected array $options; - /** - * @var int - */ - protected $commandLineLength; + protected int $commandLineLength; - public function __construct($commandLineLength = 100) + public function __construct(int $commandLineLength = 100) { $this->commandLineLength = $commandLineLength; } diff --git a/src/Middleware/LogMiddleware.php b/src/Middleware/LogMiddleware.php index eb44af9..250636c 100644 --- a/src/Middleware/LogMiddleware.php +++ b/src/Middleware/LogMiddleware.php @@ -17,8 +17,8 @@ class LogMiddleware { - protected $logger; - protected $options; + protected LoggerInterface $logger; + protected array $options; public function __construct(LoggerInterface $logger, array $options = []) { @@ -45,105 +45,62 @@ public function __invoke(callable $handler): Closure }; } - private function logExceptionOnly(): bool - { - return isset($this->options['exceptions_only']) ? (bool)$this->options['exceptions_only'] : false; - } - - private function logSuccessOnly(): bool - { - return isset($this->options['success_only']) ? (bool)$this->options['success_only'] : false; - } - private function logRequest(): bool { - return isset($this->options['log_request']) ? (bool)$this->options['log_request'] : true; + return (bool)($this->options['log_request'] ?? true); } - private function getDefaultRequestFormatter(): AbstractRequestFormatter + private function formatWithTag($loggable, $type) { - $length = isset($this->options['length']) ? $this->options['length'] : 100; - $length = $length < 10 ? 100 : $length; - - return new RequestCurlFormatter($length); - } + if ($tag = $this->getLogTag()) { + if ($this->shouldSeparate()) { + $tag = $tag . '.' . $type; + } - private function getRequestFormatter(): AbstractRequestFormatter - { - $formatter = null; - if (isset($this->options['request_formatter'])) { - $formatter = $this->options['request_formatter']; + return $this->forceToJson() ? json_encode([$tag => $loggable]) : [$tag => $loggable]; } - return $formatter instanceof AbstractRequestFormatter ? $formatter : $this->getDefaultRequestFormatter(); - } - - private function logResponse(): bool - { - return isset($this->options['log_response']) ? (bool)$this->options['log_response'] : true; + return $loggable; } - private function getDefaultResponseFormatter(): ResponseJsonFormatter + private function getLogTag(): string { - return new ResponseJsonFormatter(); + return $this->options['tag'] ?? ''; } - private function getResponseFormatter(): AbstractResponseFormatter + private function shouldSeparate(): bool { - $formatter = null; - if (isset($this->options['response_formatter'])) { - $formatter = $this->options['response_formatter']; - } - - return $formatter instanceof AbstractResponseFormatter ? $formatter : $this->getDefaultResponseFormatter(); + return (bool)($this->options['separate'] ?? false); } - private function getDefaultExceptionFormatter(): ExceptionJsonFormatter + private function forceToJson(): bool { - return new ExceptionJsonFormatter(); + return (bool)($this->options['force_json'] ?? true); } - private function getExceptionFormatter(): AbstractExceptionFormatter + private function getRequestFormatter(): AbstractRequestFormatter { - $formatter = null; - if (isset($this->options['exception_formatter'])) { - $formatter = $this->options['exception_formatter']; - } - - return $formatter instanceof AbstractExceptionFormatter ? $formatter : $this->getDefaultExceptionFormatter(); - } + $formatter = $this->options['request_formatter'] ?? null; - private function getLogLevel(): string - { - return isset($this->options['log_level']) ? $this->options['log_level'] : 'debug'; + return $formatter instanceof AbstractRequestFormatter ? $formatter : $this->getDefaultRequestFormatter(); } - private function getLogTag(): string + private function getDefaultRequestFormatter(): AbstractRequestFormatter { - return isset($this->options['tag']) ? $this->options['tag'] : ''; - } + $length = (int)($this->options['length'] ?? 100); + $length = $length < 10 ? 100 : $length; - private function forceToJson(): bool - { - return isset($this->options['force_json']) ? (bool)$this->options['force_json'] : true; + return new RequestCurlFormatter($length); } - private function shouldSeparate(): bool + private function getLogLevel(): string { - return isset($this->options['separate']) ? (bool)$this->options['separate'] : false; + return $this->options['log_level'] ?? 'debug'; } - private function formatWithTag($loggable, $type) + private function logResponse(): bool { - if ($tag = $this->getLogTag()) { - if ($this->shouldSeparate()) { - $tag = $tag . '.' . $type; - } - - return $this->forceToJson() ? json_encode([$tag => $loggable]) : [$tag => $loggable]; - } - - return $loggable; + return (bool)($this->options['log_response'] ?? true); } /** @@ -169,6 +126,23 @@ private function handleSuccess(RequestInterface $request, array $options): calla }; } + private function logExceptionOnly(): bool + { + return (bool)($this->options['exceptions_only'] ?? false); + } + + private function getResponseFormatter(): AbstractResponseFormatter + { + $formatter = $this->options['response_formatter'] ?? null; + + return $formatter instanceof AbstractResponseFormatter ? $formatter : $this->getDefaultResponseFormatter(); + } + + private function getDefaultResponseFormatter(): ResponseJsonFormatter + { + return new ResponseJsonFormatter(); + } + /** * Returns a function which is handled when a request was rejected. * @@ -191,4 +165,21 @@ private function handleFailure(RequestInterface $request, array $options): calla return Create::rejectionFor($reason); }; } + + private function logSuccessOnly(): bool + { + return (bool)($this->options['success_only'] ?? false); + } + + private function getExceptionFormatter(): AbstractExceptionFormatter + { + $formatter = $this->options['exception_formatter'] ?? null; + + return $formatter instanceof AbstractExceptionFormatter ? $formatter : $this->getDefaultExceptionFormatter(); + } + + private function getDefaultExceptionFormatter(): ExceptionJsonFormatter + { + return new ExceptionJsonFormatter(); + } } diff --git a/tests/Formatter/Request/RequestArrayFormatterTest.php b/tests/Formatter/Request/RequestArrayFormatterTest.php index 7770378..d6dbe93 100644 --- a/tests/Formatter/Request/RequestArrayFormatterTest.php +++ b/tests/Formatter/Request/RequestArrayFormatterTest.php @@ -2,9 +2,9 @@ use Loguzz\Formatter\AbstractRequestFormatter; use Loguzz\Formatter\RequestArrayFormatter; -use Loguzz\Test\Formatter\Request\RequestFormatterTest; +use Loguzz\Test\Formatter\Request\RequestFormatterTestCase; -class RequestArrayFormatterTest extends RequestFormatterTest +class RequestArrayFormatterTest extends RequestFormatterTestCase { public function implementAssertionForUserAgent($response) { diff --git a/tests/Formatter/Request/RequestCurlFormatterTest.php b/tests/Formatter/Request/RequestCurlFormatterTest.php index da89146..39de285 100644 --- a/tests/Formatter/Request/RequestCurlFormatterTest.php +++ b/tests/Formatter/Request/RequestCurlFormatterTest.php @@ -2,9 +2,9 @@ use Loguzz\Formatter\AbstractRequestFormatter; use Loguzz\Formatter\RequestCurlFormatter; -use Loguzz\Test\Formatter\Request\RequestFormatterTest; +use Loguzz\Test\Formatter\Request\RequestFormatterTestCase; -class RequestCurlFormatterTest extends RequestFormatterTest +class RequestCurlFormatterTest extends RequestFormatterTestCase { /** * @var RequestCurlFormatter diff --git a/tests/Formatter/Request/RequestFormatterTest.php b/tests/Formatter/Request/RequestFormatterTestCase.php similarity index 98% rename from tests/Formatter/Request/RequestFormatterTest.php rename to tests/Formatter/Request/RequestFormatterTestCase.php index 6a06934..7d9230d 100644 --- a/tests/Formatter/Request/RequestFormatterTest.php +++ b/tests/Formatter/Request/RequestFormatterTestCase.php @@ -5,7 +5,7 @@ use GuzzleHttp\Cookie\CookieJar; use Loguzz\Test\FormatterTestCase; -abstract class RequestFormatterTest extends FormatterTestCase +abstract class RequestFormatterTestCase extends FormatterTestCase { abstract public function implementAssertionForUserAgent($response); diff --git a/tests/Middleware/LogMiddlewareTest.php b/tests/Middleware/LogMiddlewareTest.php index de3a94b..2071ddf 100644 --- a/tests/Middleware/LogMiddlewareTest.php +++ b/tests/Middleware/LogMiddlewareTest.php @@ -122,6 +122,7 @@ public function testWhenTaggingItShouldLogAsJsonByDefault() public function testWhenTaggingItShouldLogAsArrayIfNotJson() { + $this->markTestSkipped('Based on user preference and LoggerInterface Implementation.'); $dto = $this->objectFactory(['tag' => 'custom.tag', 'force_json' => false]); $dto->client->send($dto->request); @@ -141,6 +142,7 @@ public function testUserCanApplyRequestFormatter() public function testResponseFormatter() { + $this->markTestSkipped('Based on user preference and LoggerInterface Implementation.'); $dto = $this->objectFactory(['response_formatter' => new ResponseArrayFormatter(),]); $dto->client->send($dto->request); diff --git a/tests/MiddlewareTestCase.php b/tests/MiddlewareTestCase.php index 00e3cf8..405f9cb 100644 --- a/tests/MiddlewareTestCase.php +++ b/tests/MiddlewareTestCase.php @@ -2,9 +2,9 @@ namespace Loguzz\Test; +use ColinODell\PsrTestLogger\TestLogger; use Loguzz\Middleware\LogMiddleware; use Psr\Log\LoggerInterface; -use Psr\Log\Test\TestLogger; class ObjectFactory {