eUczelnia API


Mailer eUczelni[edit]

W ramach projektu eUczelnia powstał system umożliwiający wysyłanie wiadomości email.

JSON-RPC vs XML-RPC[edit]

System wysyłania poczty oraz inne API systemu eUczelnia oparte są o protokół typu RPC (Remote Procedure Call), a formatem wymiany danych są JSON (JSON-RPC) lub XML (XML-RPC).

Pierwotnie system używał protokołu XML-RPC, jednak ze względu na bardzo duży stosunek ilości danych przesyłanych do ilości informacji w nich zawartych preferowany będzie protokół JSON-RPC.

Z przeprowadzonych doświadczeń wynika, że struktura danych informacji o pracowników przesyłana w formacie JSON zajmuje około 2500 bajtów, podczas gdy te same informacje w postaci XML zajmują około 12000 bajtów. W innych przypadkach różnica wielkości pakietu danych jest też wielokrotna i wynosi 400-600% więcej w przypadku XML, z korzyścią dla formatu JSON.

metoda send_message[edit]

W celu wysłania wiadomości e-mail należy wywołać metodę send_message, której sygnatura ma postać:

/**
 * Funkcja umożliwia wysłanie dowolnej liczby wiadomości email do różnych adresatów
 * @param (string) api_key                  // klucz aplikacji, na podstawie którego jest uwierzytelniona
 * @param (array (message)) messages        // tablica wiadomości przedstawionych jako obiekty
 *                                          // typu message do wysłania, każda wiadomość
 *                                          // zawiera pola do, od, tytuł i treść, patrz poniżej
 * @return (response) response              // obiekt, który jest zwracany po wykonaniu funkcji
 */
(response) send_message( (string) api_key, (array) messages );

tablica messages[edit]

Wiadomości przekazywane są w postaci tablicy tablic typu message.

Dla każdej wiadomości można zdefiniować dodatkowe nagłówki w postaci pojedynczego stringa. Nagłówki oddzielone są od siebie znakami CRLF (\r\n) tytuł nagłówka od jego zawartości oddzielony jest znakiem ":".

Struktura tablicy message (array of arrays) jako obiektu typu JSON wygląda następująco:

[
    [
        to,                          // string           pole do
        from,                        // string           pole od
        subject,                     // string           pole temat
        text,                        // string           pole treść
        headers                      // string           dodatkowe nagłówki, które będą dołączone do wiadomości (opcjonalnie)
    ],
    (...)                            // kolejne wiadomości (opcjonalnie)
]

Wysyłanie żądania[edit]

JSON-RPC[edit]

W ramach testowania możliwości wykorzystania protokołu JSON-RPC stworzony został prosty klient JSONa wykorzystujący certyfikaty do utworzenia połączenia poprzez bibliotekę cURL do serwera. Kod biblioteki ma następującą postać:

<?php
 
class JsonRpcFault extends Exception {}
 
class JsonRPCClient {
    private $uri;
    private $ca_cert;
    private $usr_cert;
 
    /**
     * Zwraca losowy id zapytania.
     */
    private function generateId() {
        $chars = array_merge(range('a', 'z'), range(0, 9));
        $id = '';
        for($c = 0; $c < 5; ++$c)
            $id .= $chars[mt_rand(0, count($chars) - 1)];
        return $id;
    }
 
    /**
     * Ustawienie uri do połączenia
     * @param string $uri
     */
    public function set_uri($uri)
    {
        $this->uri = $uri;
    }
 
    /**
     * Ustawia ścieżkę do certyfikatu CA
     * @param string $path
     */
        public function set_ca_cert($path)
    {
        $this->ca_cert = $path;
    }
 
    /**
     * Ustawia ścieżkę do certyfikatu hosta
     * @param string $path
     */
        public function set_usr_cert($path)
    {
        $this->usr_cert = $path;
    }
 
