Commit ac35f6a5 authored by Henri-Damien LAURENT's avatar Henri-Damien LAURENT
Browse files

refactoring

parent ba7e79a4
Pipeline #10303 failed with stage
in 17 minutes and 51 seconds
......@@ -4,7 +4,8 @@ test:php7_latest:
- apt update -yqq && apt install git zip -yqq
- git submodule init
- git submodule update
- ./vendor/bin/phpunit -c tests/phpunit.xml
- php --version
- ./vendor/bin/phpunit -c tests/phpunit.xml --testdox
except:
- tags
tags:
......
<?php
namespace Pellicule\Controllers;
use Slim\Routing\RouteContext;
use Slim\Psr7\Request;
use Slim\Psr7\Response;
use Fig\Http\Message\StatusCodeInterface;
use Pellicule\Models\Record;
use Pellicule\Providers\Provider;
......@@ -11,8 +9,15 @@ use Pellicule\Providers\FetchRecordResult;
class Media {
protected
$_request,
$_args = [];
public function __invoke($request, $response, $args) {
$fetch_result = $this->_fetchRecord($request, $args);
$this->_request = $request;
$this->_args = $this->_processArgs($args);
$fetch_result = $this->_fetchRecord();
if ($fetch_result->hasError()) {
$response->getBody()->write(json_encode(['error' => $fetch_result->getErrorCode()]));
......@@ -23,52 +28,56 @@ class Media {
$record = $fetch_result->getRecord();
$record->save();
$response->getBody()->write($record->asJSON());
return $response->withHeader('Content-Type', 'application/json');
}
protected function _fetchRecord($request, $args) {
$findargs = $this->_processParams($args);
if ($record = Record::findFirstBy($findargs))
protected function _fetchRecord() {
if (!$this->_args)
return (new FetchRecordResult())->beError(StatusCodeInterface::STATUS_BAD_REQUEST, 'no valid params provided');
if ($record = Record::findFirstBy($this->_args))
return (new FetchRecordResult())->beSuccess($record);
if (!$request->hasHeader('Authorization'))
if (!$this->_request->hasHeader('Authorization'))
return (new FetchRecordResult())->beError(StatusCodeInterface::STATUS_NOT_FOUND, 'record not found');
return $this->_fetchRecordFromProviders($request, $findargs);
return $this->_fetchRecordFromProviders();
}
protected function _fetchRecordFromProviders($request, $args){
$credentials = array_filter($this->_parseAuthorizationHeader($request));
if (!$credentials)
return (new FetchRecordResult())->beError(StatusCodeInterface::STATUS_PROXY_AUTHENTICATION_REQUIRED, 'no_valid_authentication_information_provided');
protected function _fetchRecordFromProviders() {
if (!$credentials = array_filter($this->_parseAuthorizationHeader()))
return (new FetchRecordResult())
->beError(StatusCodeInterface::STATUS_PROXY_AUTHENTICATION_REQUIRED,
'no_valid_authentication_information_provided');
$provider = Provider::newProvider($credentials[0]);
return $provider->fetchRecord($this->_processParams($args));
return $provider->fetchRecord($this->_args);
}
protected function _parseAuthorizationHeader($request){
return (new \Storm\Model\Collection($request->getHeader('Authorization')))
protected function _parseAuthorizationHeader() {
return (new \Storm\Collection($this->_request->getHeader('Authorization')))
->select(function($header)
{
return ($header && (strpos('Pellicule ', $header) == 0));
return ($header && 'Pellicule ' == substr($header, 0, 10));
})
->collect([$this, 'decodeAuthorizationHeader'])->getArrayCopy();
->collect([$this, 'decodeAuthorizationHeader'])
->getArrayCopy();
}
public function decodeAuthorizationHeader($string){
public function decodeAuthorizationHeader($string) {
return json_decode(base64_decode(str_replace('Pellicule ', '', $string)), true);
}
protected function _processParams($args){
protected function _processArgs($args) {
if (!$args)
return;
return [];
if (isset($args['isbn']))
return ['isbn' => $args['isbn']];
......
......@@ -38,11 +38,11 @@ class Electre extends Provider {
public function fetchRecord($search_request) {
try{
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');
}
......
<?php
namespace Pellicule\Tests;
class BrowserMockBuilder {
protected $_mock;
public function __construct() {
$stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
$this->_mock = \Storm\Test\ObjectWrapper::mock('from ' . $stack[0]['file'] . ':' . $stack[0]['line']);
}
public function expectHttpQuery($request, $response) {
$request = array_merge(['type' => 'get', 'url' => '', 'headers' => [], 'body' => []],
$request);
$with_params = [];
$with_params[] = $request['url'];
$sith_params[] = $request['headers'];
if ($request['type'] == 'post')
$with_params[] = http_build_query($request['body']);
$this
->_mock
->whenCalled($request['type'])
->with($with_params)
->answers($this->_forgePSR7Response($response));
return $this;
}
public function expectPost($post, $response) {
$post = array_merge(['url' => '', 'headers' => [], 'body' => []],
$post);
$this
->_mock
->whenCalled('post')
->with($post['url'],
$post['headers'],
http_build_query($post['body']))
->answers($this->_forgePSR7Response($response));
return $this;
}
public function expectGet($get, $response) {
$get = array_merge(['url' => '', 'headers' => []],
$get);
$this
->_mock
->whenCalled('get')
->with($get['url'],
$get['headers'])
->answers($this->_forgePSR7Response($response));
return $this;
}
public function getMock() {
return $this->_mock;
}
protected function _forgePSR7Response($response) {
$response = array_merge(['status' => 200, 'headers' => null, 'content' => ''],
$response);
return new \Slim\Psr7\Response($response['status'],
$response['headers'],
(new \Slim\Psr7\Factory\StreamFactory())->createStream($response['content']));
}
}
......@@ -7,46 +7,36 @@ use Pellicule\Models\Record;
use Pellicule\Models\Media;
use Pellicule\Providers\Electre;
use Pellicule\Providers\Orb;
use Pellicule\FileSystem;
use \org\bovigo\vfs\vfsStream;
class FileSystemTest extends TestCase {
protected $_root;
public function setUp(){
$this->_root = vfsStream::setup('myDir');
FileSystem::setBasePath(vfsStream::url('myDir'));
$mock_browser = (new BrowserMockBuilder())
->expectGet(['url' => 'http://image.org/city.jpg',
],
['content' => file_get_contents(realpath(dirname(__FILE__)) . '/' .'imagefullsize.jpg')])
->getMock()
->beStrict();
$app = AppFactory::create();
Record::newInstance(['id' => 1,
'isbn' => '2259228194',
'ean' => '9782259228190',
'ark' => 'https://catalogue.bnf.fr/ark:/12148/cb445155569'])
->assertSave();
Media::newInstance(['id'=> 135,
'record_id'=> 1,
'type' => 'cover',
'url' => 'http://image.org/city.jpg',
'provider' => 'Me',
'created_at' => '2020-01-13 08:00:00',
'updated_at' => '2020-01-16 08:00:00'
])
->assertSave();
parent::setUp();
$this->_http_client
->whenCalled('get')
->answers($this->_forgePSR7Response(['content' => file_get_contents(realpath(dirname(__FILE__)) . '/' .'imagefullsize.jpg')]));
$this->fixture(Record::class,
['id' => 1,
'isbn' => '2259228194',
'ean' => '9782259228190',
'ark' => 'https://catalogue.bnf.fr/ark:/12148/cb445155569']);
$this->fixture(Media::class,
['id'=> 135,
'record_id'=> 1,
'type' => 'cover',
'url' => 'http://image.org/city.jpg',
'provider' => 'Me',
'created_at' => '2020-01-13 08:00:00',
'updated_at' => '2020-01-16 08:00:00'
]);
}
/** @test */
public function fullsizeCoverDirectoryShouldBeCreated() {
$this->assertTrue($this->_root->hasChild('fullsize/cover/0/1'));
......@@ -57,11 +47,4 @@ class FileSystemTest extends TestCase {
public function fullsizeCoverFileShouldBeCreated() {
$this->assertTrue($this->_root->hasChild('fullsize/cover/0/1/3/5/135.jpg'));
}
//TBD
/** @test */
// public function thumbnailCoverDirectoryShouldBeCreated() {
// $this->assertTrue($this->_root->hasChild('thumbnail/cover/0/1/3/5/135.jpg'));
//}
}
......@@ -2,81 +2,62 @@
namespace Pellicule\Tests;
use \Pellicule\AppFactory;
use \Pellicule\Models\Record;
use \Pellicule\Models\Media;
use \Pellicule\FileSystem;
use \Pellicule\Providers\Electre;
use \Pellicule\Providers\Orb;
use \Pellicule\Providers\Provider;
use \org\bovigo\vfs\vfsStream;
class MediaGetTest extends TestCase {
class MediaLocalRecordTest extends TestCase {
protected
$_response,
$_root;
public function setUp(){
$this->_root = vfsStream::setup('myDir');
FileSystem::setBasePath(vfsStream::url('myDir'));
$app = AppFactory::create();
Record::newInstance(['id' => 1,
'isbn' => '2259228194',
'ean' => '9782259228190',
'ark' => 'https://catalogue.bnf.fr/ark:/12148/cb445155569'])
->assertSave();
Media::newInstance(['id'=> 135,
'record_id'=> 1,
'type' => 'cover',
'url' => 'http://image.org/city.jpg',
'provider' => 'Me',
'created_at' => '2020-01-13 08:00:00',
'updated_at' => '2020-01-16 08:00:00'
])
->assertSave();
Media::newInstance(['id'=> 147,
'record_id'=>1,
'type' => 'back_cover',
'url' => 'http://image.org/cityback.jpg',
'provider' => 'Me',
'created_at' => '2020-01-21 08:00:00',
'updated_at' => '2020-02-14 08:00:00'
])
->assertSave();
Record::newInstance(['id'=> 4,
'isbn' => '9782072843334',
'ean' => '2070380513',
'ark' => 'https://catalogue.bnf.fr/ark:/12148/cb35097231f'])
->assertSave();
Media::newInstance(['id'=> 155,
'record_id'=>4,
'type' => 'cover',
'url' => 'http://image.org/ARDTP-ALODJFEF.jpg',
'provider' => 'Me',
'created_at' => '1980-01-13 08:00:00',
'updated_at' => '1980-01-16 08:00:00'
])
->assertSave();
}
public function urlsToTest(){
return [['/1.0/media/isbn/2259228194']
,['/1.0/media/ark:/12148/cb445155569']
,['/1.0/media/ean/9782259228190']];
}
/** @test
* @dataProvider urlsToTest
parent::setUp();
$this->fixture(Record::class,
['id' => 1,
'isbn' => '9782259228190',
'ean' => '9782259228190',
'ark' => 'https://catalogue.bnf.fr/ark:/12148/cb445155569']);
$this->fixture(Media::class,
['id'=> 135,
'record_id'=> 1,
'type' => 'cover',
'url' => 'http://image.org/city.jpg',
'provider' => 'Me',
'created_at' => '2020-01-13 08:00:00',
'updated_at' => '2020-01-16 08:00:00'
]);
$this->fixture(Media::class,
['id'=> 147,
'record_id'=>1,
'type' => 'back_cover',
'url' => 'http://image.org/cityback.jpg',
'provider' => 'Me',
'created_at' => '2020-01-21 08:00:00',
'updated_at' => '2020-02-14 08:00:00'
]);
}
public function possibleRecord1Urls() {
return [['/1.0/media/isbn/9782259228190'],
['/1.0/media/ark:/12148/cb445155569'],
['/1.0/media/ean/9782259228190']];
}
/**
* @test
* @dataProvider possibleRecord1Urls
*/
public function whenCalledWithISBNAndISBNFoundResponseShouldContainsMedia($url) {
public function whenCalledWithPossibleRecord1UrlsResponseShouldContainsMedia($url) {
$expected = [ 'media'=>
[
[ 'url' => 'http://image.org/city.jpg',
......@@ -87,25 +68,26 @@ class MediaGetTest extends TestCase {
]
];
$this->assertEquals($expected,
json_decode($this->httpGet($url),TRUE));
}
public function urlsFailuresToTest(){
return [['/1.0/media/isbn/2413004084', 404]
,['/1.0/media/UNEAUTREREQUETE', 501]];
/**
* @test
*/
public function whenCalledWithunknownIsbnResponseCodeShouldBe404() {
$this->assertEquals(404,
$this->httpStatusCode('/1.0/media/isbn/2413004084'));
}
/**
* @test
* @dataProvider urlsFailuresToTest
*/
public function whenCalledWithErroneousDataResponseCodeShouldBeCorrect($url, $status) {
$this->assertEquals($status,
$this->httpHeader($url));
public function whenCalledWithBadParametersResponseCodeShouldBeNotImplemented() {
$this->assertEquals(501,
$this->httpStatusCode('/1.0/media/UNEAUTREREQUETE'));
}
}
......@@ -114,18 +96,18 @@ class MediaGetTest extends TestCase {
class MediaWithErrorOnAuthenticationTest extends TestCase {
/** @test */
public function withAuthorizationHeaderEmptyResponseStatusShouldBe407() {
public function withAuthorizationHeaderEmptyResponseStatusShouldBeProxyAuthRequired() {
$this->assertEquals(407,
$this->dispatch('/1.0/media/isbn/2259228180',
['Authorization' => ""])->getStatusCode());
$this->httpStatusCode('/1.0/media/isbn/2259228180',
['Authorization' => '']));
}
/** @test */
public function withAuthorizationHeaderEmptyWithPelliculeEmptyResponseStatusShouldBe407() {
public function withPelliculeEmptyHeaderResponseStatusShouldBeProxyAuthRequired() {
$this->assertEquals(407,
$this->dispatch('/1.0/media/isbn/2259228180',
['Authorization' => "Pellicule "])->getStatusCode());
$this->httpStatusCode('/1.0/media/isbn/2259228180',
['Authorization' => "Pellicule "]));
}
}
......@@ -133,27 +115,16 @@ class MediaWithErrorOnAuthenticationTest extends TestCase {
abstract class MediaWithElectreTestCase extends TestCase {
protected $_mock_browser;
public function setUp(){
parent::setUp();
$this->_mock_browser = (new BrowserMockBuilder())
->expectPost(['url' => 'https://electre3staging-idp.bvdep.com/connect/token',
'body' => ['client_id' => 'multipass',
'client_secret' => 'leeloo',
'grant_type'=> 'client_credentials',
'scope'=>'webapi']],
['content' => '{"access_token":"MyPr3ttyT0k3n","expires_in":3600,"token_type":"Bearer"}'])
->expectGet(['url' => 'https://electre3staging-api.bvdep.com/v1.0/eans/2259228194',
'headers' => ['Authorization' => 'Bearer MyPr3ttyT0k3n']],
['content' => '{"_links":[{"rel":"cover","href":"http://image.org/city.jpg"},{"rel":"coverCopy","href":"http://image.org/cityback.jpg"},{"rel":"summary","href":"https://electre3staging-api.bvdep.com/v1.0/eans/2259228194/summary"},{"rel":"biographie","href":"https://electre3staging-api.bvdep.com/v1.0/eans/2259228194/biography"}]}'])
->getMock();
Electre::setDefaultHttpClient($this->_mock_browser);
}
$this->_http_client
->whenCalled('post')
->answers($this->_forgePSR7Response(['content' => '{"access_token":"MyPr3ttyT0k3n","expires_in":3600,"token_type":"Bearer"}']))
->whenCalled('get')
->answers($this->_forgePSR7Response(['content' => '{"_links":[{"rel":"cover","href":"http://image.org/city.jpg"},{"rel":"coverCopy","href":"http://image.org/cityback.jpg"},{"rel":"summary","href":"https://electre3staging-api.bvdep.com/v1.0/eans/2259228194/summary"},{"rel":"biographie","href":"https://electre3staging-api.bvdep.com/v1.0/eans/2259228194/biography"}]}']));
}
/** @test */
......@@ -166,9 +137,9 @@ abstract class MediaWithElectreTestCase extends TestCase {
'type' => 'back_cover']
]
];
$this->assertEquals($return_data,
json_decode($this->_response,TRUE)
);
json_decode($this->_response, true));
}
}
......@@ -178,12 +149,14 @@ abstract class MediaWithElectreTestCase extends TestCase {
class MediaWithElectreOnIsbnTest extends MediaWithElectreTestCase {
public function setUp() {
parent::setUp();
$this->_response = $this->httpGet('/1.0/media/isbn/2259228194',
['Authorization' => ['Pellicule ' . base64_encode(json_encode(['provider'=> 'electre',
'client_secret'=>'leeloo',
'client_id'=>'multipass']))]]);
}
$payload = base64_encode(json_encode(['provider'=> 'electre',
'client_secret' => 'leeloo',
'client_id' => 'multipass']));
$headers = ['Authorization' => ['Pellicule ' . $payload]];
$this->_response = $this->httpGet('/1.0/media/isbn/2259228194', $headers);
}
/** @test */
......@@ -218,21 +191,12 @@ class MediaWithElectreWithSearchError extends TestCase {
public function setUp(){
parent::setUp();
$mock_browser = (new BrowserMockBuilder())
->expectPost(['url' => 'https://electre3staging-idp.bvdep.com/connect/token',
'body' => ['client_id' => 'multipass',
'client_secret' => 'leeloo',
'grant_type'=> 'client_credentials',
'scope'=>'webapi']],
['content' => '{"access_token":"MyPr3ttyT0k3n","expires_in":3600,"token_type":"Bearer"}'])
->expectGet(['url' => 'https://electre3staging-api.bvdep.com/v1.0/eans/123456789',
'headers' => ['Authorization' => 'Bearer MyPr3ttyT0k3n']],
['status'=>'404'])
->getMock();
$this->_http_client
->whenCalled('post')
->answers($this->_forgePSR7Response(['content' => '{"access_token":"MyPr3ttyT0k3n","expires_in":3600,"token_type":"Bearer"}']))
->whenCalled('get')
->answers($this->_forgePSR7Response(['status' => '404']));
Electre::setDefaultHttpClient($mock_browser);
$this->_response = $this->dispatch('/1.0/media/ean/123456789',
['Authorization' => ['Pellicule ' . base64_encode(json_encode(['provider'=> 'electre',
'client_secret'=>'leeloo',
......@@ -263,18 +227,11 @@ class MediaWithElectreWithInvalidCredentialsTest extends TestCase {
public function setUp(){
parent::setUp();
$mock_browser = (new BrowserMockBuilder())
->expectPost(['url' => 'https://electre3staging-idp.bvdep.com/connect/token',
'body' => ['client_id' => 'multipass',
'client_secret' => '1314',
'grant_type'=> 'client_credentials',
'scope'=>'webapi']],
['status'=> 400,
'content' => '{"error":"invalid_client"}'])
$this->_http_client
->whenCalled('post')
->answers($this->_forgePSR7Response(['status'=> '400', 'content' => '{"error": "invalid_client"}']));
->getMock();
Electre::setDefaultHttpClient($mock_browser);
$this->_response = $this->dispatch('/1.0/media/ean/123456789',
['Authorization' => ['Pellicule ' . base64_encode(json_encode(['provider'=> 'electre',
'client_secret'=>'1314',
......@@ -298,26 +255,12 @@ class MediaWithElectreWithInvalidCredentialsTest extends TestCase {
class MediaWithElectreWithServiceInErrorTest extends TestCase {
protected $_mock_browser;
public function setUp() {
parent::setUp();
$this->_mock_browser = new BrowserMockBuilder();
Electre::setDefaultHttpClient($this->_mock_browser->getMock());
}
/** @test */
public function withError500OnElectreResponseShouldHaveStatus502() {
$this->_mock_browser
->expectPost(['url' => 'https://electre3staging-idp.bvdep.com/connect/token',
'body' => ['client_id' => 'multipass',
'client_secret' => '1314',
'grant_type'=> 'client_credentials',
'scope'=>'webapi']],
['status'=> 500,
'content' => '']);
$this->_http_client
->whenCalled('post')
->answers($this->_forgePSR7Response(['status'=>'500']));
$response = $this->dispatch('/1.0/media/ean/123456789',
['Authorization' => ['Pellicule ' . base64_encode(json_encode(['provider'=> 'electre',
......@@ -342,23 +285,12 @@ class MediaWithElectreWithServiceInErrorTest extends TestCase {
class MediaWithElectreWithServiceInTimeOutTest extends TestCase {
protected $_mock_browser;