Commit 9c340ba4 authored by Patrick Barroca's avatar Patrick Barroca 😁
Browse files

add electre rest api v2

parent 00eb138b
Pipeline #11847 passed with stage
in 43 seconds
Subproject commit 5ba247b497b6d52c021f218dea128eee6986856b
Subproject commit eecbf3db87fe62321104801f4b81850caf7ade58
......@@ -8,7 +8,6 @@ use \Storm\Collection;
class FileSystem {
use HttpClientAware, StormFileSystem;
protected static $_base_path = '/tmp/';
......@@ -27,6 +26,11 @@ class FileSystem {
}
public function mkdir($path, $mode=0777, $recursive=false, $context=null) {
return $this->getFileSystem()->mkdir($path, $mode, $recursive, $context);
}
public function fileExtension($response) {
return (new Collection($response->getHeader('content-type')))
->collect(function($value)
......@@ -54,24 +58,44 @@ class FileSystem {
if (!$url || !$path)
return;
$directory = pathinfo(static::$_base_path . $path, PATHINFO_DIRNAME);
if (false === $this->fileExists($directory))
mkdir($directory, 0755, true);
$request = (new RequestFactory())->createRequest('GET', $url);
$request = $this->_injectProviderHeaders($request, $media);
$client = $this->newFileGetContents();
$response = $client->sendRequest($request);
if (StatusCodeInterface::STATUS_OK != ($status = $response->getStatusCode())) {
echo $url . ' file was not download error: '. $status . ' '. $response->getBody();
if (StatusCodeInterface::STATUS_OK != ($status = $response->getStatusCode()))
return;
}
$extension = $this->fileExtension($response);
$this->writeMediaFrom($media, $response);
}
public function writeMediaFrom($media, $response) {
if (!$path = $media->getFullsize())
return false;
$response->getBody()->rewind();
$this->_ensureDirectory($path);
$path .= $this->fileExtension($response);
$media->setFullsize($path);
$this->_writeResponseTo($response, $path);
return true;
}
protected function _writeResponseTo($response, $path) {
$this->getFileSystem()
->filePutContents(static::$_base_path . $path . $extension, $response->getBody());
$media->setFullsize($path . $extension);
->filePutContents(static::$_base_path . $path, $response->getBody());
}
protected function _ensureDirectory($path) {
$directory = pathinfo(static::$_base_path . $path, PATHINFO_DIRNAME);
if (false === $this->fileExists($directory))
$this->mkdir($directory, 0755, true);
}
......
......@@ -52,6 +52,11 @@ class Media extends ModelAbstract {
}
public function beCover() {
return $this->setType(static::TYPE_COVER);
}
public function beforeSave() {
$datenow = $this->getCurrentDateTime();
......
......@@ -7,7 +7,8 @@ use \Fig\Http\Message\StatusCodeInterface;
class Electre extends Provider {
const
TOKEN_END_POINT = 'https://electre3staging-idp.bvdep.com',
API_END_POINT = 'https://electre3staging-api.bvdep.com';
API_END_POINT = 'https://electre3staging-api.bvdep.com',
ID = 'electre';
protected
$_client_id,
......@@ -16,15 +17,14 @@ class Electre extends Provider {
public function __construct($data = []) {
if (!array_key_exists('client_id', $data))
return $this;
return;
$this->_client_id = $data['client_id'];
if (!array_key_exists('client_secret',$data))
return $this;
if (!array_key_exists('client_secret', $data))
return;
$this->_client_secret = $data['client_secret'];
return $this;
}
......@@ -37,49 +37,49 @@ class Electre extends Provider {
try {
$response = $this->_getToken($this->_client_id, $this->_client_secret);
} catch(\Exception $exception) {
return (new FetchRecordResult())->beError(StatusCodeInterface::STATUS_GATEWAY_TIMEOUT,
'no_answer_from_gateway');
return $this->_newError(StatusCodeInterface::STATUS_GATEWAY_TIMEOUT,
'no_answer_from_gateway');
}
if ($response->getStatusCode() == StatusCodeInterface::STATUS_BAD_REQUEST)
return (new FetchRecordResult())->beError(StatusCodeInterface::STATUS_UNAUTHORIZED,
'invalid_credentials');
return $this->_newError(StatusCodeInterface::STATUS_UNAUTHORIZED,
'invalid_credentials');
if ($response->getStatusCode() != StatusCodeInterface::STATUS_OK)
return (new FetchRecordResult())->beError(StatusCodeInterface::STATUS_BAD_GATEWAY,
'provider_error');
return $this->_newError(StatusCodeInterface::STATUS_BAD_GATEWAY,
'provider_error');
$json_response = $this->_parseJsonResponse($response);
if (! $json_response)
return (new FetchRecordResult())->beError(StatusCodeInterface::STATUS_UNPROCESSABLE_ENTITY,
'cannot_parse_response');
return $this->_newError(StatusCodeInterface::STATUS_UNPROCESSABLE_ENTITY,
'cannot_parse_response');
if (!isset($json_response->access_token))
return (new FetchRecordResult())->beError(StatusCodeInterface::STATUS_NOT_ACCEPTABLE,
'no_expected_attribute_in_data');
return $this->_newError(StatusCodeInterface::STATUS_NOT_ACCEPTABLE,
'no_expected_attribute_in_data');
$token = $json_response->access_token;
$this->setHeaders(['Authorization' => 'Bearer '. $token]);
try{
$response = $this->_getAllLinks($this->_getIsbnOrEan($this->_search_args));
$response = $this->_getAllLinks($this->_getIsbnOrEan());
} catch(\Exception $exception) {
return (new FetchRecordResult())->beError(StatusCodeInterface::STATUS_GATEWAY_TIMEOUT,
'no_answer_from_gateway');
return $this->_newError(StatusCodeInterface::STATUS_GATEWAY_TIMEOUT,
'no_answer_from_gateway');
}
if ($response->getStatusCode() != StatusCodeInterface::STATUS_OK)
return (new FetchRecordResult())->beError(StatusCodeInterface::STATUS_NOT_FOUND,
'ean_not_found');
return $this->_newError(StatusCodeInterface::STATUS_NOT_FOUND,
'ean_not_found');
$json_response = $this->_parseJsonResponse($response);
if (!$json_response)
return (new FetchRecordResult())->beError(StatusCodeInterface::STATUS_UNPROCESSABLE_ENTITY,
'cannot_parse_response');
return $this->_newError(StatusCodeInterface::STATUS_UNPROCESSABLE_ENTITY,
'cannot_parse_response');
if (!isset($json_response->_links))
return (new FetchRecordResult())->beError(StatusCodeInterface::STATUS_NOT_ACCEPTABLE,
'no_expected_attribute_in_data');
return $this->_newError(StatusCodeInterface::STATUS_NOT_ACCEPTABLE,
'no_expected_attribute_in_data');
$links = new \Storm\Model\Collection($json_response->_links);
......@@ -91,11 +91,11 @@ class Electre extends Provider {
->collect(function($link)
{
return (new \Pellicule\Models\Media())
->setProvider($this->providerName())
->setProviderHeaders($this->getHeaders())
->setUrl($link->href)
->setType($link->rel)
->setFullsizeFromIdentifier($this->_getIsbnOrEan($this->_search_args));
->setProvider($this->providerName())
->setProviderHeaders($this->getHeaders())
->setUrl($link->href)
->setType($link->rel)
->setFullsizeFromIdentifier($this->_getIsbnOrEan());
});
......
<?php
namespace Pellicule\Providers;
use \Fig\Http\Message\StatusCodeInterface;
use \Pellicule\Traits\TimeSource;
use \Pellicule\Models\Record;
use \Pellicule\Models\Media;
use \Pellicule\FileSystem;
class ElectreRest2 extends Provider {
use TimeSource;
const
ID = 'electre_rest_2',
API_URL = 'http://restapi.electre.com/Service/couverture/{ean}.jpg',
ID_KEY = 'client_id',
SECRET_KEY = 'client_secret';
protected $_id, $_secret;
public function __construct($credentials) {
$this->_id = isset($credentials[static::ID_KEY])
? $credentials[static::ID_KEY]
: '';
$this->_secret = isset($credentials[static::SECRET_KEY])
? $credentials[static::SECRET_KEY]
: '';
}
public function fetchRecord() {
if (!$this->_id || !$this->_secret)
return $this->_newError(StatusCodeInterface::STATUS_UNAUTHORIZED, 'invalid_credentials');
$identifier = $this->_getIsbnOrEan();
$base_url = str_replace('{ean}', $identifier, static::API_URL);
$params = ['oauth_consumer_key' => $this->_id,
'oauth_nonce' => $this->_nonce(),
'oauth_signature_method' => 'HMAC-SHA1',
'oauth_timestamp' => static::getTimeSource()->time(),
'oauth_version' => '1.0'];
$params['oauth_signature'] = $this->_sign($base_url, $params);
$url = $base_url . '?' . http_build_query($params);
try {
$response = static::newHttpClient()->get($url);
} catch(\Exception $e) {
return $this->_newError(StatusCodeInterface::STATUS_GATEWAY_TIMEOUT,
'no_answer_from_gateway');
}
if ($response->getStatusCode() != StatusCodeInterface::STATUS_OK
|| (!$body = $response->getBody())
|| 0 == $body->getSize())
return $this->_newError(StatusCodeInterface::STATUS_BAD_GATEWAY,
'provider_error');
$media = (new Media())
->setProvider($this->providerName())
->setUrl($base_url)
->beCover()
->setFullsizeFromIdentifier($identifier);
if (!(new FileSystem)->writeMediaFrom($media, $response))
return $this->_newError(StatusCodeInterface::STATUS_INTERNAL_SERVER_ERROR,
'error_writing_cover');
$record = (new Record())
->updateAttributes($this->_search_args)
->setMedia([$media]);
return (new FetchRecordResult())->beSuccess($record);
}
protected function _nonce() {
return bin2hex(random_bytes(8));
}
protected function _sign($url, $params) {
$base_string = 'GET&' . rawurlencode($url) . '&' . rawurlencode(http_build_query($params));
$key = rawurlencode($this->_secret) . '&';
return base64_encode(hash_hmac('sha1', $base_string, $key, true));
}
}
......@@ -6,7 +6,9 @@ use \Fig\Http\Message\StatusCodeInterface;
class Orb extends Provider {
const END_POINT = 'https://api.base-orb.fr';
const
END_POINT = 'https://api.base-orb.fr',
ID = 'orb';
protected
$_username,
......@@ -32,7 +34,7 @@ class Orb extends Provider {
public function fetchRecord() {
$browser = static::newHttpClient();
$ean = $this->_getIsbnOrEan($this->_search_args);
$ean = $this->_getIsbnOrEan();
try{
$response = $browser->get( static::END_POINT . '/v1/products?sort=ean_asc&eans=' . $ean,
......@@ -40,24 +42,24 @@ class Orb extends Provider {
'Accept-Encoding' => 'deflate, gzip'
]);
} catch(\Exception $exception) {
return (new FetchRecordResult())->beError(StatusCodeInterface::STATUS_GATEWAY_TIMEOUT,
'no_answer_from_gateway');
return $this->_newError(StatusCodeInterface::STATUS_GATEWAY_TIMEOUT,
'no_answer_from_gateway');
}
if ($response->getStatusCode() != StatusCodeInterface::STATUS_OK)
return (new FetchRecordResult())->beError($response->getStatusCode(),
$response->getReasonPhrase());
return $this->_newError($response->getStatusCode(),
$response->getReasonPhrase());
$response->getBody()->rewind();
$json_content = json_decode(zlib_decode($response->getBody()->getContents()), TRUE);
if (! $json_content)
return (new FetchRecordResult())->beError(StatusCodeInterface::STATUS_UNPROCESSABLE_ENTITY,
'cannot_parse_response');
return $this->_newError(StatusCodeInterface::STATUS_UNPROCESSABLE_ENTITY,
'cannot_parse_response');
if (!isset($json_content['data']))
return (new FetchRecordResult())->beError(StatusCodeInterface::STATUS_NOT_ACCEPTABLE,
'no_expected_attribute_in_data');
return $this->_newError(StatusCodeInterface::STATUS_NOT_ACCEPTABLE,
'no_expected_attribute_in_data');
$images = $json_content['data'][0]['images'];
......@@ -70,8 +72,8 @@ class Orb extends Provider {
->setProvider($this->providerName())
->setType($imagetype_translate[$image_type])
->setUrl($images[$image_type]['original']['src'])
->setFullsizeFromIdentifier($this->_getIsbnOrEan($this->_search_args));
}
->setFullsizeFromIdentifier($this->_getIsbnOrEan());
}
$record = (new Record())
->updateAttributes($this->_search_args)
......
......@@ -11,7 +11,8 @@ use \Fig\Http\Message\StatusCodeInterface;
abstract class Provider {
use HttpClientAware;
protected $_search_args = [],
protected
$_search_args = [],
$_headers = [];
public static function newProvider($request, $args) {
......@@ -46,15 +47,10 @@ abstract class Provider {
return new Error(StatusCodeInterface::STATUS_PROXY_AUTHENTICATION_REQUIRED,
'no_valid_authentication_information_provided');
$first_credentials = array_shift($credentials);
if ($first_credentials['provider'] == 'electre')
return new Electre($first_credentials);
if ($first_credentials['provider'] == 'orb')
return new Orb($first_credentials);
return new Error(StatusCodeInterface::STATUS_BAD_REQUEST,
'no valid params provided');
return ($provider = (new RemoteProviders)->providerFor(array_shift($credentials)))
? $provider
: new Error(StatusCodeInterface::STATUS_BAD_REQUEST,
'no valid params provided');
}
......@@ -64,10 +60,12 @@ abstract class Provider {
}
protected function _getIsbnOrEan($search_args) {
return (isset($search_args['ean']))
? $search_args['ean']
: $search_args['isbn'];
protected function _getIsbnOrEan() {
if (isset($this->_search_args['ean']))
return $this->_search_args['ean'];
if (isset($this->_search_args['isbn']))
return $this->_search_args['isbn'];
}
......@@ -94,5 +92,17 @@ abstract class Provider {
}
/**
* @param int $status StatusCodeInterface::STATUS_* constant
* @param string $code
*
* @return FetchRecordResult
*/
public function _newError($status, $code) {
return (new FetchRecordResult())->beError($status, $code);
}
/** @return FetchRecordResult */
abstract public function fetchRecord();
}
\ No newline at end of file
<?php
namespace Pellicule\Providers;
use \Storm\Collection;
class RemoteProviders {
const PROVIDER_KEY = 'provider';
protected $_providers;
public function __construct() {
$this->_providers = new Collection([ElectreRest2::class,
Electre::class,
Orb::class]);
}
public function providerFor($credentials) {
if (!isset($credentials[static::PROVIDER_KEY]))
return;
$provider = $this->_providers
->detect(function($provider) use($credentials)
{
return $provider::ID == $credentials[static::PROVIDER_KEY];
});
return $provider
? new $provider($credentials)
: null;
}
}
\ No newline at end of file
<?php
namespace Pellicule\Tests;
use \Pellicule\FileSystem;
use \Pellicule\Models\Media;
use \Pellicule\Providers\ElectreRest2;
use \Fig\Http\Message\StatusCodeInterface;
use \Slim\Psr7\Headers;
class MediaElectreRest2Test extends TestCase {
public function setUp() {
parent::setUp();
Media::getFileSystem()->whenCalled('fileExists')->answers(true);
FileSystem::getFileSystem()
->whenCalled('fileExists')->answers(true)
->whenCalled('filePutContents')->answers(null)
;
$this->_http_client
->whenCalled('get')
->answers($this->_forgePSR7Response(['status' => 200,
'headers' => new Headers(['Content-Type' => 'image/jpg']),
'content' => '1']));
$provider = base64_encode(json_encode(['provider'=> 'electre_rest_2',
'client_id' => 'leeloo',
'client_secret' => 'multipass']));
$this->dispatch('/1.0/media/isbn/9782818976173',
['Authorization' => ['Pellicule ' . $provider]]);
}
public function tearDown() {
parent::tearDown();
}
/** @test */
public function fileShouldBeWriten() {
$call_params = FileSystem::getFileSystem()
->getAttributesForLastCallOn('filePutContents');
$this->assertEquals('vfs://myDir/images/fullsize/cover/9/7/8/2/9782818976173.jpg',
$call_params[0]);
}
/** @test */
public function responseShouldBeSuccess() {
$this->assertResponseCode(StatusCodeInterface::STATUS_OK);
}
public function urlParts() {
return [
['http://restapi.electre.com/Service/couverture/9782818976173.jpg?'],
['oauth_consumer_key=leeloo'],
];
}
/**
* @test
* @dataProvider urlParts
*/
public function electreUrlShouldBeCalledWithPart($part) {
$this->assertContains($part,
$this->_http_client->getAttributesForLastCallOn('get')[0]);
}
}
......@@ -19,7 +19,8 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase {
protected
$_root,
$_http_client;
$_http_client,
$_response;
public function setUp() {
parent::setUp();
......@@ -28,6 +29,7 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase {
$this->_root = vfsStream::setup('myDir');
FileSystem::setBasePath(vfsStream::url('myDir'));
FileSystem::setFileSystem($this->mock());
$this->_http_client = $this->mock();
Provider::setDefaultHttpClient($this->_http_client);
......@@ -40,7 +42,9 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase {
public function tearDown() {
Provider::setDefaultHttpClient(null);
FileSystem::setDefaultHttpClient(null);
FileSystem::setFileSystem(null);
Media::setFileSystem(null);
Media::setTimeSource(null);
ModelAbstract::unsetLoaders();
parent::tearDown();
}
......@@ -67,20 +71,25 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase {
[]
);
$response = $app->handle($request);
$this->_response = $response = $app->handle($request);
$response->getBody()->rewind();
return $response;
}
public function httpGet($url, $headers = []) {
$response = $this->dispatch($url, $headers);
$this->_response = $response = $this->dispatch($url, $headers);
return $response->getBody()->getContents();
}
public function httpStatusCode($url, $headers = []){
$response = $this->dispatch($url, $headers);
$this->_response = $response = $this->dispatch($url, $headers);
return $response->getStatusCode();
}
public function assertResponseCode($code, $message='') {
$this->assertEquals($code, $this->_response->getStatusCode(), $message);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment