Skip to content

Commit

Permalink
Merge pull request #290 from saloonphp/feature/v3-final-tweaks
Browse files Browse the repository at this point in the history
Feature | v3 Small Tweaks
  • Loading branch information
Sammyjo20 committed Sep 13, 2023
2 parents 54527e1 + efc8a7b commit a26788e
Show file tree
Hide file tree
Showing 19 changed files with 387 additions and 6 deletions.
10 changes: 10 additions & 0 deletions phpstan.baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,13 @@ parameters:
message: "#^Match arm is unreachable because previous comparison is always true.$#"
count: 1
path: src/Http/Pool.php

-
message: "#^Parameter \\#1 \\$object of method ReflectionMethod\\:\\:invoke\\(\\) expects object\\|null, class-string\\|object given.$#"
count: 2
path: src/Traits/Macroable.php

-
message: "#^Parameter \\#1 \\$callback of function call_user_func_array expects callable\\(\\)\\: mixed, \\(callable\\(\\)\\: mixed\\)\\|object given.$#"
count: 4
path: src/Traits/Macroable.php
10 changes: 10 additions & 0 deletions src/Exceptions/InvalidHeaderException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Saloon\Exceptions;

class InvalidHeaderException extends SaloonException
{
//
}
16 changes: 16 additions & 0 deletions src/Http/BaseResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Saloon\Http;

class BaseResource
{
/**
* Constructor
*/
public function __construct(readonly protected Connector $connector)
{
//
}
}
2 changes: 1 addition & 1 deletion src/Http/Faking/Fixture.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

namespace Saloon\Http\Faking;

use Saloon\MockConfig;
use Saloon\Helpers\Storage;
use Saloon\Helpers\MockConfig;
use Saloon\Data\RecordedResponse;
use Saloon\Helpers\FixtureHelper;
use Saloon\Exceptions\FixtureException;
Expand Down
28 changes: 28 additions & 0 deletions src/Http/Middleware/ValidateProperties.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Saloon\Http\Middleware;

use Saloon\Http\PendingRequest;
use Saloon\Contracts\RequestMiddleware;
use Saloon\Exceptions\InvalidHeaderException;

class ValidateProperties implements RequestMiddleware
{
/**
* Validate the properties on the request before it is sent
*
* @throws \Saloon\Exceptions\InvalidHeaderException
*/
public function __invoke(PendingRequest $pendingRequest): void
{
// Validate that each header provided has a string key

foreach ($pendingRequest->headers()->all() as $key => $unused) {
if (! is_string($key)) {
throw new InvalidHeaderException('One or more of the headers are invalid. Make sure to use the header name as the key. For example: \'Content-Type\' => \'application/json\'.');
}
}
}
}
8 changes: 8 additions & 0 deletions src/Http/PendingRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Saloon\Config;
use Saloon\Enums\Method;
use Saloon\Helpers\Helpers;
use Saloon\Traits\Macroable;
use Saloon\Helpers\URLHelper;
use Saloon\Traits\Conditionable;
use Saloon\Traits\HasMockClient;
Expand All @@ -18,6 +19,7 @@
use Saloon\Contracts\Body\BodyRepository;
use Saloon\Http\Middleware\DelayMiddleware;
use Saloon\Traits\Auth\AuthenticatesRequests;
use Saloon\Http\Middleware\ValidateProperties;
use Saloon\Http\Middleware\AuthenticateRequest;
use Saloon\Http\Middleware\DetermineMockResponse;
use Saloon\Http\Middleware\MergeRequestProperties;
Expand All @@ -32,6 +34,7 @@ class PendingRequest
use ManagesPsrRequests;
use Conditionable;
use HasMockClient;
use Macroable;

/**
* The connector making the request.
Expand Down Expand Up @@ -177,6 +180,11 @@ protected function registerAndExecuteMiddleware(): void
->merge($this->connector->middleware())
->merge($this->request->middleware());

// Next, we'll register our ValidateProperties middleware. This will validate
// any properties on the pending request like headers.

$middleware->onRequest(new ValidateProperties, 'validateProperties');

// Next, we'll delay the request if we need to. This needs to be as near to
// the end as possible to apply delay right before the request is sent.

Expand Down
3 changes: 3 additions & 0 deletions src/Http/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Throwable;
use LogicException;
use SimpleXMLElement;
use Saloon\Traits\Macroable;
use InvalidArgumentException;
use Saloon\Helpers\ArrayHelpers;
use Illuminate\Support\Collection;
Expand All @@ -22,6 +23,8 @@

class Response
{
use Macroable;

/**
* The PSR request
*/
Expand Down
3 changes: 1 addition & 2 deletions src/Helpers/MockConfig.php → src/MockConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

declare(strict_types=1);

namespace Saloon\Helpers;
namespace Saloon;

use Saloon\Config;
use Saloon\Enums\PipeOrder;
use Saloon\Http\PendingRequest;
use Saloon\Exceptions\StrayRequestException;
Expand Down
100 changes: 100 additions & 0 deletions src/Traits/Macroable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php

declare(strict_types=1);

namespace Saloon\Traits;

use Closure;
use ReflectionClass;
use ReflectionMethod;
use BadMethodCallException;

