Simplify signature of the low-level API
Art4 opened this issue · comments
At the moment we have these 7 methods to interact with the low-level API:
/**
* Create and send a GET request.
*/
public function requestGet(string $path): bool;
/**
* Create and send a POST request.
*/
public function requestPost(string $path, string $body): bool;
/**
* Create and send a PUT request.
*/
public function requestPut(string $path, string $body): bool;
/**
* Create and send a DELETE request.
*/
public function requestDelete(string $path): bool;
/**
* Returns status code of the last response.
*/
public function getLastResponseStatusCode(): int;
/**
* Returns content type of the last response.
*/
public function getLastResponseContentType(): string;
/**
* Returns the body of the last response.
*/
public function getLastResponseBody(): string;
The first 4 methods will send a request to the server and save the response in the Client. The last 3 methods can now be used to get the response details. This has some downsides:
- If we send multiple requests (like we do in the mid-level API
AbstractApi::retrieveData()
), only the latest response will be available in the client. This leads to possible race conditions: There is no way to guarantee, that the last response in Client really belongs to our request. - We have to call the client 3 times to retrieve all response data. This is a code smell and should be replaced with a small object.
Proposal
I propose to introduce a new Request
containing the method, path, content type and body and a new Response
interface, containing the status code, content type and body.
interface Request
{
/**
* Returns the http method.
*/
public function getMethod(): string;
/**
* Returns the path with optional attached query string.
*/
public function getPath(): string;
/**
* Returns content type.
*/
public function getContentType(): string;
/**
* Returns the body content.
*/
public function getContent(): string;
}
interface Response
{
/**
* Returns status code.
*/
public function getStatusCode(): int;
/**
* Returns content type.
*/
public function getContentType(): string;
/**
* Returns the body content.
*/
public function getContent(): string;
}
The client methods could then simplified into one method.
public function request(Request $request): Response;
Another point that I would like to improve is the "content type guessing" based on the path in the clients. In NativeCurlClient
and Psr18Client
we have the following code to determine the content type:
if ($this->isUploadCall($path)) { // (false !== strpos($path, '/uploads.json')) || (false !== strpos($path, '/uploads.xml'))
$httpHeaders[] = 'Content-Type: application/octet-stream';
} elseif ('json' === substr($tmp['path'], -4)) {
$httpHeaders[] = 'Content-Type: application/json';
} elseif ('xml' === substr($tmp['path'], -3)) {
$httpHeaders[] = 'Content-Type: text/xml';
}
This method is very speculative and uncertain. The client should not make any assumptions about the request.
Instead we should add a way to provide the content type via the request()
method. It makes sense to bundle the 4 parameters (method, path, content-type, content) in a simplified request interface that is compatible to PSR-7. I will update have updated the proposal.
Usage of the low-level API could then work like this:
$response = $this->getHttpClient()->request(XmlRequest::put(
'/issues/' . $id . '.xml',
['issue' => $params],
));
$array = XmlSerializer::createFromString($response->getContent())->toArray();