How to get elapsed time of concurrent Guzzle Promise pool - guzzle6

I'm using Guzzle php version: 6.2.2. Is it possible in the code below to send the elapsed time the Promise has been running? E.g. every 5 seconds, send the elapsed time to some function?
$client = new Client([
'base_uri' => BASE_URL . 'sync/import', // Base URI is used with relative requests
'timeout' => 0, // 0 no timeout for operations and watching Promises
'verify' => true
]);
$requests = function ($syncRequests) {
foreach ($syncRequests as $key => $headers) {
yield new Request('PUT', '', ['Content-type' => 'application/json'], json_encode(['json' => ['sync' => $headers]]));
}
};
$pool = new Pool($client, $requests($this->syncRequests), [
'concurrency' => 10,
'fulfilled' => function ($response, $index) {
$this->promiseFulfilled($response, $index);
},
'rejected' => function ($reason, $index) {
$this->promiseRejected($reason, $index);
},
]);
$promise = $pool->promise(); // Initiate the transfers and create a promise
$promise->wait(); // Force the pool of requests to complete.
For example:
$pool = new Pool($client, $requests($this->syncRequests), [
'concurrency' => 10,
'while' => function () { // CALLED WHILE THE CONCURRENT REQUESTS ARE RUNNING!!
$this->elapsedTime();
},
'fulfilled' => function ($response, $index) {
$this->promiseFulfilled($response, $index);
},
'rejected' => function ($reason, $index) {
$this->promiseRejected($reason, $index);
},
]);

It's possible you could make something work with the "progress" request option. This will hook up a callback to CURLOPT_PROGRESSFUNCTION for every request in your pool. You might be able to get the time when these callbacks are triggered and compare it to the time before you executed the pool.
Another option could be to inject a custom TaskQueue into the promise library's queue() function and hook in custom logic there.

You can call a function inside the fulfilled function. The fulfilled function is called each time a request completes
Inside the fulfilled function you can call another function that for example updates the progress of the request in database. This function may be a member of the current object. So inside your fulfilled function you can have the following line:
$this->UpdateProgress();

This may not answer your question but to anyone who is searching how to obtain the elapsed time of every Promise you can simply do this:
$startTime = microtime(true);
$pool = new Pool($client, $requests(100), [
'concurrency' => 5,
'fulfilled' => function (Response $response, $index) {
$endTime = microtime(true);
$executionTime = round($endTime - $this->startTime, 2);
// dd($executionTim); or log it
},
'rejected' => function (RequestException $reason, $index) {
// this is delivered each failed request
},
]);
Similarly you can use then to do this
$promise = $client->requestAsync('GET', 'http://httpbin.org/get');
$startTime = microtime(true);
$promise->then(
function (ResponseInterface $res) {
$endTime = microtime(true);
$executionTime = round($endTime - $this->startTime, 2);
// dd($executionTim); or log it
},
function (RequestException $e) {
echo $e->getMessage() . "\n";
echo $e->getRequest()->getMethod();
}
);

Related

How can I get the amount spent / Faceook Marketing API

I'm working on Facebook Marketing API (v6.0) and trying to get the amount spent of each adset I have, actually I've started with Graph API explorer but I found nothing called spend and amount spent as a field, so after a while I got this trick which actually return false data , so the equation is like the following: daily_budget - budget_remaining = amount_spent , so let's assume that we have 900 - 800 = it returns 100 in my application while in business manager I got 50, here is my endpoint api
public function facebookData()
{
$fb = new \Facebook\Facebook([
'app_id' => 'xxxxx',
'app_secret' => 'xxxxxxx',
'default_graph_version' => 'v6.0',
//'default_access_token' => '{access-token}', // optional
]);
try {
// Returns a `FacebookFacebookResponse` object
$response = $fb->get(
'/act_xxxxxx/?fields=business,adsets.limit(1000){name,budget_remaining,daily_budget}',
'my_accesstoken'
);
} catch(FacebookExceptionsFacebookResponseException $e) {
echo 'Graph return=<i></i>ed an error: ' . $e->getMessage();
exit;
} catch(FacebookExceptionsFacebookSDKException $e) {
echo 'Facebook SDK returned an error: ' . $e->getMessage();
exit;
}
$data = $response->getGraphObject()->getProperty('adsets');
try
{
foreach ($data as $ad) {
\App\FacebookAd::UpdateOrCreate([
'ad_id' => $ad['id'],
],[
'name' => $ad['name'],
'budget_remaining' => substr($ad['budget_remaining'],0,-2),
'daily_budget' => substr($ad['daily_budget'],0,-2),
'total' => substr($ad['daily_budget'],0,-2) - substr($ad['budget_remaining'],0,-2), // remove last two zeros
'account_id' => "unset",
]);
}
}
catch(\Exception $e)
{
Log::error($e->getMessage());
}
}
Try to use insights.
https://developers.facebook.com/docs/marketing-api/reference/ads-insights/
In your link, add next "insights{spend}". So it will give:
'/act_xxxxxx/?fields=business,adsets.limit(1000){name,insights{spend}}'