/**
* Many thanks to Spatie for building this excellent trait.
*
* @see https://github.com/spatie/macroable
*/
trait Macroable
{
/**
* Macros stored
*
* @var array<object|callable>
*/
protected static array $macros = [];

/**
* Create a macro
*/
public static function macro(string $name, object|callable $macro): void
{
static::$macros[$name] = $macro;
}

/**
* Add a mixin
*
* @param object|class-string $mixin
* @throws \ReflectionException
*/
public static function mixin(object|string $mixin): void
{
$methods = (new ReflectionClass($mixin))->getMethods(
ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
);

foreach ($methods as $method) {
$method->setAccessible(true);

static::macro($method->name, $method->invoke($mixin));
}
}

/**
* Check if we have a macro
*/
public static function hasMacro(string $name): bool
{
return isset(static::$macros[$name]);
}

/**
* Handle a static call
*
* @param array<string, mixed> $parameters
*/
public static function __callStatic(string $method, array $parameters): mixed
{
if (! static::hasMacro($method)) {
throw new BadMethodCallException("Method {$method} does not exist.");
}

$macro = static::$macros[$method];

if ($macro instanceof Closure) {
return call_user_func_array(Closure::bind($macro, null, static::class), $parameters);
}

return call_user_func_array($macro, $parameters);
}

/**
* Handle a method call
*
* @param array<string, mixed> $parameters
*/
public function __call(string $method, array $parameters): mixed
{
if (! static::hasMacro($method)) {
throw new BadMethodCallException("Method {$method} does not exist.");
}

$macro = static::$macros[$method];

if ($macro instanceof Closure) {
return call_user_func_array($macro->bindTo($this, static::class), $parameters);
}

return call_user_func_array($macro, $parameters);
}
}
22 changes: 22 additions & 0 deletions tests/Feature/BaseResourceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

use Saloon\Http\Faking\MockClient;
use Saloon\Http\Faking\MockResponse;
use Saloon\Tests\Fixtures\Connectors\ResourceConnector;

test('a resource can be used to send a request', function () {
$mockClient = new MockClient([
MockResponse::fixture('user'),
]);

$connector = new ResourceConnector;
$connector->withMockClient($mockClient);

expect($connector->user()->get())->toEqual([
'name' => 'Sammyjo20',
'actual_name' => 'Sam',
'twitter' => '@carre_sam',
]);
});
2 changes: 1 addition & 1 deletion tests/Feature/MockRequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

declare(strict_types=1);

use Saloon\MockConfig;
use Saloon\Http\Response;
use Saloon\Helpers\MockConfig;
use Saloon\Http\PendingRequest;
use League\Flysystem\Filesystem;
use Saloon\Http\Faking\MockClient;
Expand Down
15 changes: 15 additions & 0 deletions tests/Fixtures/Connectors/ResourceConnector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Saloon\Tests\Fixtures\Connectors;

use Saloon\Tests\Fixtures\Resources\UserBaseResource;

class ResourceConnector extends TestConnector
{
public function user(): UserBaseResource
{
return new UserBaseResource($this);
}
}
23 changes: 23 additions & 0 deletions tests/Fixtures/Resources/UserBaseResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Saloon\Tests\Fixtures\Resources;

use Saloon\Http\BaseResource;
use Saloon\Tests\Fixtures\Requests\UserRequest;

class UserBaseResource extends BaseResource
{
/**
* Get User
*
* @throws \JsonException
* @throws \ReflectionException
* @throws \Throwable
*/
public function get(): array
{
return $this->connector->send(new UserRequest)->array();
}
}
1 change: 1 addition & 0 deletions tests/Fixtures/Saloon/user.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"statusCode":200,"headers":{"Date":"Mon, 11 Sep 2023 21:20:43 GMT","Content-Type":"application\/json","Content-Length":"63","Connection":"keep-alive","access-control-allow-origin":"*","Cache-Control":"no-cache, private","x-ratelimit-limit":"1000","x-ratelimit-remaining":"999","x-frame-options":"SAMEORIGIN","x-xss-protection":"1; mode=block","x-content-type-options":"nosniff","CF-Cache-Status":"DYNAMIC","Report-To":"{\"endpoints\":[{\"url\":\"https:\\\/\\\/a.nel.cloudflare.com\\\/report\\\/v3?s=6FQ2ADSCfKyvoEWPs9KjRyZXMfPJmR6bUu%2BXwFY0wYhRdQacLvV%2FTlZdmMX8vS%2FkoEoaTr%2B0kDpLjTd8PFH0%2FhuFShA7T1FxFLE9b6kf%2BM8T4FIJPiaWJSq2MnsZzle07j%2BR\"}],\"group\":\"cf-nel\",\"max_age\":604800}","NEL":"{\"success_fraction\":0,\"report_to\":\"cf-nel\",\"max_age\":604800}","Server":"cloudflare","CF-RAY":"8052f4d0a80f0743-MAN","alt-svc":"h3=\":443\"; ma=86400"},"data":"{\"name\":\"Sammyjo20\",\"actual_name\":\"Sam\",\"twitter\":\"@carre_sam\"}"}
2 changes: 1 addition & 1 deletion tests/Unit/ConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
declare(strict_types=1);

use Saloon\Config;
use Saloon\MockConfig;
use Saloon\Http\Response;
use Saloon\Helpers\MockConfig;
use Saloon\Http\PendingRequest;
use Saloon\Http\Faking\MockClient;
use Saloon\Http\Faking\MockResponse;
Expand Down
Loading

0 comments on commit a26788e

Please sign in to comment.