From db9078a16f39246f3ac0690882c40d05ba8e68c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Oct 2023 00:49:04 +0000 Subject: [PATCH 1/8] Bump actions/checkout from 4.1.0 to 4.1.1 Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.0 to 4.1.1. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4.1.0...v4.1.1) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/pr-validation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml index 88ebd6f..d6b6701 100644 --- a/.github/workflows/pr-validation.yml +++ b/.github/workflows/pr-validation.yml @@ -15,7 +15,7 @@ jobs: php-versions: ['7.4', '8.0', '8.1', '8.2'] steps: - name: Checkout - uses: actions/checkout@v4.1.0 + uses: actions/checkout@v4.1.1 - name: Setup PHP and Xdebug for Code Coverage report uses: shivammathur/setup-php@v2 with: From 48231bd6afa1a9cc66b2a237212d48c6534b1026 Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Wed, 25 Oct 2023 20:02:27 +0300 Subject: [PATCH 2/8] Use generic Promise return types --- composer.json | 2 +- src/GuzzleRequestAdapter.php | 23 +++++++++++------------ tests/GuzzleRequestAdapterTest.php | 11 ++++++----- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/composer.json b/composer.json index 4b8d52a..4d85aa2 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "require": { "php": "^7.4 | ^8.0", "guzzlehttp/guzzle": "^7.0", - "microsoft/kiota-abstractions": "^0.8.0", + "microsoft/kiota-abstractions": "^0.9.0", "ext-zlib": "*", "ext-json": "*" }, diff --git a/src/GuzzleRequestAdapter.php b/src/GuzzleRequestAdapter.php index ad7b982..c66ea4a 100644 --- a/src/GuzzleRequestAdapter.php +++ b/src/GuzzleRequestAdapter.php @@ -14,7 +14,6 @@ use Exception; use GuzzleHttp\ClientInterface; use GuzzleHttp\Psr7\Request; -use Http\Promise\FulfilledPromise; use Http\Promise\Promise; use InvalidArgumentException; use Microsoft\Kiota\Abstractions\ApiClientBuilder; @@ -107,11 +106,12 @@ public function __construct(AuthenticationProvider $authenticationProvider, } /** + * @template T of Parsable * @param RequestInformation $requestInfo * @param ResponseInterface $result - * @param array|null $errorMappings + * @param array, string}>|null $errorMappings * @param SpanInterface $span - * @return Promise|null + * @return Promise|null */ private function tryHandleResponse(RequestInformation $requestInfo, ResponseInterface $result, @@ -119,10 +119,9 @@ private function tryHandleResponse(RequestInformation $requestInfo, SpanInterface $span): ?Promise { $responseHandlerOption = $requestInfo->getRequestOptions()[ResponseHandlerOption::class] ?? null; - if ($responseHandlerOption && is_a($responseHandlerOption, ResponseHandlerOption::class)) { + if ($responseHandlerOption && $responseHandlerOption instanceof ResponseHandlerOption) { $responseHandler = $responseHandlerOption->getResponseHandler(); $span->addEvent('Event - com.microsoft.kiota.response_handler_invoked?'); - /** @phpstan-ignore-next-line False alarm?*/ return $responseHandler->handleResponseAsync($result, $errorMappings); } return null; @@ -196,11 +195,11 @@ function (ResponseInterface $result) use ($targetCallable, $requestInfo, $errorM if ($response !== null) { $span->addEvent(self::EVENT_RESPONSE_HANDLER_INVOKED_KEY); - return $result; + return $response; } $this->throwFailedResponse($result, $errorMappings, $span); if ($this->is204NoContentResponse($result)) { - return new FulfilledPromise(null); + return null; } $rootNode = $this->getRootParseNode($result, $span); $spanForDeserialization = $this->tracer->spanBuilder('ParseNode.getCollectionOfObjectValues') @@ -232,7 +231,7 @@ function (ResponseInterface $result) use ($primitiveType, $requestInfo, $errorMa $response = $this->tryHandleResponse($requestInfo, $result, $errorMappings, $span); if ($response !== null) { - return $result; + return $response; } $this->throwFailedResponse($result, $errorMappings, $span); $this->setResponseType($primitiveType, $span); @@ -294,7 +293,7 @@ function (ResponseInterface $result) use ($primitiveType, $requestInfo, $errorMa $response = $this->tryHandleResponse($requestInfo, $result, $errorMappings, $span); if ($response !== null) { - return $result; + return $response; } $this->throwFailedResponse($result, $errorMappings, $span); if ($this->is204NoContentResponse($result)) { @@ -324,7 +323,7 @@ function (ResponseInterface $result) use ($requestInfo, $errorMappings, &$span) $response = $this->tryHandleResponse($requestInfo, $result, $errorMappings, $span); if ($response !== null) { - return $result; + return $response; } $this->throwFailedResponse($result, $errorMappings, $span); return null; @@ -409,7 +408,7 @@ public function getPsrRequestFromRequestInformation(RequestInformation $requestI * Converts RequestInformation object to an authenticated(containing auth header) PSR-7 Request Object. * * @param RequestInformation $requestInformationInformation - * @return Promise + * @return Promise */ public function convertToNative(RequestInformation $requestInformationInformation): Promise { @@ -461,7 +460,7 @@ private function getRootParseNode(ResponseInterface $response, SpanInterface $sp * @param RequestInformation $requestInfo * @param string $claims additional claims to request if CAE fails * @param SpanInterface $span - * @return Promise + * @return Promise */ private function getHttpResponseMessage(RequestInformation $requestInfo, string $claims, diff --git a/tests/GuzzleRequestAdapterTest.php b/tests/GuzzleRequestAdapterTest.php index 06f4e84..4a8cc16 100644 --- a/tests/GuzzleRequestAdapterTest.php +++ b/tests/GuzzleRequestAdapterTest.php @@ -8,6 +8,7 @@ use GuzzleHttp\Psr7\Response; use GuzzleHttp\Psr7\Utils; use Http\Promise\FulfilledPromise; +use Http\Promise\Promise; use Microsoft\Kiota\Abstractions\ApiException; use Microsoft\Kiota\Abstractions\Authentication\AuthenticationProvider; use Microsoft\Kiota\Abstractions\Enum; @@ -113,7 +114,7 @@ public function testSendAsyncWithResponseHandler(): void $customResponseHandler->expects($this->once()) ->method('handleResponseAsync'); $this->requestInformation->addRequestOptions(new ResponseHandlerOption($customResponseHandler)); - $requestAdapter->sendAsync($this->requestInformation, array(TestUser::class, 'createFromDiscriminatorValue')); + $this->assertInstanceOf(Promise::class, $requestAdapter->sendAsync($this->requestInformation, array(TestUser::class, 'createFromDiscriminatorValue'))->wait()); } public function testSendCollectionAsync(): void @@ -133,7 +134,7 @@ public function testSendCollectionAsyncWithResponseHandler(): void $customResponseHandler->expects($this->once()) ->method('handleResponseAsync'); $this->requestInformation->addRequestOptions(new ResponseHandlerOption($customResponseHandler)); - $requestAdapter->sendCollectionAsync($this->requestInformation, array(TestUser::class, 'createFromDiscriminatorValue')); + $this->assertInstanceOf(Promise::class, $requestAdapter->sendCollectionAsync($this->requestInformation, array(TestUser::class, 'createFromDiscriminatorValue'))->wait()); } public function testSendPrimitiveAsync(): void @@ -158,7 +159,7 @@ public function testSendPrimitiveAsyncWithResponseHandler(): void $customResponseHandler->expects($this->once()) ->method('handleResponseAsync'); $this->requestInformation->addRequestOptions(new ResponseHandlerOption($customResponseHandler)); - $requestAdapter->sendPrimitiveAsync($this->requestInformation, 'int'); + $this->assertInstanceOf(Promise::class, $requestAdapter->sendPrimitiveAsync($this->requestInformation, 'int')->wait()); } public function testSendPrimitiveCollectionAsync(): void @@ -178,7 +179,7 @@ public function testSendPrimitiveCollectionAsyncWithResponseHandler(): void $customResponseHandler->expects($this->once()) ->method('handleResponseAsync'); $this->requestInformation->addRequestOptions(new ResponseHandlerOption($customResponseHandler)); - $requestAdapter->sendPrimitiveCollectionAsync($this->requestInformation, 'string'); + $this->assertInstanceOf(Promise::class, $requestAdapter->sendPrimitiveCollectionAsync($this->requestInformation, 'string')->wait()); } public function testSendNoContentAsync(): void @@ -195,7 +196,7 @@ public function testSendNoContentAsyncWithResponseHandler(): void $customResponseHandler->expects($this->once()) ->method('handleResponseAsync'); $this->requestInformation->addRequestOptions(new ResponseHandlerOption($customResponseHandler)); - $requestAdapter->sendNoContentAsync($this->requestInformation); + $this->assertInstanceOf(Promise::class, $requestAdapter->sendNoContentAsync($this->requestInformation)->wait()); } public function testExceptionThrownOnErrorResponses(): void From c362812294d0c91d0f2ca32eeb61a8e59555c661 Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Wed, 25 Oct 2023 20:23:20 +0300 Subject: [PATCH 3/8] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed196a3..e14f07d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- Adds generics to Promise types in PHPDocs ### Changed From 1c988e47a9fabc6a018464fb153c2402c049d16b Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Fri, 27 Oct 2023 15:40:31 +0300 Subject: [PATCH 4/8] Return value from waiting on the custom response handler --- src/GuzzleRequestAdapter.php | 43 ++++++++++++++++++++++------ tests/GuzzleRequestAdapterTest.php | 45 +++++++++++++++++++----------- 2 files changed, 63 insertions(+), 25 deletions(-) diff --git a/src/GuzzleRequestAdapter.php b/src/GuzzleRequestAdapter.php index c66ea4a..c1029b8 100644 --- a/src/GuzzleRequestAdapter.php +++ b/src/GuzzleRequestAdapter.php @@ -45,6 +45,7 @@ use Psr\Http\Message\StreamInterface; use RuntimeException; use Throwable; +use UnexpectedValueException; /** * Class GuzzleRequestAdapter @@ -111,7 +112,7 @@ public function __construct(AuthenticationProvider $authenticationProvider, * @param ResponseInterface $result * @param array, string}>|null $errorMappings * @param SpanInterface $span - * @return Promise|null + * @return Promise|null */ private function tryHandleResponse(RequestInformation $requestInfo, ResponseInterface $result, @@ -139,11 +140,15 @@ public function sendAsync(RequestInformation $requestInfo, array $targetCallable $responseMessage = $this->getHttpResponseMessage($requestInfo, '', $span); $finalResponse = $responseMessage->then( function (ResponseInterface $result) use ($targetCallable, $requestInfo, $errorMappings, &$span) { - $response = $this->tryHandleResponse($requestInfo, $result, $errorMappings, $span); $this->setHttpResponseAttributesInSpan($span, $result); + $response = $this->tryHandleResponse($requestInfo, $result, $errorMappings, $span); if ($response !== null) { $span->addEvent(self::EVENT_RESPONSE_HANDLER_INVOKED_KEY); - return $response; + $customResponse = $response->wait(); + if ($customResponse instanceof $targetCallable[0] || is_null($customResponse)) { + return $customResponse; + } + throw new UnexpectedValueException("Custom response handler failed to return object of expected type {$targetCallable[0]}|null"); } $this->throwFailedResponse($result, $errorMappings, $span); @@ -195,7 +200,19 @@ function (ResponseInterface $result) use ($targetCallable, $requestInfo, $errorM if ($response !== null) { $span->addEvent(self::EVENT_RESPONSE_HANDLER_INVOKED_KEY); - return $response; + $customResponse = $response->wait(); + if (is_array($customResponse)) { + foreach ($customResponse as $item) { + if (!is_null($item) && !$item instanceof $targetCallable[0]) { + throw new UnexpectedValueException("Custom response handler returned array containing invalid type. Expected type: {$targetCallable[0]}|null"); + } + } + return $customResponse; + } + else if (is_null($customResponse)) { + return $customResponse; + } + throw new UnexpectedValueException("Custom response handler failed to return array of expected type: {$targetCallable[0]}|null"); } $this->throwFailedResponse($result, $errorMappings, $span); if ($this->is204NoContentResponse($result)) { @@ -214,7 +231,7 @@ function (ResponseInterface $result) use ($targetCallable, $requestInfo, $errorM $scope->detach(); $span->end(); } - return $finalResponse; + return $finalResponse; /** @phpstan-ignore-line */ } /** @@ -231,7 +248,7 @@ function (ResponseInterface $result) use ($primitiveType, $requestInfo, $errorMa $response = $this->tryHandleResponse($requestInfo, $result, $errorMappings, $span); if ($response !== null) { - return $response; + return $response->wait(); } $this->throwFailedResponse($result, $errorMappings, $span); $this->setResponseType($primitiveType, $span); @@ -293,7 +310,11 @@ function (ResponseInterface $result) use ($primitiveType, $requestInfo, $errorMa $response = $this->tryHandleResponse($requestInfo, $result, $errorMappings, $span); if ($response !== null) { - return $response; + $customResponse = $response->wait(); + if (is_array($customResponse) || is_null($customResponse)) { + return $customResponse; + } + throw new UnexpectedValueException("Custom response handler failed to return array of expected type: {$primitiveType}"); } $this->throwFailedResponse($result, $errorMappings, $span); if ($this->is204NoContentResponse($result)) { @@ -320,10 +341,14 @@ public function sendNoContentAsync(RequestInformation $requestInfo, ?array $erro $finalResponse = $this->getHttpResponseMessage($requestInfo, '', $span)->then( function (ResponseInterface $result) use ($requestInfo, $errorMappings, &$span) { $this->setHttpResponseAttributesInSpan($span, $result); - $response = $this->tryHandleResponse($requestInfo, $result, $errorMappings, $span); + $response = $this->tryHandleResponse($requestInfo, $result,$errorMappings, $span); if ($response !== null) { - return $response; + $customResponse = $response->wait(); + if (is_null($customResponse)) { + return $customResponse; + } + throw new UnexpectedValueException("Custom response handler failed to return value of expected return type: null"); } $this->throwFailedResponse($result, $errorMappings, $span); return null; diff --git a/tests/GuzzleRequestAdapterTest.php b/tests/GuzzleRequestAdapterTest.php index 4a8cc16..eaf28ad 100644 --- a/tests/GuzzleRequestAdapterTest.php +++ b/tests/GuzzleRequestAdapterTest.php @@ -22,6 +22,8 @@ use Microsoft\Kiota\Http\GuzzleRequestAdapter; use Microsoft\Kiota\Http\Middleware\Options\ResponseHandlerOption; use PHPUnit\Framework\TestCase; +use Microsoft\Kiota\Abstractions\NativeResponseHandler; +use Psr\Http\Message\ResponseInterface; class GuzzleRequestAdapterTest extends TestCase { @@ -109,12 +111,19 @@ public function testSendAsync(): void public function testSendAsyncWithResponseHandler(): void { - $requestAdapter = $this->mockRequestAdapter([new Response(200, ['Content-Type' => $this->contentType])]); + $requestAdapter = $this->mockRequestAdapter([ + new Response(200, ['Content-Type' => $this->contentType]), + new Response(200, ['Content-Type' => $this->contentType]) + ]); $customResponseHandler = $this->createMock(ResponseHandler::class); - $customResponseHandler->expects($this->once()) - ->method('handleResponseAsync'); + $customResponseHandler->method('handleResponseAsync')->willReturn(new FulfilledPromise(new TestUser())); $this->requestInformation->addRequestOptions(new ResponseHandlerOption($customResponseHandler)); - $this->assertInstanceOf(Promise::class, $requestAdapter->sendAsync($this->requestInformation, array(TestUser::class, 'createFromDiscriminatorValue'))->wait()); + $this->assertInstanceOf(TestUser::class, $requestAdapter->sendAsync($this->requestInformation, array(TestUser::class, 'createFromDiscriminatorValue'))->wait()); + + $nativeResponseHandler = new NativeResponseHandler(); + $this->requestInformation->addRequestOptions(new ResponseHandlerOption($nativeResponseHandler)); + $this->assertNull($requestAdapter->sendAsync($this->requestInformation, array(TestUser::class, 'createFromDiscriminatorValue'))->wait()); + $this->assertInstanceOf(ResponseInterface::class, $nativeResponseHandler->getResponse()); } public function testSendCollectionAsync(): void @@ -131,10 +140,12 @@ public function testSendCollectionAsyncWithResponseHandler(): void { $requestAdapter = $this->mockRequestAdapter([new Response(200, ['Content-Type' => 'application/json'])]); $customResponseHandler = $this->createMock(ResponseHandler::class); - $customResponseHandler->expects($this->once()) - ->method('handleResponseAsync'); + $customResponseHandler->method('handleResponseAsync')->willReturn(new FulfilledPromise([new TestUser()])); $this->requestInformation->addRequestOptions(new ResponseHandlerOption($customResponseHandler)); - $this->assertInstanceOf(Promise::class, $requestAdapter->sendCollectionAsync($this->requestInformation, array(TestUser::class, 'createFromDiscriminatorValue'))->wait()); + $result = $requestAdapter->sendCollectionAsync($this->requestInformation, array(TestUser::class, 'createFromDiscriminatorValue'))->wait(); + $this->assertIsArray($result); + $this->assertEquals(1, sizeof($result)); + $this->assertInstanceOf(TestUser::class, $result[0]); } public function testSendPrimitiveAsync(): void @@ -156,10 +167,11 @@ public function testSendPrimitiveAsyncWithResponseHandler(): void { $requestAdapter = $this->mockRequestAdapter([new Response(200, ['Content-Type' => 'application/json'])]); $customResponseHandler = $this->createMock(ResponseHandler::class); - $customResponseHandler->expects($this->once()) - ->method('handleResponseAsync'); + $customResponseHandler->method('handleResponseAsync')->willReturn(new FulfilledPromise(100)); $this->requestInformation->addRequestOptions(new ResponseHandlerOption($customResponseHandler)); - $this->assertInstanceOf(Promise::class, $requestAdapter->sendPrimitiveAsync($this->requestInformation, 'int')->wait()); + $result = $requestAdapter->sendPrimitiveAsync($this->requestInformation, 'int')->wait(); + $this->assertIsInt($result); + $this->assertEquals(100, $result); } public function testSendPrimitiveCollectionAsync(): void @@ -176,10 +188,12 @@ public function testSendPrimitiveCollectionAsyncWithResponseHandler(): void { $requestAdapter = $this->mockRequestAdapter([new Response(200, ['Content-Type' => 'application/json'])]); $customResponseHandler = $this->createMock(ResponseHandler::class); - $customResponseHandler->expects($this->once()) - ->method('handleResponseAsync'); + $customResponseHandler->method('handleResponseAsync')->willReturn(new FulfilledPromise(['hello', 'world'])); $this->requestInformation->addRequestOptions(new ResponseHandlerOption($customResponseHandler)); - $this->assertInstanceOf(Promise::class, $requestAdapter->sendPrimitiveCollectionAsync($this->requestInformation, 'string')->wait()); + $result = $requestAdapter->sendPrimitiveCollectionAsync($this->requestInformation, 'string')->wait(); + $this->assertIsArray($result); + $this->assertEquals(2, sizeof($result)); + $this->assertEquals('hello', $result[0]); } public function testSendNoContentAsync(): void @@ -193,10 +207,9 @@ public function testSendNoContentAsyncWithResponseHandler(): void { $requestAdapter = $this->mockRequestAdapter([new Response(200, ['Content-Type' => 'application/json'])]); $customResponseHandler = $this->createMock(ResponseHandler::class); - $customResponseHandler->expects($this->once()) - ->method('handleResponseAsync'); + $customResponseHandler->method('handleResponseAsync')->willReturn(new FulfilledPromise(null)); $this->requestInformation->addRequestOptions(new ResponseHandlerOption($customResponseHandler)); - $this->assertInstanceOf(Promise::class, $requestAdapter->sendNoContentAsync($this->requestInformation)->wait()); + $this->assertNull($requestAdapter->sendNoContentAsync($this->requestInformation)->wait()); } public function testExceptionThrownOnErrorResponses(): void From 457f6e3a067e681e4b517fa62b27dbe7f097bb90 Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Mon, 30 Oct 2023 15:23:09 +0300 Subject: [PATCH 5/8] Fix codesmells --- src/GuzzleRequestAdapter.php | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/GuzzleRequestAdapter.php b/src/GuzzleRequestAdapter.php index c1029b8..c094d6f 100644 --- a/src/GuzzleRequestAdapter.php +++ b/src/GuzzleRequestAdapter.php @@ -148,7 +148,9 @@ function (ResponseInterface $result) use ($targetCallable, $requestInfo, $errorM if ($customResponse instanceof $targetCallable[0] || is_null($customResponse)) { return $customResponse; } - throw new UnexpectedValueException("Custom response handler failed to return object of expected type {$targetCallable[0]}|null"); + throw new UnexpectedValueException( + "Custom response handler failed to return object of expected type {$targetCallable[0]}|null" + ); } $this->throwFailedResponse($result, $errorMappings, $span); @@ -204,15 +206,20 @@ function (ResponseInterface $result) use ($targetCallable, $requestInfo, $errorM if (is_array($customResponse)) { foreach ($customResponse as $item) { if (!is_null($item) && !$item instanceof $targetCallable[0]) { - throw new UnexpectedValueException("Custom response handler returned array containing invalid type. Expected type: {$targetCallable[0]}|null"); + throw new UnexpectedValueException( + "Custom response handler returned array containing invalid type. + Expected type: {$targetCallable[0]}|null" + ); } } return $customResponse; } - else if (is_null($customResponse)) { + elseif (is_null($customResponse)) { return $customResponse; } - throw new UnexpectedValueException("Custom response handler failed to return array of expected type: {$targetCallable[0]}|null"); + throw new UnexpectedValueException( + "Custom response handler failed to return array of expected type:{$targetCallable[0]}|null" + ); } $this->throwFailedResponse($result, $errorMappings, $span); if ($this->is204NoContentResponse($result)) { @@ -314,7 +321,9 @@ function (ResponseInterface $result) use ($primitiveType, $requestInfo, $errorMa if (is_array($customResponse) || is_null($customResponse)) { return $customResponse; } - throw new UnexpectedValueException("Custom response handler failed to return array of expected type: {$primitiveType}"); + throw new UnexpectedValueException( + "Custom response handler failed to return array of expected type: {$primitiveType}" + ); } $this->throwFailedResponse($result, $errorMappings, $span); if ($this->is204NoContentResponse($result)) { @@ -348,7 +357,9 @@ function (ResponseInterface $result) use ($requestInfo, $errorMappings, &$span) if (is_null($customResponse)) { return $customResponse; } - throw new UnexpectedValueException("Custom response handler failed to return value of expected return type: null"); + throw new UnexpectedValueException( + "Custom response handler failed to return value of expected return type: null" + ); } $this->throwFailedResponse($result, $errorMappings, $span); return null; From 9ee53de011428e2211690738ea8459a1490fa02d Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Mon, 30 Oct 2023 15:34:29 +0300 Subject: [PATCH 6/8] Bump test coverage --- tests/GuzzleRequestAdapterTest.php | 111 ++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 1 deletion(-) diff --git a/tests/GuzzleRequestAdapterTest.php b/tests/GuzzleRequestAdapterTest.php index eaf28ad..f3b20d0 100644 --- a/tests/GuzzleRequestAdapterTest.php +++ b/tests/GuzzleRequestAdapterTest.php @@ -24,6 +24,8 @@ use PHPUnit\Framework\TestCase; use Microsoft\Kiota\Abstractions\NativeResponseHandler; use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\StreamInterface; +use UnexpectedValueException; class GuzzleRequestAdapterTest extends TestCase { @@ -126,6 +128,27 @@ public function testSendAsyncWithResponseHandler(): void $this->assertInstanceOf(ResponseInterface::class, $nativeResponseHandler->getResponse()); } + public function testSendAsyncWithCustomResponseHandlerWithWrongReturnTypeThrowsException(): void + { + $this->expectException(UnexpectedValueException::class); + $requestAdapter = $this->mockRequestAdapter([ + new Response(200, ['Content-Type' => $this->contentType]), + new Response(200, ['Content-Type' => $this->contentType]) + ]); + $customResponseHandler = $this->createMock(ResponseHandler::class); + $customResponseHandler->method('handleResponseAsync')->willReturn(new FulfilledPromise(100)); + $this->requestInformation->addRequestOptions(new ResponseHandlerOption($customResponseHandler)); + $requestAdapter->sendAsync($this->requestInformation, array(TestUser::class, 'createFromDiscriminatorValue'))->wait(); + } + + public function testSendAsyncWithNoContentResponseReturnsNull(): void + { + $requestAdapter = $this->mockRequestAdapter([ + new Response(204, ['Content-Type' => $this->contentType]), + ]); + $this->assertNull($requestAdapter->sendAsync($this->requestInformation, array(TestUser::class, 'createFromDiscriminatorValue'))->wait()); + } + public function testSendCollectionAsync(): void { $requestAdapter = $this->mockRequestAdapter([new Response(200, ['Content-Type' => $this->contentType])]); @@ -136,9 +159,19 @@ public function testSendCollectionAsync(): void $this->assertInstanceOf(TestUser::class, $result[0]); } + public function testSendCollectionAsyncWithNoContentResponseReturnsNull(): void + { + $requestAdapter = $this->mockRequestAdapter([ + new Response(204, ['Content-Type' => $this->contentType]), + ]); + $this->assertNull($requestAdapter->sendCollectionAsync($this->requestInformation, array(TestUser::class, 'createFromDiscriminatorValue'))->wait()); + } + public function testSendCollectionAsyncWithResponseHandler(): void { - $requestAdapter = $this->mockRequestAdapter([new Response(200, ['Content-Type' => 'application/json'])]); + $requestAdapter = $this->mockRequestAdapter([ + new Response(200, ['Content-Type' => 'application/json']), + ]); $customResponseHandler = $this->createMock(ResponseHandler::class); $customResponseHandler->method('handleResponseAsync')->willReturn(new FulfilledPromise([new TestUser()])); $this->requestInformation->addRequestOptions(new ResponseHandlerOption($customResponseHandler)); @@ -146,6 +179,32 @@ public function testSendCollectionAsyncWithResponseHandler(): void $this->assertIsArray($result); $this->assertEquals(1, sizeof($result)); $this->assertInstanceOf(TestUser::class, $result[0]); + + } + + public function testSendCollectionAsyncWithCustomResponseHandlerReturnsNull(): void + { + $requestAdapter = $this->mockRequestAdapter([ + new Response(200, ['Content-Type' => 'application/json']), + ]); + $customResponseHandler = $this->createMock(ResponseHandler::class); + $customResponseHandler->method('handleResponseAsync')->willReturn(new FulfilledPromise(null)); + $this->requestInformation->addRequestOptions(new ResponseHandlerOption($customResponseHandler)); + $result = $requestAdapter->sendCollectionAsync($this->requestInformation, array(TestUser::class, 'createFromDiscriminatorValue'))->wait(); + $this->assertNull($result); + } + + public function testSendCollectionAsyncWithCustomResponseHandlerWithWrongReturnTypeThrowsException(): void + { + $this->expectException(UnexpectedValueException::class); + $requestAdapter = $this->mockRequestAdapter([ + new Response(200, ['Content-Type' => $this->contentType]), + new Response(200, ['Content-Type' => $this->contentType]) + ]); + $customResponseHandler = $this->createMock(ResponseHandler::class); + $customResponseHandler->method('handleResponseAsync')->willReturn(new FulfilledPromise([new TestUser(), 'string'])); + $this->requestInformation->addRequestOptions(new ResponseHandlerOption($customResponseHandler)); + $requestAdapter->sendCollectionAsync($this->requestInformation, array(TestUser::class, 'createFromDiscriminatorValue'))->wait(); } public function testSendPrimitiveAsync(): void @@ -163,6 +222,24 @@ public function testSendPrimitiveAsyncThrowsExceptionForUnsupportedType(): void $promise->wait(); } + public function testSendPrimitiveAsyncWithNoContentResponseReturnsNull(): void + { + $requestAdapter = $this->mockRequestAdapter([ + new Response(204, ['Content-Type' => $this->contentType]), + ]); + $this->assertNull($requestAdapter->sendPrimitiveAsync($this->requestInformation, 'int')->wait()); + } + + public function testSendPrimitiveAsyncReturnsStream(): void + { + $requestAdapter = $this->mockRequestAdapter([ + new Response(200, ['Content-Type' => $this->contentType], Utils::streamFor('hello world')), + ]); + $result = $requestAdapter->sendPrimitiveAsync($this->requestInformation, StreamInterface::class)->wait(); + $this->assertInstanceOf(StreamInterface::class, $result); + $this->assertEquals('hello world', $result->getContents()); + } + public function testSendPrimitiveAsyncWithResponseHandler(): void { $requestAdapter = $this->mockRequestAdapter([new Response(200, ['Content-Type' => 'application/json'])]); @@ -196,6 +273,26 @@ public function testSendPrimitiveCollectionAsyncWithResponseHandler(): void $this->assertEquals('hello', $result[0]); } + public function testSendPrimitiveCollectionAsyncWithNoContentResponseReturnsNull(): void + { + $requestAdapter = $this->mockRequestAdapter([ + new Response(204, ['Content-Type' => $this->contentType]), + ]); + $this->assertNull($requestAdapter->sendPrimitiveCollectionAsync($this->requestInformation, 'int')->wait()); + } + + public function testSendPrimitiveCollectionAsyncWithCustomResponseHandlerWithWrongReturnTypeThrowsException(): void + { + $this->expectException(UnexpectedValueException::class); + $requestAdapter = $this->mockRequestAdapter([ + new Response(200, ['Content-Type' => $this->contentType]), + ]); + $customResponseHandler = $this->createMock(ResponseHandler::class); + $customResponseHandler->method('handleResponseAsync')->willReturn(new FulfilledPromise('string')); + $this->requestInformation->addRequestOptions(new ResponseHandlerOption($customResponseHandler)); + $requestAdapter->sendPrimitiveCollectionAsync($this->requestInformation, 'string')->wait(); + } + public function testSendNoContentAsync(): void { $requestAdapter = $this->mockRequestAdapter([new Response(204, ['Content-Type' => 'application/json'])]); @@ -212,6 +309,18 @@ public function testSendNoContentAsyncWithResponseHandler(): void $this->assertNull($requestAdapter->sendNoContentAsync($this->requestInformation)->wait()); } + public function testSendNoContentAsyncWithCustomResponseHandlerWithWrongReturnTypeThrowsException(): void + { + $this->expectException(UnexpectedValueException::class); + $requestAdapter = $this->mockRequestAdapter([ + new Response(200, ['Content-Type' => $this->contentType]), + ]); + $customResponseHandler = $this->createMock(ResponseHandler::class); + $customResponseHandler->method('handleResponseAsync')->willReturn(new FulfilledPromise('string')); + $this->requestInformation->addRequestOptions(new ResponseHandlerOption($customResponseHandler)); + $requestAdapter->sendNoContentAsync($this->requestInformation)->wait(); + } + public function testExceptionThrownOnErrorResponses(): void { $this->expectException(ApiException::class); From 24b34a891a3bc97e397753385351487dcf047381 Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Mon, 30 Oct 2023 16:15:33 +0300 Subject: [PATCH 7/8] Update CHANGELOG and bump version --- CHANGELOG.md | 6 +++++- src/Constants.php | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e14f07d..f0ec37f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added -- Adds generics to Promise types in PHPDocs ### Changed +## [0.9.0] - 2023-10-30 + +### Added +- Adds generics to Promise types in PHPDocs + ## [0.8.4] - 2023-10-11 ### Added diff --git a/src/Constants.php b/src/Constants.php index 7b949b3..bea24e7 100644 --- a/src/Constants.php +++ b/src/Constants.php @@ -5,5 +5,5 @@ final class Constants { /** @var string The current version for this Library */ - public const KIOTA_HTTP_CLIENT_VERSION = '0.8.4'; + public const KIOTA_HTTP_CLIENT_VERSION = '0.9.0'; } From 9ace7504b732365ae83bb27e3b863d638d74c85d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Oct 2023 00:49:33 +0000 Subject: [PATCH 8/8] Update microsoft/kiota-abstractions requirement || ^0.9.0 Updates the requirements on [microsoft/kiota-abstractions](https://github.com/microsoft/kiota-abstractions-php) to permit the latest version. - [Release notes](https://github.com/microsoft/kiota-abstractions-php/releases) - [Changelog](https://github.com/microsoft/kiota-abstractions-php/blob/dev/CHANGELOG.md) - [Commits](https://github.com/microsoft/kiota-abstractions-php/compare/0.8.0...0.9.0) --- updated-dependencies: - dependency-name: microsoft/kiota-abstractions dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 4b8d52a..338d502 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,7 @@ "require": { "php": "^7.4 | ^8.0", "guzzlehttp/guzzle": "^7.0", - "microsoft/kiota-abstractions": "^0.8.0", + "microsoft/kiota-abstractions": "^0.8.0 || ^0.9.0", "ext-zlib": "*", "ext-json": "*" },