From daefe960069ce7373eed0c75ebb46921bf831b7a Mon Sep 17 00:00:00 2001 From: Shaun McCormick Date: Fri, 29 Jul 2022 13:43:51 -0500 Subject: [PATCH] Add `getFullyQualifiedMethodName` and `getExpectedResponseMessageClass` to base Client Interceptor class, drop php 7.3 --- .circleci/config.yml | 12 +- CHANGELOG.md | 6 + README.md | 2 +- composer.json | 2 +- src/Grphp/Client.php | 6 +- src/Grphp/Client/Interceptors/Base.php | 59 +++++++-- .../ResponseMessageLookupFailedException.php | 24 ++++ .../Interceptors/StubNotFoundException.php | 24 ++++ tests/Support/Compiled/ThingsClient.php | 8 +- tests/Support/TestInterceptors.php | 11 ++ .../Grphp/Client/Interceptors/BaseTest.php | 114 ++++++++++++++++++ 11 files changed, 247 insertions(+), 21 deletions(-) create mode 100644 src/Grphp/Client/Interceptors/ResponseMessageLookupFailedException.php create mode 100644 src/Grphp/Client/Interceptors/StubNotFoundException.php create mode 100644 tests/Unit/Grphp/Client/Interceptors/BaseTest.php diff --git a/.circleci/config.yml b/.circleci/config.yml index 15ba408..78fe348 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,7 +9,7 @@ jobs: parameters: php-version: type: string - default: 7.3-fpm + default: 7.4-fpm executor: name: php/php php-version: << parameters.php-version >> @@ -27,7 +27,7 @@ jobs: parameters: php-version: type: string - default: 7.3-fpm + default: 7.4-fpm executor: name: php/php php-version: << parameters.php-version >> @@ -44,7 +44,7 @@ jobs: parameters: php-version: type: string - default: 7.3-fpm + default: 7.4-fpm executor: name: php/php php-version: << parameters.php-version >> @@ -65,12 +65,12 @@ workflows: - tests-unit: matrix: parameters: - php-version: [ "7.3-fpm", "7.4-fpm", "8.0-fpm" ] + php-version: [ "7.4-fpm", "8.0-fpm" ] - codesniffer: matrix: parameters: - php-version: [ "7.3-fpm", "7.4-fpm", "8.0-fpm" ] + php-version: [ "7.4-fpm", "8.0-fpm" ] - cs-fixer: matrix: parameters: - php-version: [ "7.3-fpm", "7.4-fpm", "8.0-fpm" ] + php-version: [ "7.4-fpm", "8.0-fpm" ] diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f47b03..b5da112 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,13 @@ Changelog for grphp. ### Pending Release +### 3.3.0 + +* Add `getFullyQualifiedMethodName` and `getExpectedResponseMessageClass` to base Client Interceptor class +* Drop PHP 7.3 support + ### 3.2.2 + * Add header `TE: trailers` for envoy requests. ### 3.2.1 diff --git a/README.md b/README.md index 9440edd..795b3dd 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ preserving gRPC BadStatus codes * Client execution timings in responses * H2Proxy via nghttpx support that allows gRPC-based communication without the gRPC C libraries -grphp currently has active support for gRPC 1.9.0, and requires PHP 7.0+ to run. +grphp currently has active support for gRPC 1.9.0, and requires PHP 7.4+ to run. ## Installation diff --git a/composer.json b/composer.json index e75bd08..d78411d 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "description": "gRPC PHP Framework", "license": "MIT", "require": { - "php": "^7.3 || ^8.0", + "php": "^7.4 || ^8.0", "google/protobuf": "^3.7", "grpc/grpc": "^1.13" }, diff --git a/src/Grphp/Client.php b/src/Grphp/Client.php index 18ebed1..853eda9 100644 --- a/src/Grphp/Client.php +++ b/src/Grphp/Client.php @@ -42,11 +42,11 @@ class Client /** @var BaseStub $client */ protected $client; /** @var Config $config */ - protected $config; + protected Config $config; /** @var array $interceptors */ - protected $interceptors = []; + protected array $interceptors = []; /** @var string */ - private $clientClassName; + private string $clientClassName; /** * @param string $clientClassName diff --git a/src/Grphp/Client/Interceptors/Base.php b/src/Grphp/Client/Interceptors/Base.php index 8b5c3ee..66a7736 100644 --- a/src/Grphp/Client/Interceptors/Base.php +++ b/src/Grphp/Client/Interceptors/Base.php @@ -15,7 +15,7 @@ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -declare(strict_types = 1); +declare(strict_types=1); namespace Grphp\Client\Interceptors; @@ -31,13 +31,13 @@ abstract class Base { /** @var array */ - protected $options = []; + protected array $options = []; /** @var Message */ protected $request; /** @var string */ - protected $method; + protected string $method; /** @var array */ - protected $metadata = []; + protected array $metadata = []; /** @var BaseStub */ protected $stub; @@ -72,6 +72,47 @@ public function setRequest(&$request) $this->request = $request; } + /** + * Gets the fully qualified method name, e.g. grphp.catalog.products/GetProduct + * + * @return string + * @throws StubNotFoundException + */ + public function getFullyQualifiedMethodName(): string + { + $methodName = $this->getMethod(); + $stub = $this->getStub(); + if (empty($stub)) { + throw new StubNotFoundException("Stub not found for $methodName"); + } + + return $stub->getServiceName() . '/' . ucfirst($methodName); + } + + /** + * Get the expected response protobuf message class + * + * @return string + * @throws StubNotFoundException + * @throws ResponseMessageLookupFailedException + */ + public function getExpectedResponseMessageClass(): string + { + $methodName = $this->getMethod(); + $stub = $this->getStub(); + if (empty($stub)) { + throw new StubNotFoundException("Stub not found for $methodName"); + } + + $responseMessages = $stub->getExpectedResponseMessages(); + $methodName = lcfirst($methodName); + + if (!array_key_exists($methodName, $responseMessages)) { + throw new ResponseMessageLookupFailedException(); + } + return $responseMessages[$methodName]; + } + /** * @return string */ @@ -84,7 +125,7 @@ public function getMethod(): string * @param string $method * @return void */ - public function setMethod(string &$method) + public function setMethod(string &$method): void { $this->method = $method; } @@ -101,7 +142,7 @@ public function getMetadata(): array * @param array $metadata * @return void */ - public function setMetadata(array &$metadata = []) + public function setMetadata(array &$metadata = []): void { $this->metadata = $metadata; } @@ -118,7 +159,7 @@ public function getOptions(): array * @param array $options * @return void */ - public function setOptions(array &$options = []) + public function setOptions(array &$options = []): void { $this->options = $options; } @@ -136,13 +177,13 @@ public function getOption(string $k, $default = null) /** * @return BaseStub */ - public function getStub(): BaseStub + public function getStub(): ?BaseStub { return $this->stub; } /** - * @param $stub + * @param BaseStub $stub */ public function setStub(BaseStub &$stub) { diff --git a/src/Grphp/Client/Interceptors/ResponseMessageLookupFailedException.php b/src/Grphp/Client/Interceptors/ResponseMessageLookupFailedException.php new file mode 100644 index 0000000..cdb94dc --- /dev/null +++ b/src/Grphp/Client/Interceptors/ResponseMessageLookupFailedException.php @@ -0,0 +1,24 @@ + '\Grphp\Test\GetThingResp', ]; } + public function getServiceName(): string + { + return 'grphp.test.Things'; + } + protected $channel; /** * @param string $hostname hostname * @param array $opts channel options * @param \Grpc\Channel $channel (optional) re-use channel object + * @throws \Exception */ public function __construct($hostname, $opts, $channel = null) { diff --git a/tests/Support/TestInterceptors.php b/tests/Support/TestInterceptors.php index 065d595..1d8ab5a 100644 --- a/tests/Support/TestInterceptors.php +++ b/tests/Support/TestInterceptors.php @@ -26,3 +26,14 @@ public function call(callable $callback) return $callback(); } } + +class TestMetadataSetInterceptor extends Base +{ + public function call(callable $callback) + { + $md = $this->getMetadata(); + $md['test'] = 'one'; + $this->setMetadata($md); + return $callback(); + } +} diff --git a/tests/Unit/Grphp/Client/Interceptors/BaseTest.php b/tests/Unit/Grphp/Client/Interceptors/BaseTest.php new file mode 100644 index 0000000..4df8ec0 --- /dev/null +++ b/tests/Unit/Grphp/Client/Interceptors/BaseTest.php @@ -0,0 +1,114 @@ +call(function () { + return 42; + }); + $this->assertSame($result, 42); + } + + public function testMetadata(): void + { + $interceptor = new TestMetadataSetInterceptor(); + $result = $interceptor->call(function () { + return 1; + }); + $this->assertSame($result, 1); + $md = $interceptor->getMetadata(); + $this->assertArrayHasKey('test', $md); + $this->assertEquals('one', $md['test']); + } + + // Happy path for getting a FQN of the RPC in question + public function testGetExpectedResponseMessageClass(): void + { + $client = new ThingsClient('127.0.0.1:9000', [ 'credentials' => null ]); + $method = 'getThing'; + $metadata = ['some' => 'value']; + + $interceptor = new TestInterceptor(); + $interceptor->setMethod($method); + $interceptor->setMetadata($metadata); + $interceptor->setStub($client); + $this->assertEquals('\Grphp\Test\GetThingResp', $interceptor->getExpectedResponseMessageClass()); + } + + // When then stub is not set for some wild reason + public function testGetExpectedResponseMessageClassWhenNoStub(): void + { + $this->expectException(StubNotFoundException::class); + $method = 'getThing'; + $interceptor = new TestInterceptor(); + $interceptor->setMethod($method); + $interceptor->getExpectedResponseMessageClass(); + } + + // When there is no matching response message + public function testGetExpectedResponseMessageClassWhenNoResponseMessageFound(): void + { + $this->expectException(ResponseMessageLookupFailedException::class); + + $client = new ThingsClient('127.0.0.1:9000', [ 'credentials' => null ]); + $method = 'fakeGetThing'; + + $interceptor = new TestInterceptor(); + $interceptor->setMethod($method); + $interceptor->setStub($client); + $interceptor->getExpectedResponseMessageClass(); + } + + // Get the full service name + public function testGetFullyQualifiedMethodName(): void + { + $requestMessage = new GetThingReq(); + $requestMessage->setId(1); + $client = new ThingsClient('127.0.0.1:9000', [ 'credentials' => null ]); + $method = 'getThing'; + + $interceptor = new TestInterceptor(); + $interceptor->setMethod($method); + $interceptor->setStub($client); + $this->assertEquals('grphp.test.Things/GetThing', $interceptor->getFullyQualifiedMethodName()); + } + + // When then stub is not set for some wild reason + public function testGetFullyQualifiedMethodNameWhenNoStub(): void + { + $this->expectException(StubNotFoundException::class); + $method = 'getThing'; + $interceptor = new TestInterceptor(); + $interceptor->setMethod($method); + $interceptor->getExpectedResponseMessageClass(); + } +}