I'm using Guzzle version 6.3.3. I want to make multiple HTTP requests from an external API. The code shown below worker perfect for me. This is just one single request.
public function getAllTeams()
{
$client = new Client();
$uri = 'https://api.football-data.org/v2/competitions/2003/teams';
$header = ['headers' => ['X-Auth-Token' => 'MyKey']];
$res = $client->get($uri, $header);
$data = json_decode($res->getBody()->getContents(), true);
return $data['teams'];
}
But now I want to make multiple requests at once. In the documentation of Guzzle I found out how to do it, but it still didn't work properly. This is the code I try to use.
$header = ['headers' => ['X-Auth-Token' => 'MyKey']];
$client = new Client(['debug' => true]);
$res = $client->send(array(
$client->get('https://api.football-data.org/v2/teams/666', $header),
$client->get('https://api.football-data.org/v2/teams/1920', $header),
$client->get('https://api.football-data.org/v2/teams/6806', $header)
));
$data = json_decode($res->getBody()->getContents(), true);
return $data;
I get the error:
Argument 1 passed to GuzzleHttp\Client::send() must implement interface Psr\Http\Message\RequestInterface, array given called in TeamsController.
If I remove the $header after each URI then I get this error:
resulted in a '403 Forbidden' response: {"message": "The resource you are looking for is restricted. Please pass a valid API token and check your subscription fo (truncated...)
I tried several ways to set X-Auth-Token with my API key. But I still get errors and I don't know many other ways with Guzzle to set them.
I hope someone can help me out :)
Guzzle 6 uses a different approach to Guzzle 3, so you should use something like:
use function GuzzleHttp\Promise\all;
$header = ['headers' => ['X-Auth-Token' => 'MyKey']];
$client = new Client(['debug' => true]);
$responses = all([
$client->getAsync('https://api.football-data.org/v2/teams/666', $header),
$client->getAsync('https://api.football-data.org/v2/teams/1920', $header),
$client->getAsync('https://api.football-data.org/v2/teams/6806', $header)
])->wait();
$data = [];
foreach ($responses as $i => $res) {
$data[$i] = json_decode($res->getBody()->getContents(), true);
}
return $data;
Take a look at different questions on the same topic (#1, #2) to see more usage examples.
Related
How do I make a mock of third party api call . That is happening from controller . I have this line of code in controller .
public function store(){
$response = $request->post('http://thirdpaty.app/rmis/api/ebp/requests', [
"headers" => [
'Content-Type' => 'application/json',
],
"json" => [
"data"=>1
]
]);
$data = json_decode($response->getBody()->getContents());
$token = $data->token;
// Saving that token to database
}
And from the test I am doing
$response = $this->post('/to-store-method');
How do I mock the api request . So that in testing I don't have to call the third api request .
Right now I am doing
if(app()->get('env') == 'testing'){
$token = 123;
}else{
//Api call here
}
Is there any better alternative of doing this test
You'll need some way of injecting a mock handler into the Guzzle Client your controller is using. Traditionally, you'd either leverage dependency injection by passing the Guzzle Client via the constructor or through some referenced service in that code that you can mock (using Mockery) behind the scenes.
After that, check out the Guzzle documentation for a peak on how to mock requests in the HTTP Client:
http://docs.guzzlephp.org/en/stable/testing.html
You'd use a MockHandler to do something resembling the following code by building a stack of fake requests and responses.
// Create a mock and queue two responses.
$mock = new MockHandler([
new Response(200, ['X-Foo' => 'Bar'], 'Hello, World'),
new Response(202, ['Content-Length' => 0]),
new RequestException('Error Communicating with Server', new Request('GET', 'test'))
]);
$handlerStack = HandlerStack::create($mock);
$client = new Client(['handler' => $handlerStack]);
// The first request is intercepted with the first response.
$response = $client->request('GET', '/');
Actually, it is a bad practice to mock the network libraries. What I would recommend is to wrap the network request by the httpService and mock the httpService instead to return the required response.
public function store(){
$response = httpService.postData();
$data = json_decode($response->getBody()->getContents());
$token = $data->token;
// Saving that token to database
}
So, the you would get the response as return from the httpService.postData function and you can mock the postData instead of the network library.
When I perform this post call in my terminal with curl everything works great I see the post call coming in:
my curl call:
curl -X POST https://requestloggerbin.herokuapp.com/bin/a4d73cbb-2ddc-4fc7-ac38-60c2fac7e015 -d '{"test": "foo"}'
I am trying to replicate this call in my laravel app with guzzle, but I don't see the post call coming in and I get no error messages whatsoever so I have no idea what's going wrong.
My guzzle call:
$client = new Client();
$request = $client->post(
'https://requestloggerbin.herokuapp.com/bin/a4d73cbb-2ddc-4fc7-ac38-60c2fac7e015',
['body' => ['foo' => 'bar']]
);
$response = $request->send();
What am I doing wrong here?
$response = $request->send();
Which is not required at all .
use GuzzleHttp\Client;
$client = new Client();
$response = $client->post('http://localhost.com/23', ['body' => $requestXmlBody]);
$result = $response->getBody()->getContents();
$result1 = simplexml_load_string($result);
I am trying to mock a guzzle response from a specific api.
My controller code looks like this (amended for brevity):
class SomeClass
{
private $guzzle;
public function __construct(\GuzzleHttp\Client $guzzle) {
$this->guzzle = new $guzzle();
}
public function makeRequest(){
$client = $this->guzzle;
$url = 'http//somerurl';
$options = [];
$response = $client->request('POST', $url, $options);
return $response;
}
}
And the test looks something like this (again edited)...
public function someTest(){
$mock = $this->createMock(\GuzzleHttp\Client::class);
$mock->method('request')->willReturn([
'response' => 'somedata'
]);
$someClass = new $SomeClass($mock);
$response = $someClass->makeRequest();
$body = $response->getBody();
...
}
At this point the test returns "Call to a member function getBody on null";
How can the getBody response of a guzzle call be tested?
Thank you in advance...
One approach to testing with Guzzle is to configure a MockHandler
http://docs.guzzlephp.org/en/stable/testing.html
So instead of mocking the guzzle client, you create one like so:
public function someTest() {
$mock = new MockHandler([
new Response(200, [], 'The body!'),
// Add more responses for each response you need
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$someClass = new SomeClass($client);
$response = $someClass->makeRequest();
$body = $response->getBody();
$this->assertSame('The body!', $body);
}
The MockHandler requires you to 'queue' responses, meaning you need to know in what order external API calls will be made. I've taken this a step further and wrapped the MockHandler in another handler capable of stuffing a dummy-response into it at the last moment, if one isn't already waiting in the wings. See https://gist.github.com/kmuenkel/d4d473beb7b2297ac2d8cd480089a738
Just use that trait in your test, and call $this->mockGuzzleResponses(); from the test class's setUp() method. At that point, all requests intended to pass through Guzzle will be available for assertions by way of the $guzzleRequestLog property, and all responses can be mocked by calling $this->guzzleHandler->append(RequestInterface); at the beginning of your test.
Just make sure that all implementations of Guzzle in your code are instantiated by way of the app(Client::class) helper and not new Client. Otherwise the binding override won't take effect. That may have been your issue earlier.
Take a look at my composer package https://packagist.org/packages/doppiogancio/mocked-client.
In my opinion, it's a really simple way to mock a Guzzle Client, binding request URLs with responses.
$builder = new HandlerStackBuilder();
// Add a route with a response via callback
$builder->addRoute(
'GET', '/country/IT', static function (ServerRequestInterface $request): Response {
return new Response(200, [], '{"id":"+39","code":"IT","name":"Italy"}');
}
);
// Add a route with a response in a text file
$builder->addRouteWithFile('GET', '/country/IT/json', __DIR__ . '/fixtures/country.json');
// Add a route with a response in a string
$builder->addRouteWithString('GET', '/country/IT', '{"id":"+39","code":"IT","name":"Italy"}');
// Add a route mocking directly the response
$builder->addRouteWithResponse('GET', '/admin/dashboard', new Response(401));
$client = new Client(['handler' => $builder->build()]);
Once you did you will have a fully functional client to use normally
$response = $client->request('GET', '/country/DE/json');
$body = (string) $response->getBody();
$country = json_decode($body, true);
print_r($country);
// will return
Array
(
[id] => +49
[code] => DE
[name] => Germany
)
I have a very awkward issue here:
I am trying to call my own Laravel API like this:
$api_url `enter code here`= env("API_URL")."login";
$data= json_encode($data);
$api_url = env("API_URL")."login";
$client = new \GuzzleHttp\Client();
$headers = array('Accept: application/json','Content-Type: application/json', 'X-XSRF-TOKEN' => csrf_token());
$response = $client->request('POST', $api_url , ['headers'=>$headers,'json' => $data]);
print_r($response);
Then my API end point has code like this:
public function login(Request $request)
{
//This line gives me NULL
dump($request->all());
//the lines below print this "{"email":"dev#gumption.pk","password":"password01"}"
//but I cannot get this POSTED data in variables even with $request->input('email')
$content = $request->getContent();
print_r($content);
}
I am NOT able to retrieve variables from this POSTED data. I have tried $request->input('email');
$request->json('email');
Nothing is working. It simply says NULL.
I am not sure what is wrong here.
How to insert the records to couchDB in laravel. i have done the retrieval part but now I want to do insert, update and delete .
My retrieval code is below.
class couchdbcontroller extends Controller
{
public function getdata()
{
$content =null;
try {
$client = new Client();
$apiRequest = $client->request('GET','http://localhost:5984/user/_design/userdesign/_view/user-view?limit=20&reduce=false');
$code = $apiRequest->getBody()->getContents();
} catch (RequestException $re) {
//For handling exception
return $re->error;
}
return $code;
//return response()->json($code);
}
}
Inserting code below:
public function guzzle_insert_doc()
{
$client = new Client();
$res = $client->request('PUT', 'http://localhost:5984/login/new_doc',[
'uname' => 'admin',
'password' => 'admin123',
]);
//return $res;
}
Error: Client error: PUT http://localhost:5984/login/new_doc resulted in a 400 Bad Request response:
{"error":"bad_request","reason":"invalid UTF-8 JSON"}
From my google search, you could do something like this :
<?php
$client = new Client();
$doc = ['title'=>'This is a new doc'];
$res = $client->request('PUT', 'http://localhost:5984/db/new_doc',['json' => $doc]);
I assume you're using Guzzle (If I am wrong, tell us what your are using)
I didn't test my code since I don't have time to setup a laravel project with Guzzle. See documentation for further help.