Skip to content
Snippets Groups Projects
Commit d2b66434 authored by Grzegorz Rola's avatar Grzegorz Rola
Browse files

Feature/first release

parent 24ca0857
No related branches found
No related tags found
No related merge requests found
Showing
with 969 additions and 3 deletions
tests/ export-ignore
vendor/ export-ignore
.editorconfig export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.git/ export-ignore
.gitlab-ci.yml export-ignore
.idea export-ignore
apigen.neon export-ignore
build-coverage/ export-ignore
docs/ export-ignore
LICENSE.md export-ignore
phpcs.xml.dist export-ignore
phpunit-integration.xml export-ignore
phpunit-unit.xml export-ignore
/vendor/
.idea
composer.lock
build-coverage
swagger
\ No newline at end of file
variables:
DISABLE_ACCEPTANCE: "1"
DISABLE_FUNCTIONAL: "1"
include: 'https://gitlab.com/wpdesk/gitlab-ci/raw/master/gitlab-ci-1.2.yml'
# wp-api-client
[![pipeline status](https://gitlab.com/wpdesk/wp-api-client/badges/master/pipeline.svg)](https://gitlab.com/wpdesk/wp-api-client/commits/master)
Integration: [![coverage report](https://gitlab.com/wpdesk/wp-api-client/badges/master/coverage.svg?job=integration+test+lastest+coverage)](https://gitlab.com/wpdesk/wp-api-client/commits/master)
Unit: [![coverage report](https://gitlab.com/wpdesk/wp-api-client/badges/master/coverage.svg?job=unit+test+lastest+coverage)](https://gitlab.com/wpdesk/wp-api-client/commits/master)
API Client
==========
= 1.0 - 2019-01-31 =
* First release
......@@ -9,8 +9,8 @@
"require": {
"php": ">=5.5",
"psr/log": "^1.0.1",
"wpdesk/wp-cache": "dev-master",
"wpdesk/wp-http-client": "dev-master",
"wpdesk/wp-cache": "dev-feature/first-release",
"wpdesk/wp-http-client": "dev-feature/first-release",
"psr/simple-cache": "^1.0"
},
"require-dev": {
......
<phpunit bootstrap="tests/integration/bootstrap.php"
backupGlobals="false"
>
<testsuites>
<testsuite>
<directory prefix="Test" suffix=".php">./tests/integration</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src</directory>
</whitelist>
</filter>
<logging>
<log type="junit" target="build-coverage/report.junit.xml"/>
<log type="coverage-html" target="build-coverage/coverage" charset="UTF-8" yui="true" highlight="true"/>
<log type="coverage-text" target="build-coverage/coverage.txt"/>
<log type="coverage-clover" target="build-coverage/clover.xml"/>
</logging>
<php>
<env name="WP_DEVELOP_DIR" value="/tmp/wordpress-develop"/>
<env name="WC_DEVELOP_DIR" value="/tmp/woocommerce"/>
</php>
</phpunit>
\ No newline at end of file
<phpunit bootstrap="tests/unit/bootstrap.php">
<testsuites>
<testsuite>
<directory prefix="Test" suffix=".php">./tests/unit/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src</directory>
</whitelist>
</filter>
<logging>
<log type="junit" target="build-coverage/report.junit.xml"/>
<log type="coverage-html" target="build-coverage/coverage" charset="UTF-8" yui="true" highlight="true"/>
<log type="coverage-text" target="build-coverage/coverage.txt"/>
<log type="coverage-clover" target="build-coverage/clover.xml"/>
</logging>
</phpunit>
<?php
namespace WPDesk\ApiClient\Authentication;
class JWTSaasToken implements Token
{
const SHOP_ID_PARAM = 'shop';
const ROLE_PARAM = 'ROLE_SHOP';
/** @var JWTToken */
private $token;
/**
* JWTToken constructor.
* @param string $token
*/
public function __construct(JWTToken $token)
{
$this->token = $token;
}
public function getAuthString()
{
return $this->token->getAuthString();
}
public function isExpired()
{
return $this->token->isExpired();
}
public function isSignatureValid()
{
return $this->token->isSignatureValid();
}
public function __toString()
{
return $this->token->__toString();
}
/**
* If there is shop id in the token
*
* @return bool
*/
public function hasShopId()
{
$info = $this->token->getDecodedPublicTokenInfo();
return !empty($info[self::SHOP_ID_PARAM]) && in_array(self::ROLE_PARAM, $info['roles']);
}
/**
* Get shop id from token
*
* @return int
*/
public function getShopId()
{
$info = $this->token->getDecodedPublicTokenInfo();
return (int)$info[self::SHOP_ID_PARAM];
}
}
\ No newline at end of file
<?php
namespace WPDesk\ApiClient\Authentication;
class JWTToken implements Token
{
const CONSIDER_EXPIRED_WHEN_LESS = 2;
const EXPIRED_IN_SECONDS_PARAM = 'exp';
/** @var string */
private $token;
/**
* JWTToken constructor.
* @param string $token
*/
public function __construct($token)
{
$this->token = $token;
}
/**
* @return string
*/
public function __toString()
{
return $this->token;
}
/**
* Get string to perform authentication
*
* @return string
*/
public function getAuthString()
{
return 'Bearer ' . $this->__toString();
}
/**
* Returns public data from token
*
* @return array
*/
public function getDecodedPublicTokenInfo()
{
$tokenParts = explode('.', $this->__toString());
if (!empty($tokenParts[1])) {
$infoPart = base64_decode($tokenParts[1]);
return json_decode($infoPart, true);
}
return [];
}
/**
* Is token expired or very soon to be expired?
*
* @return bool
*/
public function isExpired()
{
$tokenInfo = $this->getDecodedPublicTokenInfo();
if (!empty($tokenInfo[self::EXPIRED_IN_SECONDS_PARAM])) {
return $tokenInfo[self::EXPIRED_IN_SECONDS_PARAM] - time() < self::CONSIDER_EXPIRED_WHEN_LESS;
}
return true;
}
/**
* Validates token signature
*
* @return bool
*/
public function isSignatureValid()
{
// @TODO
return true;
}
}
\ No newline at end of file
<?php
namespace WPDesk\ApiClient\Authentication;
/**
* Null object pattern
*
* @package WPDesk\SaasPlatformClient\Authentication
*/
class NullToken implements Token
{
public function __construct()
{
}
/**
* @return string
*/
public function __toString()
{
return '';
}
/**
* Get string to perform authentication
*
* @return string
*/
public function getAuthString()
{
return '';
}
/**
* Is token expired or very soon to be expired?
*
* @return bool
*/
public function isExpired()
{
return true;
}
/**
* Validates token signature
*
* @return bool
*/
public function isSignatureValid()
{
return false;
}
}
\ No newline at end of file
<?php
namespace WPDesk\ApiClient\Authentication;
interface Token
{
/**
* Get string to perform authentication
*
* @return string
*/
public function getAuthString();
/**
* Is token expired or very soon to be expired?
*
* @return bool
*/
public function isExpired();
/**
* Validates token signature
*
* @return bool
*/
public function isSignatureValid();
/**
* @return string
*/
public function __toString();
}
\ No newline at end of file
<?php
namespace WPDesk\ApiClient\Client;
use Psr\Log\LoggerInterface;
use WPDesk\ApiClient\Serializer\SerializerOptions;
use WPDesk\HttpClient\HttpClientOptions;
interface ApiClientOptions extends HttpClientOptions, SerializerOptions
{
/**
* @return LoggerInterface
*/
public function getLogger();
/**
* @return string
*/
public function getApiUrl();
/**
* @return array
*/
public function getDefaultRequestHeaders();
/**
* @return bool
*/
public function isCachedClient();
}
\ No newline at end of file
<?php
namespace WPDesk\ApiClient\Client;
use Psr\SimpleCache\CacheInterface;
use WPDesk\Cache\CacheDispatcher;
use WPDesk\Cache\CacheItemCreator;
use WPDesk\Cache\CacheItemVerifier;
use WPDesk\HttpClient\HttpClient;
use WPDesk\ApiClient\Request\Request;
use WPDesk\ApiClient\Response\Response;
use WPDesk\ApiClient\Serializer\Serializer;
class CachedClient implements Client, CacheItemCreator, CacheItemVerifier
{
/** @var Client */
private $client;
/** @var CacheInterface */
private $cache;
/**
* @var CacheDispatcher
*/
private $cacheDispatcher;
/**
* CachedClient constructor.
* @param Client $decorated Decorated client
* @param CacheInterface $cache
*/
public function __construct(Client $decorated, CacheInterface $cache)
{
$this->client = $decorated;
$this->cache = $cache;
$this->cacheDispatcher = new CacheDispatcher($cache, [new RequestCacheInfoResolver()]);
}
/**
* Create item to cache.
*
* @param Request $request
* @return Response
*/
public function createCacheItem($request)
{
return $this->client->sendRequest($request);
}
/**
* Verify cache item.
*
* @param $object
* @return Response;
*/
public function getVerifiedItemOrNull($object)
{
if ($object instanceof Response) {
return $object;
}
return null;
}
/**
* Send request.
*
* @param Request $request
* @return mixed|Response
* @throws \Psr\SimpleCache\InvalidArgumentException
*/
public function sendRequest(Request $request)
{
$response = $this->cacheDispatcher->dispatch($request, $this, $this);
return $response;
}
/**
* @return HttpClient
*/
public function getHttpClient()
{
return $this->client->getHttpClient();
}
/**
* @param HttpClient $client
* @return mixed
*/
public function setHttpClient(HttpClient $client)
{
return $this->client->setHttpClient($client);
}
/**
* @return Serializer
*/
public function getSerializer()
{
return $this->client->getSerializer();
}
/**
* @return string
*/
public function getApiUrl()
{
return $this->client->getApiUrl();
}
}
\ No newline at end of file
<?php
namespace WPDesk\ApiClient\Client;
use WPDesk\HttpClient\HttpClient;
use WPDesk\ApiClient\Request\Request;
use WPDesk\ApiClient\Response\Response;
use WPDesk\ApiClient\Serializer\Serializer;
interface Client
{
/**
* Send given request trough HttpClient
*
* @param Request $request
* @return Response
*/
public function sendRequest(Request $request);
/**
* @return HttpClient
*/
public function getHttpClient();
/**
* @param HttpClient $client
*/
public function setHttpClient(HttpClient $client);
/**
* @return Serializer
*/
public function getSerializer();
/**
* Returns api url. Always without ending /
*
* @return string
*/
public function getApiUrl();
}
\ No newline at end of file
<?php
namespace WPDesk\ApiClient\Client;
use WPDesk\Cache\WordpressCache;
use WPDesk\HttpClient\HttpClientFactory;
use WPDesk\ApiClient\Serializer\SerializerFactory;
class ClientFactory
{
/**
* @param ApiClientOptions $options
* @return Client
*/
public function createClient(ApiClientOptions $options)
{
$httpClientFactory = new HttpClientFactory();
$serializerFactory = new SerializerFactory();
$client = new ClientImplementation(
$httpClientFactory->createClient($options),
$serializerFactory->createSerializer($options),
$options->getLogger(),
$options->getApiUrl(),
$options->getDefaultRequestHeaders()
);
if ($options->isCachedClient()) {
$client = new CachedClient($client, new WordpressCache());
}
return $client;
}
}
\ No newline at end of file
<?php
namespace WPDesk\ApiClient\Client;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerInterface;
use WPDesk\HttpClient\HttpClient;
use WPDesk\HttpClient\HttpClientResponse;
use WPDesk\ApiClient\Request\Request;
use WPDesk\ApiClient\Response\RawResponse;
use WPDesk\ApiClient\Response\Response;
use WPDesk\ApiClient\Serializer\Serializer;
use WPDesk\HttpClient\HttpClientRequestException;
class ClientImplementation implements Client, LoggerAwareInterface
{
const CLIENT_VERSION = '1.6.5';
const DEFAULT_TIMEOUT = 10;
/** @var HttpClient */
private $client;
/** @var Serializer */
private $serializer;
/** @var LoggerInterface */
private $logger;
/** @var string */
private $apiUrl;
/** @var array */
private $defaultRequestHeaders;
/**
* Client constructor.
* @param HttpClient $client
* @param Serializer $serializer
* @param LoggerInterface $logger
* @param string $apiUri
* @param array $defaultRequestHeaders
*/
public function __construct(
HttpClient $client,
Serializer $serializer,
LoggerInterface $logger,
$apiUri,
array $defaultRequestHeaders
) {
$this->client = $client;
$this->serializer = $serializer;
$this->logger = $logger;
$this->apiUrl = $apiUri;
$this->defaultRequestHeaders = $defaultRequestHeaders;
}
/**
* Send given request trough HttpClient
*
* @param Request $request
* @throws HttpClientRequestException
* @return Response
*/
public function sendRequest(Request $request)
{
$this->logger->debug("Sends request with METHOD: {$request->getMethod()}; to ENDPOINT {$request->getEndpoint()}",
$this->getLoggerContext());
try {
$httpResponse = $this->client->send(
$fullUrl = $this->prepareFullUrl($request),
$method = $request->getMethod(),
$body = $this->prepareRequestBody($request),
$headers = $this->prepareRequestHeaders($request), self::DEFAULT_TIMEOUT
);
$this->logger->debug(
"Sent request with: URL: {$fullUrl};\n METHOD: {$method};\n BODY: {$body};\n"
. "HEADERS: " . json_encode($headers) . "\n\n and got response as CODE: {$httpResponse->getResponseCode()};\n"
. "with RESPONSE BODY {$httpResponse->getBody()}",
$this->getLoggerContext());
return $this->mapHttpResponseToApiResponse($httpResponse);
} catch (HttpClientRequestException $e) {
$this->logger->error("Exception {$e->getMessage()}; {$e->getCode()} occurred while sending request");
throw $e;
}
}
/**
* Returns full request url with endpoint
*
* @param Request $request
* @return string
*/
private function prepareFullUrl(Request $request)
{
$endpoint = $request->getEndpoint();
if (strpos('http', $endpoint) === 0) {
return $endpoint;
}
return $this->getApiUrl() . $endpoint;
}
/**
* Map response from http client to api response using serializer
*
* @param HttpClientResponse $response
* @return RawResponse
*/
private function mapHttpResponseToApiResponse(HttpClientResponse $response)
{
$apiResponse = new RawResponse(
$this->serializer->unserialize($response->getBody()),
$response->getResponseCode(),
$response->getHeaders()
);
return $apiResponse;
}
/**
* Prepare serialized request body
*
* @param Request $request
* @return string
*/
private function prepareRequestBody(Request $request)
{
return $this->serializer->serialize($request->getBody());
}
/**
* Prepares array of http headers
*
* @param Request $request
* @return array
*/
private function prepareRequestHeaders(Request $request)
{
$headers = array(
'User-Agent' => 'saas-client-' . self::CLIENT_VERSION,
'Accept-Encoding' => '*',
'Content-Type' => $this->serializer->getMime()
);
$headers = array_merge($this->defaultRequestHeaders, $headers);
return array_merge($headers, $request->getHeaders());
}
/**
* @return HttpClient
*/
public function getHttpClient()
{
return $this->client;
}
/**
* @param HttpClient $client
*/
public function setHttpClient(HttpClient $client)
{
$this->client = $client;
}
/**
* @return Serializer
*/
public function getSerializer()
{
return $this->serializer;
}
/**
* Returns api url. Always without ending /
*
* @return string
*/
public function getApiUrl()
{
return trim($this->apiUrl, '/');
}
/**
* Sets logger
*
* @param LoggerInterface $logger
*/
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
}
/**
* Returns logger context for
*
* @param string $additional_context Optional additional context
* @return array
*/
protected function getLoggerContext($additional_context = '')
{
$context = [
Platform::LIBARY_LOGIN_CONTEXT,
self::class
];
if ($additional_context !== '') {
$context[] = $additional_context;
}
return $context;
}
}
\ No newline at end of file
<?php
namespace WPDesk\ApiClient\Client;
use WPDesk\Cache\CacheInfoResolver;
use WPDesk\Cache\HowToCache;
use WPDesk\SaasPlatformClient\Request\AuthRequest;
use WPDesk\SaasPlatformClient\Request\BasicRequest;
use WPDesk\SaasPlatformClient\Request\Request;
use WPDesk\SaasPlatformClient\Request\ShippingServicesSettings\PutSettingsRequest;
use WPDesk\SaasPlatformClient\Request\Status\GetStatusRequest;
use WPDesk\SaasPlatformClient\Response\ApiResponse;
use WPDesk\SaasPlatformClient\Response\RawResponse;
class RequestCacheInfoResolver implements CacheInfoResolver
{
const DEFAULT_CACHE_TTL = 86400; //24 hours
const CACHE_TTL_ONE_MINUTE = 60;
const OPTION_FS_SAAS_PLATFORM_VERSION_HASH = 'fs-saas-platform-version-hash';
/**
*
* @param Request $request
*
* @return bool
*/
private function prepareCacheKey($request)
{
return md5($request->getEndpoint());
}
/**
*
* @param Request $request
*
* @return bool
*/
public function isSupported($request)
{
if ($request instanceof BasicRequest) {
return true;
}
return false;
}
/**
*
* @param Request $request
*
* @return bool
*/
public function shouldCache($request)
{
if ($request instanceof ConnectKeyInfoRequest) {
return false;
}
if ($request instanceof GetStatusRequest) {
return false;
}
if ($request instanceof BasicRequest) {
if ('GET' === $request->getMethod()) {
return true;
}
}
return false;
}
/**
*
* @param Request $request
*
* @return HowToCache
*/
public function prepareHowToCache($request)
{
$howToCache = new HowToCache($this->prepareCacheKey($request), self::DEFAULT_CACHE_TTL);
return $howToCache;
}
/**
* @param ApiResponse $response
*
* @return bool
*/
private function isPlatformVersionFromResponseChanged(ApiResponse $response)
{
$stored_hash = get_option(self::OPTION_FS_SAAS_PLATFORM_VERSION_HASH, '');
if ($stored_hash !== $response->getPlatformVersionHash()) {
return true;
}
return false;
}
/**
* @param ApiResponse $response
*/
private function storePlatformVersionHashFromResponse(ApiResponse $response)
{
update_option(self::OPTION_FS_SAAS_PLATFORM_VERSION_HASH, $response->getPlatformVersionHash());
}
/**
*
* @param Request $request
* @param mixed $item
*
* @return bool
*/
public function shouldClearCache($request, $item)
{
if ($request instanceof PutSettingsRequest) {
return true;
}
if ($item instanceof ApiResponse && $this->isPlatformVersionFromResponseChanged($item)) {
$this->storePlatformVersionHashFromResponse($item);
return true;
}
return false;
}
/**
*
* @param Request $request
* @param mixed $item
*
* @return string[]
*/
public function shouldClearKeys($request, $item)
{
if ('GET' !== $request->getMethod()) {
return [$this->prepareCacheKey($request)];
}
return [];
}
}
\ No newline at end of file
<?php
namespace WPDesk\ApiClient\Request;
class BasicRequest implements Request
{
/** @var string */
protected $method;
/** @var array */
protected $data;
/** @var string */
protected $endPoint;
/**
* @return string
*/
public function getMethod()
{
return $this->method;
}
/**
* Return endpoint in format /[^/]+/
*
* @return string
*/
public function getEndpoint()
{
return '/' . trim($this->endPoint, '/');
}
/**
* Returns array of http headers
*
* @return array
*/
public function getHeaders()
{
return array();
}
/**
* Return unserialized request body as array
*
* @return array
*/
public function getBody()
{
return $this->data;
}
}
\ No newline at end of file
<?php
namespace WPDesk\ApiClient\Request;
interface Request
{
/**
* @return string
*/
public function getMethod();
/**
* @return array
*/
public function getHeaders();
/**
* @return array
*/
public function getBody();
/**
* @return string
*/
public function getEndpoint();
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment