Skip to content

Commit

Permalink
Merge pull request #96 from microsoft/dev
Browse files Browse the repository at this point in the history
Release 1.1.0
  • Loading branch information
Ndiritu committed Dec 1, 2023
2 parents 8fb23cc + 4579d98 commit 881ab52
Show file tree
Hide file tree
Showing 13 changed files with 443 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,8 @@
/vendor export-ignore
.gitattributes export-ignore
.gitignore export-ignore
/.github export-ignore
/tests export-ignore
phpstan.neon export-ignore
phpunit.xml export-ignore
sonar-project.properties export-ignore
2 changes: 1 addition & 1 deletion .github/workflows/pr-validation.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: PHP Http
name: Build

on:
workflow_dispatch:
Expand Down
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [1.1.0]

### Added
- Adds Headers Inspection Handler to expose raw request and response headers

### Changed
- Support 2xx range responses where response bodies are not present in the payload.
- Exclude non-prod files from shipped archive

## [1.0.0]

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Kiota HTTP Library for PHP

[![Build Status](https://travis-ci.org/microsoft/kiota-http-guzzle-php.svg?branch=main)](https://travis-ci.org/microsoft/kiota-http-guzzle-php)
![Build Status](https://github.com/microsoft/kiota-http-guzzle-php/actions/workflows/pr-validation.yml/badge.svg)
[![Latest Stable Version](https://poser.pugx.org/microsoft/kiota-http-guzzle/version)](https://packagist.org/packages/microsoft/kiota-http-guzzle)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=microsoft_kiota-http-guzzle-php&metric=coverage)](https://sonarcloud.io/dashboard?id=microsoft_kiota-http-guzzle-php)

Expand All @@ -17,7 +17,7 @@ run `composer require microsoft/kiota-http-guzzle` or add the following to your
```Shell
{
"require": {
"microsoft/kiota-http-guzzle": "^0.8.0"
"microsoft/kiota-http-guzzle": "^1.1.0"
}
}
```
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"require": {
"php": "^7.4 | ^8.0",
"guzzlehttp/guzzle": "^7.0",
"microsoft/kiota-abstractions": "^1.0.0",
"microsoft/kiota-abstractions": "^1.0.2",
"ext-zlib": "*",
"ext-json": "*"
},
Expand Down
2 changes: 1 addition & 1 deletion src/Constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
final class Constants
{
/** @var string The current version for this Library */
public const KIOTA_HTTP_CLIENT_VERSION = '1.0.0';
public const KIOTA_HTTP_CLIENT_VERSION = '1.1.0';
}
37 changes: 33 additions & 4 deletions src/GuzzleRequestAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ class GuzzleRequestAdapter implements RequestAdapter
* @var AuthenticationProvider
*/
private AuthenticationProvider $authenticationProvider;

/**
* @var TracerInterface
*/
private TracerInterface $tracer;

/**
Expand Down Expand Up @@ -159,6 +163,9 @@ function (ResponseInterface $result) use ($targetCallable, $requestInfo, $errorM
return null;
}
$rootNode = $this->getRootParseNode($result, $span);
if (is_null($rootNode)) {
return null;
}
$this->setResponseType($targetCallable[0], $span);
return $rootNode->getObjectValue($targetCallable);
}
Expand Down Expand Up @@ -226,6 +233,9 @@ function (ResponseInterface $result) use ($targetCallable, $requestInfo, $errorM
return null;
}
$rootNode = $this->getRootParseNode($result, $span);
if (is_null($rootNode)) {
return null;
}
$spanForDeserialization = $this->tracer->spanBuilder('ParseNode.getCollectionOfObjectValues')
->addLink($span->getContext())
->startSpan();
Expand Down Expand Up @@ -263,9 +273,15 @@ function (ResponseInterface $result) use ($primitiveType, $requestInfo, $errorMa
return null;
}
if ($primitiveType === StreamInterface::class) {
if ($result->getBody()->getSize() === 0) {
return null;
}
return $result->getBody();
}
$rootParseNode = $this->getRootParseNode($result, $span);
if (is_null($rootParseNode)) {
return null;
}
if (is_subclass_of($primitiveType, Enum::class)) {
return $rootParseNode->getEnumValue($primitiveType);
}
Expand Down Expand Up @@ -329,7 +345,11 @@ function (ResponseInterface $result) use ($primitiveType, $requestInfo, $errorMa
if ($this->is204NoContentResponse($result)) {
return null;
}
return $this->getRootParseNode($result, $span)->getCollectionOfPrimitiveValues($primitiveType);
$rootParseNode = $this->getRootParseNode($result, $span);
if (is_null($rootParseNode)) {
return null;
}
return $rootParseNode->getCollectionOfPrimitiveValues($primitiveType);
}
);
} finally {
Expand Down Expand Up @@ -467,17 +487,17 @@ public function convertToNative(RequestInformation $requestInformationInformatio
*
* @param ResponseInterface $response
* @param SpanInterface $span
* @return ParseNode
* @return ParseNode|null
*/
private function getRootParseNode(ResponseInterface $response, SpanInterface $span): ParseNode
private function getRootParseNode(ResponseInterface $response, SpanInterface $span): ?ParseNode
{
$rootParseNodeSpan = $this->tracer->spanBuilder('getRootParseNode')
->addLink($span->getContext())
->startSpan();
$scope = $rootParseNodeSpan->activate();
try {
if (!$response->hasHeader(RequestInformation::$contentTypeHeader)) {
throw new RuntimeException("No response content type header for deserialization");
return null;
}
$contentType = explode(';', $response->getHeaderLine(RequestInformation::$contentTypeHeader));

Expand Down Expand Up @@ -629,6 +649,15 @@ private function throwFailedResponse(ResponseInterface $response, ?array $errorM
$errorClass = array_key_exists($statusCodeAsString, $errorMappings) ? $errorMappings[$statusCodeAsString] : ($errorMappings[$statusCodeAsString[0] . 'XX'] ?? null);

$rootParseNode = $this->getRootParseNode($response, $errorSpan);
if (is_null($rootParseNode)) {
$ex = new ApiException(
"The server returned an unexpected status code but no response body for code: $statusCode"
);
$ex->setResponseStatusCode($response->getStatusCode());
$ex->setResponseHeaders($response->getHeaders());
$errorSpan->recordException($ex, ['message' => '', 'know_error' => false]);
throw $ex;
}
if ($errorClass !== null) {
$spanForDeserialization = $this->tracer->spanBuilder('ParseNode.GetObjectValue()')
->setParent(Context::getCurrent())
Expand Down
2 changes: 2 additions & 0 deletions src/KiotaClientFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware as GuzzleMiddleware;
use GuzzleHttp\Utils;
use Microsoft\Kiota\Http\Middleware\HeadersInspectionHandler;
use Microsoft\Kiota\Http\Middleware\KiotaMiddleware;
use Microsoft\Kiota\Http\Middleware\ParametersNameDecodingHandler;
use Microsoft\Kiota\Http\Middleware\RetryHandler;
Expand Down Expand Up @@ -74,6 +75,7 @@ public static function getDefaultHandlerStack(): HandlerStack
$handlerStack->push(GuzzleMiddleware::redirect(), 'kiotaRedirectHandler');
$handlerStack->push(KiotaMiddleware::userAgent(), UserAgentHandler::HANDLER_NAME);
$handlerStack->push(KiotaMiddleware::retry(), RetryHandler::HANDLER_NAME);
$handlerStack->push(KiotaMiddleware::headersInspection(), HeadersInspectionHandler::HANDLER_NAME);
return $handlerStack;
}
}
105 changes: 105 additions & 0 deletions src/Middleware/HeadersInspectionHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php
/**
* Copyright (c) Microsoft Corporation. All Rights Reserved.
* Licensed under the MIT License. See License in the project root
* for license information.
*/


namespace Microsoft\Kiota\Http\Middleware;

use GuzzleHttp\Promise\PromiseInterface;
use Microsoft\Kiota\Abstractions\RequestHeaders;
use Microsoft\Kiota\Http\Middleware\Options\HeadersInspectionHandlerOption;
use Microsoft\Kiota\Http\Middleware\Options\ObservabilityOption;
use OpenTelemetry\API\Trace\TracerInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

/**
* Class HeadersInspectionHandler
*
* Handler that allows you to get the raw request and response headers while still using the default deserialization
* of response bodies to models. Disabled by default. Configured using a {@link HeadersInspectionHandlerOption}
*
* @package Microsoft\Kiota\Http\Middleware
* @copyright 2023 Microsoft Corporation
* @license https://opensource.org/licenses/MIT MIT License
*/
class HeadersInspectionHandler
{
public const HANDLER_NAME = "kiotaHeadersInspectionHandler";
private const SPAN_NAME = "HeadersInspectionHandler_invoke?";
private const HANDLER_ENABLED_KEY = "com.microsoft.kiota.handler.headers_inspection.enable";

/**
* @var HeadersInspectionHandlerOption
*/
private HeadersInspectionHandlerOption $headersInspectionOption;

/**
* @var callable(RequestInterface, array<string,mixed>): PromiseInterface
*/
private $nextHandler;

/**
* @var TracerInterface
*/
private TracerInterface $tracer;

/**
* @param callable $nextHandler
* @param HeadersInspectionHandlerOption|null $headersInspectionOption
*/
public function __construct(callable $nextHandler, ?HeadersInspectionHandlerOption $headersInspectionOption = null)
{
$this->nextHandler = $nextHandler;
$this->headersInspectionOption = ($headersInspectionOption) ?: new HeadersInspectionHandlerOption();
$this->tracer = ObservabilityOption::getTracer();
}

/**
* @param RequestInterface $request
* @param array<string, mixed> $options
* @return PromiseInterface
*/
public function __invoke(RequestInterface $request, array $options): PromiseInterface
{
$span = $this->tracer->spanBuilder(self::SPAN_NAME)->startSpan();
$scope = $span->activate();
$span->setAttribute(self::HANDLER_ENABLED_KEY, true);

try {
if (array_key_exists(HeadersInspectionHandlerOption::class, $options)
&& $options[HeadersInspectionHandlerOption::class] instanceof HeadersInspectionHandlerOption
) {
$this->headersInspectionOption = $options[HeadersInspectionHandlerOption::class];
}

if ($this->headersInspectionOption->isInspectRequestHeaders()) {
$requestHeaders = new RequestHeaders();
$requestHeaders->putAll($request->getHeaders());
$this->headersInspectionOption->setRequestHeaders($requestHeaders);
}

$fn = $this->nextHandler;
return $fn($request, $options)->then(
function (?ResponseInterface $response) {
if (!$response) {
return $response;
}
if ($this->headersInspectionOption->isInspectResponseHeaders()) {
$responseHeaders = new RequestHeaders();
$responseHeaders->putAll($response->getHeaders());
$this->headersInspectionOption->setResponseHeaders($responseHeaders);
}
return $response;
}
);
} finally {
$scope->detach();
$span->end();
}
}

}
8 changes: 8 additions & 0 deletions src/Middleware/KiotaMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

use Microsoft\Kiota\Http\Middleware\Options\ChaosOption;
use Microsoft\Kiota\Http\Middleware\Options\CompressionOption;
use Microsoft\Kiota\Http\Middleware\Options\HeadersInspectionHandlerOption;
use Microsoft\Kiota\Http\Middleware\Options\ParametersDecodingOption;
use Microsoft\Kiota\Http\Middleware\Options\RetryOption;
use Microsoft\Kiota\Http\Middleware\Options\TelemetryOption;
Expand Down Expand Up @@ -114,4 +115,11 @@ public static function urlReplace(?UrlReplaceOption $urlReplaceOption = null): c
return new UrlReplaceHandler($handler, $urlReplaceOption ?? (new UrlReplaceOption(false, [])));
};
}

public static function headersInspection(?HeadersInspectionHandlerOption $headersInspectionOption = null): callable
{
return static function (callable $handler) use ($headersInspectionOption): HeadersInspectionHandler {
return new HeadersInspectionHandler($handler, $headersInspectionOption);
};
}
}
Loading

0 comments on commit 881ab52

Please sign in to comment.