Cómo acceder a la API de Magento desde un cliente nativo con JavaScript

9

Necesito acceder a la API de Magento desde una aplicación local basada en JavaScript (Titanium Desktop) y me pregunto cuál es la mejor manera de hacerlo.

Lo que descubrí hasta ahora:

Preguntas:

  • ¿Es factible intercambiar el mecanismo de autenticación por algo como la autenticación basada en HMAC con clave de aplicación y secreto? ¿Hay incluso soluciones probadas?
  • Si no, ¿es posible el flujo de agente de usuario de OAuth con Magento? La documentación no lo menciona.
  • ¿Es posible enviar las credenciales de usuario con AJAX (Cross-Origin-Policy no es un problema aquí) para ocultar la mayor parte del proceso de autorización del usuario? El token de acceso podría entonces extraerse directamente de la respuesta.
Fabian Schmengler
fuente
OK, descubrí que estaba demasiado enfocado en REST, la API SOAP debería resolver mi problema, aunque SOAP con JavaScript es un poco engorroso. Hay una biblioteca para Titanium ( github.com/kwhinnery/Suds ), lo probaré y publicaré los resultados aquí.
Fabian Schmengler

Respuestas:

8

Editar: Encontrado una mejor manera, vea la Solución 2 a continuación

Como se menciona en el comentario, la API SOAP es el camino a seguir.

Solución 1:

Suds funcionó para mí con una ligera modificación (Uso de en Titanium.Network.HTTPClientlugar de XMLHttpRequest), pero no hace mucho más que crear un sobre SOAP para la llamada y devolver toda la respuesta XML.

Implementación de prueba de concepto, utilizando jQuery diferido para el encadenamiento de solicitudes:

Service.MagentoClient = function()
{
    var self = this;
    var suds = new SudsClient({
        endpoint : "http://the-magento-host/api/v2_soap/",
        targetNamespace : "urn:Magento",
    });

    self.login = function() {
        var deferred = new $.Deferred();
        var args = {
            username : 'the-username',
            apiKey: 'the-api-key'
        };
        suds.invoke("login", args, function(xmlDoc) {
            self.sessionId = $(xmlDoc).find("loginReturn").text();
            deferred.resolve({});
            //TODO reject if no sessionid returned
        });
        return deferred;
    };

    self.setStatus = function(orderId, status, comment, notify) {
        var deferred = new $.Deferred();
        if (!self.sessionId) {
            deferred.reject({ error: 'Login not successful.' });
            return;
        }
        var args = {
            sessionId        : self.sessionId,
            orderIncrementId : orderId,
            status           : status,
            comment          : comment,
            notify           : notify
        }
        suds.invoke("salesOrderAddComment", args, function(xmlDoc) {
            var success = $(xmlDoc).find("salesOrderAddCommentResponse").text();
            if (success) {
                deferred.resolve({});
            } else {
                deferred.reject({ error: 'Update not successful.' });
            }

        });
        return deferred;
    };
};

Ejemplo de uso:

        var magento = new Service.MagentoClient();
        magento.login().then(function() {
            magento.setStatus('100000029', 'complete', 'soap test');
        }).then(function() {
            alert('Update successful');
        }, function(reject) {
            alert('Update failed: ' + reject.error);
        });

Solución 2:

Resultó que escribir un adaptador de API propio puede ser realmente fácil. Con el ejemplo deeste truco(enlace muerto) Pude escribir un módulo limpio para un adaptador JSON-RPC basado en Zend_Json_Server. Utiliza la misma autenticación y ACL que las API SOAP y XML-RPC.

Para usar el punto de entrada /api/jsonrpc, el nuevo controlador debe agregarse a la apiruta:

<config>
    <frontend>
        <routers>
            <api>
                <args>
                    <modules>
                        <my_jsonrpc before="Mage_Api">My_JsonRpc_Api</my_jsonrpc>
                    </modules>
                </args>
            </api>
        </routers>
    </frontend>
</config>

Actualización 02/2015: el enlace anterior está muerto ahora, así que abro el adaptador JSON-RPC como una extensión completa: https://github.com/sgh-it/jsonrpc

