From c332eeb11df0827788e5d72c8aca0dd9aa1ca4bc Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Wed, 18 May 2022 21:08:39 +0300 Subject: [PATCH 01/11] Remove unused QueryParameterBase --- abstractions/php/src/QueryParameterBase.php | 29 --------------------- 1 file changed, 29 deletions(-) delete mode 100644 abstractions/php/src/QueryParameterBase.php diff --git a/abstractions/php/src/QueryParameterBase.php b/abstractions/php/src/QueryParameterBase.php deleted file mode 100644 index 6d5f518e6d..0000000000 --- a/abstractions/php/src/QueryParameterBase.php +++ /dev/null @@ -1,29 +0,0 @@ -|null $target - */ - public function addQueryParameters(?array &$target): void - { - if (is_null($target)) { - throw new \InvalidArgumentException('$target'); - } - $objectClassProperties = get_object_vars($this); - - $staticClassProperties = get_class_vars(get_class($this)); - // Static properties of the class are not included in the output for - // get_object_vars($this). To solve this, I call get_class_vars which - // includes static properties and then merge the results. - $combinedProperties = array_merge($staticClassProperties, $objectClassProperties); - - foreach($combinedProperties as $classPropertyName => $classPropertyValue) { - $target[$classPropertyName] = $classPropertyValue; - } - } -} From 8b49feaa4fd1d1f79d042fa97f701597ec2cfe84 Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Thu, 19 May 2022 00:47:25 +0300 Subject: [PATCH 02/11] Add custom QueryParameter annotation --- abstractions/php/composer.json | 4 +-- abstractions/php/src/QueryParameter.php | 34 +++++++++++++++++++ abstractions/php/src/RequestInformation.php | 32 +++++++++++++---- .../php/tests/RequestInformationTest.php | 31 ++++++++++++++--- 4 files changed, 88 insertions(+), 13 deletions(-) create mode 100644 abstractions/php/src/QueryParameter.php diff --git a/abstractions/php/composer.json b/abstractions/php/composer.json index 7c10a6a6e8..78b0d9466b 100644 --- a/abstractions/php/composer.json +++ b/abstractions/php/composer.json @@ -23,11 +23,11 @@ } }, "require": { - "ramsey/uuid": "^3 || ^4", "php": "^7.4 || ^8.0", "php-http/promise": "^1.1.0", "php-http/message-factory": "^1.0.2", - "rize/uri-template": "^0.3.4" + "league/uri": "^6.5", + "doctrine/annotations": "^1.13" }, "require-dev": { "phpstan/phpstan": "^1.2.0", diff --git a/abstractions/php/src/QueryParameter.php b/abstractions/php/src/QueryParameter.php new file mode 100644 index 0000000000..f78948cbb8 --- /dev/null +++ b/abstractions/php/src/QueryParameter.php @@ -0,0 +1,34 @@ +name = $name; + } +} \ No newline at end of file diff --git a/abstractions/php/src/RequestInformation.php b/abstractions/php/src/RequestInformation.php index b7b928e75a..cbe7f7faf7 100644 --- a/abstractions/php/src/RequestInformation.php +++ b/abstractions/php/src/RequestInformation.php @@ -1,11 +1,14 @@ uri)) { @@ -49,9 +61,9 @@ public function getUri(): string { && is_string($this->pathParameters[self::$RAW_URL_KEY])) { $this->setUri($this->pathParameters[self::$RAW_URL_KEY]); } else { - $template = (new UriTemplate()); + $template = new UriTemplate($this->urlTemplate); $params = array_merge($this->pathParameters, $this->queryParameters); - return $template->expand($this->urlTemplate, $params); + return $template->expand($params); } return $this->uri; } @@ -136,10 +148,18 @@ public function setContentFromParsable(RequestAdapter $requestAdapter, string $c /** * Set the query parameters. - * @param array $queryParameters + * @param object $queryParameters */ - public function setQueryParameters(array $queryParameters): void { - $this->queryParameters = $queryParameters; + public function setQueryParameters(object $queryParameters): void { + $reflectionClass = new \ReflectionClass($queryParameters); + foreach ($reflectionClass->getProperties() as $classProperty) { + $propertyAnnotation = self::$annotationReader->getPropertyAnnotation($classProperty, QueryParameter::class); + if ($propertyAnnotation) { + $this->queryParameters[$propertyAnnotation->name] = $classProperty->getValue($queryParameters); + continue; + } + $this->queryParameters[$classProperty->name] = $classProperty->getValue($queryParameters); + } } /** diff --git a/abstractions/php/tests/RequestInformationTest.php b/abstractions/php/tests/RequestInformationTest.php index ae055c2e8f..c0b6c2488b 100644 --- a/abstractions/php/tests/RequestInformationTest.php +++ b/abstractions/php/tests/RequestInformationTest.php @@ -1,9 +1,10 @@ 'https://google.com', - 'user' => 'silas', + 'user%2Did' => 'silas', ]; - $queryParameters = ['startDate' => (new DateTime('2022-01-17'))->format('Y-m-d')]; - $this->requestInformation->urlTemplate = '{+baseUrl}/{user}/mails/?startDate=\'{startDate}\''; + $queryParameters = ['%24select' => ['subject', 'importance']]; + $this->requestInformation->urlTemplate = '{+baseUrl}/{user%2Did}/mails{?%24select}'; $this->requestInformation->pathParameters = $pathParameters; $this->requestInformation->queryParameters = $queryParameters; - $this->assertEquals("https://google.com/silas/mails/?startDate='2022-01-17'", $this->requestInformation->getUri()); + $this->assertEquals("https://google.com/silas/mails?%24select=subject,importance", $this->requestInformation->getUri()); } + + public function testSetQueryParameters(): void { + $this->requestInformation->urlTemplate = '{?%24select,top}'; + + $queryParam = new TestQueryParameter(); + $queryParam->select = ['displayName', 'age']; + $this->requestInformation->setQueryParameters($queryParam); + $this->assertArrayHasKey('%24select', $this->requestInformation->queryParameters); + $this->assertEquals(['displayName', 'age'], $this->requestInformation->queryParameters['%24select']); + $this->assertArrayHasKey('top', $this->requestInformation->queryParameters); + $this->assertEquals('?%24select=displayName,age&top=10', $this->requestInformation->getUri()); + } +} + +class TestQueryParameter { + /** + * @QueryParameter("%24select") + */ + public array $select = []; + public int $top = 10; // no annotation } \ No newline at end of file From f80e2eaa27215b71eee7df1c0a7dfefb601ed2f2 Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Thu, 19 May 2022 01:18:49 +0300 Subject: [PATCH 03/11] Add annotation to generated code --- src/Kiota.Builder/Refiners/PhpRefiner.cs | 3 +-- src/Kiota.Builder/Writers/Php/CodePropertyWriter.cs | 10 ++++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Kiota.Builder/Refiners/PhpRefiner.cs b/src/Kiota.Builder/Refiners/PhpRefiner.cs index aeda06d23d..9b85119814 100644 --- a/src/Kiota.Builder/Refiners/PhpRefiner.cs +++ b/src/Kiota.Builder/Refiners/PhpRefiner.cs @@ -132,8 +132,7 @@ public override void Refine(CodeNamespace generatedCode) new(x => x is CodeProperty {Type.Name: {}} property && property.Type.Name.Equals("DateTime", StringComparison.OrdinalIgnoreCase), "", "DateTime"), new(x => x is CodeProperty {Type.Name: {}} property && property.Type.Name.Equals("DateTimeOffset", StringComparison.OrdinalIgnoreCase), "", "DateTime"), new(x => x is CodeMethod method && method.IsOfKind(CodeMethodKind.ClientConstructor), "Microsoft\\Kiota\\Abstractions", "ApiClientBuilder"), - new(x => (x is CodeMethod {ReturnType.Name: {} } method && method.ReturnType.Name.Equals("Byte", StringComparison.OrdinalIgnoreCase)) || (x is CodeParameter parameter && parameter.Type.Name.Equals("Byte", StringComparison.OrdinalIgnoreCase)), "Microsoft\\Kiota\\Abstractions\\Types", "Byte"), - new(x => x is CodeProperty {Type.Name: {}} property && property.Type.Name.Equals("Byte", StringComparison.OrdinalIgnoreCase), "Microsoft\\Kiota\\Abstractions\\Types", "Byte") + new(x => x is CodeProperty property && property.IsOfKind(CodePropertyKind.QueryParameter) && !string.IsNullOrEmpty(property.SerializationName), "Microsoft\\Kiota\\Abstractions", "QueryParameter") }; private static void CorrectPropertyType(CodeProperty currentProperty) { if(currentProperty.IsOfKind(CodePropertyKind.RequestAdapter)) { diff --git a/src/Kiota.Builder/Writers/Php/CodePropertyWriter.cs b/src/Kiota.Builder/Writers/Php/CodePropertyWriter.cs index cf761a2622..0981bbe224 100644 --- a/src/Kiota.Builder/Writers/Php/CodePropertyWriter.cs +++ b/src/Kiota.Builder/Writers/Php/CodePropertyWriter.cs @@ -34,8 +34,14 @@ private void WritePropertyDocComment(CodeProperty codeProperty, LanguageWriter w var typeString = (collectionKind ? GetCollectionDocString(codeProperty) : conventions.GetTypeString(codeProperty.Type, codeProperty)); - writer.WriteLine($"{conventions.DocCommentStart} @var {typeString}{(codeProperty.Type.IsNullable ? "|null" : string.Empty)} ${codeProperty.Name} " + - $"{(hasDescription ? propertyDescription : string.Empty)} {conventions.DocCommentEnd}"); + writer.WriteLine(conventions.DocCommentStart); + if (codeProperty.IsOfKind(CodePropertyKind.QueryParameter) && codeProperty.IsNameEscaped) + { + writer.WriteLine($"{conventions.DocCommentPrefix}@QueryParameter(\"{codeProperty.SerializationName}\")"); + } + writer.WriteLine($"{conventions.DocCommentPrefix}@var {typeString}{(codeProperty.Type.IsNullable ? "|null" : string.Empty)} ${codeProperty.Name} " + + $"{(hasDescription ? propertyDescription : string.Empty)}"); + writer.WriteLine(conventions.DocCommentEnd); } private string GetCollectionDocString(CodeProperty codeProperty) From c8a8dae31c7f3dffd3857620057df2e6243ded0a Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Thu, 19 May 2022 02:11:26 +0300 Subject: [PATCH 04/11] Add annotation tests --- abstractions/php/src/RequestInformation.php | 15 +++++++----- .../php/tests/RequestInformationTest.php | 4 +++- .../Writers/Php/CodePropertyWriterTests.cs | 23 +++++++++++++++++++ 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/abstractions/php/src/RequestInformation.php b/abstractions/php/src/RequestInformation.php index cbe7f7faf7..a640f8237d 100644 --- a/abstractions/php/src/RequestInformation.php +++ b/abstractions/php/src/RequestInformation.php @@ -148,17 +148,20 @@ public function setContentFromParsable(RequestAdapter $requestAdapter, string $c /** * Set the query parameters. - * @param object $queryParameters + * @param object|null $queryParameters */ - public function setQueryParameters(object $queryParameters): void { + public function setQueryParameters(?object $queryParameters): void { + if (!$queryParameters) return; $reflectionClass = new \ReflectionClass($queryParameters); foreach ($reflectionClass->getProperties() as $classProperty) { + $propertyValue = $classProperty->getValue($queryParameters); $propertyAnnotation = self::$annotationReader->getPropertyAnnotation($classProperty, QueryParameter::class); - if ($propertyAnnotation) { - $this->queryParameters[$propertyAnnotation->name] = $classProperty->getValue($queryParameters); - continue; + if ($propertyValue) { + if ($propertyAnnotation) { + $this->queryParameters[$propertyAnnotation->name] = $propertyValue; + } + $this->queryParameters[$classProperty->name] = $propertyValue; } - $this->queryParameters[$classProperty->name] = $classProperty->getValue($queryParameters); } } diff --git a/abstractions/php/tests/RequestInformationTest.php b/abstractions/php/tests/RequestInformationTest.php index c0b6c2488b..5d7acc319e 100644 --- a/abstractions/php/tests/RequestInformationTest.php +++ b/abstractions/php/tests/RequestInformationTest.php @@ -29,6 +29,8 @@ public function testSetQueryParameters(): void { $this->requestInformation->urlTemplate = '{?%24select,top}'; $queryParam = new TestQueryParameter(); + $this->requestInformation->setQueryParameters($queryParam); + $this->assertEquals('?top=10', $this->requestInformation->getUri()); $queryParam->select = ['displayName', 'age']; $this->requestInformation->setQueryParameters($queryParam); $this->assertArrayHasKey('%24select', $this->requestInformation->queryParameters); @@ -42,6 +44,6 @@ class TestQueryParameter { /** * @QueryParameter("%24select") */ - public array $select = []; + public ?array $select = null; public int $top = 10; // no annotation } \ No newline at end of file diff --git a/tests/Kiota.Builder.Tests/Writers/Php/CodePropertyWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Php/CodePropertyWriterTests.cs index f695732789..3cea8e6252 100644 --- a/tests/Kiota.Builder.Tests/Writers/Php/CodePropertyWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Php/CodePropertyWriterTests.cs @@ -140,5 +140,28 @@ public void WriteRequestAdapter() Assert.Contains("private RequestAdapter $adapter;", result); } + + [Fact] + public void WriteQueryParameter() + { + var queryParameter = new CodeProperty() + { + Name = "select", + Kind = CodePropertyKind.QueryParameter, + SerializationName = "%24select", + Access = AccessModifier.Private, + Type = new CodeType() + { + CollectionKind = CodeTypeBase.CodeTypeCollectionKind.Array, Name = "string" + } + }; + parentClass.AddProperty(queryParameter); + propertyWriter.WriteCodeElement(queryParameter, writer); + var result = tw.ToString(); + + Assert.Contains("@QueryParameter(\"%24select\")", result); + Assert.Contains("@var array|null $select", result); + Assert.Contains("private ?array $select", result); + } } } From d7338f9536b50b84728ce3fc0a9d7e3522701893 Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Mon, 23 May 2022 09:12:23 +0300 Subject: [PATCH 05/11] Add ParametersNameDecodingHandler --- http/php/guzzle/src/KiotaClientFactory.php | 1 + .../guzzle/src/Middleware/KiotaMiddleware.php | 15 ++++ .../Options/ParametersDecodingOption.php | 76 ++++++++++++++++ .../ParametersNameDecodingHandler.php | 78 ++++++++++++++++ .../ParametersNameDecodingHandlerTest.php | 88 +++++++++++++++++++ 5 files changed, 258 insertions(+) create mode 100644 http/php/guzzle/src/Middleware/Options/ParametersDecodingOption.php create mode 100644 http/php/guzzle/src/Middleware/ParametersNameDecodingHandler.php create mode 100644 http/php/guzzle/tests/Middleware/ParametersNameDecodingHandlerTest.php diff --git a/http/php/guzzle/src/KiotaClientFactory.php b/http/php/guzzle/src/KiotaClientFactory.php index 3264835026..99932644ad 100644 --- a/http/php/guzzle/src/KiotaClientFactory.php +++ b/http/php/guzzle/src/KiotaClientFactory.php @@ -67,6 +67,7 @@ public static function createWithConfig(array $guzzleConfig): Client public static function getDefaultHandlerStack(): HandlerStack { $handlerStack = new HandlerStack(Utils::chooseHandler()); + $handlerStack->push(KiotaMiddleware::parameterNamesDecoding()); $handlerStack->push(KiotaMiddleware::retry()); $handlerStack->push(GuzzleMiddleware::redirect()); return $handlerStack; diff --git a/http/php/guzzle/src/Middleware/KiotaMiddleware.php b/http/php/guzzle/src/Middleware/KiotaMiddleware.php index 5674e5a47e..6c15ffe329 100644 --- a/http/php/guzzle/src/Middleware/KiotaMiddleware.php +++ b/http/php/guzzle/src/Middleware/KiotaMiddleware.php @@ -10,6 +10,7 @@ use Microsoft\Kiota\Http\Middleware\Options\ChaosOption; use Microsoft\Kiota\Http\Middleware\Options\CompressionOption; +use Microsoft\Kiota\Http\Middleware\Options\ParametersDecodingOption; use Microsoft\Kiota\Http\Middleware\Options\RetryOption; use Microsoft\Kiota\Http\Middleware\Options\TelemetryOption; @@ -78,4 +79,18 @@ public static function chaos(?ChaosOption $chaosOption = null): callable return new ChaosHandler($handler, $chaosOption); }; } + + /** + * Middleware that decodes special characters in the request query parameter names that had to be encoded due to RFC 6570 + * restrictions before executing the request. Configured by the $decodingOption + * + * @param ParametersDecodingOption|null $decodingOption + * @return callable + */ + public static function parameterNamesDecoding(?ParametersDecodingOption $decodingOption = null): callable + { + return static function (callable $handler) use ($decodingOption): ParametersNameDecodingHandler { + return new ParametersNameDecodingHandler($handler, $decodingOption); + }; + } } diff --git a/http/php/guzzle/src/Middleware/Options/ParametersDecodingOption.php b/http/php/guzzle/src/Middleware/Options/ParametersDecodingOption.php new file mode 100644 index 0000000000..0a6a4ff2cb --- /dev/null +++ b/http/php/guzzle/src/Middleware/Options/ParametersDecodingOption.php @@ -0,0 +1,76 @@ +parametersToDecode = $parametersToDecode; + $this->enabled = $enabled; + } + + /** + * @return bool + */ + public function isEnabled(): bool + { + return $this->enabled; + } + + /** + * @param bool $enabled + */ + public function setEnabled(bool $enabled): void + { + $this->enabled = $enabled; + } + + /** + * @return array|string[] + */ + public function getParametersToDecode(): array + { + return $this->parametersToDecode; + } + + /** + * @param array|string[] $parametersToDecode + */ + public function setParametersToDecode(array $parametersToDecode): void + { + $this->parametersToDecode = $parametersToDecode; + } +} diff --git a/http/php/guzzle/src/Middleware/ParametersNameDecodingHandler.php b/http/php/guzzle/src/Middleware/ParametersNameDecodingHandler.php new file mode 100644 index 0000000000..5f743211e5 --- /dev/null +++ b/http/php/guzzle/src/Middleware/ParametersNameDecodingHandler.php @@ -0,0 +1,78 @@ +nextHandler = $nextHandler; + $this->decodingOption = ($decodingOption) ?: new ParametersDecodingOption(); + } + + /** + * @param RequestInterface $request + * @param array $options + * @return PromiseInterface + */ + public function __invoke(RequestInterface $request, array $options): PromiseInterface + { + // Request-level options override global options + if (array_key_exists(ParametersDecodingOption::class, $options)) { + $this->decodingOption = $options[ParametersDecodingOption::class]; + } + $request = $this->decodeQueryParameters($request); + $fn = $this->nextHandler; + return $fn($request, $options); + } + + /** + * @param RequestInterface $request + * @return RequestInterface + */ + private function decodeQueryParameters(RequestInterface $request): RequestInterface + { + if (!$this->decodingOption->isEnabled() || !$this->decodingOption->getParametersToDecode()) { + return $request; + } + $encodingsToReplace = array_map(function ($character) { return "%".dechex(ord($character)); }, $this->decodingOption->getParametersToDecode()); + $decodedUri = str_ireplace($encodingsToReplace, $this->decodingOption->getParametersToDecode(), $request->getUri()); + return $request->withUri(new Uri($decodedUri)); + } +} diff --git a/http/php/guzzle/tests/Middleware/ParametersNameDecodingHandlerTest.php b/http/php/guzzle/tests/Middleware/ParametersNameDecodingHandlerTest.php new file mode 100644 index 0000000000..6e11e537dd --- /dev/null +++ b/http/php/guzzle/tests/Middleware/ParametersNameDecodingHandlerTest.php @@ -0,0 +1,88 @@ +assertEquals($this->defaultDecodedUrl, strval($request->getUri())); + return new Response(200); + } + ]; + $this->executeMockRequest($mockResponse); + } + + public function testDisableDecoding() + { + $mockResponse = [ + function (RequestInterface $request, array $options) { + $this->assertEquals($this->defaultUrl, strval($request->getUri())); + return new Response(200); + } + ]; + $decodingOption = new ParametersDecodingOption(); + $decodingOption->setEnabled(false); + $this->executeMockRequest($mockResponse, $decodingOption); + } + + public function testCustomCharactersToDecode() + { + $url = $this->defaultUrl."&%23someId=1"; + $expectedDecoded = $this->defaultUrl."&#someId=1"; + + $mockResponse = [ + function (RequestInterface $request, array $options) use ($expectedDecoded) { + $this->assertEquals($expectedDecoded, strval($request->getUri())); + return new Response(200); + } + ]; + $decodingOption = new ParametersDecodingOption(['#']); + $this->executeMockRequest($mockResponse, $decodingOption, $url); + } + + public function testRequestLevelConfigOverridesGlobal() + { + $url = $this->defaultUrl."&%23someId=1"; + $expectedDecoded = $this->defaultUrl."&#someId=1"; + + $mockResponse = [ + function (RequestInterface $request, array $options) use ($expectedDecoded) { + $this->assertEquals($expectedDecoded, strval($request->getUri())); + return new Response(200); + } + ]; + $decodingOption = new ParametersDecodingOption(['#']); + $requestOptions = [ + ParametersDecodingOption::class => $decodingOption + ]; + $this->executeMockRequest($mockResponse, new ParametersDecodingOption(), $url, $requestOptions); + } + + private function executeMockRequest(array $mockResponses, ?ParametersDecodingOption $decodingOption = null, string $url = null, array $requestOptions = []): ResponseInterface + { + $mockHandler = new MockHandler($mockResponses); + $handlerStack = new HandlerStack($mockHandler); + $decodingOption = $decodingOption ?: new ParametersDecodingOption(); + $handlerStack->push(KiotaMiddleware::parameterNamesDecoding($decodingOption)); + + $guzzle = new Client(['handler' => $handlerStack]); + $url = $url ?: $this->defaultUrl; + return $guzzle->get($url, $requestOptions); + } +} From 2731c2b9dd79c89264aa05c129fcf3b208d1d098 Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Mon, 23 May 2022 09:20:19 +0300 Subject: [PATCH 06/11] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24a6df82bb..e5fef5f3c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added support for discriminator downcast in PHP. [#1255](https://github.com/microsoft/kiota/issues/1255) - Added support for multiple collections indexing under the same parent. - Added code exclusions placeholder in the generation. (oneOf) +- Added support for special characters in URL query parameter names. [#1584](https://github.com/microsoft/kiota/pull/1584) ### Changed From b759e18c7bc369b0e847a60a1e19ca5dba49eb79 Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Mon, 23 May 2022 09:25:20 +0300 Subject: [PATCH 07/11] Return UUID dependency lib --- abstractions/php/composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/abstractions/php/composer.json b/abstractions/php/composer.json index 78b0d9466b..523dbbd73f 100644 --- a/abstractions/php/composer.json +++ b/abstractions/php/composer.json @@ -27,7 +27,8 @@ "php-http/promise": "^1.1.0", "php-http/message-factory": "^1.0.2", "league/uri": "^6.5", - "doctrine/annotations": "^1.13" + "doctrine/annotations": "^1.13", + "ramsey/uuid": "^3 || ^4" }, "require-dev": { "phpstan/phpstan": "^1.2.0", From eddb99d17e20287f627fbb8e8265be451c4d3ea3 Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Mon, 23 May 2022 14:48:51 +0300 Subject: [PATCH 08/11] Separate multiple scopes with space --- .../src/PhpLeagueAccessTokenProvider.php | 2 +- .../tests/PhpLeagueAccessTokenProviderTest.php | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/authentication/php/phpleague/src/PhpLeagueAccessTokenProvider.php b/authentication/php/phpleague/src/PhpLeagueAccessTokenProvider.php index 52516c1312..6b34181c5b 100644 --- a/authentication/php/phpleague/src/PhpLeagueAccessTokenProvider.php +++ b/authentication/php/phpleague/src/PhpLeagueAccessTokenProvider.php @@ -78,7 +78,7 @@ public function getAuthorizationTokenAsync(string $url): Promise return new FulfilledPromise(null); } try { - $params = array_merge($this->tokenRequestContext->getParams(), ['scope' => implode(',', $this->scopes)]); + $params = array_merge($this->tokenRequestContext->getParams(), ['scope' => implode(' ', $this->scopes)]); if ($this->cachedToken) { if ($this->cachedToken->getExpires() && $this->cachedToken->hasExpired()) { if ($this->cachedToken->getRefreshToken()) { diff --git a/authentication/php/phpleague/tests/PhpLeagueAccessTokenProviderTest.php b/authentication/php/phpleague/tests/PhpLeagueAccessTokenProviderTest.php index e4b6bac0b3..eebe353109 100644 --- a/authentication/php/phpleague/tests/PhpLeagueAccessTokenProviderTest.php +++ b/authentication/php/phpleague/tests/PhpLeagueAccessTokenProviderTest.php @@ -14,6 +14,7 @@ use Microsoft\Kiota\Authentication\Oauth\OnBehalfOfContext; use Microsoft\Kiota\Authentication\PhpLeagueAccessTokenProvider; use PHPUnit\Framework\TestCase; +use Psr\Http\Message\RequestInterface; class PhpLeagueAccessTokenProviderTest extends TestCase { @@ -33,6 +34,23 @@ public function testPassingEmptyScopesThrowsException(): void $tokenProvider = new PhpLeagueAccessTokenProvider(new ClientCredentialContext('', '', ''), []); } + public function testPassingMultipleScopes(): void + { + $tokenProvider = new PhpLeagueAccessTokenProvider(new ClientCredentialContext( + 'tenantId', 'clientId', 'secret' + ), ['User.Read', 'Calendar.ReadWrite']); + $mockResponses = [ + function (RequestInterface $request) { + parse_str($request->getBody()->getContents(), $requestBodyMap); + $this->assertArrayHasKey('scope', $requestBodyMap); + $this->assertEquals('User.Read Calendar.ReadWrite', $requestBodyMap['scope']); + return new Response(200); + } + ]; + $tokenProvider->getOauthProvider()->setHttpClient($this->getMockHttpClient($mockResponses)); + $tokenProvider->getAuthorizationTokenAsync('https://example.com'); + } + public function testGetAuthorizationTokenWithSuccessfulTokenResponse(): void { $oauthContexts = $this->getOauthContexts(); From 074ce92f66a671fda0ebf3817c7671e94ebecf9f Mon Sep 17 00:00:00 2001 From: Andrew Omondi Date: Tue, 24 May 2022 12:49:35 +0300 Subject: [PATCH 09/11] Adds check for empty serializationName in method writers --- CHANGELOG.md | 1 + src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs | 2 +- src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs | 4 ++-- src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs | 4 ++-- src/Kiota.Builder/Writers/TypeScript/CodeMethodWriter.cs | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3229bbf4fb..4eed21a158 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed the lookup of model namespaces to only look in the target namespace to avoid reference collisions. - Fixed a bug for the generated send method for paths returning Enums in dotnet. - Breaking: renamed the --loglevel parameter to --log-level. +- Fixed a bug where some path parameter objects would have empty key values [#1586](https://github.com/microsoft/kiota/issues/1586) ## [0.1.3] - 2022-05-06 diff --git a/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs index 784cbe2b14..cb69b125dd 100644 --- a/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/CSharp/CodeMethodWriter.cs @@ -143,7 +143,7 @@ private void WriteConstructorBody(CodeClass parentClass, CodeMethod currentMetho pathParametersParam.Name.ToFirstCharacterLowerCase(), currentMethod.Parameters .Where(x => x.IsOfKind(CodeParameterKind.Path)) - .Select(x => (x.Type, x.SerializationName, x.Name.ToFirstCharacterLowerCase())) + .Select(x => (x.Type, string.IsNullOrEmpty(x.SerializationName) ? x.Name : x.SerializationName, x.Name.ToFirstCharacterLowerCase())) .ToArray()); AssignPropertyFromParameter(parentClass, currentMethod, CodeParameterKind.PathParameters, CodePropertyKind.PathParameters, writer, conventions.TempDictionaryVarName); } diff --git a/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs index 7ca0633e8a..24a888c63b 100644 --- a/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Go/CodeMethodWriter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Kiota.Builder.Extensions; @@ -266,7 +266,7 @@ private void WriteConstructorBody(CodeClass parentClass, CodeMethod currentMetho pathParametersParam.Name.ToFirstCharacterLowerCase(), currentMethod.Parameters .Where(x => x.IsOfKind(CodeParameterKind.Path)) - .Select(x => (x.Type, x.SerializationName, x.Name.ToFirstCharacterLowerCase())) + .Select(x => (x.Type, string.IsNullOrEmpty(x.SerializationName) ? x.Name : x.SerializationName, x.Name.ToFirstCharacterLowerCase())) .ToArray()); AssignPropertyFromParameter(parentClass, currentMethod, CodeParameterKind.PathParameters, CodePropertyKind.PathParameters, writer, conventions.TempDictionaryVarName); } diff --git a/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs index 16c120f6f5..c25ff0ea48 100644 --- a/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Java/CodeMethodWriter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Kiota.Builder.Extensions; @@ -158,7 +158,7 @@ private void WriteConstructorBody(CodeClass parentClass, CodeMethod currentMetho pathParametersParam.Name.ToFirstCharacterLowerCase(), currentMethod.Parameters .Where(x => x.IsOfKind(CodeParameterKind.Path)) - .Select(x => (x.Type, x.SerializationName, x.Name.ToFirstCharacterLowerCase())) + .Select(x => (x.Type, string.IsNullOrEmpty(x.SerializationName) ? x.Name : x.SerializationName, x.Name.ToFirstCharacterLowerCase())) .ToArray()); AssignPropertyFromParameter(parentClass, currentMethod, CodeParameterKind.PathParameters, CodePropertyKind.PathParameters, writer, conventions.TempDictionaryVarName); } diff --git a/src/Kiota.Builder/Writers/TypeScript/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/TypeScript/CodeMethodWriter.cs index 94ae391499..be1ac88178 100644 --- a/src/Kiota.Builder/Writers/TypeScript/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/TypeScript/CodeMethodWriter.cs @@ -155,7 +155,7 @@ private void WriteConstructorBody(CodeClass parentClass, CodeMethod currentMetho pathParametersParam.Name.ToFirstCharacterLowerCase(), currentMethod.Parameters .Where(x => x.IsOfKind(CodeParameterKind.Path)) - .Select(x => (x.Type, x.SerializationName, x.Name.ToFirstCharacterLowerCase())) + .Select(x => (x.Type, string.IsNullOrEmpty(x.SerializationName) ? x.Name : x.SerializationName, x.Name.ToFirstCharacterLowerCase())) .ToArray()); AssignPropertyFromParameter(parentClass, currentMethod, CodeParameterKind.PathParameters, CodePropertyKind.PathParameters, writer, conventions.TempDictionaryVarName); } From ee0105e83e36b0ef6503418a65031b370d4401ef Mon Sep 17 00:00:00 2001 From: Philip Gichuhi Date: Tue, 24 May 2022 15:38:35 +0300 Subject: [PATCH 10/11] Fix bug in setQueryParameters() --- abstractions/php/src/RequestInformation.php | 1 + abstractions/php/tests/RequestInformationTest.php | 2 ++ 2 files changed, 3 insertions(+) diff --git a/abstractions/php/src/RequestInformation.php b/abstractions/php/src/RequestInformation.php index a640f8237d..625ad23c00 100644 --- a/abstractions/php/src/RequestInformation.php +++ b/abstractions/php/src/RequestInformation.php @@ -159,6 +159,7 @@ public function setQueryParameters(?object $queryParameters): void { if ($propertyValue) { if ($propertyAnnotation) { $this->queryParameters[$propertyAnnotation->name] = $propertyValue; + continue; } $this->queryParameters[$classProperty->name] = $propertyValue; } diff --git a/abstractions/php/tests/RequestInformationTest.php b/abstractions/php/tests/RequestInformationTest.php index 5d7acc319e..b1f2d83c9c 100644 --- a/abstractions/php/tests/RequestInformationTest.php +++ b/abstractions/php/tests/RequestInformationTest.php @@ -31,8 +31,10 @@ public function testSetQueryParameters(): void { $queryParam = new TestQueryParameter(); $this->requestInformation->setQueryParameters($queryParam); $this->assertEquals('?top=10', $this->requestInformation->getUri()); + $this->assertTrue(sizeof($this->requestInformation->queryParameters) == 1); $queryParam->select = ['displayName', 'age']; $this->requestInformation->setQueryParameters($queryParam); + $this->assertTrue(sizeof($this->requestInformation->queryParameters) == 2); $this->assertArrayHasKey('%24select', $this->requestInformation->queryParameters); $this->assertEquals(['displayName', 'age'], $this->requestInformation->queryParameters['%24select']); $this->assertArrayHasKey('top', $this->requestInformation->queryParameters); From 93bef763d6ee8251fbc5ea3206961985ba47a241 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 24 May 2022 09:34:44 -0400 Subject: [PATCH 11/11] - releases for build --- CHANGELOG.md | 6 ++++++ src/kiota/kiota.csproj | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e6a9167bc..33071ba5df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +### Changed + +## [0.2.0] - 2022-05-24 + +### Added + - Added support for enum options descriptions (C#/Go/Java/TypeScript). [#90](https://github.com/microsoft/kiota/issues/90) - Added support for file parameters types. [#221](https://github.com/microsoft/kiota/issues/221) - Added support for no content responses in PHP. [#1458](https://github.com/microsoft/kiota/issues/1458) diff --git a/src/kiota/kiota.csproj b/src/kiota/kiota.csproj index 1361c3f544..7ea1d255d1 100644 --- a/src/kiota/kiota.csproj +++ b/src/kiota/kiota.csproj @@ -14,7 +14,7 @@ Microsoft.OpenApi.Kiota Microsoft.OpenApi.Kiota ./nupkg - 0.1.3-preview + 0.2.0-preview https://github.com/microsoft/kiota/releases