    /**
     * otwiera połączenie, wysyła zapytanie i zwraca odpowiedź
     * @param string $name
     * @param array $arguments
     * @throws JsonRpcFault
     */
    public function use_method($name, $arguments) {
        $id = $this->generateId();
 
        $request = array(
            'jsonrpc' => '2.0',
            'method'  => $name,
            'params'  => $arguments,
            'id'      => $id
        );
 
        $jsonRequest = json_encode($request);
 
 
        $curl = curl_init($this->uri);
                curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
                curl_setopt($curl, CURLOPT_POST, 1);
                curl_setopt($curl, CURLOPT_HTTPHEADER, array(
                        'Content-type' => 'application/json; charset=UTF-8'));
                curl_setopt($curl, CURLOPT_POSTFIELDS, $jsonRequest);
                curl_setopt($curl, CURLOPT_HEADER, 0);
                curl_setopt($curl, CURLOPT_SSLCERT, $this->usr_cert);
                curl_setopt($curl, CURLOPT_CAINFO,  $this->ca_cert);
                curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, TRUE);
                curl_setopt($curl, CURLOPT_VERBOSE, FALSE);
 
 
        $jsonResponse = curl_exec($curl);
 
        if ($jsonResponse === false)
            throw new JsonRpcFault('curl_exec failed', -32603);
 
        $response = json_decode($jsonResponse);
 
        if ($response === null)
            throw new JsonRpcFault('JSON cannot be decoded', -32603);
 
        if ($response->id != $id)
            throw new JsonRpcFault('Mismatched JSON-RPC IDs', -32603);
 
        if (property_exists($response, 'error'))
            throw new JsonRpcFault($response->error->message, $response->error->code);
        else if (property_exists($response, 'result'))
            return $response->result;
        else
            throw new JsonRpcFault('Invalid JSON-RPC response', -32603);
    }
}
 
?>

Tak napisaną bibliotekę należy wykorzystywać zgodnie ze schematem:

<?php
  include('lib/JsonRPCClient.php');
  $con = new JsonRPCClient();
  $con->set_usr_cert('<path>');
  $con->set_ca_cert('<path>');
  print_r($con->use_method('TestAPI.echo', array('Hello world') ));
?>

Metoda TestAPI.echo zwraca cały komunikat jej przesłany i może być wykorzystywana do testowania poprawności połączenia. Serwer kraken jest serwerem testowym Politechniki Gdańskiej. Zmienna text przetrzymuje parametry przekazywane do funkcji (w tym wypadku pojedyńczą zmienną).

Wewnętrzna struktura komunikatu używana przez bibliotekę znajduje się w lini 57 klienta i wygląda następująco:

$request = array(
    'jsonrpc' => '2.0',
    'method'  => $name,
    'params'  => $arguments,
    'id'      => $id
);

Gdzie name to nazwa metody, arguments to tablica argumentów przekazana do metody.

Uzyskana w ten sposób odpwiedź od serwera będzie miała postać typu:

{
    "jsonrpc"       : "2.0",
    "id"            : 1,
    "result"        : { obiekt odpowiedzi}
}

Przy czym funkcja echo nie zwraca obiektu w takiej postaci, a jedynie zwraca obiekt do niej przesłany.

Wysłanie żądania wysyłki maili na konkretny adres będzie miało taką postać

<?php
    include('lib/JsonRPCClient.php');
    $con = new JsonRPCClient();
    $con->set_usr_cert('<path>');
    $con->set_ca_cert('<path>');
    print_r($con->use_method('TestAPI.echo',
        array(
            'API-Secret-key',
            array(
                array('receiver@example.org', 'sender@example.org', 'Subject', 'Hello world!', 'AdditionalHeader: XML-RPC mail sender')
            )
        )
    ));
?>

XML-RPC[edit]

W wykorzystaniu protokołu XML-RPC wykorzystaliśmy istniejące funkcje, które ułatwiają jego obsluge z poziomu PHP. Przykładowe żądanie ma następującą strukturę (opis w kodzie PHP, obiekt messages również w notacji PHP):

$send_message_request = array('API-Secret-key', array(
    array('receiver@example.org', 'sender@example.org', 'Subject', 'Hello world!', 'AdditionalHeader: XML-RPC mail sender'),
);

Przy użyciu języka PHP żądanie można zakodować do struktury XML używając następującej instrukcji:

$request = xmlrpc_encode_request("send_message", $send_message_request));