Where can I find others callback of infinite grid store?

I use an Infinite Grid, actually all work fine but in the PHP that the store uses I do a request to an external webservice.
This is my Store Load:
myInfGrid.getStore().load({
scope : this,
url : 'download.php',
params : { },
callback: function(records, operation, success) {
if (success) {
// Here a process is done only once time even
// for others automated launched requests
}
}
});
My download.php file contains a curl request to a webservice like below.
Sometimes the WebService takes a very long time to answer.
...
$url = "https://external_webservice";
$params = array('callback' => $callback,
'q' => $query,
'rows' => $rows,
'sort' => $sort,
'start' => $start);
$url .= '?' . http_build_query($params);
$myCurl = curl_init($url);
curl_setopt($myCurl, CURLOPT_RETURNTRANSFER, true);
$fetchResult = curl_exec($myCurl);
curl_close($myCurl);
// HERE my process of $fetchResult content
...
I would like to extract this WebService Curl call and put it only on each callback.
My problem is: I don't know where are others callbacks.
I mean callbacks associated to all requests that are launched automatically by the infinitegrid ?

Slim 3: How to access user information from request headers

I am using Slim 3.1 and able to authenticate correctly i.e. able to generate the token and use it for another POST request. Now I want to parse the request header to extract the user information so I can identify which user have sent the request.
Here is my code to get the token.
$app->post('/login/token', function (Request $request, Response $response,
array $args) {
$input = $request->getParsedBody();
$now = new DateTime();
$future = new DateTime("+10 minutes");
$server = $request->getServerParams();
$jti = (new Base62)->encode(random_bytes(16));
$payload = [
"iat" => $now->getTimeStamp(),
"exp" => $future->getTimeStamp(),
"jti" => $jti,
"sub" => $input['username']
];
$sql = "SELECT * FROM user WHERE User_Name= :username";
$sth = $this->db->prepare($sql);
$sth->bindParam("username", $input['username']);
$sth->execute();
$user = $sth->fetchObject();
// verify email address.
if(!$user) {
return $this->response->withJson(['error' => true, 'message' => 'These credentials do not match our records.']);
}
// verify password.
if (!password_verify($input['password'],$user->User_Password)) {
return $this->response->withJson(['error' => true, 'message' => 'These credentials do not match our records.']);
}
$settings = $this->get('settings'); // get settings array.
//$token = JWT::encode(['User_ID' => $user->User_ID, 'username' => $user->User_Name], $settings['jwt']['secret'], "HS256");
$token = JWT::encode($payload, $settings['jwt']['secret'], "HS256");
return $this->response->withJson(['token' => $token, 'ACL' => $user->User_ACL]);
});
This returns me a token that I send in the following POST request
$app->group('/api', function(\Slim\App $app) {
$app->post('/createuser', function (Request $request, Response $response,
array $args) {
$headerValueArray = $request->getHeader('HTTP_AUTHORIZATION');
return $this->response->withJson(['success' => true, $token]);
});
});
The above POST request gives the following output
{
"success": true,
"0": ["Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MzU4Mjk0OTUsImV4cCI6MTUzNTgzNjY5NSwianRpIjoiMWc5ZFM3dUNLbzl1blRQZzBmYjU2diIsInN1YiI6InN5c2FkbWluIn0.vo3FBPhBkhfA2y7AG-afmjfeEhygIYY7lIaaVNX5i5k"]
}
I need to parse this token to extract the user information to see if its the valid user to perform this operation.In other words, how I can decode the above token.
Any help here will be much appreciated!

How to Retrieve HTTP Status Code with Guzzle?

