Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] API v4 #90

Draft
wants to merge 5 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [WIP]
- API v4
- Support for getting records via external ids

## [5.2.0]
- Added support for illuminate collections v9
- Added tests for php 8.1
Expand Down
13 changes: 7 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,19 @@
}
],
"require": {
"php" : "^7.3 || ^8.0",
"php": "^8.0",
"ext-json": "*",
"guzzlehttp/guzzle": "^6.3 || ^7.0",
"illuminate/collections": "^8.0 || ^9.0",
"doctrine/inflector": "^2.0",
"weble/zohoclient": "^4.2",
"ext-json": "*"
"weble/zohoclient": "dev-feature/v5"
},
"require-dev": {
"phpunit/phpunit": "^8.0 || ^9.0",
"nunomaduro/collision": "^5.0 || ^6.0",
"phpunit/phpunit": "^9.1",
"nunomaduro/collision": "^6.0",
"cache/filesystem-adapter": "^1.0",
"caseyamcl/guzzle_retry_middleware": "^2.6"
"caseyamcl/guzzle_retry_middleware": "^2.6",
"phpstan/phpstan": "^1.9"
},
"autoload": {
"psr-4": {
Expand Down
6 changes: 6 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
parameters:
level: 6
paths:
- src
- tests
checkGenericClassInNonGenericObjectType: false
124 changes: 51 additions & 73 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Webleit\ZohoCrmApi;

use Closure;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\ClientException;
use Psr\Http\Message\ResponseInterface;
Expand All @@ -14,34 +15,20 @@

class Client
{
protected const ZOHOCRM_API_URL_PATH = "/crm/v2/";
protected const ZOHOCRM_API_URL_PATH = "/crm/v4/";
protected const ZOHOCRM_API_PRODUCION_PARTIAL_HOST = "https://www.zohoapis";
protected const ZOHOCRM_API_DEVELOPER_PARTIAL_HOST = "https://developer.zoho";
protected const ZOHOCRM_API_SANDBOX_PARTIAL_HOST = "https://crmsandbox.zoho";

public const SUCCESS_CODE = 'SUCCESS';

/**
* @var bool
*/
protected $retriedRefresh = false;

/**
* @var \GuzzleHttp\Client
*/
protected $client;

/**
* @var OAuthClient
*/
protected $oAuthClient;
protected bool $retriedRefresh = false;
protected ClientInterface $client;
protected OAuthClient $oAuthClient;

/**
* @var string
*/
protected $mode;
protected string $mode;

public function __construct(OAuthClient $oAuthClient, ClientInterface $client = null)
public function __construct(OAuthClient $oAuthClient, ?ClientInterface $client = null, string $mode = Mode::PRODUCTION)
{
if (! $client) {
$client = new \GuzzleHttp\Client();
Expand All @@ -50,15 +37,21 @@ public function __construct(OAuthClient $oAuthClient, ClientInterface $client =
$this->client = $client;
$this->oAuthClient = $oAuthClient;

$this->setMode(Mode::PRODUCTION);
$this->setMode($mode);
}

public function __call($name, $arguments)
/**
* @param string $name
* @param array<string|int,mixed> $arguments
*/
public function __call(string $name, array $arguments): mixed
{
return call_user_func_array([
/** @var Closure $callback */
$callback = [
$this->oAuthClient,
$name,
], $arguments);
];
return call_user_func_array($callback, $arguments);
}

public function getOAuthClient(): OAuthClient
Expand All @@ -82,27 +75,11 @@ public function setRegion(string $region): self

/**
* @param string $uri
* @param array|ListParameters $params
* @param array|ListHeaders $headers
* @return array
* @throws ApiError
* @throws Exception\AuthFailed
* @throws Exception\DuplicateData
* @throws Exception\InvalidData
* @throws Exception\InvalidDataFormat
* @throws Exception\InvalidDataType
* @throws Exception\InvalidModule
* @throws Exception\InvalidUrlPattern
* @throws Exception\LimitExceeded
* @throws Exception\MandatoryDataNotFound
* @throws Exception\MethodNotAllowed
* @throws Exception\OAuthScopeMismatch
* @throws Exception\RequestEntityTooLarge
* @throws Exception\TooManyRequests
* @throws Exception\Unauthorized
* @throws Exception\UnsupportedMediaType
* @param array<string,mixed>|ListParameters $params
* @param array<string,mixed>|ListHeaders $headers
* @return array<string|int,mixed>
*/
public function getList(string $uri, $params = [], $headers = []): array
public function getList(string $uri, array|ListParameters $params = [], array|ListHeaders $headers = []): array
{
if (! $params instanceof ListParameters) {
$params = new ListParameters($params);
Expand All @@ -123,6 +100,9 @@ public function getList(string $uri, $params = [], $headers = []): array
return $data ?? [];
}

/**
* @param array<string|int,mixed> $data
*/
public function call(string $uri, string $method, array $data = []): ResponseInterface
{
$options = array_merge([
Expand All @@ -132,8 +112,7 @@ public function call(string $uri, string $method, array $data = []): ResponseInt
], $data);

$options['headers'] = array_merge($data['headers'] ?? [], [
'Authorization' =>
'Zoho-oauthtoken ' . $this->oAuthClient->getAccessToken(),
'Authorization' => 'Zoho-oauthtoken ' . $this->oAuthClient->getAccessToken(),
]);

try {
Expand All @@ -153,10 +132,6 @@ public function call(string $uri, string $method, array $data = []): ResponseInt

$response = $e->getResponse();

if (! $response) {
throw $e;
}

ApiError::throwFromResponse($response);

return $response;
Expand Down Expand Up @@ -223,62 +198,65 @@ public function getRegion(): string
return $this->oAuthClient->getRegion();
}

public function get(string $url, string $id = null, array $params = [])
/**
* @param array<string|int,mixed> $params
* @param array<string|int,mixed> $options
* @return array<string|int,mixed>|string
*/
public function get(string $url, string $id = null, array $params = [], array $options = []): array|string
{
if ($id !== null) {
$url .= '/' . $id;
}

$result = $this->call($url, 'GET', ['query' => $params]);
$options['query'] = array_merge($options['query'] ?? [], $params);
$result = $this->call($url, 'GET', $options);

return $this->processResult($result);
}

public function post(string $url, array $params = [], array $queryParams = [])
/**
* @param array<string|int,mixed> $params
* @param array<string|int,mixed> $queryParams
* @return array<string|int,mixed>|string
*/
public function post(string $url, array $params = [], array $queryParams = []): array|string
{
return $this->processResult($this->call($url, 'POST', [
'query' => $queryParams,
'json' => $params,
]));
}

public function put(string $url, array $params = [], array $queryParams = [])
/**
* @param array<string|int,mixed> $params
* @param array<string|int,mixed> $queryParams
* @return array<string|int,mixed>|string
*/
public function put(string $url, array $params = [], array $queryParams = []): array|string
{
return $this->processResult($this->call($url, 'PUT', [
'query' => $queryParams,
'json' => $params,
]));
}

public function delete(string $url, $id)
/**
* @return array<string|int,mixed>|string
*/
public function delete(string $url, string $id): array|string
{
return $this->processResult($this->call($url . '/' . $id, 'DELETE'));
}

public function getHttpClient(): \GuzzleHttp\Client
public function getHttpClient(): ClientInterface
{
return $this->client;
}

/**
* @param ResponseInterface $response
* @return array|string
* @throws ApiError
* @throws Exception\AuthFailed
* @throws Exception\DuplicateData
* @throws Exception\InvalidData
* @throws Exception\InvalidDataFormat
* @throws Exception\InvalidDataType
* @throws Exception\InvalidModule
* @throws Exception\InvalidUrlPattern
* @throws Exception\LimitExceeded
* @throws Exception\MandatoryDataNotFound
* @throws Exception\MethodNotAllowed
* @throws Exception\OAuthScopeMismatch
* @throws Exception\RequestEntityTooLarge
* @throws Exception\TooManyRequests
* @throws Exception\Unauthorized
* @throws Exception\UnsupportedMediaType
* @return array<int|string,mixed>|string
*/
public function processResult(ResponseInterface $response)
{
Expand Down
3 changes: 3 additions & 0 deletions src/Contracts/Model.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ interface Model extends Arrayable, Jsonable, JsonSerializable
{
public function getModule(): Module;

/**
* @return array<string,mixed>
*/
public function getData(): array;

public function isNew(): bool;
Expand Down
19 changes: 16 additions & 3 deletions src/Exception/ApiError.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Webleit\ZohoCrmApi\Exception;

use Illuminate\Support\Collection;
use Psr\Http\Message\ResponseInterface;

/**
Expand All @@ -10,8 +11,7 @@
*/
class ApiError extends \Exception
{
/** @var ResponseInterface */
protected $response;
protected ResponseInterface $response;

// Error Codes
protected const INVALID_MODULE = 'INVALID_MODULE';
Expand All @@ -28,8 +28,14 @@ class ApiError extends \Exception
protected const TOO_MANY_REQUESTS = 'TOO_MANY_REQUESTS';
protected const INVALID_TOKEN = 'INVALID_TOKEN';

protected $details = [];
/**
* @var array<string,mixed>
*/
protected array $details = [];

/**
* @param array<string,mixed> $details
*/
public function __construct(ResponseInterface $response, array $details = [])
{
parent::__construct($response->getReasonPhrase(), $response->getStatusCode());
Expand All @@ -38,6 +44,9 @@ public function __construct(ResponseInterface $response, array $details = [])
$this->details = $details;
}

/**
* @return array<string,mixed>
*/
public function details(): array
{
return $this->details;
Expand Down Expand Up @@ -123,6 +132,9 @@ public static function throwFromResponse(ResponseInterface $response): void
}
}

/**
* @return array<string,mixed>
*/
protected static function getErrorCodeAndDetailsFromResponse(ResponseInterface $response): array
{
try {
Expand Down Expand Up @@ -156,6 +168,7 @@ protected static function getErrorCodeAndDetailsFromResponse(ResponseInterface $
];
}

/** @phpstan-ignore-next-line $body */
$body = collect($body);

return [
Expand Down
6 changes: 2 additions & 4 deletions src/Mixins/HasInflector.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@

trait HasInflector
{
/**
* @var \Doctrine\Inflector\Inflector
*/
protected $inflector = null;

protected ?Inflector $inflector = null;

public function inflector(): Inflector
{
Expand Down
4 changes: 3 additions & 1 deletion src/Mixins/ProvidesModules.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ public function createModule(string $name): ?Module
if ($this->getAvailableModules()->has($name)) {
$class = $this->getAvailableModules()->get($name);

return new $class($this->client);
/** @var Module $module */
$module = new $class($this->client);
return $module;
}

return null;
Expand Down
Loading