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

Merge branch 'dev#105493_utilisation_dans_le_serveur_de_cache_websvc' into 'master'

Dev#105493 utilisation dans le serveur de cache websvc

See merge request !2
parents 071efbb8 52d99129
Pipeline #11348 passed with stage
in 41 seconds
*~
GPATH
GRTAGS
GTAGS
conf/config.php
conf/database.php
conf/http_client.php
conf/persistence.php
public/images
\ No newline at end of file
......@@ -3,4 +3,4 @@
url = https://git.afi-sa.net/afi/pellicule-dependency.git
[submodule "library/storm"]
path = library/storm
url = https://gitlab.com/patbator/storm.git
url = https://git.afi-sa.net/afi/storm-light.git
<?php
return ['base_path' => './images'];
\ No newline at end of file
create table if not exists `records` (
`id` int(11) unsigned not null auto_increment,
`ean` varchar(255),
`isbn` varchar(255),
`ark` varchar(255),
`created_at` datetime not null,
`updated_at` datetime not null,
primary key (`id`),
key `ean` (`ean`),
key `isbn` (`isbn`),
key `ark` (`ark`)
) engine=Aria default charset=utf8;
`id` int(11) unsigned not null auto_increment,
`ean` varchar(255) null,
`isbn` varchar(255) null,
`ark` varchar(255) null,
`created_at` datetime not null,
`updated_at` datetime not null,
primary key (`id`),
key `ean` (`ean`),
key `isbn` (`isbn`),
key `ark` (`ark`)
) engine=Aria default charset=utf8;
create table if not exists `media` (
`id` int(11) unsigned not null auto_increment,
`record_id` int(11) unsigned not null,
`type` varchar(255) not null,
`url` varchar(255) not null,
`provider` varchar(255) not null,
`created_at` datetime not null,
`updated_at` datetime not null,
primary key (`id`),
key `record_id` (`record_id`)
) engine=Aria default charset=utf8;
`id` int(11) unsigned not null auto_increment,
`record_id` int(11) unsigned not null,
`type` varchar(255) not null,
`url` varchar(255) not null,
`provider` varchar(255) not null,
`fullsize` varchar(255) null default '',
`created_at` datetime not null,
`updated_at` datetime not null,
primary key (`id`),
key `record_id` (`record_id`)
) engine=Aria default charset=utf8;
......@@ -5,8 +5,9 @@ use \Storm\Persistence\Configuration;
use \Storm\Model\Loader;
use \Storm\Persistence\SqlStrategy;
use \Pellicule\HttpClientAware;
use \Pellicule\FileSystem;
require __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../vendor/autoload.php';
$app = \Pellicule\AppFactory::create();
......@@ -20,6 +21,9 @@ $app->add(function($request, $handler)
$settings = require __DIR__ . '/../conf/http_client.php';
HttpClientAware::setHttpClientOptions($settings);
$settings = require __DIR__ . '/../conf/persistence.php';
FileSystem::setBasePath($settings['base_path']);
return $handler->handle($request);
});
......
<?php
namespace Pellicule;
use \Buzz\Client\Curl;
use \Slim\Psr7\Factory\RequestFactory;
use \Fig\Http\Message\StatusCodeInterface;
use \Storm\Collection;
class FileSystem {
use HttpClientAware;
use HttpClientAware, StormFileSystem;
protected static $_base_path = '/tmp/';
protected static $_base_path;
public static function setBasePath($path) {
static::$_base_path = $path;
}
public function getBasePath() {
return static::$_base_path;
}
public function fileExists($path) {
return \file_exists(static::$_base_path . $path);
return $this->getFileSystem()->fileExists($path);
}
public function fileExtension($response) {
return (new Collection($response->getHeader('content-type')))
->collect(function($value)
{
return '.' . $this->_getExtensionFromContentType($value);
})
->detect(function($value) { return '.' != $value; });
}
public function download($url, $path) {
protected function _getExtensionFromContentType($value) {
$elements = explode('/', $value);
return (isset($elements[1])
&& in_array($elements[1],
['jpg', 'bmp', 'gif', 'png', 'jpeg']))
? $elements[1]
: '';
}
public function download($media) {
$url = $media->getUrl();
$path = $media->getFullsize();
if (!$url || !$path)
return;
$directory = pathinfo(static::$_base_path . $path, PATHINFO_DIRNAME);
if (false === \file_exists($directory))
\mkdir($directory, 0755, true);
if (false === $this->fileExists($directory))
mkdir($directory, 0755, true);
// can throw exception on invalid url
$request = (new RequestFactory())->createRequest('GET', $url);
$request = $this->_injectProviderHeaders($request, $media);
$client = $this->newFileGetContents();
$response = $client->sendRequest($request);
if (false === ($local_file = fopen(static::$_base_path . $path , 'w')))
if (StatusCodeInterface::STATUS_OK != ($status = $response->getStatusCode())) {
echo $url . ' file was not download error: '. $status . ' '. $response->getBody();
return;
}
$extension = $this->fileExtension($response);
$this->getFileSystem()
->filePutContents(static::$_base_path . $path . $extension, $response->getBody());
$media->setFullsize($path . $extension);
}
$this->newHttpClient()
->sendRequest($request,
['curl' => ['CURLOPT_FILE', $local_file]]);
protected function _injectProviderHeaders($request, $media) {
foreach($media->getProviderHeaders() as $key => $value)
$request = $request->withHeader($key, $value);
fclose($local_file);
return $request;
}
}
......@@ -3,6 +3,7 @@ namespace Pellicule;
use \Buzz\Browser;
use \Buzz\Client\Curl;
use \Buzz\Client\FileGetContents;
use \Slim\Psr7\Factory\ResponseFactory;
use \Slim\Psr7\Factory\RequestFactory;
......@@ -17,9 +18,16 @@ trait HttpClientAware {
return static::$_default_http_client;
$client = new Curl(new ResponseFactory(), static::_httpClientOptions());
$browser = new Browser($client, new RequestFactory());
return new Browser($client, new RequestFactory());
}
static public function newFileGetContents() {
if (static::$_default_http_client)
return static::$_default_http_client;
return $browser;
return new FileGetContents(new ResponseFactory(), array_merge(static::_httpClientOptions(),
['allow_redirects' => true]));
}
......
......@@ -2,31 +2,36 @@
namespace Pellicule\Models;
use \Storm\Model\ModelAbstract;
use \Pellicule\StormFileSystem;
use \Pellicule\Traits\TimeSource;
class Media extends ModelAbstract {
use StormFileSystem, TimeSource;
const
SIZE_FULL = 'fullsize',
TYPE_COVER = 'cover',
TYPE_BACKCOVER = 'back_cover';
protected static $_file_system;
protected
$_table_name = 'media',
$_belongs_to = [ 'record' => ['model' => Record::class ] ];
$_belongs_to = [ 'record' => ['model' => Record::class ] ],
$_default_attribute_values = ['fullsize' => '',
'created_at' => '',
'updated_at' => ''],
$_provider_headers = [];
/** @category testing */
public static function setFileSystem($filesystem) {
static::$_file_system = $filesystem;
public function getProviderHeaders() {
return $this->_provider_headers;
}
public static function getFileSystem() {
return isset(static::$_file_system)
? static::$_file_system
: new \Pellicule\FileSystem();
public function setProviderHeaders($headers) {
$this->_provider_headers = $headers;
return $this;
}
......@@ -34,22 +39,42 @@ class Media extends ModelAbstract {
return array_intersect_key($this->getRawAttributes(),
['type' => 1,
'url' => 1,
'fullsize' => 1,
'provider' => 1,
'created_at' => 1,
'updated_at' => 1]);
}
public function afterSave() {
public function beforeSave() {
$datenow = $this->getCurrentDateTime();
if (!$this->getCreatedAt())
$this->setCreatedAt($datenow);
$this->setUpdatedAt($datenow);
if (!$this->fileExists())
$this->getFileSystem()->download($this->getUrl(), $this->getFilePath());
$this->getPelliculeFileSystem()->download($this);
}
public function setFullsizeFromIdentifier($identifier) {
return $this->setFullsize($this->_getFilePathFromIdentifier($identifier));
}
public function _getFilePathFromIdentifier($identifier = '') {
return '/' . implode('/',
array_merge(['images',
$this->getSize(),
$this->getType()],
$this->splitId($identifier)))
. '/'. $identifier;
}
public function fileExists() {
return $this->isNew()
? false
: $this->getFileSystem()->fileExists($this->getFilePath());
return $this->getPelliculeFileSystem()->fileExists($this->getFullsize());
}
......@@ -66,36 +91,14 @@ class Media extends ModelAbstract {
}
public function getFilePath() {
return $this->getLocation() . $this->getLocalFileName();
}
public function splitId() {
return array_slice(str_split(sprintf('%04d', $this->getId())),
public function splitId($identifier = '') {
return array_slice(str_split(sprintf('%04d',
($identifier ? $identifier : $this->getId()))),
0, 4);
}
protected function getLocation() {
return $this->isNew()
? ''
: '/' . implode('/', array_merge([$this->getSize(),
$this->getType()],
$this->splitId())) . '/';
}
protected function getLocalFileName() {
return $this->isNew()
? ''
: $this->getId() . '.' . $this->getExtension();
}
protected function getExtension() {
return $this->hasUrl()
? pathinfo($this->getUrl(), PATHINFO_EXTENSION)
: '';
protected function getLocation($identifier = '') {
return $this->_getFilePathFromIdentifier($identifier);
}
}
......@@ -2,9 +2,12 @@
namespace Pellicule\Models;
use \Storm\Model\ModelAbstract;
use \Pellicule\Traits\TimeSource;
class Record extends ModelAbstract {
use TimeSource;
protected
$_table_name = 'records',
......@@ -13,7 +16,17 @@ class Record extends ModelAbstract {
'model' => Media::class,
'role' => 'record'
]
];
],
$_default_attribute_values = ['created_at' => '',
'updated_at' => ''];
public function beforeSave(){
$datenow = $this->getCurrentDateTime();
if (!$this->getCreatedAt())
$this->setCreatedAt($datenow);
$this->setUpdatedAt($datenow);
}
public function asJSON() {
......
<?php
namespace Pellicule\Providers;
use \Buzz\Browser;
use \Buzz\Client\FileGetContents;
use \Slim\Psr7\Factory\ResponseFactory;
use \Slim\Psr7\Factory\RequestFactory;
use \Slim\Psr7\Request;
use \Pellicule\Models\Record;
use \Fig\Http\Message\StatusCodeInterface;
......@@ -19,8 +14,8 @@ class Electre extends Provider {
$_client_secret;
public function __construct($data = []){
if (!array_key_exists('client_id',$data))
public function __construct($data = []) {
if (!array_key_exists('client_id', $data))
return $this;
$this->_client_id = $data['client_id'];
......@@ -29,6 +24,7 @@ class Electre extends Provider {
return $this;
$this->_client_secret = $data['client_secret'];
return $this;
}
......@@ -63,11 +59,11 @@ class Electre extends Provider {
return (new FetchRecordResult())->beError(StatusCodeInterface::STATUS_NOT_ACCEPTABLE,
'no_expected_attribute_in_data');
$token = $json_response->access_token;
$this->setHeaders(['Authorization' => 'Bearer '. $token]);
try{
$response = $this->_getAllLinks($token, $this->_getIsbnOrEan($this->_search_args));
$response = $this->_getAllLinks($this->_getIsbnOrEan($this->_search_args));
} catch(\Exception $exception) {
return (new FetchRecordResult())->beError(StatusCodeInterface::STATUS_GATEWAY_TIMEOUT,
'no_answer_from_gateway');
......@@ -78,7 +74,7 @@ class Electre extends Provider {
'ean_not_found');
$json_response = $this->_parseJsonResponse($response);
if (! $json_response)
if (!$json_response)
return (new FetchRecordResult())->beError(StatusCodeInterface::STATUS_UNPROCESSABLE_ENTITY,
'cannot_parse_response');
if (!isset($json_response->_links))
......@@ -87,22 +83,22 @@ class Electre extends Provider {
$links = new \Storm\Model\Collection($json_response->_links);
$medialist =
$links->select(
function($element){
return in_array($element->rel, ['cover','coverCopy']);
return in_array($element->rel, ['cover']);
})
->collect(function($link)
{
return (new \Pellicule\Models\Media())
->setProvider($this->providerName())
->setProviderHeaders($this->getHeaders())
->setUrl($link->href)
->setType( (($link->rel == 'coverCopy')
? 'back_cover'
: $link->rel));
->setType($link->rel)
->setFullsizeFromIdentifier($this->_getIsbnOrEan($this->_search_args));
});
$record = (new Record())
->updateAttributes($this->_search_args)
->setMedia($medialist);
......@@ -111,31 +107,20 @@ class Electre extends Provider {
}
protected function _getIsbnOrEan($search_args) {
if (isset($search_args['ean']))
return $search_args['ean'];
return $search_args['isbn'];
}
protected function _getToken($login, $secret){
protected function _getToken($id, $secret) {
$browser = static::newHttpClient();
$response = $browser->post(static::TOKEN_END_POINT . '/connect/token',
[],
http_build_query(['client_id' => $login,
'client_secret' =>$secret,
'grant_type'=> 'client_credentials',
'scope'=>'webapi']));
http_build_query(['client_id' => $id,
'client_secret' => $secret,
'grant_type' => 'client_credentials',
'scope' => 'webapi']));
return $response;
}
public function _getAllLinks($token, $ean) {
$browser = static::newHttpClient();
$response = $browser->get(static::API_END_POINT . '/v1.0/eans/' . $ean,
['Authorization'=>'Bearer '. $token]);
return $response;
public function _getAllLinks($ean) {
return static::newHttpClient()->get(static::API_END_POINT . '/v1.0/eans/' . $ean,
$this->getHeaders());
}
}
<?php
namespace Pellicule\Providers;
use \Buzz\Browser;
use \Buzz\Client\FileGetContents;
use \Slim\Psr7\Factory\ResponseFactory;
use \Slim\Psr7\Factory\RequestFactory;
use \Slim\Psr7\Request;
use \Pellicule\Models\Record;
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';
protected
$_username,
$_secret;
public function __construct($data = []){
public function __construct($data = []) {
if (!array_key_exists('username',$data))
return $this;
......@@ -67,12 +62,15 @@ class Orb extends Provider {
$images = $json_content['data'][0]['images'];
$medialist =[];
$imagetype_translate = ['front'=>'cover','back'=>'back_cover','additional'=>'extract'];
foreach (array_keys($images) as $image_type){
$medialist[] = (new \Pellicule\Models\Media())
$imagetype_translate = ['front' => 'cover',
'back '=> 'back_cover',
'additional' => 'extract'];
foreach (array_keys($images) as $image_type) {
$medialist[] = (new \Pellicule\Models\Media())
->setProvider($this->providerName())
->setType($imagetype_translate[$image_type])
->setUrl($images[$image_type]['original']['src']);
->setUrl($images[$image_type]['original']['src'])
->setFullsizeFromIdentifier($this->_getIsbnOrEan($this->_search_args));
}
$record = (new Record())
......
......@@ -4,14 +4,16 @@ namespace Pellicule\Providers;
use \Pellicule\HttpClientAware;
use \Pellicule\Models\Record;
use \Pellicule\Providers\FetchRecordResult;
use Fig\Http\Message\StatusCodeInterface;
use \Fig\Http\Message\StatusCodeInterface;
use \Storm\Collection;
abstract class Provider {
use HttpClientAware;
protected $_search_args = [];
protected $_search_args = [],
$_headers = [];
public static function newProvider($request, $args) {
return static::_realNewProvider($request, $args)
......@@ -30,7 +32,7 @@ abstract class Provider {
if (!$request->hasHeader('Authorization'))
return new Error(StatusCodeInterface::STATUS_NOT_FOUND, 'record not found');
$credentials = array_filter((new \Storm\Collection($request->getHeader('Authorization')))
$credentials = array_filter((new Collection($request->getHeader('Authorization')))
->select(function($header)
{
return ($header && 'Pellicule ' == substr($header, 0, 10));
......@@ -47,10 +49,10 @@ abstract class Provider {
$first_credentials = array_shift($credentials);
if ($first_credentials['provider'] == 'electre')
return new Electre($credentials);
return new Electre($first_credentials);
if ($first_credentials['provider'] == 'orb')
return new Orb($credentials);
return new Orb($first_credentials);
return new Error(StatusCodeInterface::STATUS_BAD_REQUEST,
'no valid params provided');
......@@ -64,10 +66,9 @@ abstract class Provider {
protected function _getIsbnOrEan($search_args) {
if (isset($search_args['ean']))
return $search_args['ean'];
return $search_args['isbn'];
return (isset($search_args['ean']))
? $search_args['ean']
: $search_args['isbn'];
}
......@@ -77,10 +78,22 @@ abstract class Provider {
public function setSearchArgs($args) {
$this->_search_args = $args;
if (isset($args['isbn'])|| isset($args['ean']))
$this->_search_args = $args;
return $this;
}
public function setHeaders($value) {
$this->_headers = $value;
}