New to Guzzle/Http.
I have a API rest url login that answer with 401 code if not authorized, or 400 if missing values.
I would get the http status code to check if there is some issues, but cannot have only the code (integer or string).
This is my piece of code, I did use instruction here ( http://docs.guzzlephp.org/en/stable/quickstart.html#exceptions )
namespace controllers;
use GuzzleHttp\Psr7;
use GuzzleHttp\Exception\ClientException;
$client = new \GuzzleHttp\Client();
$url = $this->getBaseDomain().'/api/v1/login';
try {
$res = $client->request('POST', $url, [
'form_params' => [
'username' => 'abc',
'password' => '123'
]
]);
} catch (ClientException $e) {
//echo Psr7\str($e->getRequest());
echo Psr7\str($e->getResponse());
}
You can use the getStatusCode function.
$response = $client->request('GET', $url);
$statusCode = $response->getStatusCode();
Note: If your URL redirects to some other URL then you need to set false value for allow_redirects property to be able to detect initial status code for parent URL.
// On client creation
$client = new GuzzleHttp\Client([
'allow_redirects' => false
]);
// Using with request function
$client->request('GET', '/url/with/redirect', ['allow_redirects' => false]);
If you want to check status code in catch block, then you need to use $exception->getCode()
More about responses
More about allow_redirects
you can also use this code :
$client = new \GuzzleHttp\Client(['base_uri' 'http://...', 'http_errors' => false]);
hope help you

Use CakePHP Http Client with Magento2 rest API search criteria

I'm trying to send a GET request to a local Magento2 rest API to get all the orders after a certain time. I'm following http://devdocs.magento.com/guides/v2.1/howdoi/webapi/search-criteria.html#simple-search-using-a-timestamp. I'm using CakePHP 3.4's Http Client (https://book.cakephp.org/3.0/en/core-libraries/httpclient.html) and have successfully integrated with Magento using Oauth1 and have no problems with simpler GET requests like http://www.magento.dev.com/rest/V1/stockItems/:productSku. It is a problem with passing the search criteria. The response is always a 401 Invalid Signature.
Using Postman, I can get a valid response to http://www.magento.dev.com/rest/V1/orders?searchCriteria[filter_groups][0][filters][0][field]=created_at&searchCriteria[filter_groups][0][filters][0][value]=2016-07-01 00:00:00&searchCriteria[filter_groups][0][filters][0][condition_type]=gt
This is what I have so far/how I'm sending the request:
In Model/Table/OrdersTable.php:
public function importNewOrders(\App\Model\Entity\OauthIntegration $integrationDetails)
{
$this->OauthIntegrations = TableRegistry::get('OauthIntegrations');
$this->Orders = TableRegistry::get('Orders');
$timeCutOff = '2015-01-01 00:00:00';
$search = [
'searchCriteria' => [
'filterGroups' => [
0 => [
'filters' => [
0 => [
'field' => 'created_at',
'value' => $timeCutOff,
'condition_type' => 'gt'
]
]
]
]
]
];
// 'searchCriteria[filter_groups][0][filters][0][field]' => 'created_at',
// 'searchCriteria[filter_groups][0][filters][0][value]' => $timeCutOff,
// 'searchCriteria[filter_groups][0][filters][0][condition_type]' => 'gt'
$action = '/V1/orders';
$type = "GET";
$response = $this->OauthIntegrations->sendRequest(
$integrationDetails,
$action,
$type,
'',
$search);
Log::write('debug', $response->body());
return $response;
}
and in Model\Table\OauthIntegrationsTable.php:
public function sendRequest(\App\Model\Entity\OauthIntegration $integrationDetails,
string $action, string $method = "GET", string $data = '', array $search = null)
{
$http = new Client([
'auth' => [
'type' => 'oauth',
'consumerKey' => $integrationDetails->oauth_consumer_key,
'consumerSecret' => $integrationDetails->oauth_consumer_secret,
'token' => $integrationDetails->oauth_token,
'tokenSecret' => $integrationDetails->oauth_token_secret
]
]);
$url = $integrationDetails->store_base_url . 'rest' . $action;
if ($method == 'GET'){
if (!isset($search)){
$search = [];
}
$response = $http->get($url, $search, []);
} else if ($method == 'POST'){
$response = $http->post($url, $data, [
'type' => 'json',
]);
} else if($method == 'PUT'){
$response = $http->put($url, $data, [
'type' => 'json',
]);
}
Log::write('debug', 'url: ' . $url . ' and status code: ' . $response->getStatusCode());
return $response;
}
and this is the error (I'm hoping) is the cause of the Invalid Signature response:
2017-03-28 10:07:01 Notice: Notice (8): Array to string conversion in [/var/www/cakephp/html/beacon/vendor/cakephp/cakephp/src/Http/Client/Auth/Oauth.php, line 315]
Trace:
Cake\Error\BaseErrorHandler::handleError() - CORE/src/Error/BaseErrorHandler.php, line 153
Cake\Http\Client\Auth\Oauth::_normalizedParams() - CORE/src/Http/Client/Auth/Oauth.php, line 315
Cake\Http\Client\Auth\Oauth::baseString() - CORE/src/Http/Client/Auth/Oauth.php, line 246
Cake\Http\Client\Auth\Oauth::_hmacSha1() - CORE/src/Http/Client/Auth/Oauth.php, line 143
Cake\Http\Client\Auth\Oauth::authentication() - CORE/src/Http/Client/Auth/Oauth.php, line 61
Cake\Http\Client::_addAuthentication() - CORE/src/Http/Client.php, line 501
Cake\Http\Client::_createRequest() - CORE/src/Http/Client.php, line 448
Cake\Http\Client::_doRequest() - CORE/src/Http/Client.php, line 341
Cake\Http\Client::get() - CORE/src/Http/Client.php, line 211
App\Model\Table\OauthIntegrationsTable::sendRequest() - APP/Model/Table/OauthIntegrationsTable.php, line 134
App\Model\Table\OrdersTable::importNewOrders() - APP/Model/Table/OrdersTable.php, line 672
App\Shell\MagentoShell::main() - APP/Shell/MagentoShell.php, line 36
Cake\Console\Shell::runCommand() - CORE/src/Console/Shell.php, line 472
Cake\Console\ShellDispatcher::_dispatch() - CORE/src/Console/ShellDispatcher.php, line 227
Cake\Console\ShellDispatcher::dispatch() - CORE/src/Console/ShellDispatcher.php, line 182
Cake\Console\ShellDispatcher::run() - CORE/src/Console/ShellDispatcher.php, line 128
[main] - ROOT/bin/cake.php, line 33
Code from Http\Client\Oauth.php where error occurs:
$pairs = [];
foreach ($args as $k => $val) {
if (is_array($val)) {
sort($val, SORT_STRING);
Log::write('debug', 'about to go through foreach($val as $nestedVal)');
foreach ($val as $nestedVal) {
Log::write('debug', $nestedVal);
$pairs[] = "$k=$nestedVal"; // <<< HERE
}
} else {
$pairs[] = "$k=$val";
}
}
debugging from above results in:
2017-03-28 10:07:01 Debug: about to go through foreach($val as $nestedVal)
2017-03-28 10:07:01 Debug: Array
(
[0] => Array
(
[filters] => Array
(
[0] => Array
(
[field] => created_at
[value] => 2015-01-01 00:00:00
[condition_type] => gt
)
)
)
)
In summary, is it possible to pass a multi-dimensional array to the 2nd parameter in a get request using Cake's Http Client?
// Is it possible to replace ['q' => 'widget'] with a multi-dimensional array??
$response = $http->get('http://example.com/search', ['q' => 'widget']);
If not, what would be the best way to use Cake's Http Client to send GET request to: http://www.magento.dev.com/rest/V1/orders?searchCriteria[filter_groups][0][filters][0][field]=created_at&searchCriteria[filter_groups][0][filters][0][value]=2016-07-01 00:00:00&searchCriteria[filter_groups][0][filters][0][condition_type]=gt ?
Thanks in advance!!!
Possible bug
This may be considered as a possible bug. I don't think the OAuth specs take this PHP style bracket stuff in URLs into account, and therefore sorting/encoding the parameters is limited to flat key=value sets, ie a key would be
searchCriteria[filter_groups][0][filters][0][field]
and the value would be
created_at
The CakePHP OAuth adapter however parses the requests query string into a possibly deeply nested array structure, which will then fail, as it doesn't handle that case.
I'd suggest that you report this as a possible bug. Further problems may occour as encoding seems to be ment to be applied before sorting, where in the CakePHP implementation, additonal parameter encoding is applied after sorting (that may actually be fine though, I'm not sure).
Try a custom OAuth adapter as a workaround
Until this is being fixed/enhanced, you could use a custom OAuth adapter that handles things "properly" (whatever that means in this context). Here's a quick and dirty example (works for me with the Magento API).
Create src/Http/Client/Auth/AppOAuth.php
<?php
namespace App\Http\Client\Auth;
use Cake\Http\Client\Auth\Oauth;
class AppOAuth extends Oauth
{
protected function _normalizedParams($request, $oauthValues)
{
$query = parse_url($request->url(), PHP_URL_QUERY);
parse_str($query, $queryArgs);
$post = [];
$body = $request->body();
if (is_string($body) &&
$request->getHeaderLine('content-type') === 'application/x-www-form-urlencoded'
) {
parse_str($body, $post);
}
if (is_array($body)) {
$post = $body;
}
$args = array_merge($queryArgs, $oauthValues, $post);
$query = http_build_query($args);
$args = [];
foreach (explode('&', $query) as $value) {
$pair = explode('=', $value, 2);
$args[] =
rawurlencode(rawurldecode($pair[0])) .
'=' .
rawurlencode(rawurldecode($pair[1]));
}
usort($args, 'strcmp');
return implode('&', $args);
}
}
Compare to \Cake\Http\Client\Auth\Oauth::_normalizedParams()
Use it by specifying the classname in the type option for your client instance:
'type' => 'AppOAuth',
ps
shouldn't it be filter_groups instead of filterGroups in your $search array?