Zugriff auf die Magento-API vom nativen Client mit JavaScript

9

Ich muss über eine lokale JavaScript-basierte Anwendung (Titanium Desktop) auf die Magento-API zugreifen und mich fragen, wie dies am besten funktioniert.

Was ich bisher herausgefunden habe:

Fragen:

  • Ist es möglich, den Authentifizierungsmechanismus gegen eine HMAC-basierte Authentifizierung mit Anwendungsschlüssel und Geheimnis auszutauschen? Gibt es überhaupt bewährte Lösungen?
  • Wenn nicht, ist der OAuth User Agent Flow mit Magento möglich? Die Dokumentation erwähnt es nicht.
  • Ist es möglich, die Benutzeranmeldeinformationen mit AJAX zu übermitteln (Cross-Origin-Policy ist hier kein Problem), um den größten Teil des Autorisierungsprozesses vor dem Benutzer zu verbergen? Das Zugriffstoken könnte dann möglicherweise direkt aus der Antwort extrahiert werden.
Fabian Schmengler
quelle
OK, ich habe herausgefunden, dass ich mich zu sehr auf REST konzentriert habe. Die SOAP-API sollte mein Problem lösen, obwohl SOAP mit JavaScript etwas umständlich ist. Es gibt eine Bibliothek für Titan ( github.com/kwhinnery/Suds ), ich werde es ausprobieren und die Ergebnisse hier veröffentlichen.
Fabian Schmengler

Antworten:

8

Bearbeiten: Einen besseren Weg gefunden, siehe Lösung 2 unten

Wie im Kommentar erwähnt, ist die SOAP-API der richtige Weg.

Lösung 1:

Suds hat bei mir mit geringfügigen Änderungen (Verwendung von Titanium.Network.HTTPClientstatt XMLHttpRequest) funktioniert, aber es ist nicht viel mehr als das Erstellen eines SOAP-Umschlags für den Anruf und das Zurückgeben der gesamten XML-Antwort.

Proof-of-Concept-Implementierung mit jQuery Deferred für die Verkettung von Anforderungen:

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;
    };
};

Anwendungsbeispiel:

        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);
        });

Lösung 2:

Es stellte sich heraus, dass das Schreiben eines eigenen API-Adapters sehr einfach sein kann. Am Beispiel vondieser Core-Hack(toter Link) Ich konnte ein sauberes Modul für einen JSON-RPC-Adapter basierend auf schreiben Zend_Json_Server. Es verwendet dieselbe Authentifizierung und ACL wie die SOAP- und XML-RPC-APIs.

Um den Einstiegspunkt zu verwenden /api/jsonrpc, muss der apiRoute der neue Controller hinzugefügt werden :

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

Update 02/2015: Der obige Link ist jetzt nicht mehr verfügbar , daher habe ich meinen JSON-RPC-Adapter als vollständige Erweiterung als Open-Source-Adresse bereitgestellt: https://github.com/sgh-it/jsonrpc

Mein JS-Client sieht jetzt so aus (wieder mit JQuery.Deferred, aber keine zusätzlichen Bibliotheken von Drittanbietern für die 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;
    };
};

Beachten Sie, dass alle Methoden nach der Anmeldung weitergeleitet werden call. Der methodParameter ist so etwas wie sales_order.listder argsParameter ein Array oder Objekt mit den Methodenargumenten.

Anwendungsbeispiel:

        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
quelle
Wie konfiguriere ich den Endpunkt in Ihrem Skript?
Mohamed
Sie müssen die Frontend-Router-Definition in ändern config.xml(wenn Sie die apiRoute nicht verwenden möchten , können Sie stattdessen auch eine benutzerdefinierte Route verwenden und diese wie in jedem anderen Magento-Modul definieren
Fabian Schmengler
Wo kann ich diesen Code in magento
er.irfankhan11
Installationsanweisungen sind da: github.com/sgh-it/jsonrpc
Fabian Schmengler
Und der JavaScript-Code gehört offensichtlich nicht in Magento, sondern in den externen Client
Fabian Schmengler