Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Magento REST API Create Method - Resource Method Not Implemented

Tags:

rest

php

magento

I have been trying to extend the REST API of magento to add configurable product, but I run into the error 405 - Resource Method Not Implemented.

This is my api.xml:

<?xml version="1.0"?>
<config>
    <api>
        <resources>
            <extapi_catalog translate="title" module="extapi">
                <model>extapi/catalog_api2</model>
                <title>Extapi Catalog API</title>
                <acl>extapi/product</acl>
                <methods>
                    <list translate="title" module="extapi">
                        <title>Retrieve Product Count</title>
                        <method>count</method>
                        <acl>extapi/product/info</acl>
                    </list>
                    <create translate="title" module="extapi">
                        <title>Create products</title>
                        <acl>extapi/product/create</acl>
                    </create>
                    <update translate="title" module="extapi">
                        <title>Update products</title>
                        <method>update</method>
                        <acl>extapi/product/update</acl>
                    </update>
                    <delete translate="title" module="extapi">
                        <title>Delete products</title>
                        <method>delete</method>
                        <acl>extapi/product/delete</acl>
                    </delete>
                </methods>
                <faults module="extapi">
                    <data_invalid>
                        <code>100</code>
                        <message>Invalid Request. Details in error message.</message>
                    </data_invalid>
                    <filters_invalid>
                        <code>101</code>
                        <message>Invalid filters specified. Details in error message.</message>
                    </filters_invalid>
                    <not_exists>
                        <code>102</code>
                        <message>No products.</message>
                    </not_exists>
                    <not_deleted>
                        <code>103</code>
                        <message>Product not deleted. Details in error message.</message>
                    </not_deleted>
                </faults>
            </extapi_catalog>
        </resources>
        <v2>
            <resources_function_prefix>
                <extapi_catalog>extapi_catalog</extapi_catalog>
            </resources_function_prefix>
        </v2>

        <acl>
            <resources>
                <extapi_catalog translate="title" module="extapi">
                    <title>Catalog</title>
                    <sort_order>3</sort_order>
                    <info translate="title" module="extapi">
                        <title>Retrieve product count</title>
                    </info>
                    <create translate="title" module="extapi">
                        <title>Create product count</title>
                    </create>
                    <update translate="title" module="extapi">
                        <title>Update product count</title>
                    </update>
                    <delete translate="title" module="extapi">
                        <title>Delete product count</title>
                    </delete>
                </extapi_catalog>
            </resources>
        </acl>
    </api>
</config>

this is my api2.xml:

<?xml version="1.0"?>
<config>
    <api2>
        <resource_groups>
            <extapi translate="title" module="api2">
                <title>Magepim API calls</title>
                <sort_order>30</sort_order>
                <children>
                    <extapi_product translate="title" module="api2">
                        <title>Product</title>
                        <sort_order>50</sort_order>
                    </extapi_product>
                </children>
            </extapi>
        </resource_groups>
        <resources>
            <extapiproducts translate="title" module="api2">
                <group>extapi_product</group>
                <model>extapi/catalog_api2_product</model>
                <working_model>extapi/catalog_api2_product</working_model>
                <title>Catalog Product</title>
                <sort_order>10</sort_order>
                <privileges>
                    <admin>
                        <create>1</create>
                        <retrieve>1</retrieve>
                        <update>1</update>
                        <delete>1</delete>
                    </admin>
                </privileges>
                <attributes translate="product_count" module="api2">
                    <product_count>Product Count</product_count>
                    <catalog_size>Product Count</catalog_size>
                </attributes>
                <entity_only_attributes>
                    <catalog>
                        <read>
                            <has_custom_options>1</has_custom_options>
                            <tier_price>1</tier_price>
                            <total_reviews_count>1</total_reviews_count>
                            <url>1</url>
                            <buy_now_url>1</buy_now_url>
                            <has_custom_options>1</has_custom_options>
                            <is_in_stock>1</is_in_stock>

                        </read>
                    </catalog>
                    </entity_only_attributes>
                <exclude_attributes>
                    <catalog>
                        <read>
                            <attribute_set_id>1</attribute_set_id>
                            <stock_data>1/</stock_data>
                            <use_config_gift_message_available>1</use_config_gift_message_available>
                            <use_config_gift_wrapping_available>1</use_config_gift_wrapping_available>
                            <url_key_create_redirect>1</url_key_create_redirect>

                        </read>
                    </catalog>
                    <admin>
                        <read>
                            <product_count>1</product_count>
                        </read>
                    </admin>
                </exclude_attributes>
                <routes>
                    <route_entity_count>
                        <route>/magepim/products</route>
                        <action_type>entity</action_type>
                    </route_entity_count>
                </routes>
                <versions>1</versions>
            </extapiproducts>
        </resources>
    </api2>