Która po zwraca następujący kod XML:

Nastęnie należy tak stworzone zapytanie opakować w nagłówki oraz przesłać do serwera docelowego:

$context = stream_context_create(array('http' => array(
    'method' => "POST",
    'header' => "Content-Type: text/xml\r\nUser-Agent: PHPRPC/1.0\r\n",
    'content' => $request
)));
 
$response_xml = file_get_contents("http://example.org/mailer.php", false, $context);

Odbieranie odpowiedzi[edit]

Otrzymana od serwera odpowiedź informująca o powodzeniu zakolejkowania wiadomośći ma następującą postać: Uwaga: sukces oznacza tylko i wyłącznie zakolejkowanie wiadomości do wysłania. Nie wiemy (i nie będzie możliwe otrzymanie takiej informacji) czy adresat docelowy istnieje oraz czy i kiedy otrzymał wiadomość.

W przypadku niepowodzenia autoryzacji (błędny klucz aplikacji) wiadomość ma następującą postać:

W przypadku błędów w wiadomościach (błędny adres odbiorcy) odpowiedź wygląda następująco: Uwaga: w przypadku błędnej chociaż jednej wiadomości nie zostanie wysłana żadna wiadomość. Wszystkie muszą być poprawne, aby paczka wiadomości (wszystkie przesłane przez jedno wywołanie funkcji) zostały zakolejkowane.

Ostatnią czynnością jest rozkodowania odpowiedzi otrzymanej z serwera i przetworzenie jej:

$response = xmlrpc_decode($response_xml);

Odpowiedź w postaci JSON dla wiadomości wysłanych prawidłowo ma następującą postać:

{
    "status":           "ok",
    "errors":           [],
    "user_message":     "1 message(s) successfully queued for sending."
}

Analogicznie, dla niepoprawnego klucza aplikacji, generowana jest taka odpowiedź:

{
    "status":           "error",
    "error_code":       100,
    "user_message":     "Error: Authorization failed."
}

Wynikowa postać JSON w przypadku błędnych wiadomości ma następującą strukturę:

{
    "status":                       "error",
    "errors":
        [
            {
                "to":               "wrong@mail@example.org",
                "from":             "sender@example.org",
                "subject":          "Subject",
                "message":          "Hello world!",
                "extra_headers":    "AdditionalHeader: XML-RPC mail sender",
                "error_code":       50,
                "user_message":     "Error: Recepient address (wrong@mail@example.org) incorrect"
            }
        ],
    "user_message":                 "0 were correct"
}

Metody[edit]

send_message[edit]

send_message( (String) api_key, (array) messages );
/**
 * Funkcja umożliwia wysłanie dowolnej liczby wiadomości email do różnych adresatów
 * @param api_key                   // klucz aplikacji, na podstawie którego jest uwierzytelniona
 * @param messages                  // tablica wiadomości do wysłania, każda wiadomość zawiera pola do, od, tytuł i treść
 * @return response                 // obiekt, który jest zwracany po wykonaniu funkcji
 */

Obiekty pomocnicze[edit]

messages[edit]

[
    [
        to                          // string           pole do
        from                        // string           pole od
        subject                     // string           pole temat
        text                        // string           pole treść
        headers                     // string           dodatkowe nagłówki, które będą dołączone do wiadomości
    ]  
]  

response[edit]

{  
    status: "error",                                                                            // string           error / ok
    errors:                                                                                     // array            podaje status oraz zwraca błędne wiadomości
        {                                                                                      
            to              : "bob@example.org",                                                // string
            from            : "alice@example.org",                                              // string
            subject         : "test",                                                           // string
            text            : "message body",                                                   // string
            headers         : "",                                                               // string (opcjonalnie)
            error_code      : 100,                                                              // int              kod błędu
            user_message    : "Error: Recepient address (wrong@email@example.com) incorrect"    // string
        },
    user_message: "2 message(s) were submitted successfully"                                    // string           podaje status poprawnych wiadomości (wysłane/ poprawne)
}