Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PHP - Telemetry & Chaos Handlers #1025

Merged
merged 5 commits into from
Jan 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 193 additions & 0 deletions http/php/guzzle/src/Middleware/ChaosHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
<?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\Create;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7\Response;
use Microsoft\Kiota\Http\Middleware\Options\ChaosOption;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

/**
* Class ChaosHandler
*
* Middleware that selects a chaos response (configured via {@link ChaosOption}) at random x% of the time
* If criteria is not met for a chaos response, the request is forwarded down the middleware chain
*
* @package Microsoft\Kiota\Http\Middleware
* @copyright 2022 Microsoft Corporation
* @license https://opensource.org/licenses/MIT MIT License
* @link https://developer.microsoft.com/graph
*/
class ChaosHandler
{
/**
* Key to use to set request specific {@link ChaosOption}'s within Guzzle's request options
*/
public const CHAOS_GUZZLE_CONFIG = 'kiota_chaos_option';

/**
* @var callable Next handler in the middleware pipeline
*/
private $nextHandler;

/**
* @var ChaosOption {@link ChaosOption}
*/
private ChaosOption $chaosOption;

/**
* @param callable $nextHandler
* @param ChaosOption|null $chaosOption
*/
public function __construct(callable $nextHandler, ?ChaosOption $chaosOption = null)
{
$this->nextHandler = $nextHandler;
$this->chaosOption = ($chaosOption) ?: new ChaosOption();
}

/**
* @param RequestInterface $request
* @param array $options
* @return PromiseInterface
*/
public function __invoke(RequestInterface $request, array $options): PromiseInterface
{
if (array_key_exists(self::CHAOS_GUZZLE_CONFIG, $options)) {
$this->chaosOption = $options[self::CHAOS_GUZZLE_CONFIG];
}

$randomPercentage = rand(0, ChaosOption::MAX_CHAOS_PERCENTAGE);
if ($randomPercentage < $this->chaosOption->getChaosPercentage()) {
$response = $this->randomChaosResponse($request, $options);
if ($response) {
return Create::promiseFor($response);
}
}
$fn = $this->nextHandler;
return $fn($request, $options);
}

/**
* Selects a chaos response from ChaosOptions at random or falls back to selecting a random response
* from pre-configured possible responses per HTTP request method
*
* @param RequestInterface $request
* @param array $options
* @return ResponseInterface|null
*/
private function randomChaosResponse(RequestInterface $request, array $options): ?ResponseInterface
{
$chaosResponses = $this->chaosOption->getChaosResponses();
if (empty($chaosResponses)) {
$chaosResponses = $this->getRandomResponsesByRequestMethod($request->getMethod());
}
if (!$chaosResponses) {
return null;
}
$randomIndex = rand(0, sizeof($chaosResponses) - 1);
$chaosResponse = $chaosResponses[$randomIndex];
return is_callable($chaosResponse) ? $chaosResponse($request, $options) : $chaosResponse;
}

/**
* Returns list of possible responses by HTTP request method
*
* @param string $httpMethod
* @return Response[]|null
*/
private function getRandomResponsesByRequestMethod(string $httpMethod): ?array
{
$randomResponses = [
'GET' => [
new Response(200),
new Response(301),
new Response(307),
new Response(400),
new Response(401),
new Response(403),
new Response(404),
new Response(405),
new Response(429),
new Response(500),
new Response(502),
new Response(503),
new Response(504)
],
'POST' => [
new Response(200),
new Response(201),
new Response(204),
new Response(307),
new Response(400),
new Response(401),
new Response(403),
new Response(404),
new Response(405),
new Response(429),
new Response(500),
new Response(502),
new Response(503),
new Response(504),
new Response(507)
],
'PUT' => [
new Response(200),
new Response(201),
new Response(400),
new Response(401),
new Response(403),
new Response(404),
new Response(405),
new Response(409),
new Response(429),
new Response(500),
new Response(502),
new Response(503),
new Response(504),
new Response(507)
],
'PATCH' => [
new Response(200),
new Response(204),
new Response(400),
new Response(401),
new Response(403),
new Response(404),
new Response(405),
new Response(429),
new Response(500),
new Response(502),
new Response(503),
new Response(504)
],
'DELETE' => [
new Response(200),
new Response(204),
new Response(400),
new Response(401),
new Response(403),
new Response(404),
new Response(405),
new Response(429),
new Response(500),
new Response(502),
new Response(503),
new Response(504),
new Response(507)
]
];

if (!array_key_exists(strtoupper($httpMethod), $randomResponses)) {
return null;
}
return $randomResponses[strtoupper($httpMethod)];
}
}
32 changes: 30 additions & 2 deletions http/php/guzzle/src/Middleware/KiotaMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@

namespace Microsoft\Kiota\Http\Middleware;

use Microsoft\Kiota\Http\Middleware\Options\ChaosOption;
use Microsoft\Kiota\Http\Middleware\Options\CompressionOption;
use Microsoft\Kiota\Http\Middleware\Options\RetryOption;
use Microsoft\Kiota\Http\Middleware\Options\TelemetryOption;

