In this post I’m going to go through all the steps you need to make in order to extend the REST Api, there is very little documentation scattered around on this and I had to pull pieces together from various official sources, not only that but the responses Magento gives you can at times be confusing and does not really give you a hint to what the problem is, finally this is a full guide so no knowledge is assumed other than setting up the rest roles/users and assuming you have ssh access on the server.
This guide is split into two parts. Part 1 configuring REST access and Oauth setup, Part 2 extending the Api if you already have the api access and oauth configured correctly then skip to part 2.
Part 1 – configuring REST access and Oauth setup
Firstly set up your roles/users follow the guide on the Magento site
- Add a new user with an admin role
- Add all resources to the role in Role Api Resources
- In Rest attributes for the admin role select All resources
- Add a new consumer
Install/Configure Oath
it’s likely you won’t have this set up on your server, this needs to be installed via PECL, if you do want to check that you don’t already have it, check phpinfo(); and see if you have these lines:
It’s important here to have curl in the Request engine support, if you don’t this means you need to install curl (apt-get install php5-curl) or oauth has not been installed correctly. If you do have it you can try upgrading
pecl upgrade oauth
or you may have to uninstall/reinstall if for example you didn’t have curl before installing oauth.
pecl uninstall oauth
There is another caveat with 1.2.3 of oauth and that is Magento uses the multi update HTTP_MULTI_STATUS with a 207 response code when you issue PUT requests to update multiple items. In this version of oauth it incorrectly gives Invalid/bad auth response when using this response code, to get around this you can compile your own version with a fix (the bug has been fixed in the latest SVN release).
If your not bothered about using the multi update via PUT then go ahead and install oauth
pecl install oauth
To compile your own version download it from the PECL site http://pecl.php.net/package/oauth
Replace oauth.c with the fixed version
Upload all the files into a directory, if you haven’t already then issue the following commands a line at a time: (replace extname with the directory your working with)
$ cd extname
$ phpize
$ ./configure
$ make
$ make install
This should now have installed the extension correctly, you can check by doing:
pecl info oauth
And by looking at the phpinfo(), ok so now oauth is configured correctly we can run some tests.
Create a directory in the root of your site, e.g /tests and create a file called customers.php place the following code below changing:
- mydomain.com
- CONSUMEY_KEY
- CONSUMER_SECRET
You can get the key and secret from System > Web Services > Oauth Consumers > Select the appropriate key you set up earlier.
<?php
require '../app/Mage.php'; //Path to Magento
Mage::app();
// $callbackUrl is a path to your file with OAuth authentication example for the Admin user
$callbackUrl = "http://mydomain.com/tests/customers.php";
$temporaryCredentialsRequestUrl = "http://mydomain.com/oauth/initiate?oauth_callback=" . urlencode($callbackUrl);
$adminAuthorizationUrl = 'http://mydomain.com/admin/oauth_authorize';
$accessTokenRequestUrl = 'http://mydomain.com/oauth/token';
$apiUrl = 'http://mydomain.com/api/rest';
$consumerKey = 'CONSUMER_KEY';
$consumerSecret = 'CONSUMER_SECRET';
session_start();
if (!isset($_GET['oauth_token']) && isset($_SESSION['state']) && $_SESSION['state'] == 1) {
$_SESSION['state'] = 0;
}
try {
$authType = ($_SESSION['state'] == 2) ? OAUTH_AUTH_TYPE_AUTHORIZATION : OAUTH_AUTH_TYPE_URI;
$oauthClient = new OAuth($consumerKey, $consumerSecret, OAUTH_SIG_METHOD_HMACSHA1, $authType);
$oauthClient->enableDebug();
if (!isset($_GET['oauth_token']) && !$_SESSION['state']) {
$requestToken = $oauthClient->getRequestToken($temporaryCredentialsRequestUrl);
$_SESSION['secret'] = $requestToken['oauth_token_secret'];
$_SESSION['state'] = 1;
header('Location: ' . $adminAuthorizationUrl . '?oauth_token=' . $requestToken['oauth_token']);
exit;
} else if ($_SESSION['state'] == 1) {
$oauthClient->setToken($_GET['oauth_token'], $_SESSION['secret']);
$accessToken = $oauthClient->getAccessToken($accessTokenRequestUrl);
$_SESSION['state'] = 2;
$_SESSION['token'] = $accessToken['oauth_token'];
$_SESSION['secret'] = $accessToken['oauth_token_secret'];
header('Location: ' . $callbackUrl);
exit;
} else {
$oauthClient->setToken($_SESSION['token'], $_SESSION['secret']);
$resourceUrl = "$apiUrl/customers";
$oauthClient->fetch($resourceUrl, array(), 'GET', array('Content-Type' => 'application/json'));
// print_r($_SESSION);
// die();
echo $oauthClient->getLastResponse();
}
} catch (OAuthException $e) {
print_r($e->getMessage());
echo "<br/>";
print_r($e->lastResponse);
}
This should first ask you to authorise , proceed and accept the request and if all goes well you should now have a list of customers in JSON format, if you don’t want JSON then replace the fetch with
$oauthClient->fetch($resourceUrl, array(), 'GET', array('Accept' => 'text/xml'));
If you’ve got this far your now ready to extend the API, If you get any other response check that you have assigned the role properly and everywhere there is a resource dropdown it’s set to ALL. I’m not going to cover how to do this in a browser as well as Magento provides a guide on this , follow the guide you can get the access token/secret by uncommenting the two lines in the above script:
// print_r($_SESSION);
// die();
Part 2 extending the Api
Assuming you’ve set up the access and oauth correctly, (note incorrect oauth set up may look like it’s working when it isn’t!).
Let’s create a custom call for managing customer groups, create the following structure:
app/code/local/Custom
app/code/local/Custom/Restapi
app/code/local/Custom/Restapi/Groups
app/code/local/Custom/Restapi/Groups/Model
app/code/local/Custom/Restapi/Groups/Model/Api2
app/code/local/Custom/Restapi/Groups/Model/Api2/Group
app/code/local/Custom/Restapi/Groups/Model/Api2/Group/Rest
app/code/local/Custom/Restapi/Groups/Model/Api2/Group/Rest/Admin
app/code/local/Custom/Restapi/Groups/Model/Api2/Group/Rest/Admin/V1.php
app/code/local/Custom/Restapi/Groups/etc
app/code/local/Custom/Restapi/Groups/etc/api2.xml
app/code/local/Custom/Restapi/Groups/etc/config.xml
Enable our extension, create the following file
app/etc/modules/Custom_Restapi_Groups.xml
<config>
<modules>
<Custom_Restapi_Groups>
<active>true</active>
<codePool>local</codePool>
</Custom_Restapi_Groups>
</modules>
</config>
Set up the config.xml
<?xml version="1.0"?>
<config>
<modules>
<Custom_Restapi_Groups>
<version>0.1.0.0</version>
</Custom_Restapi_Groups>
</modules>
<global>
<models>
<groups>
<class>Custom_Restapi_Groups_Model</class>
</groups>
</models>
</global>
</config>
set up the api2.xml
<?xml version="1.0"?>
<config>
<api2>
<resource_groups>
<catalog translate="title" module="api2">
<title>Catalog</title>
<sort_order>10</sort_order>
</catalog>
</resource_groups>
<resources>
<groups translate="title" module="api2">
<group>catalog</group>
<model>groups/api2_group</model>
<title>Groups</title>
<sort_order>10</sort_order>
<privileges>
<admin>
<retrieve>1</retrieve>
<create>1</create>
<update>1</update>
<delete>1</delete>
</admin>
</privileges>
<attributes>
<name>Name</name>
</attributes>
<routes>
<route_entity>
<route>/groups/group/:id</route>
<action_type>entity</action_type>
</route_entity>
<route_collection>
<route>/groups/</route>
<action_type>collection</action_type>
</route_collection>
</routes>
<versions>1</versions>
</groups>
</resources>
</api2>
</config>
Here the resource groups refer to which group it appears in the backend role configuration in the admin, it’s title and sort order and the privileges available, The attributes section is important for when your using POST if you do not specify the variables your posting you will get an error 400 bad request.
Finally we define our routes another important note here is when using PUT/POST you need to use the action_type collection as certain methods are only available to the collection type, if you unsure or trying to replicate how the default api works have a look in the api2.xml of the module for example this is the same as /app/code/core/Mage/Customer/etc/api2.xml
<routes>
<route_entity>
<route>/customers/:id</route>
<action_type>entity</action_type>
</route_entity>
<route_collection>
<route>/customers</route>
<action_type>collection</action_type>
</route_collection>
</routes>
Now that’s set up lets populate our model in V1.php
<?php
class Custom_Restapi_Groups_Model_Api2_Group_Rest_Admin_V1 extends Mage_Api2_Model_Resource
{
/**
* Create a customer group
* @return array
*/
public function _create() {
//Create Customer Group
$requestData = $this->getRequest()->getBodyParams();
$groupName = $requestData['name'];
Mage::getSingleton('customer/group')->setData(
array('customer_group_code' => $groupName,'tax_class_id' => 3))
->save();
$targetGroup = Mage::getSingleton('customer/group');
$groupId = $targetGroup->load($groupName, 'customer_group_code')->getId();
if($groupId) {
$json = array('id' => $groupId);
echo json_encode($json);
exit();
}
}
/**
* Retrieve a group name by ID
* @return string
*/
public function _retrieve()
{
//retrieve a group name by ID
$customerGroupId = $this->getRequest()->getParam('id');
$groupname = Mage::getModel('customer/group')->load($customerGroupId)->getCustomerGroupCode();
return $groupname;
}
}
?>
Now we have our extension completed let’s try it out in a test, create the file groupInsert.php in the tests folder as before replace mydomain and the keys..
<?php
require '../app/Mage.php'; //Path to Magento
Mage::app();
// $callbackUrl is a path to your file with OAuth authentication example for the Admin user
$callbackUrl = "http://mydomain.com/tests/index.php";
$temporaryCredentialsRequestUrl = "http://mydomain.com/oauth/initiate?oauth_callback=" . urlencode($callbackUrl);
$adminAuthorizationUrl = 'http://mydomain.com/admin/oauth_authorize';
$accessTokenRequestUrl = 'http://mydomain.com/oauth/token';
$apiUrl = 'http://mydomain.com/api/rest';
$consumerKey = 'CONSUMER_KEY';
$consumerSecret = 'CONSUMER_SECRET';
session_start();
if (!isset($_GET['oauth_token']) && isset($_SESSION['state']) && $_SESSION['state'] == 1) {
$_SESSION['state'] = 0;
}
try {
$authType = ($_SESSION['state'] == 2) ? OAUTH_AUTH_TYPE_AUTHORIZATION : OAUTH_AUTH_TYPE_URI;
$oauthClient = new OAuth($consumerKey, $consumerSecret, OAUTH_SIG_METHOD_HMACSHA1, $authType);
$oauthClient->enableDebug();
if (!isset($_GET['oauth_token']) && !$_SESSION['state']) {
$requestToken = $oauthClient->getRequestToken($temporaryCredentialsRequestUrl);
$_SESSION['secret'] = $requestToken['oauth_token_secret'];
$_SESSION['state'] = 1;
header('Location: ' . $adminAuthorizationUrl . '?oauth_token=' . $requestToken['oauth_token']);
exit;
} else if ($_SESSION['state'] == 1) {
$oauthClient->setToken($_GET['oauth_token'], $_SESSION['secret']);
$accessToken = $oauthClient->getAccessToken($accessTokenRequestUrl);
$_SESSION['state'] = 2;
$_SESSION['token'] = $accessToken['oauth_token'];
$_SESSION['secret'] = $accessToken['oauth_token_secret'];
header('Location: ' . $callbackUrl);
exit;
} else {
$oauthClient->setToken($_SESSION['token'], $_SESSION['secret']);
$resourceUrl = "$apiUrl/groups/";
$productData = Mage::helper('core')->jsonEncode(array(
'name' => 'test_group'
));
$headers = array('Content-Type' => 'application/json');
$oauthClient->fetch($resourceUrl, $productData, 'POST', array('Content-Type' => 'application/json'));
echo $oauthClient->getLastResponse();
}
} catch (OAuthException $e) {
print_r($e);
}
Run this file in your browser and you should get a JSON response with the name and ID, likewise we can get the group ID from a name by replacing $resourceURL and $oauthClient with the following:
$resourceUrl = "$apiUrl/groups/group/1";
$oauthClient->fetch($resourceUrl, array(), 'GET', array('Content-Type' => 'application/json'));
That’s it, you’ve now extended the API! If you want to use the other methods for example to GET all groups you will need to write the various functions, check your exception log to see what function is required as you will get an error similar to:
#0 /var/www/website/app/code/core/Mage/Api2/Model/Resource.php(301): Mage_Api2_Model_Resource->_critical('Resource method...')
#1 /var/www/website/app/code/core/Mage/Api2/Model/Resource.php(251): Mage_Api2_Model_Resource->_errorIfMethodNotExist('_update')
#2 /var/www/website/app/code/core/Mage/Api2/Model/Dispatcher.php(74): Mage_Api2_Model_Resource->dispatch()
Then simply add this to your class , monitor the exception log for other errors too, sometimes I would receive the error
exception 'Mage_Api2_Exception' with message 'oauth_problem=token_rejected' in /var/www/website/app/code/core/Mage/Api2/Model/Auth/Adapter/Oauth.php:61
When I didn’t have the correct attribute set up or something else was wrong event though the token was valid, the other errors your likely to encounter
exception 'Mage_Api2_Exception' with message 'Decoding error.'
The input here is wrong check your JSON/XML formatting
exception 'Mage_Api2_Exception' with message 'The request data is invalid.'
Did you add the correct attribute?
exception 'Mage_Api2_Exception' with message 'Resource data pre-validation error.'
The data is not in the expected format, for example if your trying to populate a multi-select attribute this needs to be in an array with the correct ID’s
Please do get in touch if you have anything to add to my example or i’ve missed something or alternatively if you would like your own API developed or help writing one.