├── .gitignore ├── src └── app │ ├── locale │ ├── en_US │ │ └── TS │ │ │ └── ApiPlus │ │ │ ├── Adminhtml.csv │ │ │ └── Frontend.csv │ └── pt_BR │ │ └── TS │ │ └── ApiPlus │ │ ├── Adminhtml.csv │ │ └── Frontend.csv │ ├── code │ └── community │ │ └── TS │ │ └── ApiPlus │ │ ├── Exception.php │ │ ├── Helper │ │ └── Data.php │ │ ├── Controller │ │ └── Front │ │ │ └── Action.php │ │ ├── controllers │ │ └── JsonController.php │ │ ├── etc │ │ ├── api.xml │ │ ├── adminhtml.xml │ │ ├── config.xml │ │ └── system.xml │ │ ├── Model │ │ ├── Config.php │ │ ├── Http │ │ │ ├── Response.php │ │ │ └── Request.php │ │ └── Server │ │ │ └── Handler.php │ │ └── Trait │ │ ├── Config.php │ │ ├── Http.php │ │ └── Data.php │ └── etc │ └── modules │ └── TS_ApiPlus.xml ├── modman ├── dev └── tests │ └── apiplus │ ├── tests │ ├── DataTest.php │ └── AliasTest.php │ ├── AbstractTest.php │ ├── phpunit.xml.dist │ └── bootstrap.php ├── setup └── run.sh ├── LICENSE.txt ├── .travis.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/locale/en_US/TS/ApiPlus/Adminhtml.csv: -------------------------------------------------------------------------------- 1 | "API Plus","API Plus" 2 | "Configuration","Configuration" 3 | "API Plus Settings","API Plus Settings" 4 | "Enabled","Enabled" 5 | "Enable Result Caching","Enable Result Caching" 6 | "Result Caching Lifetime","Result Caching Lifetime" 7 | -------------------------------------------------------------------------------- /modman: -------------------------------------------------------------------------------- 1 | src/app/code/community/TS/ApiPlus app/code/community/TS/ApiPlus 2 | src/app/etc/modules/TS_ApiPlus.xml app/etc/modules/TS_ApiPlus.xml 3 | src/app/locale/en_US/TS/ApiPlus app/locale/en_US/TS/ApiPlus 4 | src/app/locale/pt_BR/TS/ApiPlus app/locale/pt_BR/TS/ApiPlus 5 | -------------------------------------------------------------------------------- /src/app/locale/pt_BR/TS/ApiPlus/Adminhtml.csv: -------------------------------------------------------------------------------- 1 | "API Plus","API Plus" 2 | "Configuration","Configuração" 3 | "API Plus Settings","Configurações API Plus" 4 | "Enabled","Habilitado" 5 | "Enable Result Caching","Habilitar Cache de Resultado" 6 | "Result Caching Lifetime","Tempo de Vida do Cache de Resultado" 7 | -------------------------------------------------------------------------------- /dev/tests/apiplus/tests/DataTest.php: -------------------------------------------------------------------------------- 1 | getFaults(); 11 | 12 | $this->assertInternalType('array', $faults, 'Faults must be an array.'); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /dev/tests/apiplus/AbstractTest.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class TS_ApiPlus_Exception extends Mage_Core_Exception 16 | { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/app/code/community/TS/ApiPlus/Helper/Data.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class TS_ApiPlus_Helper_Data extends Mage_Core_Helper_Data 16 | { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /dev/tests/apiplus/phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | tests 14 | 15 | -------------------------------------------------------------------------------- /src/app/locale/pt_BR/TS/ApiPlus/Frontend.csv: -------------------------------------------------------------------------------- 1 | "There was a problem in the request.","Houve um problema na sua requisição." 2 | "Your account has been deactivated.","Sua conta foi desativada." 3 | "Access denied.","Acesso negado." 4 | "Unable to login.","Não foi possível logar." 5 | "There is no resource method defined in your request. Please check it and try again.","Não existe um método de recurso definido na sua requisição. Por favor, verifique-a e tente novamente." 6 | "Your credentials are incorrect. Please verify the username and password.","Suas credenciais estão incorretas. Por favor, verifique o nome de usuário e senha." 7 | -------------------------------------------------------------------------------- /src/app/locale/en_US/TS/ApiPlus/Frontend.csv: -------------------------------------------------------------------------------- 1 | "There was a problem in the request.","There was a problem in the request." 2 | "Your account has been deactivated.","Your account has been deactivated." 3 | "Access denied.","Access denied.","Access denied.","Access denied." 4 | "Unable to login.","Unable to login." 5 | "There is no resource method defined in your request. Please check it and try again.","There is no resource method defined in your request. Please check it and try again." 6 | "Your credentials are incorrect. Please verify the username and password.","Your credentials are incorrect. Please verify the username and password." 7 | -------------------------------------------------------------------------------- /src/app/code/community/TS/ApiPlus/Controller/Front/Action.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class TS_ApiPlus_Controller_Front_Action extends Mage_Core_Controller_Front_Action 16 | { 17 | 18 | use TS_ApiPlus_Trait_Data, 19 | TS_ApiPlus_Trait_Http; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/app/code/community/TS/ApiPlus/controllers/JsonController.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class TS_ApiPlus_JsonController extends TS_ApiPlus_Controller_Front_Action 16 | { 17 | 18 | public function indexAction() 19 | { 20 | $apiRequest = $this->getApiRequest(); 21 | $apiRequest->dispatch(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/app/etc/modules/TS_ApiPlus.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | true 21 | community 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /dev/tests/apiplus/bootstrap.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('TS_ApiPlus_Helper_Data', $helper); 10 | } 11 | 12 | 13 | public function testModelConfig() 14 | { 15 | $model = Mage::getModel('ts_apiplus/config'); 16 | $this->assertInstanceOf('TS_ApiPlus_Model_Config', $model); 17 | } 18 | 19 | 20 | public function testModelServerHandler() 21 | { 22 | $model = Mage::getModel('ts_apiplus/server_handler'); 23 | $this->assertInstanceOf('TS_ApiPlus_Model_Server_Handler', $model); 24 | } 25 | 26 | 27 | public function testModelHttpRequest() 28 | { 29 | $model = Mage::getConfig()->getModelClassName('ts_apiplus/http_request'); 30 | $this->assertEquals('TS_ApiPlus_Model_Http_Request', $model); 31 | } 32 | 33 | 34 | public function testModelHttpResponse() 35 | { 36 | $model = Mage::getModel('ts_apiplus/http_response'); 37 | $this->assertInstanceOf('TS_ApiPlus_Model_Http_Response', $model); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 5.3 4 | - 5.4 5 | - 5.5 6 | - 5.6 7 | - 7.0 8 | 9 | matrix: 10 | allow_failures: 11 | - php: 5.3 12 | - php: 7.0 13 | fast_finish: true 14 | 15 | env: 16 | global: 17 | - MAGENTO_DB_ALLOWSAME=1 18 | - SKIP_CLEANUP=1 19 | - TEST_BASEDIR=.modman/ApiPlus/dev/tests/apiplus 20 | matrix: 21 | - MAGENTO_VERSION=magento-mirror-1.9.2.4 22 | - MAGENTO_VERSION=magento-mirror-1.9.2.3 23 | - MAGENTO_VERSION=magento-mirror-1.9.2.2 24 | - MAGENTO_VERSION=magento-mirror-1.9.2.1 25 | - MAGENTO_VERSION=magento-mirror-1.9.2.0 26 | - MAGENTO_VERSION=magento-mirror-1.9.1.1 27 | - MAGENTO_VERSION=magento-mirror-1.9.1.0 28 | - MAGENTO_VERSION=magento-mirror-1.9.0.1 29 | - MAGENTO_VERSION=magento-mirror-1.9.0.0 30 | - MAGENTO_VERSION=magento-mirror-1.8.1.0 31 | - MAGENTO_VERSION=magento-mirror-1.8.0.0 32 | - MAGENTO_VERSION=magento-mirror-1.7.0.2 33 | 34 | mysql: 35 | database: magento 36 | username: root 37 | encoding: utf8 38 | 39 | script: 40 | - bash ./setup/run.sh 41 | 42 | notifications: 43 | email: 44 | recipients: 45 | - tiago@tiagosampaio.com 46 | on_success: always 47 | on_failure: always 48 | -------------------------------------------------------------------------------- /src/app/code/community/TS/ApiPlus/etc/api.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 500 22 | 23 | 24 | 500 25 | 26 | 27 | 401 28 | 29 | 30 | 405 31 | 32 | 33 | 405 34 | 35 | 36 | 410 37 | 38 | 39 | 400 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/app/code/community/TS/ApiPlus/Model/Config.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class TS_ApiPlus_Model_Config extends Mage_Api_Model_Config 16 | { 17 | 18 | use TS_ApiPlus_Trait_Data; 19 | 20 | /** @var string */ 21 | const CACHE_TAG = 'API_PLUS_RESULTS'; 22 | 23 | 24 | /** 25 | * @param string|null $resourceName 26 | * 27 | * @return array 28 | */ 29 | public function getFaults($resourceName = null) 30 | { 31 | if (is_null($resourceName) 32 | || !isset($this->getResources()->$resourceName) 33 | || !isset($this->getResources()->$resourceName->faults)) { 34 | $faultsNode = $this->getNode('faults'); 35 | } else { 36 | $faultsNode = $this->getResources()->$resourceName->faults; 37 | } 38 | /* @var $faultsNode Varien_Simplexml_Element */ 39 | 40 | $translateModule = 'api'; 41 | 42 | if (isset($faultsNode['module'])) { 43 | $translateModule = (string) $faultsNode['module']; 44 | } 45 | 46 | $faults = array(); 47 | 48 | foreach ($faultsNode->children() as $faultName => $fault) { 49 | $faults[$faultName] = array( 50 | 'code' => (string) $fault->code, 51 | 'http_code' => (int) $fault->http_code, 52 | 'message' => Mage::helper($translateModule)->__((string)$fault->message) 53 | ); 54 | } 55 | 56 | return $faults; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/app/code/community/TS/ApiPlus/Trait/Config.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | trait TS_ApiPlus_Trait_Config 16 | { 17 | 18 | /** 19 | * @param string $section 20 | * @param string $group 21 | * @param string $field 22 | * @param int|Mage_Core_Model_Store $store 23 | * 24 | * @return mixed 25 | */ 26 | protected function getGlobalConfig($section, $group, $field, $store = null) 27 | { 28 | $configPath = implode('/', array($section, $group, $field)); 29 | return Mage::getStoreConfig($configPath, $store); 30 | } 31 | 32 | 33 | /** 34 | * @param string $field 35 | * @param int|Mage_Core_Model_Store $store 36 | * 37 | * @return mixed 38 | */ 39 | protected function getApiPlusConfig($field, $store = null) 40 | { 41 | return $this->getGlobalConfig('api', 'api_plus', $field, $store); 42 | } 43 | 44 | 45 | /** 46 | * @param Mage_Core_Model_Store $store 47 | * 48 | * @return bool 49 | */ 50 | protected function isEnabled($store = null) 51 | { 52 | return (bool) $this->getApiPlusConfig('enabled', $store); 53 | } 54 | 55 | 56 | /** 57 | * @param Mage_Core_Model_Store $store 58 | * 59 | * @return bool 60 | */ 61 | protected function isResultCacheEnabled($store = null) 62 | { 63 | return (bool) $this->getApiPlusConfig('cache_enabled', $store); 64 | } 65 | 66 | 67 | /** 68 | * @param Mage_Core_Model_Store $store 69 | * 70 | * @return int 71 | */ 72 | protected function getResultCacheLifetime($store = null) 73 | { 74 | return (int) $this->getApiPlusConfig('cache_lifetime', $store); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/app/code/community/TS/ApiPlus/etc/adminhtml.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | API Plus 28 | 1000 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | API Plus 45 | 1000 46 | 47 | 48 | Configuration 49 | 10 50 | adminhtml/system_config/edit/section/api 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/app/code/community/TS/ApiPlus/Trait/Http.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | trait TS_ApiPlus_Trait_Http 16 | { 17 | 18 | /** 19 | * @param int $code 20 | * @param null $message 21 | * 22 | * @return void 23 | * 24 | * @throws Zend_Controller_Response_Exception 25 | */ 26 | protected function sendHttpSuccessResponse($code, $message = null, $additionalData = array()) 27 | { 28 | $data = array( 29 | 'error' => false, 30 | 'message' => $message 31 | ); 32 | 33 | if (!empty($additionalData) && is_array($additionalData)) { 34 | foreach ((array) $additionalData as $key => $value) { 35 | $data[$key] = $value; 36 | } 37 | } 38 | 39 | $this->sendHttpResponse($code, $data); 40 | } 41 | 42 | /** 43 | * @param int $code 44 | * @param null $message 45 | * 46 | * @return void 47 | * 48 | * @throws Zend_Controller_Response_Exception 49 | */ 50 | protected function sendHttpErrorResponse($code, $message = null, $additionalData = array()) 51 | { 52 | $data = array( 53 | 'error' => true, 54 | 'message' => $message 55 | ); 56 | 57 | if (!empty($additionalData) && is_array($additionalData)) { 58 | foreach ((array) $additionalData as $key => $value) { 59 | $data[$key] = $value; 60 | } 61 | } 62 | 63 | $this->sendHttpResponse($code, $data); 64 | } 65 | 66 | 67 | /** 68 | * @param string $code 69 | * @param array $data 70 | * 71 | * @throws Zend_Controller_Response_Exception 72 | */ 73 | protected function sendHttpResponse($code, $data = array()) 74 | { 75 | $this->getHttpResponse()->setHttpResponseCode($code); 76 | $this->getHttpResponse()->setBody(Zend_Json_Encoder::encode($data)); 77 | $this->getHttpResponse()->sendResponse(); 78 | 79 | die(); 80 | } 81 | 82 | 83 | /** 84 | * @return Mage_Core_Controller_Response_Http 85 | */ 86 | protected function getHttpResponse() 87 | { 88 | return Mage::app()->getResponse(); 89 | } 90 | 91 | 92 | /** 93 | * @return Mage_Core_Controller_Request_Http 94 | */ 95 | protected function getHttpRequest() 96 | { 97 | return Mage::app()->getRequest(); 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/app/code/community/TS/ApiPlus/etc/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 0.1.0.0 21 | 22 | 23 | 24 | 25 | 26 | TS_ApiPlus_Model 27 | 28 | 29 | 30 | 31 | TS_ApiPlus_Block 32 | 33 | 34 | 35 | 36 | TS_ApiPlus_Helper 37 | 38 | 39 | 40 | 41 | 42 | 43 | API Plus Results will be cached if this option and API Plus config are enabled (System > Configuration > Api > API Plus Settings). 44 | API_PLUS_RESULTS 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | TS/ApiPlus/Frontend.csv 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | TS_ApiPlus 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | TS/ApiPlus/Adminhtml.csv 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 1 84 | 1 85 | 7200 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /src/app/code/community/TS/ApiPlus/Model/Http/Response.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | class TS_ApiPlus_Model_Http_Response 17 | { 18 | 19 | /** 2XX Codes - Successful */ 20 | const HTTP_OK = 200; 21 | const HTTP_CREATED = 201; 22 | const HTTP_ACCEPTED = 202; 23 | const HTTP_NON_AUTHORITATIVE_INFORMATION = 203; 24 | const HTTP_NO_CONTENT = 204; 25 | const HTTP_RESET_CONTENT = 205; 26 | const HTTP_PARTIAL_CONTENT = 206; 27 | 28 | /** 3XX Codes - Redirection */ 29 | const HTTP_MULTIPLE_CHOICES = 300; 30 | const HTTP_MOVED_PERMANENTLY = 301; 31 | const HTTP_FOUND = 302; 32 | const HTTP_SEE_OTHER = 303; 33 | const HTTP_NOT_MODIFIED = 304; 34 | const HTTP_USED_PROXY = 305; 35 | const HTTP_TEMPORARY_REDIRECT = 307; 36 | 37 | /** 4XX Codes - Client Error*/ 38 | const HTTP_BAD_REQUEST = 400; 39 | const HTTP_UNAUTHORIZED = 401; 40 | const HTTP_PAYMENT_REQUIRED = 402; 41 | const HTTP_FORBIDDEN = 403; 42 | const HTTP_NOT_FOUND = 404; 43 | const HTTP_METHOD_NOT_ALLOWED = 405; 44 | const HTTP_NOT_ACCEPTABLE = 406; 45 | const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407; 46 | const HTTP_REQUEST_TIMEOUT = 408; 47 | const HTTP_CONFLICT = 409; 48 | const HTTP_GONE = 410; 49 | const HTTP_LENGTH_REQUIRED = 411; 50 | const HTTP_PRECONDITION_FAILED = 412; 51 | const HTTP_REQUEST_ENTITY_TOO_LARGE = 413; 52 | const HTTP_REQUEST_URI_TOO_LONG = 414; 53 | const HTTP_UNSUPPORTED_MEDIA_TYPE = 415; 54 | const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416; 55 | const HTTP_EXPECTATION_FAILED = 417; 56 | 57 | /** 5XX Codes - Server Error */ 58 | const HTTP_INTERNAL_SERVER_ERROR = 500; 59 | const HTTP_NOT_IMPLEMENTED = 501; 60 | const HTTP_BAD_GATEWAY = 502; 61 | const HTTP_SERVICE_UNAVAILABLE = 503; 62 | const HTTP_GATEWAY_TIMEOUT = 504; 63 | const HTTP_HTTP_VERSION_NOT_SUPPORTED = 505; 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/app/code/community/TS/ApiPlus/etc/system.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | text 24 | 100 25 | 1 26 | 1 27 | 1 28 | 29 | 30 | 31 | select 32 | adminhtml/system_config_source_yesno 33 | 10 34 | 1 35 | 1 36 | 1 37 | 38 | 39 | 40 | select 41 | adminhtml/system_config_source_yesno 42 | 20 43 | 1 44 | 1 45 | 1 46 | 47 | 1 48 | 49 | 50 | 51 | 52 | text 53 | 30 54 | 1 55 | 1 56 | 1 57 | validate-not-negative-number 58 | 59 | 1 60 | 1 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/app/code/community/TS/ApiPlus/Trait/Data.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | trait TS_ApiPlus_Trait_Data 16 | { 17 | 18 | use TS_ApiPlus_Trait_Config; 19 | 20 | 21 | /** 22 | * @return TS_ApiPlus_Model_Server_Handler 23 | */ 24 | protected function getApiServerHandler() 25 | { 26 | return Mage::getSingleton('ts_apiplus/server_handler'); 27 | } 28 | 29 | 30 | /** 31 | * @return TS_ApiPlus_Model_Http_Request 32 | */ 33 | protected function getApiRequest() 34 | { 35 | return Mage::getSingleton('ts_apiplus/http_request'); 36 | } 37 | 38 | 39 | /** 40 | * @return TS_ApiPlus_Model_Http_Response 41 | */ 42 | protected function getApiResponse() 43 | { 44 | return Mage::getSingleton('ts_apiplus/http_response'); 45 | } 46 | 47 | 48 | /** 49 | * @return bool 50 | */ 51 | protected function canUserResultCache() 52 | { 53 | if (!$this->isResultCacheEnabled()) { 54 | return false; 55 | } 56 | 57 | if (!$this->getCoreCacheInstance()->canUse('api_plus_results')) { 58 | return false; 59 | } 60 | 61 | return true; 62 | } 63 | 64 | 65 | /** 66 | * @return Mage_Core_Model_Cache 67 | */ 68 | protected function getCoreCacheInstance() 69 | { 70 | return Mage::getSingleton('core/cache'); 71 | } 72 | 73 | 74 | /** 75 | * Get the common helper. 76 | * 77 | * @return TS_ApiPlus_Helper_Data 78 | */ 79 | protected function helper() 80 | { 81 | return Mage::helper('ts_apiplus'); 82 | } 83 | 84 | 85 | /** 86 | * Retrieve the class group of the self object. 87 | * 88 | * @return string 89 | */ 90 | protected function getClassGroupName() 91 | { 92 | $classGroup = strtolower($this->getClassGroup()); 93 | 94 | if ('mage' === substr($classGroup, 0, 4)) { 95 | $classGroup = substr($classGroup, 5, strlen($classGroup) - 5); 96 | } 97 | 98 | return $classGroup; 99 | } 100 | 101 | 102 | /** 103 | * Retrieve helper module name 104 | * 105 | * @return string 106 | */ 107 | protected function getClassGroup() 108 | { 109 | $className = get_class($this); 110 | $classGroup = substr($className, 0, strpos($className, '_', strpos($className, '_') + 1)); 111 | 112 | if (!class_exists($classGroup . '_Helper_Data')) { 113 | $classGroup = 'Mage_Core'; 114 | } 115 | 116 | return $classGroup; 117 | } 118 | 119 | 120 | /** 121 | * Translate 122 | * 123 | * @return string 124 | */ 125 | public function __() 126 | { 127 | $args = func_get_args(); 128 | $expr = new Mage_Core_Model_Translate_Expr(array_shift($args), $this->getClassGroup()); 129 | array_unshift($args, $expr); 130 | 131 | return Mage::app()->getTranslator()->translate($args); 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # API Plus for Magento 2 | 3 | [![Build Status](https://travis-ci.org/tiagosampaio/ApiPlus.svg?branch=master)](https://travis-ci.org/tiagosampaio/ApiPlus) 4 | [![GitHub license](https://img.shields.io/badge/version-0.1.0.1-blue.svg)](#) 5 | [![GitHub issues](https://img.shields.io/github/issues/tiagosampaio/ApiPlus.svg)](https://github.com/tiagosampaio/ApiPlus/issues) 6 | [![GitHub forks](https://img.shields.io/github/forks/tiagosampaio/ApiPlus.svg)](https://github.com/tiagosampaio/ApiPlus/network) 7 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/tiagosampaio/ApiPlus/master/LICENSE.txt) 8 | 9 | Rather than using (slow) SOAP to make calls to Magento's API why not to simply send a *POST* to an endpoint provinding your authentication data in request header and a JSON-encoded body with the resource name you want to consume and arguments needed for the call? 10 | 11 | API Plus for Magento is a module that improves how Magendo API can be consumed by third party applications. By using API Plus in your Magento store you make the API connection much eaiser but with the same abstraction layer and providing the same flexibility and security that Magento platform already provides for all its community. 12 | 13 | ## NOTE 14 | 15 | This documentation is not complete but I'm working on it to improve and make it better everytime I have a little free time. 16 | 17 | ## Features 18 | 19 | - **Much easier!**: API Plus is much, much, much easier to be used in your Magento platform. By using it you don't need to make SOAP calls anymore. 20 | - **Flexibility**: API Plus uses the Magento API's abstraction so, the same flexibility is provided by API Plus. 21 | - **Security**: API Plus is as secure as Magento's authentication system. 22 | - **Performance**: By using API Plus your API connections and results will be much faster then using the default SOAP calls. 23 | 24 | ## Authors, contributors and maintainers 25 | 26 | Author: 27 | - [Tiago Sampaio](http://tiagosampaio.com) 28 | 29 | ## Compability 30 | 31 | - PHP: 32 | - 5.4 33 | - 5.5 34 | - 5.6 35 | - Magento CE: 36 | - 1.6.x 37 | - 1.7.x 38 | - 1.8.x 39 | - 1.9.x 40 | - Magento EE: 41 | - 1.11.x 42 | - 1.12.x 43 | - 1.13.x 44 | - 1.14.x 45 | 46 | ## Documentation 47 | 48 | ### Basic 49 | 50 | It's quite simple! Rather then using SOAP process described in [Magento Webservice Documentation](http://devdocs.magento.com/guides/m1x/api/soap/introduction.html) all you need to do is to send a POST request to: 51 | 52 | [https://www.yourmagentohost.com/`api/json`](#) 53 | 54 | Providing the following authentication parameters in request header: 55 | 56 | Parameter Key | Parameter Value 57 | --- | --- 58 | `apiUsername` | [Your Magento's API Username] 59 | `apiKey` | [Your Magento's API Secret Key] 60 | 61 | And the body in JSON format: 62 | 63 | ```json 64 | { 65 | "resource": "catalog_product.list" 66 | } 67 | ``` 68 | 69 | If you need to send any arguments, like you do in SOAP request for some resources, you simply add a new node called `args` in the JSON request body: 70 | 71 | ```json 72 | { 73 | "resource": "catalog_product.list", 74 | "args": { 75 | "complex_filter": [ 76 | { 77 | "key": "type", 78 | "value": { 79 | "key": "in", 80 | "value": "configurable,grouped" 81 | } 82 | } 83 | ] 84 | } 85 | } 86 | ``` 87 | 88 | ## Links 89 | 90 | 🇧🇷 [Novo Módulo Magento para Integração com API: API Plus!](http://tiagosampaio.com/novo-modulo-magento-para-integracao-com-api-api-plus/) 91 | 92 | ## License 93 | 94 | (c) 2016 [MIT License](LICENSE.txt) - [Tiago Sampaio](http://tiagosampaio.com/) 95 | -------------------------------------------------------------------------------- /src/app/code/community/TS/ApiPlus/Model/Server/Handler.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class TS_ApiPlus_Model_Server_Handler extends Mage_Api_Model_Server_Handler 16 | { 17 | 18 | use TS_ApiPlus_Trait_Data; 19 | 20 | 21 | /** 22 | * Call resource functionality 23 | * 24 | * @param string $sessionId 25 | * @param string $apiPath 26 | * @param array $args 27 | * @return mixed 28 | */ 29 | public function callSimple($apiPath, $args = array()) 30 | { 31 | list($resourceName, $methodName) = explode('.', $apiPath); 32 | 33 | if (empty($resourceName) || empty($methodName)) { 34 | /** @todo Return 404 */ 35 | $this->_fault('resource_path_invalid'); 36 | return $this; 37 | } 38 | 39 | $resourcesAlias = $this->_getConfig()->getResourcesAlias(); 40 | $resources = $this->_getConfig()->getResources(); 41 | if (isset($resourcesAlias->$resourceName)) { 42 | $resourceName = (string) $resourcesAlias->$resourceName; 43 | } 44 | 45 | if (!isset($resources->$resourceName) || !isset($resources->$resourceName->methods->$methodName)) { 46 | /** @todo Return 404 */ 47 | $this->_fault('resource_path_invalid'); 48 | return $this; 49 | } 50 | 51 | if (!isset($resources->$resourceName->public) 52 | && isset($resources->$resourceName->acl) 53 | && !$this->_isAllowed((string)$resources->$resourceName->acl)) { 54 | /** @todo Return 503 */ 55 | $this->_fault('access_denied'); 56 | } 57 | 58 | 59 | if (!isset($resources->$resourceName->methods->$methodName->public) 60 | && isset($resources->$resourceName->methods->$methodName->acl) 61 | && !$this->_isAllowed((string)$resources->$resourceName->methods->$methodName->acl)) { 62 | /** @todo Return 503 */ 63 | $this->_fault('access_denied'); 64 | } 65 | 66 | $methodInfo = $resources->$resourceName->methods->$methodName; 67 | 68 | try { 69 | $method = (isset($methodInfo->method) ? (string) $methodInfo->method : $methodName); 70 | $modelName = $this->_prepareResourceModelName((string) $resources->$resourceName->model); 71 | 72 | try { 73 | $model = Mage::getModel($modelName); 74 | 75 | if ($model instanceof Mage_Api_Model_Resource_Abstract) { 76 | $model->setResourceConfig($resources->$resourceName); 77 | } 78 | } catch (Exception $e) { 79 | /** @todo Return 503 */ 80 | throw new Mage_Api_Exception('resource_path_not_callable'); 81 | } 82 | 83 | if (method_exists($model, $method)) { 84 | if (isset($methodInfo->arguments) && ((string)$methodInfo->arguments) == 'array') { 85 | return $model->$method((is_array($args) ? $args : array($args))); 86 | } elseif (!is_array($args)) { 87 | return $model->$method($args); 88 | } else { 89 | return call_user_func_array(array(&$model, $method), $args); 90 | } 91 | } else { 92 | /** @todo Return 503 */ 93 | throw new Mage_Api_Exception('resource_path_not_callable'); 94 | } 95 | } catch (Mage_Api_Exception $e) { 96 | $this->_fault($e->getMessage(), $resourceName, $e->getCustomMessage()); 97 | } catch (Exception $e) { 98 | Mage::logException($e); 99 | /** @todo Return 503 */ 100 | $this->_fault('internal', null, $e->getMessage()); 101 | } 102 | 103 | return $this; 104 | } 105 | 106 | 107 | /** 108 | * Dispatch webservice fault 109 | * 110 | * @param string $faultName 111 | * @param string $resourceName 112 | * @param string $customMessage 113 | */ 114 | protected function _fault($faultName, $resourceName = null, $customMessage = null) 115 | { 116 | $faults = $this->_getConfig()->getFaults($resourceName); 117 | 118 | if (isset($faults[$faultName])) { 119 | $fault = (array) $faults[$faultName]; 120 | } else { 121 | $fault = (array) $faults['unknown']; 122 | } 123 | 124 | $httpCode = isset($fault['http_code']) ? $fault['http_code'] : $this->_getDefaultHttpErrorCode(); 125 | $message = isset($fault['message']) ? $fault['message'] : $this->_getDefaultHttpErrorMessage(); 126 | 127 | if (!is_null($customMessage)) { 128 | $message = $customMessage; 129 | } 130 | 131 | throw Mage::exception('TS_ApiPlus', $message, $httpCode); 132 | } 133 | 134 | 135 | /** 136 | * @return int 137 | */ 138 | protected function _getDefaultHttpErrorCode() 139 | { 140 | return TS_ApiPlus_Model_Http_Response::HTTP_INTERNAL_SERVER_ERROR; 141 | } 142 | 143 | 144 | /** 145 | * @return string 146 | */ 147 | protected function _getDefaultHttpErrorMessage() 148 | { 149 | return $this->__('There was a problem in the request.'); 150 | } 151 | 152 | 153 | /** 154 | * Retrieve webservice configuration 155 | * 156 | * @return TS_ApiPlus_Model_Config 157 | */ 158 | protected function _getConfig() 159 | { 160 | return Mage::getSingleton('ts_apiplus/config'); 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /src/app/code/community/TS/ApiPlus/Model/Http/Request.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class TS_ApiPlus_Model_Http_Request 16 | { 17 | 18 | use TS_ApiPlus_Trait_Data, 19 | TS_ApiPlus_Trait_Http; 20 | 21 | 22 | /** @var Mage_Api_Model_User */ 23 | protected $_user = null; 24 | 25 | /** @var string */ 26 | protected $_data = null; 27 | 28 | /** @var stdClass */ 29 | protected $_decodedData = null; 30 | 31 | /** @var string */ 32 | protected $_resource = null; 33 | 34 | /** @var string */ 35 | protected $_args = null; 36 | 37 | 38 | public function __construct() 39 | { 40 | $this->initUser(); 41 | $this->initRequestData(); 42 | } 43 | 44 | 45 | /** 46 | * @return bool 47 | */ 48 | public function validate() 49 | { 50 | if ($this->getUser()->getId() && $this->getUser()->getIsActive() != '1') { 51 | 52 | $message = $this->__('Your account has been deactivated.'); 53 | $this->sendHttpErrorResponse(TS_ApiPlus_Model_Http_Response::HTTP_UNAUTHORIZED, $message); 54 | 55 | } elseif (!Mage::getModel('api/user')->hasAssigned2Role($this->getUser()->getId())) { 56 | 57 | $message = $this->__('Access denied.'); 58 | $this->sendHttpErrorResponse(TS_ApiPlus_Model_Http_Response::HTTP_UNAUTHORIZED, $message); 59 | 60 | } else { 61 | if (!$this->getUser()->getId()) { 62 | $message = $this->__('Unable to login.'); 63 | $this->sendHttpErrorResponse(TS_ApiPlus_Model_Http_Response::HTTP_UNAUTHORIZED, $message); 64 | } 65 | 66 | } 67 | 68 | return true; 69 | } 70 | 71 | 72 | /** 73 | * Dispatch the request. 74 | */ 75 | public function dispatch() 76 | { 77 | try { 78 | $this->validate(); 79 | 80 | $result = $this->getResultFromCache(); 81 | 82 | if ((false === $result) || empty($result)) { 83 | $result = $this->getApiServerHandler()->callSimple($this->getResource(), $this->getArgs()); 84 | $result = Zend_Json_Encoder::encode($result); 85 | 86 | $this->saveResultInCache($result); 87 | } 88 | 89 | $this->getHttpResponse()->setHeader('Content-type', 'application/json', true); 90 | $this->getHttpResponse()->setBody($result); 91 | } catch (Exception $e) { 92 | $this->sendHttpErrorResponse($e->getCode(), $e->getMessage()); 93 | } 94 | 95 | $this->getHttpResponse()->sendHeaders(); 96 | } 97 | 98 | 99 | /** 100 | * @return Mage_Api_Model_User 101 | */ 102 | public function getUser() 103 | { 104 | return $this->_user; 105 | } 106 | 107 | 108 | /** 109 | * @return string 110 | */ 111 | public function getData() 112 | { 113 | if (empty($this->_data)) { 114 | $this->_data = file_get_contents('php://input'); 115 | } 116 | 117 | return $this->_data; 118 | } 119 | 120 | 121 | /** 122 | * @return mixed|stdClass 123 | * 124 | * @throws Zend_Json_Exception 125 | */ 126 | public function getDecodedData() 127 | { 128 | if (empty($this->_decodedData)) { 129 | $this->_decodedData = Zend_Json_Decoder::decode($this->getData(), Zend_Json::TYPE_OBJECT); 130 | } 131 | 132 | return $this->_decodedData; 133 | } 134 | 135 | 136 | /** 137 | * @return string 138 | */ 139 | public function getResource() 140 | { 141 | if (empty($this->_resource)) { 142 | $reflection = new ReflectionObject($this->getDecodedData()); 143 | 144 | if (false === $reflection->hasProperty('resource')) { 145 | $this->sendHttpErrorResponse( 146 | TS_ApiPlus_Model_Http_Response::HTTP_METHOD_NOT_ALLOWED, 147 | $this->__('There is no resource method defined in your request. Please check it and try again.') 148 | ); 149 | } 150 | 151 | $this->_resource = $this->getDecodedData()->resource; 152 | } 153 | 154 | return $this->_resource; 155 | } 156 | 157 | 158 | /** 159 | * @return string 160 | */ 161 | public function getArgs() 162 | { 163 | if (empty($this->_args)) { 164 | $this->_args = $this->getDecodedData()->args; 165 | } 166 | 167 | return $this->_args; 168 | } 169 | 170 | 171 | /** 172 | * @return bool|string 173 | */ 174 | protected function getResultFromCache() 175 | { 176 | if (!$this->canUserResultCache()) { 177 | return false; 178 | } 179 | 180 | $result = $this->getCoreCacheInstance()->load($this->getCacheKey()); 181 | 182 | return $result; 183 | } 184 | 185 | 186 | /** 187 | * @param string $result 188 | * 189 | * @return $this 190 | */ 191 | protected function saveResultInCache($result) 192 | { 193 | if (!$this->canUserResultCache()) { 194 | return $this; 195 | } 196 | 197 | $tags = array(TS_ApiPlus_Model_Config::CACHE_TAG); 198 | $lifeTime = $this->getResultCacheLifetime(); 199 | 200 | $this->getCoreCacheInstance()->save($result, $this->getCacheKey(), $tags, $lifeTime); 201 | 202 | return $this; 203 | } 204 | 205 | 206 | /** 207 | * @return string 208 | */ 209 | protected function getCacheKey() 210 | { 211 | $prefix = 'ts_apiplus_'; 212 | $cacheKey = md5($this->getData()); 213 | return $prefix . $cacheKey; 214 | } 215 | 216 | 217 | /** 218 | * @return $this 219 | * 220 | * @throws Zend_Controller_Request_Exception 221 | */ 222 | protected function initUser() 223 | { 224 | $username = (string) $this->getHttpRequest()->getHeader('apiUsername'); 225 | $apiKey = (string) $this->getHttpRequest()->getHeader('apiKey'); 226 | 227 | $this->_user = Mage::getModel('api/user'); 228 | 229 | if (false == $this->getUser()->authenticate($username, $apiKey)) { 230 | $message = $this->__('Your credentials are incorrect. Please verify the username and password.'); 231 | $this->sendHttpErrorResponse(TS_ApiPlus_Model_Http_Response::HTTP_UNAUTHORIZED, $message); 232 | } 233 | 234 | /** @var Mage_Api_Model_Session $session */ 235 | $session = Mage::getSingleton('api/session'); 236 | $session->setData('user', $this->getUser()); 237 | $session->setData('acl', Mage::getResourceModel('api/acl')->loadAcl()); 238 | 239 | return $this; 240 | } 241 | 242 | 243 | /** 244 | * @return $this 245 | * 246 | * @throws Zend_Json_Exception 247 | */ 248 | protected function initRequestData() 249 | { 250 | if (!($this->getResource())) { 251 | $this->sendHttpErrorResponse(TS_ApiPlus_Model_Http_Response::HTTP_BAD_REQUEST); 252 | } 253 | 254 | return $this; 255 | } 256 | 257 | } 258 | --------------------------------------------------------------------------------