/**
* Class KiotaMiddleware
Expand All @@ -33,7 +35,7 @@ class KiotaMiddleware
public static function retry(?RetryOption $retryOption = null): callable
{
return static function (callable $handler) use ($retryOption) : RetryHandler {
return new RetryHandler($retryOption, $handler);
return new RetryHandler($handler, $retryOption);
};
}

Expand All @@ -44,10 +46,36 @@ public static function retry(?RetryOption $retryOption = null): callable
* @param CompressionOption|null $compressionOption
* @return callable
*/
public static function compress(?CompressionOption $compressionOption = null): callable
public static function compression(?CompressionOption $compressionOption = null): callable
{
return static function (callable $handler) use ($compressionOption): CompressionHandler {
return new CompressionHandler($handler, $compressionOption);
};
}

/**
* Middleware that allows configuration of a RequestInterface with telemetry data
*
* @param TelemetryOption|null $telemetryOption
* @return callable
*/
public static function telemetry(?TelemetryOption $telemetryOption = null): callable
{
return static function (callable $handler) use ($telemetryOption): TelemetryHandler {
return new TelemetryHandler($handler, $telemetryOption);
};
}

/**
* Middleware that selects a chaos response (configured via {@link ChaosOption}) at random x% of the time
* If criteria is not met for a chaos response, the request is forwarded down the middleware chain
* @param ChaosOption|null $chaosOption
* @return callable
*/
public static function chaos(?ChaosOption $chaosOption = null): callable
{
return static function (callable $handler) use ($chaosOption): ChaosHandler {
return new ChaosHandler($handler, $chaosOption);
};
}
}
86 changes: 86 additions & 0 deletions http/php/guzzle/src/Middleware/Options/ChaosOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?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\Options;

use Microsoft\Kiota\Http\Middleware\ChaosHandler;
use Psr\Http\Message\ResponseInterface;

/**
* Class ChaosOption
*
* Configs for {@link ChaosHandler}
*
* @package Microsoft\Kiota\Http\Middleware\Options
* @copyright 2022 Microsoft Corporation
* @license https://opensource.org/licenses/MIT MIT License
* @link https://developer.microsoft.com/graph
*/
class ChaosOption
{
public const MAX_CHAOS_PERCENTAGE = 100;

/**
* @var int Threshold below which a random $chaosResponse is returned
*/
private int $chaosPercentage;

/**
* @var array set of chaos ResponseInterfaces or callables(RequestInterface, array):ResponseInterface that are returned/executed at random.
*/
private array $chaosResponses = [];

/**
* @param int $chaosPercentage
* @param array $chaosResponses
*/
public function __construct(array $chaosResponses = [], int $chaosPercentage = 10)
{
if ($chaosPercentage < 0 || $chaosPercentage > self::MAX_CHAOS_PERCENTAGE) {
throw new \InvalidArgumentException("Chaos percentage should be between 0 and ".self::MAX_CHAOS_PERCENTAGE);
}

$this->chaosPercentage = $chaosPercentage;
$this->chaosResponses = $chaosResponses;
}

/**
* @param int $chaosPercentage
*/
public function setChaosPercentage(int $chaosPercentage): void
{
if ($chaosPercentage < 0 || $chaosPercentage > self::MAX_CHAOS_PERCENTAGE) {
throw new \InvalidArgumentException("Chaos percentage should be between 0 and ".self::MAX_CHAOS_PERCENTAGE);
}
$this->chaosPercentage = $chaosPercentage;
}

/**
* @return int
*/
public function getChaosPercentage(): int
{
return $this->chaosPercentage;
}

/**
* @param array $chaosResponses
*/
public function setChaosResponses(array $chaosResponses): void
{
$this->chaosResponses = $chaosResponses;
}

/**
* @return array
*/
public function getChaosResponses(): array
{
return $this->chaosResponses;
}
}
55 changes: 55 additions & 0 deletions http/php/guzzle/src/Middleware/Options/TelemetryOption.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?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\Options;

use Microsoft\Kiota\Http\Middleware\TelemetryHandler;
use Psr\Http\Message\RequestInterface;

/**
* Class TelemetryOption
*
* Configs for a {@link TelemetryHandler}
*
* @package Microsoft\Kiota\Http\Middleware\Options
* @copyright 2022 Microsoft Corporation
* @license https://opensource.org/licenses/MIT MIT License
* @link https://developer.microsoft.com/graph
*/
class TelemetryOption
{
/**
* @var callable(RequestInterface):RequestInterface
* Function that adds appropriate telemetry information to a request
*/
private $telemetryConfigurator;

/**
* @param callable(RequestInterface): RequestInterface|null $telemetryConfigurator {@link $telemetryConfigurator}
*/
public function __construct(?callable $telemetryConfigurator = null)
{
$this->telemetryConfigurator = $telemetryConfigurator;
}

/**
* @return callable
*/
public function getTelemetryConfigurator(): ?callable
{
return $this->telemetryConfigurator;
}

/**
* @param callable(RequestInterface):RequestInterface|null $telemetryConfigurator
*/
public function setTelemetryConfigurator(?callable $telemetryConfigurator): void
{
$this->telemetryConfigurator = $telemetryConfigurator;
}
}
Loading