Mi cliente JS ahora se ve así (de nuevo con JQuery. Diferido, pero no hay bibliotecas de terceros adicionales para la API):

/**
 * Client for the Magento API
 */
Service.MagentoClient = function()
{
    var self = this;

    /**
     * @param string   method    the remote procedure to call
     * @param object   params    parameters for the RPC
     * @param callback onSuccess callback for successful request. Expects one parameter (decoded response object)
     * @param callback onError   callback for failed request. Expects one parameter (error message)
     * 
     * @return void
     */
    self.jsonRpc = function(method, params, onSuccess, onError) {
        var request = {
            method : method,
            params : params,
            jsonrpc : "2.0",
            id : 1
        };

        var options = {
            entryPoint : config.magentoClient.entryPoint,
            method: 'post',
            timeout: config.magentoClient.timeout
        };

        var httpClient = Titanium.Network.createHTTPClient();
        httpClient.onload = function(e) {
            try {
                var response = JSON.parse(this.responseText);
            } catch (jsonError) {
                return onError(jsonError);
            }
            if (response.error) {
                if (response.error.code == 5) { // session expired
                    self.sessionId = null;
                }
                return onError(response.error.message);
            }
            onSuccess(response);
        };
        httpClient.onerror = function(e) {
            onError(e.error + '; Response:' + this.responseText);
        };
        httpClient.setTimeout(options.timeout);

        if (httpClient.open(options.method, options.entryPoint)) {
            httpClient.setRequestHeader("Content-type", "application/json");
            httpClient.send(JSON.stringify(request));
        } else {
            onError('cannot open connection');
        }

    }
    /**
     * Retrieve session id for API
     * 
     * @return JQuery.Deferred deferred object for asynchronous chaining
     */
    self.login = function() {
        var deferred = new $.Deferred();
        if (self.sessionId) {
            deferred.resolve();
            return deferred;
        }
        var loginParams = config.magentoClient.login;
        try {
            self.jsonRpc('login', loginParams, function(response) {
                if (response && response.result) {
                    self.sessionId = response.result;
                    deferred.resolve();
                } else {
                    deferred.reject('Login failed.');
                }
            }, function(error) {
                deferred.reject(error);
            });
        } catch (rpcError) {
            deferred.reject(rpcError);
        }
        return deferred;
    };
    /**
     * Updates order states in Magento
     *
     * @param string method   name of the remote method
     * @param object args     arguments for the remote method
     * 
     * @return JQuery.Deferred deferred object for asynchronous chaining
     */
    self.call = function(method, args) {
        var deferred = new $.Deferred();
        if (!self.sessionId) {
            deferred.reject('No session.');
            return;
        }
        var callParams = {
            sessionId : self.sessionId,
            apiPath   : method,
            args      : args
        };
        try {
            self.jsonRpc('call', callParams, function(response) {
                deferred.resolve(response.result);
            }, function(error) {
                deferred.reject(error);
            });
        } catch (rpcError) {
            deferred.reject(rpcError);
        }

        return deferred;
    };
};

Tenga en cuenta que todos los métodos después del inicio de sesión se enrutan call. El methodparámetro es algo así como sales_order.list, el argsparámetro una matriz u objeto con los argumentos del método.

Ejemplo de uso:

        var filters = [];
        var magento = new Service.MagentoClient();
        magento.login().then(function() {
            magento.call('sales_order.list', [filters]).then(
                function(orders) {
                    // do something with the response
                }, function(error) {
                    alert('Magento API error: ' + error);
                }
            );
        });
Fabian Schmengler
fuente
¿Cómo configurar el punto final en su script?
Mohamed
Debe cambiar la definición del enrutador frontend en config.xml(si no desea usar la apiruta, también puede usar una ruta personalizada, definirla como lo haría en cualquier otro módulo de Magento
Fabian Schmengler
Dónde puedo poner este código en magento
er.irfankhan11
Las instrucciones de instalación están ahí: github.com/sgh-it/jsonrpc
Fabian Schmengler
Y el código JavaScript obviamente no pertenece a Magento sino al cliente externo
Fabian Schmengler