</config>

and this is my V1.php:

<?php

/**
 * Override for Magento's Catalog REST API
 */
class Magepim_Extapi_Model_Catalog_Api2_Product_Rest_Admin_V1 extends Mage_Catalog_Model_Api2_Product_Rest {

    /**
     * Retrieves the catalog collection and returns it's size
     *
     * @return int
     */

    protected function _retrieve()
    {
        /** @var $collection Mage_Catalog_Model_Resource_Product_Collection */
        $collection = Mage::getResourceModel('catalog/product_collection');
        $store = $this->_getStore();
        $collection->setStoreId($store->getId());
        $collection->addAttributeToSelect(array_keys(
                $this->getAvailableAttributes($this->getUserType(), Mage_Api2_Model_Resource::OPERATION_ATTRIBUTE_READ)
        ));
        $this->_applyCategoryFilter($collection);
        $this->_applyCollectionModifiers($collection);
        $products = $collection->load()->toArray();
        $size = $collection->getSize();
        $productCount = new stdClass();
        $productCount->catalog_size=$size;
        //return $size;
        return json_encode($productCount);
    }

    protected function _create($data)
    {
        /* @var $validator Mage_Catalog_Model_Api2_Product_Validator_Product */
        $validator = Mage::getModel('catalog/api2_product_validator_product', array(
            'operation' => self::OPERATION_CREATE
        ));

        if (!$validator->isValidData($data)) {
            foreach ($validator->getErrors() as $error) {
                $this->_error($error, Mage_Api2_Model_Server::HTTP_BAD_REQUEST);
            }
            $this->_critical(self::RESOURCE_DATA_PRE_VALIDATION_ERROR);
        }

        $type = $data['type_id'];
        if ($type !== 'simple') {
            $this->_critical("Creation of products with type '$type' is not implemented",
                Mage_Api2_Model_Server::HTTP_METHOD_NOT_ALLOWED);
        }
        $set = $data['attribute_set_id'];
        $sku = $data['sku'];

        /** @var $product Mage_Catalog_Model_Product */
        $product = Mage::getModel('catalog/product')
            ->setStoreId(Mage_Catalog_Model_Abstract::DEFAULT_STORE_ID)
            ->setAttributeSetId($set)
            ->setTypeId($type)
            ->setSku($sku);

        foreach ($product->getMediaAttributes() as $mediaAttribute) {
            $mediaAttrCode = $mediaAttribute->getAttributeCode();
            $product->setData($mediaAttrCode, 'no_selection');
        }

        $this->_prepareDataForSave($product, $data);
        try {
            $product->validate();
            $product->save();
            $this->_multicall($product->getId());
        } catch (Mage_Eav_Model_Entity_Attribute_Exception $e) {
            $this->_critical(sprintf('Invalid attribute "%s": %s', $e->getAttributeCode(), $e->getMessage()),
                Mage_Api2_Model_Server::HTTP_BAD_REQUEST);
        } catch (Mage_Core_Exception $e) {
            $this->_critical($e->getMessage(), Mage_Api2_Model_Server::HTTP_INTERNAL_ERROR);
        } catch (Exception $e) {
            $this->_critical(self::RESOURCE_UNKNOWN_ERROR);
        }

        return $this->_getLocation($product);
    }

    protected function _multicreate($data)
    {
        $this->getResponse ()->addMessage ( "", 0, array (
            'result' => "created" 
        ), Mage_Api2_Model_Response::MESSAGE_TYPE_SUCCESS );
        $this->getResponse ()->setRawHeader ( '"Content-Type" = "application/json"' );
        $base_url = Mage::getBaseUrl ( Mage_Core_Model_Store::URL_TYPE_WEB );
        $base_url = substr ( $base_url, 0, strlen ( $base_url ) - 1 );
        return $base_url . $this->_getLocation ( $order );
    }

