diff --git a/src/Http/BatchRequest.php b/src/Http/BatchRequest.php new file mode 100644 index 00000000..45002adb --- /dev/null +++ b/src/Http/BatchRequest.php @@ -0,0 +1,53 @@ +requests ??= new ArrayStore($this->defaultRequests()); + } + + /** + * Add a request dynamically. + * + * @param Request $request + * @return void + */ + public function addRequest(Request $request): void + { + $this->requests->add(uniqid('request_', true), $request); + } + + /** + * Get default requests. + * + * @return array + */ + protected function defaultRequests(): array + { + return []; + } + + /** + * Process the batch of responses. + * + * @param array $responses + * @return mixed + */ + abstract public function processResponses(array $responses): mixed; +} diff --git a/src/Http/Connector.php b/src/Http/Connector.php index 18b49c8d..a45b150b 100644 --- a/src/Http/Connector.php +++ b/src/Http/Connector.php @@ -14,7 +14,7 @@ use Saloon\Traits\Connector\SendsRequests; use Saloon\Traits\Auth\AuthenticatesRequests; use Saloon\Traits\RequestProperties\HasTries; -use Saloon\Traits\Responses\HasCustomResponses; +use Saloon\Traits\Responses\HasResponseClass; use Saloon\Traits\Request\CreatesDtoFromResponse; use Saloon\Traits\RequestProperties\HasRequestProperties; @@ -23,7 +23,7 @@ abstract class Connector use CreatesDtoFromResponse; use AuthenticatesRequests; use HasRequestProperties; - use HasCustomResponses; + use HasResponseClass; use ManagesExceptions; use HandlesPsrRequest; use HasMockClient; diff --git a/src/Http/Request.php b/src/Http/Request.php index 111c428b..cb551ee0 100644 --- a/src/Http/Request.php +++ b/src/Http/Request.php @@ -15,7 +15,7 @@ use Saloon\Traits\ManagesExceptions; use Saloon\Traits\Auth\AuthenticatesRequests; use Saloon\Traits\RequestProperties\HasTries; -use Saloon\Traits\Responses\HasCustomResponses; +use Saloon\Traits\Responses\HasResponseClass; use Saloon\Traits\Request\CreatesDtoFromResponse; use Saloon\Traits\RequestProperties\HasRequestProperties; @@ -24,7 +24,7 @@ abstract class Request use CreatesDtoFromResponse; use AuthenticatesRequests; use HasRequestProperties; - use HasCustomResponses; + use HasResponseClass; use ManagesExceptions; use HandlesPsrRequest; use HasMockClient; diff --git a/src/Traits/Connector/SendsRequests.php b/src/Traits/Connector/SendsRequests.php index a4482adf..080feb20 100644 --- a/src/Traits/Connector/SendsRequests.php +++ b/src/Traits/Connector/SendsRequests.php @@ -4,17 +4,17 @@ namespace Saloon\Traits\Connector; +use GuzzleHttp\Promise\PromiseInterface; +use GuzzleHttp\Promise\Utils; use LogicException; +use Saloon\Exceptions\Request\FatalRequestException; +use Saloon\Exceptions\Request\RequestException; +use Saloon\Http\BatchRequest; +use Saloon\Http\Faking\MockClient; +use Saloon\Http\PendingRequest; use Saloon\Http\Pool; use Saloon\Http\Request; use Saloon\Http\Response; -use GuzzleHttp\Promise\Utils; -use GuzzleHttp\Promise\Promise; -use Saloon\Http\PendingRequest; -use Saloon\Http\Faking\MockClient; -use GuzzleHttp\Promise\PromiseInterface; -use Saloon\Exceptions\Request\RequestException; -use Saloon\Exceptions\Request\FatalRequestException; trait SendsRequests { @@ -29,7 +29,7 @@ trait SendsRequests public function send(Request $request, ?MockClient $mockClient = null, ?callable $handleRetry = null): Response { if (is_null($handleRetry)) { - $handleRetry = static fn (): bool => true; + $handleRetry = static fn(): bool => true; } $attempts = 0; @@ -126,17 +126,37 @@ public function send(Request $request, ?MockClient $mockClient = null, ?callable /** * Send a request asynchronously */ - public function sendAsync(Request $request, ?MockClient $mockClient = null): PromiseInterface + public function sendAsync(Request|BatchRequest $request, ?MockClient $mockClient = null): PromiseInterface { - $sender = $this->sender(); - // We'll wrap the following logic in our own Promise which means we won't // build up our PendingRequest until the promise is actually being sent // this is great because our middleware will only run right before // the request is sent. - return Utils::task(function () use ($request, $mockClient, $sender) { - $pendingRequest = $this->createPendingRequest($request, $mockClient)->setAsynchronous(true); + if ($request instanceof Request) { + return $this->prepareAsyncRequest($request, $mockClient); + } + + $requests = array_map( + fn($singleRequest) => $this->prepareAsyncRequest($singleRequest, $mockClient), + $request->getRequests()->all() + ); + + return Utils::all($requests)->then(fn(array $responses) => $request->processResponses($responses)); + } + + /** + * Prepare an asynchronous task for sending the request. + * + * @param Request $request + * @param MockClient|null $mockClient + * @return PromiseInterface + */ + protected function prepareAsyncRequest(Request $request, MockClient $mockClient = null): PromiseInterface + { + return Utils::task(function () use ($request, $mockClient) { + $pendingRequest = $this->createPendingRequest($request, $mockClient) + ->setAsynchronous(true); // We need to check if the Pending Request contains a fake response. // If it does, then we will create the fake response. Otherwise, @@ -147,10 +167,10 @@ public function sendAsync(Request $request, ?MockClient $mockClient = null): Pro if ($pendingRequest->hasFakeResponse()) { $requestPromise = $this->createFakeResponse($pendingRequest); } else { - $requestPromise = $sender->sendAsync($pendingRequest); + $requestPromise = $this->sender()->sendAsync($pendingRequest); } - $requestPromise->then(fn (Response $response) => $pendingRequest->executeResponsePipeline($response)); + $requestPromise->then(fn(Response $response) => $pendingRequest->executeResponsePipeline($response)); return $requestPromise; }); diff --git a/src/Traits/Responses/HasCustomResponses.php b/src/Traits/Responses/HasResponseClass.php similarity index 73% rename from src/Traits/Responses/HasCustomResponses.php rename to src/Traits/Responses/HasResponseClass.php index 0100ab7f..0e0acbb4 100644 --- a/src/Traits/Responses/HasCustomResponses.php +++ b/src/Traits/Responses/HasResponseClass.php @@ -4,16 +4,16 @@ namespace Saloon\Traits\Responses; -trait HasCustomResponses +trait HasResponseClass { /** - * Specify a default response. + * Specify a default response class. * * When null or an empty string, the response on the sender will be used. * * @var class-string<\Saloon\Http\Response>|null */ - protected ?string $response = null; + protected ?string $responseClass = null; /** * Resolve the custom response class @@ -22,6 +22,6 @@ trait HasCustomResponses */ public function resolveResponseClass(): ?string { - return $this->response ?? null; + return $this->responseClass ?? null; } }