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(AppModelEntityOauthIntegration $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 ModelTableOauthIntegrationsTable.php:
public function sendRequest(AppModelEntityOauthIntegration $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:
CakeErrorBaseErrorHandler::handleError() - CORE/src/Error/BaseErrorHandler.php, line 153
CakeHttpClientAuthOauth::_normalizedParams() - CORE/src/Http/Client/Auth/Oauth.php, line 315
CakeHttpClientAuthOauth::baseString() - CORE/src/Http/Client/Auth/Oauth.php, line 246
CakeHttpClientAuthOauth::_hmacSha1() - CORE/src/Http/Client/Auth/Oauth.php, line 143
CakeHttpClientAuthOauth::authentication() - CORE/src/Http/Client/Auth/Oauth.php, line 61
CakeHttpClient::_addAuthentication() - CORE/src/Http/Client.php, line 501
CakeHttpClient::_createRequest() - CORE/src/Http/Client.php, line 448
CakeHttpClient::_doRequest() - CORE/src/Http/Client.php, line 341
CakeHttpClient::get() - CORE/src/Http/Client.php, line 211
AppModelTableOauthIntegrationsTable::sendRequest() - APP/Model/Table/OauthIntegrationsTable.php, line 134
AppModelTableOrdersTable::importNewOrders() - APP/Model/Table/OrdersTable.php, line 672
AppShellMagentoShell::main() - APP/Shell/MagentoShell.php, line 36
CakeConsoleShell::runCommand() - CORE/src/Console/Shell.php, line 472
CakeConsoleShellDispatcher::_dispatch() - CORE/src/Console/ShellDispatcher.php, line 227
CakeConsoleShellDispatcher::dispatch() - CORE/src/Console/ShellDispatcher.php, line 182
CakeConsoleShellDispatcher::run() - CORE/src/Console/ShellDispatcher.php, line 128
[main] - ROOT/bin/cake.php, line 33
Code from HttpClientOauth.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 AppHttpClientAuth;
use CakeHttpClientAuthOauth;
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 CakeHttpClientAuthOauth::_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?