    protected function _update(array $data)
    {
        return json_encode($productCount);
    }

    protected function _delete()
    {
        $this->getResponse ()->addMessage ( "", 0, array (
            'result' => "deleted" 
        ), Mage_Api2_Model_Response::MESSAGE_TYPE_SUCCESS );
        return json_encode(array("result","_delete"));
    }
}

At this stage, _retrieve() is the only method that is working properly. The _delete() seems to be able to receive method call and return code 200. _update() also seems to receive method call properly even though it returns an encoding error.

My main problem is the _create(), which doesn't get called at all. Even I remove all the statements inside the method (which is copied from /app/Mage/Model/Api2/Product/Rest/AdminV1.php), the same problem persists.

Can anyone please give me some advice? Also, is that any documentation available for extending the REST api (not Core API)? I have found a few tutorials on the web (my code is extended to one of the tutorials), but none of them specify the details of what a particular tag in the api.xml/api2.xml means and how the method mapping should be done.

Thanks in advance.

like image 870
Eddie Avatar asked Feb 18 '14 01:02

Eddie


1 Answers

I haven't worked much with Magento's RESTish API, but the

Resource method not implemented yet.

error string is defined in the following constant

#File: app/code/core/Mage/Api2/Model/Resource.php
const RESOURCE_METHOD_NOT_IMPLEMENTED = 'Resource method not implemented yet.';

This constant is used to raise a critical API error in the following locations

#File: app/code/core/Mage/Api2/Model/Resource.php
public function dispatch()
{
    switch ($this->getActionType() . $this->getOperation()) {    
        case self::ACTION_TYPE_ENTITY . self::OPERATION_CREATE:
            // Creation of objects is possible only when working with collection
            $this->_critical(self::RESOURCE_METHOD_NOT_IMPLEMENTED);
            break;
        case self::ACTION_TYPE_COLLECTION . self::OPERATION_CREATE:
            // If no of the methods(multi or single) is implemented, request body is not checked
            if (!$this->_checkMethodExist('_create') && !$this->_checkMethodExist('_multiCreate')) {
                $this->_critical(self::RESOURCE_METHOD_NOT_IMPLEMENTED);
            }
        ...
        default:
            $this->_critical(self::RESOURCE_METHOD_NOT_IMPLEMENTED);
    }
}

...

protected function _errorIfMethodNotExist($methodName)
{
    if (!$this->_checkMethodExist($methodName)) {
        $this->_critical(self::RESOURCE_METHOD_NOT_IMPLEMENTED);
    }
}

So, taking some education guesses here, it looks like this error is thrown when

  1. You attempt to call create on a single entity

  2. You attempt to call create on a collection, and the _create or _multiCreate methods haven't been implemented on your PHP class

  3. You attempt to call a method that's not one of the canonical CRUD methods (the default case)

  4. Or if the Magento system code calls the protected _errorIfMethodNotExist error.

The _errorIfMethodNotExist is used a lot, but in relation to _create calls, it's used here

#File: app/code/core/Mage/Api2/Model/Resource.php
// The create action has the dynamic type which depends on data in the request body
if ($this->getRequest()->isAssocArrayInRequestBody()) {
    $this->_errorIfMethodNotExist('_create');
    $filteredData = $this->getFilter()->in($requestData);
    if (empty($filteredData)) {
        $this->_critical(self::RESOURCE_REQUEST_DATA_INVALID);
    }
    $newItemLocation = $this->_create($filteredData);
    $this->getResponse()->setHeader('Location', $newItemLocation);

I'm not quite sure what this one's for. Maybe the comments above make sense to you (if you're used the REST API).

Regardless, I'd figure out which one of these calls to _critical is trigger your specific error (use some logging), and that might help you unravel what's wrong with your code.

Also, I wrote an article series on the implementation of the SOAP/XML-RPC API. Although the REST API uses new code not related to SOAP/XML-RPC, that series still may provide you with some insight on how the REST API is implemented.

like image 52
Alan Storm Avatar answered Sep 19 '22 17:09

Alan Storm