getServiceClient(
[
new Response(200, [], '{"foo":"bar"}'),
new Response(200, [], '{"foofoo":"barbar"}'),
],
null,
$this->commandToRequestTransformer()
);
// Synchronous
$result1 = $client->doThatThingYouDo(['fizz' => 'buzz']);
$this->assertEquals('bar', $result1['foo']);
$this->assertEquals('buzz', $result1['_request']['fizz']);
$this->assertEquals('doThatThingYouDo', $result1['_request']['action']);
// Asynchronous
$result2 = $client->doThatThingOtherYouDoAsync(['fizz' => 'buzz'])->wait();
$this->assertEquals('barbar', $result2['foofoo']);
$this->assertEquals('doThatThingOtherYouDo', $result2['_request']['action']);
}
public function testExecuteWithQueryLocation()
{
$mock = new MockHandler();
$client = $this->getServiceClient(
[
new Response(200, [], '{"foo":"bar"}'),
new Response(200, [], '{"foo":"bar"}')
],
$mock
);
$client->doQueryLocation(['foo' => 'Foo']);
$this->assertEquals('foo=Foo', $mock->getLastRequest()->getUri()->getQuery());
$client->doQueryLocation([
'foo' => 'Foo',
'bar' => 'Bar',
'baz' => 'Baz'
]);
$last = $mock->getLastRequest();
$this->assertEquals('foo=Foo&bar=Bar&baz=Baz', $last->getUri()->getQuery());
}
public function testExecuteWithBodyLocation()
{
$mock = new MockHandler();
$client = $this->getServiceClient(
[
new Response(200, [], '{"foo":"bar"}'),
new Response(200, [], '{"foo":"bar"}')
],
$mock
);
$client->doBodyLocation(['foo' => 'Foo']);
$this->assertEquals('foo=Foo', (string) $mock->getLastRequest()->getBody());
$client->doBodyLocation([
'foo' => 'Foo',
'bar' => 'Bar',
'baz' => 'Baz'
]);
$this->assertEquals('foo=Foo&bar=Bar&baz=Baz', (string) $mock->getLastRequest()->getBody());
}
public function testExecuteWithJsonLocation()
{
$mock = new MockHandler();
$client = $this->getServiceClient(
[
new Response(200, [], '{"foo":"bar"}'),
new Response(200, [], '{"foo":"bar"}')
],
$mock
);
$client->doJsonLocation(['foo' => 'Foo']);
$this->assertEquals('{"foo":"Foo"}', (string) $mock->getLastRequest()->getBody());
$client->doJsonLocation([
'foo' => 'Foo',
'bar' => 'Bar',
'baz' => 'Baz'
]);
$this->assertEquals('{"foo":"Foo","bar":"Bar","baz":"Baz"}', (string) $mock->getLastRequest()->getBody());
}
public function testExecuteWithHeaderLocation()
{
$mock = new MockHandler();
$client = $this->getServiceClient(
[
new Response(200, [], '{"foo":"bar"}'),
new Response(200, [], '{"foo":"bar"}')
],
$mock
);
$client->doHeaderLocation(['foo' => 'Foo']);
$this->assertEquals(['Foo'], $mock->getLastRequest()->getHeader('foo'));
$client->doHeaderLocation([
'foo' => 'Foo',
'bar' => 'Bar',
'baz' => 'Baz'
]);
$this->assertEquals(['Foo'], $mock->getLastRequest()->getHeader('foo'));
$this->assertEquals(['Bar'], $mock->getLastRequest()->getHeader('bar'));
$this->assertEquals(['Baz'], $mock->getLastRequest()->getHeader('baz'));
}
public function testExecuteWithXmlLocation()
{
$mock = new MockHandler();
$client = $this->getServiceClient(
[
new Response(200, [], '{"foo":"bar"}'),
new Response(200, [], '{"foo":"bar"}')
],
$mock
);
$client->doXmlLocation(['foo' => 'Foo']);
$this->assertEquals(
"\nFoo\n",
(string) $mock->getLastRequest()->getBody()
);
$client->doXmlLocation([
'foo' => 'Foo',
'bar' => 'Bar',
'baz' => 'Baz'
]);
$this->assertEquals(
"\nFooBarBaz\n",
$mock->getLastRequest()->getBody()
);
}
public function testExecuteWithMultiPartLocation()
{
$mock = new MockHandler();
$client = $this->getServiceClient(
[
new Response(200, [], '{"foo":"bar"}'),
new Response(200, [], '{"foo":"bar"}'),
new Response(200, [], '{"foo":"bar"}')
],
$mock
);
$client->doMultiPartLocation(['foo' => 'Foo']);
$multiPartRequestBody = (string) $mock->getLastRequest()->getBody();
$this->assertContains('name="foo"', $multiPartRequestBody);
$this->assertContains('Foo', $multiPartRequestBody);
$client->doMultiPartLocation([
'foo' => 'Foo',
'bar' => 'Bar',
'baz' => 'Baz'
]);
$multiPartRequestBody = (string) $mock->getLastRequest()->getBody();
$this->assertContains('name="foo"', $multiPartRequestBody);
$this->assertContains('Foo', $multiPartRequestBody);
$this->assertContains('name="bar"', $multiPartRequestBody);
$this->assertContains('Bar', $multiPartRequestBody);
$this->assertContains('name="baz"', $multiPartRequestBody);
$this->assertContains('Baz', $multiPartRequestBody);
$client->doMultiPartLocation([
'file' => fopen(dirname(__FILE__) . '/Asset/test.html', 'r'),
]);
$multiPartRequestBody = (string) $mock->getLastRequest()->getBody();
$this->assertContains('name="file"', $multiPartRequestBody);
$this->assertContains('filename="test.html"', $multiPartRequestBody);
$this->assertContains('
Title', $multiPartRequestBody);
}
public function testHasConfig()
{
$client = new HttpClient();
$description = new Description([]);
$guzzle = new GuzzleClient(
$client,
$description,
$this->commandToRequestTransformer(),
$this->responseToResultTransformer(),
null,
['foo' => 'bar']
);
$this->assertSame($client, $guzzle->getHttpClient());
$this->assertSame($description, $guzzle->getDescription());
$this->assertEquals('bar', $guzzle->getConfig('foo'));
$this->assertEquals([], $guzzle->getConfig('defaults'));
$guzzle->setConfig('abc', 'listen');
$this->assertEquals('listen', $guzzle->getConfig('abc'));
}
public function testAddsValidateHandlerWhenTrue()
{
$client = new HttpClient();
$description = new Description([]);
$guzzle = new GuzzleClient(
$client,
$description,
$this->commandToRequestTransformer(),
$this->responseToResultTransformer(),
null,
[
'validate' => true,
'process' => false
]
);
$handlers = explode("\n", $guzzle->getHandlerStack()->__toString());
$handlers = array_filter($handlers);
$this->assertCount(3, $handlers);
}
public function testDisablesHandlersWhenFalse()
{
$client = new HttpClient();
$description = new Description([]);
$guzzle = new GuzzleClient(
$client,
$description,
$this->commandToRequestTransformer(),
$this->responseToResultTransformer(),
null,
[
'validate' => false,
'process' => false
]
);
$handlers = explode("\n", $guzzle->getHandlerStack()->__toString());
$handlers = array_filter($handlers);
$this->assertCount(1, $handlers);
}
public function testValidateDescription()
{
$client = new HttpClient();
$description = new Description(
[
'name' => 'Testing API ',
'baseUri' => 'http://httpbin.org/',
'operations' => [
'Foo' => [
'httpMethod' => 'GET',
'uri' => '/get',
'parameters' => [
'bar' => [
'type' => 'string',
'required' => false,
'description' => 'Bar',
'location' => 'query'
],
'baz' => [
'type' => 'string',
'required' => false,
'description' => 'baz',
'location' => 'query'
],
],
'responseModel' => 'Foo'
],
],
'models' => [
'Foo' => [
'type' => 'object',
'properties' => [
'id' => [
'location' => 'json',
'type' => 'string'
],
'location' => [
'location' => 'header',
'sentAs' => 'Location',
'type' => 'string'
],
'age' => [
'location' => 'json',
'type' => 'integer'
],
'statusCode' => [
'location' => 'statusCode',
'type' => 'integer'
],
],
],
],
]
);
$guzzle = new GuzzleClient(
$client,
$description,
null,
null,
null,
[
'validate' => true,
'process' => false
]
);
$command = $guzzle->getCommand('Foo', ['baz' => 'BAZ']);
/** @var ResponseInterface $response */
$response = $guzzle->execute($command);
$this->assertInstanceOf(Response::class, $response);
$this->assertEquals(200, $response->getStatusCode());
}
/**
* @expectedException \GuzzleHttp\Command\Exception\CommandException
* @expectedExceptionMessage Validation errors: [baz] is a required string: baz
*/
public function testValidateDescriptionFailsDueMissingRequiredParameter()
{
$client = new HttpClient();
$description = new Description(
[
'name' => 'Testing API ',
'baseUri' => 'http://httpbin.org/',
'operations' => [
'Foo' => [
'httpMethod' => 'GET',
'uri' => '/get',
'parameters' => [
'bar' => [
'type' => 'string',
'required' => false,
'description' => 'Bar',
'location' => 'query'
],
'baz' => [
'type' => 'string',
'required' => true,
'description' => 'baz',
'location' => 'query'
],
],
'responseModel' => 'Foo'
],
],
'models' => [
'Foo' => [
'type' => 'object',
'properties' => [
'id' => [
'location' => 'json',
'type' => 'string'
],
'location' => [
'location' => 'header',
'sentAs' => 'Location',
'type' => 'string'
],
'age' => [
'location' => 'json',
'type' => 'integer'
],
'statusCode' => [
'location' => 'statusCode',
'type' => 'integer'
],
],
],
],
]
);
$guzzle = new GuzzleClient(
$client,
$description,
null,
null,
null,
[
'validate' => true,
'process' => false
]
);
$command = $guzzle->getCommand('Foo');
/** @var ResultInterface $result */
$result = $guzzle->execute($command);
$this->assertInstanceOf(Result::class, $result);
$result = $result->toArray();
$this->assertEquals(200, $result['statusCode']);
}
/**
* @expectedException \GuzzleHttp\Command\Exception\CommandException
* @expectedExceptionMessage Validation errors: [baz] must be of type integer
*/
public function testValidateDescriptionFailsDueTypeMismatch()
{
$client = new HttpClient();
$description = new Description(
[
'name' => 'Testing API ',
'baseUri' => 'http://httpbin.org/',
'operations' => [
'Foo' => [
'httpMethod' => 'GET',
'uri' => '/get',
'parameters' => [
'bar' => [
'type' => 'string',
'required' => false,
'description' => 'Bar',
'location' => 'query'
],
'baz' => [
'type' => 'integer',
'required' => true,
'description' => 'baz',
'location' => 'query'
],
],
'responseModel' => 'Foo'
],
],
'models' => [
'Foo' => [
'type' => 'object',
'properties' => [
'id' => [
'location' => 'json',
'type' => 'string'
],
'location' => [
'location' => 'header',
'sentAs' => 'Location',
'type' => 'string'
],
'age' => [
'location' => 'json',
'type' => 'integer'
],
'statusCode' => [
'location' => 'statusCode',
'type' => 'integer'
],
],
],
],
]
);
$guzzle = new GuzzleClient(
$client,
$description,
null,
null,
null,
[
'validate' => true,
'process' => false
]
);
$command = $guzzle->getCommand('Foo', ['baz' => 'Hello']);
/** @var ResultInterface $result */
$result = $guzzle->execute($command);
$this->assertInstanceOf(Result::class, $result);
$result = $result->toArray();
$this->assertEquals(200, $result['statusCode']);
}
public function testValidateDescriptionDoesNotFailWhenSendingIntegerButExpectingString()
{
$client = new HttpClient();
$description = new Description(
[
'name' => 'Testing API ',
'baseUri' => 'http://httpbin.org/',
'operations' => [
'Foo' => [
'httpMethod' => 'GET',
'uri' => '/get',
'parameters' => [
'bar' => [
'type' => 'string',
'required' => false,
'description' => 'Bar',
'location' => 'query'
],
'baz' => [
'type' => 'string',
'required' => true,
'description' => 'baz',
'location' => 'query'
],
],
'responseModel' => 'Foo'
],
],
'models' => [
'Foo' => [
'type' => 'object',
'properties' => [
'id' => [
'location' => 'json',
'type' => 'string'
],
'location' => [
'location' => 'header',
'sentAs' => 'Location',
'type' => 'string'
],
'age' => [
'location' => 'json',
'type' => 'integer'
],
'statusCode' => [
'location' => 'statusCode',
'type' => 'integer'
],
],
],
],
]
);
$guzzle = new GuzzleClient($client, $description);
$command = $guzzle->getCommand('Foo', ['baz' => 42]);
/** @var ResultInterface $result */
$result = $guzzle->execute($command);
$this->assertInstanceOf(Result::class, $result);
$result = $result->toArray();
$this->assertEquals(200, $result['statusCode']);
}
public function testMagicMethodExecutesCommands()
{
$client = new HttpClient();
$description = new Description(
[
'name' => 'Testing API ',
'baseUri' => 'http://httpbin.org/',
'operations' => [
'Foo' => [
'httpMethod' => 'GET',
'uri' => '/get',
'parameters' => [
'bar' => [
'type' => 'string',
'required' => false,
'description' => 'Bar',
'location' => 'query'
],
'baz' => [
'type' => 'string',
'required' => true,
'description' => 'baz',
'location' => 'query'
],
],
'responseModel' => 'Foo'
],
],
'models' => [
'Foo' => [
'type' => 'object',
'properties' => [
'id' => [
'location' => 'json',
'type' => 'string'
],
'location' => [
'location' => 'header',
'sentAs' => 'Location',
'type' => 'string'
],
'age' => [
'location' => 'json',
'type' => 'integer'
],
'statusCode' => [
'location' => 'statusCode',
'type' => 'integer'
],
],
],
],
]
);
$guzzle = $this->getMockBuilder(GuzzleClient::class)
->setConstructorArgs([
$client,
$description
])
->setMethods(['execute'])
->getMock();
$guzzle->expects($this->once())
->method('execute')
->will($this->returnValue('foo'));
$this->assertEquals('foo', $guzzle->foo([]));
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage No operation found named Foo
*/
public function testThrowsWhenOperationNotFoundInDescription()
{
$client = new HttpClient();
$description = new Description([]);
$guzzle = new GuzzleClient(
$client,
$description,
$this->commandToRequestTransformer(),
$this->responseToResultTransformer()
);
$guzzle->getCommand('foo');
}
public function testReturnsProcessedResponse()
{
$client = new HttpClient();
$description = new Description(
[
'name' => 'Testing API ',
'baseUri' => 'http://httpbin.org/',
'operations' => [
'Foo' => [
'httpMethod' => 'GET',
'uri' => '/get',
'parameters' => [
'bar' => [
'type' => 'string',
'required' => false,
'description' => 'Bar',
'location' => 'query'
],
'baz' => [
'type' => 'string',
'required' => true,
'description' => 'baz',
'location' => 'query'
],
],
'responseModel' => 'Foo'
],
],
'models' => [
'Foo' => [
'type' => 'object',
'properties' => [
'id' => [
'location' => 'json',
'type' => 'string'
],
'location' => [
'location' => 'header',
'sentAs' => 'Location',
'type' => 'string'
],
'age' => [
'location' => 'json',
'type' => 'integer'
],
'statusCode' => [
'location' => 'statusCode',
'type' => 'integer'
],
],
],
],
]
);
$guzzle = new GuzzleClient($client, $description, null, null);
$command = $guzzle->getCommand('foo', ['baz' => 'BAZ']);
/** @var ResultInterface $result */
$result = $guzzle->execute($command);
$this->assertInstanceOf(Result::class, $result);
$result = $result->toArray();
$this->assertEquals(200, $result['statusCode']);
}
private function getServiceClient(
array $responses,
MockHandler $mock = null,
callable $commandToRequestTransformer = null
) {
$mock = $mock ?: new MockHandler();
foreach ($responses as $response) {
$mock->append($response);
}
return new GuzzleClient(
new HttpClient([
'handler' => $mock
]),
$this->getDescription(),
$commandToRequestTransformer,
$this->responseToResultTransformer(),
null,
['foo' => 'bar']
);
}
private function commandToRequestTransformer()
{
return function (CommandInterface $command) {
$data = $command->toArray();
$data['action'] = $command->getName();
return new Request('POST', '/', [], http_build_query($data));
};
}
private function responseToResultTransformer()
{
return function (ResponseInterface $response, RequestInterface $request, CommandInterface $command) {
$data = \GuzzleHttp\json_decode($response->getBody(), true);
parse_str($request->getBody(), $data['_request']);
return new Result($data);
};
}
private function getDescription()
{
return new Description(
[
'name' => 'Testing API ',
'baseUri' => 'http://httpbin.org/',
'operations' => [
'doThatThingYouDo' => [
'responseModel' => 'Bar'
],
'doThatThingOtherYouDo' => [
'responseModel' => 'Foo'
],
'doQueryLocation' => [
'httpMethod' => 'GET',
'uri' => '/queryLocation',
'parameters' => [
'foo' => [
'type' => 'string',
'required' => false,
'description' => 'Testing query request location',
'location' => 'query'
],
'bar' => [
'type' => 'string',
'required' => false,
'description' => 'Testing query request location',
'location' => 'query'
],
'baz' => [
'type' => 'string',
'required' => false,
'description' => 'Testing query request location',
'location' => 'query'
]
],
'responseModel' => 'QueryResponse'
],
'doBodyLocation' => [
'httpMethod' => 'GET',
'uri' => '/bodyLocation',
'parameters' => [
'foo' => [
'type' => 'string',
'required' => false,
'description' => 'Testing body request location',
'location' => 'body'
],
'bar' => [
'type' => 'string',
'required' => false,
'description' => 'Testing body request location',
'location' => 'body'
],
'baz' => [
'type' => 'string',
'required' => false,
'description' => 'Testing body request location',
'location' => 'body'
]
],
'responseModel' => 'BodyResponse'
],
'doJsonLocation' => [
'httpMethod' => 'GET',
'uri' => '/jsonLocation',
'parameters' => [
'foo' => [
'type' => 'string',
'required' => false,
'description' => 'Testing json request location',
'location' => 'json'
],
'bar' => [
'type' => 'string',
'required' => false,
'description' => 'Testing json request location',
'location' => 'json'
],
'baz' => [
'type' => 'string',
'required' => false,
'description' => 'Testing json request location',
'location' => 'json'
]
],
'responseModel' => 'JsonResponse'
],
'doHeaderLocation' => [
'httpMethod' => 'GET',
'uri' => '/headerLocation',
'parameters' => [
'foo' => [
'type' => 'string',
'required' => false,
'description' => 'Testing header request location',
'location' => 'header'
],
'bar' => [
'type' => 'string',
'required' => false,
'description' => 'Testing header request location',
'location' => 'header'
],
'baz' => [
'type' => 'string',
'required' => false,
'description' => 'Testing header request location',
'location' => 'header'
]
],
'responseModel' => 'HeaderResponse'
],
'doXmlLocation' => [
'httpMethod' => 'GET',
'uri' => '/xmlLocation',
'parameters' => [
'foo' => [
'type' => 'string',
'required' => false,
'description' => 'Testing xml request location',
'location' => 'xml'
],
'bar' => [
'type' => 'string',
'required' => false,
'description' => 'Testing xml request location',
'location' => 'xml'
],
'baz' => [
'type' => 'string',
'required' => false,
'description' => 'Testing xml request location',
'location' => 'xml'
]
],
'responseModel' => 'XmlResponse'
],
'doMultiPartLocation' => [
'httpMethod' => 'POST',
'uri' => '/multipartLocation',
'parameters' => [
'foo' => [
'type' => 'string',
'required' => false,
'description' => 'Testing multipart request location',
'location' => 'multipart'
],
'bar' => [
'type' => 'string',
'required' => false,
'description' => 'Testing multipart request location',
'location' => 'multipart'
],
'baz' => [
'type' => 'string',
'required' => false,
'description' => 'Testing multipart request location',
'location' => 'multipart'
],
'file' => [
'type' => 'any',
'required' => false,
'description' => 'Testing multipart request location',
'location' => 'multipart'
]
],
'responseModel' => 'MultipartResponse'
],
],
'models' => [
'Foo' => [
'type' => 'object',
'properties' => [
'code' => [
'location' => 'statusCode'
]
]
],
'Bar' => [
'type' => 'object',
'properties' => [
'code' => ['
location' => 'statusCode'
]
]
]
]
]
);
}
public function testDocumentationExampleFromReadme()
{
$client = new HttpClient();
$description = new Description([
'baseUrl' => 'http://httpbin.org/',
'operations' => [
'testing' => [
'httpMethod' => 'GET',
'uri' => '/get{?foo}',
'responseModel' => 'getResponse',
'parameters' => [
'foo' => [
'type' => 'string',
'location' => 'uri'
],
'bar' => [
'type' => 'string',
'location' => 'query'
]
]
]
],
'models' => [
'getResponse' => [
'type' => 'object',
'additionalProperties' => [
'location' => 'json'
]
]
]
]);
$guzzle = new GuzzleClient($client, $description);
$result = $guzzle->testing(['foo' => 'bar']);
$this->assertEquals('bar', $result['args']['foo']);
}
public function testDescriptionWithExtends()
{
$client = new HttpClient();
$description = new Description([
'baseUrl' => 'http://httpbin.org/',
'operations' => [
'testing' => [
'httpMethod' => 'GET',
'uri' => '/get',
'responseModel' => 'getResponse',
'parameters' => [
'foo' => [
'type' => 'string',
'default' => 'foo',
'location' => 'query'
]
]
],
'testing_extends' => [
'extends' => 'testing',
'responseModel' => 'getResponse',
'parameters' => [
'bar' => [
'type' => 'string',
'location' => 'query'
]
]
],
],
'models' => [
'getResponse' => [
'type' => 'object',
'additionalProperties' => [
'location' => 'json'
]
]
]
]);
$guzzle = new GuzzleClient($client, $description);
$result = $guzzle->testing_extends(['bar' => 'bar']);
$this->assertEquals('bar', $result['args']['bar']);
$this->assertEquals('foo', $result['args']['foo']);
}
}