1
0
mirror of synced 2024-11-22 05:16:07 +03:00

Merge pull request #11 from gwinn/master

Packs, telephony, test & docs
This commit is contained in:
Alex Lushpai 2016-03-14 20:34:13 +03:00
commit 1c695eeda7
17 changed files with 1613 additions and 467 deletions

6
.gitignore vendored
View File

@ -1,6 +1,7 @@
vendor/ /vendor
bin/ /bin
composer.lock composer.lock
composer.phar
phpunit.xml phpunit.xml
.idea .idea
.DS_Store .DS_Store
@ -8,3 +9,4 @@ phpunit.xml
.buildpath .buildpath
.project .project
.swp .swp
/nbproject

View File

@ -1,33 +1,31 @@
# PHP-клиент для retailCRM API # retailCRM API PHP client
PHP-клиент для работы с [retailCRM API](http://www.retailcrm.ru/docs/Разработчики/Разработчики#api). PHP-client for [retailCRM API](http://www.retailcrm.ru/docs/Developers/ApiVersion3).
Рекомендуем обращаться к [документации](http://retailcrm.github.io/api-client-php) по библиотеке, в частности по классу [RetailCrm\ApiClient](http://retailcrm.github.io/api-client-php/class-RetailCrm.ApiClient.html). Use [API documentation](http://retailcrm.github.io/api-client-php)
## Обязательные требования ## Requirements
* PHP версии 5.3 и выше * PHP 5.3 and above
* PHP-расширение cURL * PHP's cURL support
## Установка ## Install
1) Установите [composer](https://getcomposer.org/download/) 1) Get [composer](https://getcomposer.org/download/)
2) Выполните в папке проекта: 2) Run into your project directory:
```bash ```bash
composer require retailcrm/api-client-php ~3.0.0 composer require retailcrm/api-client-php ~3.0.0 --no-dev
``` ```
В конфиг `composer.json` вашего проекта будет добавлена библиотека `retailcrm/api-client-php`, которая установится в папку `vendor/`. При отсутствии файла конфига или папки с вендорами они будут созданы. If you have not used `composer` before, include autoloader into your project.
В случае, если до этого в вашем проекте не использовался `composer`, подключите файл автозагрузки вендоров. Для этого укажите в коде проекта:
```php ```php
require 'path/to/vendor/autoload.php'; require 'path/to/vendor/autoload.php';
``` ```
## Примеры использования ## Usage
### Получение информации о заказе ### Get order
```php ```php
$client = new \RetailCrm\ApiClient( $client = new \RetailCrm\ApiClient(
'https://demo.retailcrm.ru', 'https://demo.retailcrm.ru',
@ -38,30 +36,30 @@ $client = new \RetailCrm\ApiClient(
try { try {
$response = $client->ordersGet('M-2342'); $response = $client->ordersGet('M-2342');
} catch (\RetailCrm\Exception\CurlException $e) { } catch (\RetailCrm\Exception\CurlException $e) {
echo "Сетевые проблемы. Ошибка подключения к retailCRM: " . $e->getMessage(); echo "Connection error: " . $e->getMessage();
} }
if ($response->isSuccessful()) { if ($response->isSuccessful()) {
echo $response->order['totalSumm']; echo $response->order['totalSumm'];
// или $response['order']['totalSumm']; // or $response['order']['totalSumm'];
// или // or
// $order = $response->getOrder(); // $order = $response->getOrder();
// $order['totalSumm']; // $order['totalSumm'];
} else { } else {
echo sprintf( echo sprintf(
"Ошибка получения информации о заказа: [Статус HTTP-ответа %s] %s", "Error: [HTTP-code %s] %s",
$response->getStatusCode(), $response->getStatusCode(),
$response->getErrorMsg() $response->getErrorMsg()
); );
// получить детализацию ошибок // error details
//if (isset($response['errors'])) { //if (isset($response['errors'])) {
// print_r($response['errors']); // print_r($response['errors']);
//} //}
} }
``` ```
### Создание заказа ### Create order
```php ```php
$client = new \RetailCrm\ApiClient( $client = new \RetailCrm\ApiClient(
@ -82,21 +80,21 @@ try {
) )
)); ));
} catch (\RetailCrm\Exception\CurlException $e) { } catch (\RetailCrm\Exception\CurlException $e) {
echo "Сетевые проблемы. Ошибка подключения к retailCRM: " . $e->getMessage(); echo "Connection error: " . $e->getMessage();
} }
if ($response->isSuccessful() && 201 === $response->getStatusCode()) { if ($response->isSuccessful() && 201 === $response->getStatusCode()) {
echo 'Заказ успешно создан. ID заказа в retailCRM = ' . $response->id; echo 'Order successfully created. Order ID into retailCRM = ' . $response->id;
// или $response['id']; // or $response['id'];
// или $response->getId(); // or $response->getId();
} else { } else {
echo sprintf( echo sprintf(
"Ошибка создания заказа: [Статус HTTP-ответа %s] %s", "Error: [HTTP-code %s] %s",
$response->getStatusCode(), $response->getStatusCode(),
$response->getErrorMsg() $response->getErrorMsg()
); );
// получить детализацию ошибок // error details
//if (isset($response['errors'])) { //if (isset($response['errors'])) {
// print_r($response['errors']); // print_r($response['errors']);
//} //}

104
README.ru.md Normal file
View File

@ -0,0 +1,104 @@
# PHP-клиент для retailCRM API
PHP-клиент для работы с [retailCRM API](http://www.retailcrm.ru/docs/Developers/ApiVersion3).
Рекомендуем обращаться к [документации](http://retailcrm.github.io/api-client-php) по библиотеке, в частности по классу [RetailCrm\ApiClient](http://retailcrm.github.io/api-client-php/class-RetailCrm.ApiClient.html).
## Обязательные требования
* PHP версии 5.3 и выше
* PHP-расширение cURL
## Установка
1) Установите [composer](https://getcomposer.org/download/)
2) Выполните в папке проекта:
```bash
composer require retailcrm/api-client-php ~3.0.0 --no-dev
```
В конфиг `composer.json` вашего проекта будет добавлена библиотека `retailcrm/api-client-php`, которая установится в папку `vendor/`. При отсутствии файла конфига или папки с вендорами они будут созданы.
В случае, если до этого в вашем проекте не использовался `composer`, подключите файл автозагрузки вендоров. Для этого укажите в коде проекта:
```php
require 'path/to/vendor/autoload.php';
```
## Примеры использования
### Получение информации о заказе
```php
$client = new \RetailCrm\ApiClient(
'https://demo.retailcrm.ru',
'T9DMPvuNt7FQJMszHUdG8Fkt6xHsqngH'
);
try {
$response = $client->ordersGet('M-2342');
} catch (\RetailCrm\Exception\CurlException $e) {
echo "Сетевые проблемы. Ошибка подключения к retailCRM: " . $e->getMessage();
}
if ($response->isSuccessful()) {
echo $response->order['totalSumm'];
// или $response['order']['totalSumm'];
// или
// $order = $response->getOrder();
// $order['totalSumm'];
} else {
echo sprintf(
"Ошибка получения информации о заказа: [Статус HTTP-ответа %s] %s",
$response->getStatusCode(),
$response->getErrorMsg()
);
// получить детализацию ошибок
//if (isset($response['errors'])) {
// print_r($response['errors']);
//}
}
```
### Создание заказа
```php
$client = new \RetailCrm\ApiClient(
'https://demo.retailcrm.ru',
'T9DMPvuNt7FQJMszHUdG8Fkt6xHsqngH'
);
try {
$response = $client->ordersCreate(array(
'externalId' => 'some-shop-order-id',
'firstName' => 'Vasily',
'lastName' => 'Pupkin',
'items' => array(
//...
),
'delivery' => array(
'code' => 'russian-post',
)
));
} catch (\RetailCrm\Exception\CurlException $e) {
echo "Сетевые проблемы. Ошибка подключения к retailCRM: " . $e->getMessage();
}
if ($response->isSuccessful() && 201 === $response->getStatusCode()) {
echo 'Заказ успешно создан. ID заказа в retailCRM = ' . $response->id;
// или $response['id'];
// или $response->getId();
} else {
echo sprintf(
"Ошибка создания заказа: [Статус HTTP-ответа %s] %s",
$response->getStatusCode(),
$response->getErrorMsg()
);
// получить детализацию ошибок
//if (isset($response['errors'])) {
// print_r($response['errors']);
//}
}
```

View File

@ -4,6 +4,7 @@
"type": "library", "type": "library",
"keywords": ["API", "retailCRM", "REST"], "keywords": ["API", "retailCRM", "REST"],
"homepage": "http://www.retailcrm.ru/", "homepage": "http://www.retailcrm.ru/",
"license": "MIT",
"authors": [ "authors": [
{ {
"name": "retailCRM", "name": "retailCRM",
@ -15,12 +16,14 @@
"ext-curl": "*" "ext-curl": "*"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "3.7.*", "phpunit/phpunit": "5.2.*",,
"phpmd/phpmd": "2.1.*", "phpunit/php-code-coverage": "3.3.0",
"phpunit/php-invoker": "1.1.4"
"phpmd/phpmd": "2.4.*",
"sebastian/phpcpd": "2.0.*", "sebastian/phpcpd": "2.0.*",
"sebastian/phpdcd": "1.0.*", "sebastian/phpdcd": "1.0.*",
"squizlabs/php_codesniffer": "dev-master", "squizlabs/php_codesniffer": "2.5.*",
"apigen/apigen": "~4.0@dev" "apigen/apigen": "4.1.*"
}, },
"support": { "support": {
"email": "support@intarocrm.ru" "email": "support@intarocrm.ru"

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,17 @@
namespace RetailCrm\Exception; namespace RetailCrm\Exception;
/**
* PHP version 5.3
*
* Class CurlException
*
* @category RetailCrm
* @package RetailCrm
* @author RetailCrm <integration@retailcrm.ru>
* @license https://opensource.org/licenses/MIT MIT License
* @link http://www.retailcrm.ru/docs/Developers/ApiVersion3
*/
class CurlException extends \RuntimeException class CurlException extends \RuntimeException
{ {
} }

View File

@ -2,6 +2,17 @@
namespace RetailCrm\Exception; namespace RetailCrm\Exception;
/**
* PHP version 5.3
*
* Class InvalidJsonException
*
* @category RetailCrm
* @package RetailCrm
* @author RetailCrm <integration@retailcrm.ru>
* @license https://opensource.org/licenses/MIT MIT License
* @link http://www.retailcrm.ru/docs/Developers/ApiVersion3
*/
class InvalidJsonException extends \DomainException class InvalidJsonException extends \DomainException
{ {
} }

View File

@ -3,10 +3,19 @@
namespace RetailCrm\Http; namespace RetailCrm\Http;
use RetailCrm\Exception\CurlException; use RetailCrm\Exception\CurlException;
use RetailCrm\Exception\InvalidJsonException;
use RetailCrm\Response\ApiResponse; use RetailCrm\Response\ApiResponse;
/** /**
* PHP version 5.3
*
* HTTP client * HTTP client
*
* @category RetailCrm
* @package RetailCrm
* @author RetailCrm <integration@retailcrm.ru>
* @license https://opensource.org/licenses/MIT MIT License
* @link http://www.retailcrm.ru/docs/Developers/ApiVersion3
*/ */
class Client class Client
{ {
@ -17,74 +26,105 @@ class Client
protected $defaultParameters; protected $defaultParameters;
protected $retry; protected $retry;
/**
* Client constructor.
*
* @param string $url api url
* @param array $defaultParameters array of parameters
*
* @throws \InvalidArgumentException
*/
public function __construct($url, array $defaultParameters = array()) public function __construct($url, array $defaultParameters = array())
{ {
if (false === stripos($url, 'https://')) { if (false === stripos($url, 'https://')) {
throw new \InvalidArgumentException('API schema requires HTTPS protocol'); throw new \InvalidArgumentException(
'API schema requires HTTPS protocol'
);
} }
$this->url = $url; $this->url = $url;
$this->defaultParameters = $defaultParameters; $this->defaultParameters = $defaultParameters;
$this->retry = 0; $this->retry = 0;
$this->curlErrors = array(
CURLE_COULDNT_RESOLVE_PROXY,
CURLE_COULDNT_RESOLVE_HOST,
CURLE_COULDNT_CONNECT,
CURLE_OPERATION_TIMEOUTED,
CURLE_HTTP_POST_ERROR,
CURLE_SSL_CONNECT_ERROR,
CURLE_SEND_ERROR,
CURLE_RECV_ERROR
);
} }
/** /**
* Make HTTP request * Make HTTP request
* *
* @param string $path * @param string $path request url
* @param string $method (default: 'GET') * @param string $method (default: 'GET')
* @param array $parameters (default: array()) * @param array $parameters (default: array())
* @param int $timeout *
* @param bool $verify * @SuppressWarnings(PHPMD.ExcessiveParameterList)
* @param bool $debug *
* @throws \InvalidArgumentException
* @throws CurlException
* @throws InvalidJsonException
*
* @return ApiResponse * @return ApiResponse
*/ */
public function makeRequest( public function makeRequest(
$path, $path,
$method, $method,
array $parameters = array(), array $parameters = array()
$timeout = 30,
$verify = false,
$debug = false
) { ) {
$allowedMethods = array(self::METHOD_GET, self::METHOD_POST); $allowedMethods = array(self::METHOD_GET, self::METHOD_POST);
if (!in_array($method, $allowedMethods)) {
throw new \InvalidArgumentException(sprintf( if (!in_array($method, $allowedMethods, false)) {
'Method "%s" is not valid. Allowed methods are %s', throw new \InvalidArgumentException(
$method, sprintf(
implode(', ', $allowedMethods) 'Method "%s" is not valid. Allowed methods are %s',
)); $method,
implode(', ', $allowedMethods)
)
);
} }
$parameters = array_merge($this->defaultParameters, $parameters); $parameters = array_merge($this->defaultParameters, $parameters);
$path = $this->url . $path; $url = $this->url . $path;
if (self::METHOD_GET === $method && sizeof($parameters)) { if (self::METHOD_GET === $method && count($parameters)) {
$path .= '?' . http_build_query($parameters, '', '&'); $url .= '?' . http_build_query($parameters, '', '&');
} }
$ch = curl_init(); $curlHandler = curl_init();
curl_setopt($ch, CURLOPT_URL, $path); curl_setopt($curlHandler, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, (int) $timeout); curl_setopt($curlHandler, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, (int) $timeout); curl_setopt($curlHandler, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curlHandler, CURLOPT_FAILONERROR, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($curlHandler, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FAILONERROR, false); curl_setopt($curlHandler, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verify); curl_setopt($curlHandler, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verify); curl_setopt($curlHandler, CURLOPT_CONNECTTIMEOUT, 30);
if (self::METHOD_POST === $method) { if (self::METHOD_POST === $method) {
curl_setopt($ch, CURLOPT_POST, true); curl_setopt($curlHandler, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $parameters); curl_setopt($curlHandler, CURLOPT_POSTFIELDS, $parameters);
} }
$responseBody = curl_exec($ch); $responseBody = curl_exec($curlHandler);
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $statusCode = curl_getinfo($curlHandler, CURLINFO_HTTP_CODE);
$errno = curl_errno($ch); $errno = curl_errno($curlHandler);
$error = curl_error($ch); $error = curl_error($curlHandler);
curl_close($ch); curl_close($curlHandler);
if ($errno && in_array($errno, $this->curlErrors, false) && $this->retry < 3) {
$errno = null;
$error = null;
++$this->retry;
$this->makeRequest($path, $method, $parameters);
}
if ($errno) { if ($errno) {
throw new CurlException($error, $errno); throw new CurlException($error, $errno);
@ -92,4 +132,14 @@ class Client
return new ApiResponse($statusCode, $responseBody); return new ApiResponse($statusCode, $responseBody);
} }
/**
* Retry connect
*
* @return int
*/
public function getRetry()
{
return $this->retry;
}
} }

View File

@ -5,7 +5,15 @@ namespace RetailCrm\Response;
use RetailCrm\Exception\InvalidJsonException; use RetailCrm\Exception\InvalidJsonException;
/** /**
* PHP version 5.3
*
* Response from retailCRM API * Response from retailCRM API
*
* @category RetailCrm
* @package RetailCrm
* @author RetailCrm <integration@retailcrm.ru>
* @license https://opensource.org/licenses/MIT MIT License
* @link http://www.retailcrm.ru/docs/Developers/ApiVersion3
*/ */
class ApiResponse implements \ArrayAccess class ApiResponse implements \ArrayAccess
{ {
@ -15,6 +23,14 @@ class ApiResponse implements \ArrayAccess
// response assoc array // response assoc array
protected $response; protected $response;
/**
* ApiResponse constructor.
*
* @param int $statusCode HTTP status code
* @param mixed $responseBody HTTP body
*
* @throws InvalidJsonException
*/
public function __construct($statusCode, $responseBody = null) public function __construct($statusCode, $responseBody = null)
{ {
$this->statusCode = (int) $statusCode; $this->statusCode = (int) $statusCode;
@ -57,6 +73,10 @@ class ApiResponse implements \ArrayAccess
* Allow to access for the property throw class method * Allow to access for the property throw class method
* *
* @param string $name * @param string $name
* @param $arguments
*
* @throws \InvalidArgumentException
*
* @return mixed * @return mixed
*/ */
public function __call($name, $arguments) public function __call($name, $arguments)
@ -75,6 +95,9 @@ class ApiResponse implements \ArrayAccess
* Allow to access for the property throw object property * Allow to access for the property throw object property
* *
* @param string $name * @param string $name
*
* @throws \InvalidArgumentException
*
* @return mixed * @return mixed
*/ */
public function __get($name) public function __get($name)
@ -89,6 +112,8 @@ class ApiResponse implements \ArrayAccess
/** /**
* @param mixed $offset * @param mixed $offset
* @param mixed $value * @param mixed $value
*
* @throws \BadMethodCallException
*/ */
public function offsetSet($offset, $value) public function offsetSet($offset, $value)
{ {
@ -97,6 +122,8 @@ class ApiResponse implements \ArrayAccess
/** /**
* @param mixed $offset * @param mixed $offset
*
* @throws \BadMethodCallException
*/ */
public function offsetUnset($offset) public function offsetUnset($offset)
{ {
@ -105,6 +132,7 @@ class ApiResponse implements \ArrayAccess
/** /**
* @param mixed $offset * @param mixed $offset
*
* @return bool * @return bool
*/ */
public function offsetExists($offset) public function offsetExists($offset)
@ -114,6 +142,9 @@ class ApiResponse implements \ArrayAccess
/** /**
* @param mixed $offset * @param mixed $offset
*
* @throws \InvalidArgumentException
*
* @return mixed * @return mixed
*/ */
public function offsetGet($offset) public function offsetGet($offset)

View File

@ -1,11 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./tests/bootstrap.php" colors="true"> <phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="phpunit.xsd"
bootstrap="./tests/bootstrap.php"
colors="true"
verbose="true"
processIsolation="false"
stopOnFailure="true">
<!-- Dummy values used to provide credentials. No need to change these. --> <!-- Dummy values used to provide credentials. No need to change these. -->
<php> <php>
<server name="CRM_URL" value="foo" /> <server name="CRM_URL" value="foo" />
<server name="CRM_API_KEY" value="bar" /> <server name="CRM_API_KEY" value="bar" />
<server name="CRM_SITE" value="zoo" /> <server name="CRM_SITE" value="zoo" />
<server name="CRM_STORE" value="moo" />
<server name="CRM_PACK_ITEM" value="boo" />
<server name="CRM_PACK_QUANTITY" value="goo" />
</php> </php>
<testsuites> <testsuites>

251
phpunit.xsd Normal file
View File

@ -0,0 +1,251 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:annotation>
<xs:documentation source="http://www.phpunit.de/manual/3.7/en/appendixes.configuration.html">
This Schema file defines the rules by which the XML configuration file of PHPUnit 3.7 may be structured.
</xs:documentation>
<xs:appinfo source="http://www.phpunit.de/manual/current/en/appendixes.configuration.html"/>
</xs:annotation>
<xs:element name="phpunit" type="phpUnitType">
<xs:annotation>
<xs:documentation>Root Element</xs:documentation>
</xs:annotation>
</xs:element>
<xs:complexType name="filtersType">
<xs:choice>
<xs:sequence>
<xs:element name="blacklist" type="filterType"/>
<xs:element name="whitelist" type="whiteListType" minOccurs="0"/>
</xs:sequence>
<xs:sequence>
<xs:element name="whitelist" type="whiteListType"/>
</xs:sequence>
</xs:choice>
</xs:complexType>
<xs:complexType name="filterType">
<xs:sequence>
<xs:group ref="pathGroup"/>
<xs:element name="exclude" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:group ref="pathGroup"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="whiteListType">
<xs:complexContent>
<xs:extension base="filterType">
<xs:attribute name="processUncoveredFilesFromWhitelist" default="true" type="xs:boolean"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="groupsType">
<xs:choice>
<xs:sequence>
<xs:element name="include" type="groupType"/>
<xs:element name="exclude" type="groupType" minOccurs="0"/>
</xs:sequence>
<xs:sequence>
<xs:element name="exclude" type="groupType"/>
</xs:sequence>
</xs:choice>
</xs:complexType>
<xs:complexType name="groupType">
<xs:sequence>
<xs:element name="group" type="xs:string" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="listenersType">
<xs:sequence>
<xs:element name="listener" type="objectType" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="objectType">
<xs:sequence>
<xs:element name="arguments" minOccurs="0">
<xs:complexType>
<xs:group ref="argumentsGroup"/>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="class" type="xs:string" use="required"/>
<xs:attribute name="file" type="xs:anyURI"/>
</xs:complexType>
<xs:complexType name="arrayType">
<xs:sequence>
<xs:element name="element" type="argumentType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="argumentType">
<xs:group ref="argumentChoice"/>
<xs:attribute name="key" use="required"/>
</xs:complexType>
<xs:group name="argumentsGroup">
<xs:sequence>
<xs:element name="array" type="arrayType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="integer" type="xs:integer" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="string" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="double" type="xs:double" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="null" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="object" type="objectType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="file" type="xs:anyURI" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="directory" type="xs:anyURI" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:group>
<xs:group name="argumentChoice">
<xs:choice>
<xs:element name="array" type="arrayType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="integer" type="xs:integer" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="string" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="double" type="xs:double" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="null" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="object" type="objectType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="file" type="xs:anyURI" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="directory" type="xs:anyURI" minOccurs="0" maxOccurs="unbounded"/>
</xs:choice>
</xs:group>
<xs:complexType name="loggersType">
<xs:sequence>
<xs:element name="log" type="loggerType" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="loggerType">
<xs:attribute name="type">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="coverage-html"/>
<xs:enumeration value="coverage-clover"/>
<xs:enumeration value="json"/>
<xs:enumeration value="plain"/>
<xs:enumeration value="tap"/>
<xs:enumeration value="junit"/>
<xs:enumeration value="testdox-html"/>
<xs:enumeration value="testdox-text"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="target" type="xs:anyURI"/>
<xs:attribute name="title" type="xs:string"/>
<xs:attribute name="charset" type="xs:string" default="UTF-8"/>
<xs:attribute name="yui" type="xs:boolean" default="true"/>
<xs:attribute name="highlight" type="xs:boolean" default="false"/>
<xs:attribute name="lowUpperBound" type="xs:nonNegativeInteger" default="35"/>
<xs:attribute name="highLowerBound" type="xs:nonNegativeInteger" default="70"/>
<xs:attribute name="logIncompleteSkipped" type="xs:boolean" default="false"/>
</xs:complexType>
<xs:group name="pathGroup">
<xs:sequence>
<xs:element name="directory" type="directoryFilterType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="file" type="fileFilterType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:group>
<xs:complexType name="directoryFilterType">
<xs:simpleContent>
<xs:extension base="xs:anyURI">
<xs:attribute type="xs:string" name="suffix" default="Test.php"/>
<xs:attributeGroup ref="phpVersionGroup"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="fileFilterType">
<xs:simpleContent>
<xs:extension base="xs:anyURI">
<xs:attributeGroup ref="phpVersionGroup"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:attributeGroup name="phpVersionGroup">
<xs:attribute name="phpVersion" type="xs:string" default="5.3.0"/>
<xs:attribute name="phpVersionOperator" type="xs:string" default="&gt;="/>
</xs:attributeGroup>
<xs:complexType name="phpType">
<xs:sequence>
<xs:element name="includePath" type="xs:anyURI" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="ini" type="namedValueType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="const" type="namedValueType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="var" type="namedValueType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="env" type="namedValueType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="post" type="namedValueType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="get" type="namedValueType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="cookie" type="namedValueType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="server" type="namedValueType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="files" type="namedValueType" minOccurs="0" maxOccurs="unbounded"/>
<xs:element name="request" type="namedValueType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="namedValueType">
<xs:attribute name="name" use="required" type="xs:string"/>
<xs:attribute name="value" use="required" type="xs:anySimpleType"/>
</xs:complexType>
<xs:complexType name="phpUnitType">
<xs:annotation>
<xs:documentation>The main type specifying the document structure</xs:documentation>
</xs:annotation>
<xs:group ref="configGroup"/>
<xs:attributeGroup ref="configAttributeGroup"/>
</xs:complexType>
<xs:attributeGroup name="configAttributeGroup">
<xs:attribute name="backupGlobals" type="xs:boolean" default="true"/>
<xs:attribute name="backupStaticAttributes" type="xs:boolean" default="false"/>
<xs:attribute name="bootstrap" type="xs:anyURI"/>
<xs:attribute name="cacheTokens" type="xs:boolean"/>
<xs:attribute name="colors" type="xs:boolean" default="false"/>
<xs:attribute name="convertErrorsToExceptions" type="xs:boolean" default="true"/>
<xs:attribute name="convertNoticesToExceptions" type="xs:boolean" default="true"/>
<xs:attribute name="convertWarningsToExceptions" type="xs:boolean" default="true"/>
<xs:attribute name="forceCoversAnnotation" type="xs:boolean" default="false"/>
<xs:attribute name="mapTestClassNameToCoveredClassName" type="xs:boolean" default="false"/>
<xs:attribute name="printerClass" type="xs:string" default="PHPUnit_TextUI_ResultPrinter"/>
<xs:attribute name="printerFile" type="xs:anyURI"/>
<xs:attribute name="processIsolation" type="xs:boolean" default="false"/>
<xs:attribute name="stopOnError" type="xs:boolean" default="false"/>
<xs:attribute name="stopOnFailure" type="xs:boolean" default="false"/>
<xs:attribute name="stopOnIncomplete" type="xs:boolean" default="false"/>
<xs:attribute name="stopOnSkipped" type="xs:boolean" default="false"/>
<xs:attribute name="strict" type="xs:boolean" default="false"/>
<xs:attribute name="testSuiteLoaderClass" type="xs:string" default="PHPUnit_Runner_StandardTestSuiteLoader"/>
<xs:attribute name="testSuiteLoaderFile" type="xs:anyURI"/>
<xs:attribute name="timeoutForSmallTests" type="xs:integer" default="1"/>
<xs:attribute name="timeoutForMediumTests" type="xs:integer" default="10"/>
<xs:attribute name="timeoutForLargeTests" type="xs:integer" default="60"/>
<xs:attribute name="verbose" type="xs:boolean" default="false"/>
</xs:attributeGroup>
<xs:group name="configGroup">
<xs:all>
<xs:element ref="testSuiteFacet" minOccurs="0"/>
<xs:element name="groups" type="groupsType" minOccurs="0"/>
<xs:element name="filter" type="filtersType" minOccurs="0"/>
<xs:element name="logging" type="loggersType" minOccurs="0"/>
<xs:element name="listeners" type="listenersType" minOccurs="0"/>
<xs:element name="php" type="phpType" minOccurs="0"/>
<xs:element name="selenium" type="seleniumType" minOccurs="0"/>
</xs:all>
</xs:group>
<xs:complexType name="seleniumType">
<xs:sequence>
<xs:element name="browser" type="browserType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="browserType">
<xs:attribute name="name" type="xs:string"/>
<xs:attribute name="browser" type="xs:string"/>
<xs:attribute name="host" type="xs:anyURI"/>
<xs:attribute name="port" type="xs:nonNegativeInteger"/>
<xs:attribute name="timeout" type="xs:nonNegativeInteger"/>
</xs:complexType>
<xs:element name="testSuiteFacet" abstract="true"/>
<xs:element name="testsuite" type="testSuiteType" substitutionGroup="testSuiteFacet"/>
<xs:element name="testsuites" type="testSuitesType" substitutionGroup="testSuiteFacet"/>
<xs:complexType name="testSuitesType">
<xs:sequence>
<xs:element name="testsuite" type="testSuiteType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="testSuiteType">
<xs:sequence>
<xs:group ref="pathGroup"/>
<xs:element name="exclude" type="xs:anyURI" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="required"/>
</xs:complexType>
</xs:schema>

View File

@ -27,15 +27,20 @@ class TestCase extends \PHPUnit_Framework_TestCase
/** /**
* Return Client object * Return Client object
* *
* @param string $url (default: null) * @param string $url (default: null)
* @param string $apiKey (default: null) * @param array $defaultParameters (default: array())
*
* @return Client * @return Client
*/ */
public static function getClient($url = null, $defaultParameters = array()) public static function getClient($url = null, $defaultParameters = array())
{ {
return new Client( return new Client(
$url ?: $_SERVER['CRM_URL'] . '/api/' . ApiClient::VERSION, $url ?: $_SERVER['CRM_URL'] . '/api/' . ApiClient::VERSION,
array('apiKey' => isset($defaultParameters['apiKey']) ? $defaultParameters['apiKey'] : $_SERVER['CRM_API_KEY']) array(
'apiKey' => array_key_exists('apiKey', $defaultParameters)
? $defaultParameters['apiKey']
: $_SERVER['CRM_API_KEY']
)
); );
} }
} }

View File

@ -199,27 +199,6 @@ class ApiClientOrdersTest extends TestCase
'API returns generatedAt in orders history' 'API returns generatedAt in orders history'
); );
} }
/**
* @group integration
*/
public function testOrdersPacksHistory()
{
$client = static::getApiClient();
$response = $client->ordersPacksHistory();
$this->assertInstanceOf('RetailCrm\Response\ApiResponse', $response);
$this->assertEquals(200, $response->getStatusCode());
$this->assertTrue($response->success);
$this->assertTrue(
isset($response['history']),
'API returns orders assembly history'
);
$this->assertTrue(
isset($response['generatedAt']),
'API returns generatedAt in orders assembly history'
);
}
/** /**
* @group integration * @group integration

View File

@ -0,0 +1,91 @@
<?php
namespace RetailCrm\Tests;
use RetailCrm\Test\TestCase;
class ApiClientPacksTest extends TestCase
{
/**
* @group integration
*/
public function testOrdersPacksHistory()
{
$client = static::getApiClient();
$response = $client->ordersPacksHistory();
$this->assertInstanceOf('RetailCrm\Response\ApiResponse', $response);
$this->assertEquals(200, $response->getStatusCode());
$this->assertTrue($response->success);
$this->assertTrue(
isset($response['history']),
'API returns orders assembly history'
);
$this->assertTrue(
isset($response['generatedAt']),
'API returns generatedAt in orders assembly history'
);
}
/**
* @group integration
*/
public function testOrdersPacksCreate()
{
$client = static::getApiClient();
$pack = array(
'itemId' => $_SERVER['CRM_PACK_ITEM'],
'quantity' => $_SERVER['CRM_PACK_QUANTITY'],
'store' => $_SERVER['CRM_STORE']
);
$response = $client->ordersPacksCreate($pack);
$this->assertInstanceOf('RetailCrm\Response\ApiResponse', $response);
$this->assertEquals(201, $response->getStatusCode());
$this->assertTrue($response->success);
}
/**
* @group integration
*/
public function testOrdersPacksCreateFailed()
{
$client = static::getApiClient();
$pack = array(
'itemId' => 12,
'store' => $_SERVER['CRM_STORE'],
'quantity' => 2
);
$response = $client->ordersPacksCreate($pack);
$this->assertInstanceOf('RetailCrm\Response\ApiResponse', $response);
$this->assertEquals(400, $response->getStatusCode());
$this->assertFalse($response->success);
}
/**
* @group integration
*/
public function testOrdersPacksGet()
{
$client = static::getApiClient();
$response = $client->ordersPacksGet(1);
$this->assertInstanceOf('RetailCrm\Response\ApiResponse', $response);
$this->assertEquals(200, $response->getStatusCode());
$this->assertTrue($response->success);
}
/**
* @group integration
*/
public function testOrdersPacksDelete()
{
$client = static::getApiClient();
$response = $client->ordersPacksDelete(1);
$this->assertInstanceOf('RetailCrm\Response\ApiResponse', $response);
$this->assertEquals(200, $response->getStatusCode());
$this->assertTrue($response->success);
}
}

View File

@ -4,6 +4,10 @@ namespace RetailCrm\Tests;
use RetailCrm\Test\TestCase; use RetailCrm\Test\TestCase;
/**
* Class ApiClientStoreTest
* @package RetailCrm\Tests
*/
class ApiClientStoreTest extends TestCase class ApiClientStoreTest extends TestCase
{ {
/** /**
@ -30,8 +34,7 @@ class ApiClientStoreTest extends TestCase
public function testStoreInventoriesUploadExceptionEmpty() public function testStoreInventoriesUploadExceptionEmpty()
{ {
$client = static::getApiClient(); $client = static::getApiClient();
$client->storeInventoriesUpload(array());
$response = $client->storeInventoriesUpload(array());
} }
/** /**
@ -47,20 +50,27 @@ class ApiClientStoreTest extends TestCase
$response = $client->storeInventoriesUpload(array( $response = $client->storeInventoriesUpload(array(
array( array(
'externalId' => $externalIdA, 'externalId' => $externalIdA,
'available' => 10, 'stores' => array(
'purchasePrice' => 1700 array(
'code' => $_SERVER['CRM_STORE'],
'available' => 10,
'purchasePrice' => 1700
)
)
), ),
array( array(
'externalId' => $externalIdB, 'externalId' => $externalIdB,
'available' => 20, 'stores' => array(
'purchasePrice' => 1500 array(
'code' => $_SERVER['CRM_STORE'],
'available' => 20,
'purchasePrice' => 1500
)
)
), ),
)); ));
$this->assertInstanceOf('RetailCrm\Response\ApiResponse', $response); $this->assertInstanceOf('RetailCrm\Response\ApiResponse', $response);
$this->assertTrue( $this->assertTrue($response->isSuccessful());
$response->isSuccessful(),
'Got offer'
);
} }
/** /**

View File

@ -58,14 +58,4 @@ class ClientTest extends TestCase
$this->assertInstanceOf('RetailCrm\Response\ApiResponse', $response); $this->assertInstanceOf('RetailCrm\Response\ApiResponse', $response);
$this->assertEquals(200, $response->getStatusCode()); $this->assertEquals(200, $response->getStatusCode());
} }
/**
* @group integration
* @expectedException \RetailCrm\Exception\CurlException
*/
public function testMakeRequestTimeout()
{
$client = static::getClient();
$client->makeRequest('/orders', Client::METHOD_GET, array(), 1, false, true);
}
} }

View File

@ -29,7 +29,7 @@ class ApiResponseTest extends TestCase
/** /**
* @group unit * @group unit
* @expectedException RetailCrm\Exception\InvalidJsonException * @expectedException \RetailCrm\Exception\InvalidJsonException
*/ */
public function testJsonInvalid() public function testJsonInvalid()
{ {