Commit c789e8f7 authored by Karamel's avatar Karamel
Browse files

Add image routes and add tests. Ignore hasImage in fromStruct.

hasImage is ignored in fromStruct to be always read from database,
because it can only be updated with imageAPI.
parent 19d7aaf7
<?php
use \Pasteque\Server\Model\Image;
use \Pasteque\Server\System\API\APICaller;
use \Pasteque\Server\System\API\APIResult;
......@@ -47,3 +48,49 @@ $app->GET('/api/image/{model}/{id}', function($request, $response, $args) {
return $response->withAPIResult($imgCall);
}
});
$app->DELETE('/api/image/{model}/{id}', function($request, $response, $args) {
$ptApp = $this->get('settings')['ptApp'];
$imgCall = APICaller::run($ptApp, 'image', 'delete',
[['model' => $args['model'], 'modelId' => $args['id']]]);
return $response->withAPIResult($imgCall);
});
$app->PUT('/api/image/{model}/{id}', function($request, $response, $args) {
$ptApp = $this->get('settings')['ptApp'];
$existingImgReq = APICaller::run($ptApp, 'image', 'get',
[['model' => $args['model'], 'modelId' => $args['id']]]);
if ($existingImgReq->getStatus() != APIResult::STATUS_CALL_OK) {
return $response->withAPIResult($existingImgReq);
}
if ($existingImgReq->getContent() != null) {
return $response->withStatus(400, 'The image already exists');
}
$data = $request->getParsedBody();
$image = new Image();
$image->setModel($args['model']);
$image->setModelId($args['id']);
$image->setMimeType('unchecked');
$image->setImage($request->getBody()->getContents());
$imgCall = APICaller::run($ptApp, 'image', 'write', $image);
return $response->withAPIResult($imgCall);
});
$app->PATCH('/api/image/{model}/{id}', function($request, $response, $args) {
$ptApp = $this->get('settings')['ptApp'];
$existingImgReq = APICaller::run($ptApp, 'image', 'get',
[['model' => $args['model'], 'modelId' => $args['id']]]);
if ($existingImgReq->getStatus() != APIResult::STATUS_CALL_OK) {
return $response->withAPIResult($existingImgReq);
}
if ($existingImgReq->getContent() == null) {
return $response->withStatus(404, 'No image found.');
}
$data = $request->getParsedBody();
$image = $existingImgReq->getContent();
$image->setMimeType('unchecked');
$image->setImage($request->getBody()->getContents());
$imgCall = APICaller::run($ptApp, 'image', 'write', $image);
return $response->withAPIResult($imgCall);
});
......@@ -45,10 +45,14 @@ class ImageAPI extends APIHelper implements API
return new static($app->getThumbnailer(), $app->getDao());
}
protected function updateHasImage($class, $id, $hasImage) {
protected function updateHasImage($modelName, $id, $hasImage) {
$model = null;
switch ($class) {
case PaymentModeValue::class:
$class = Image::getModelClass($modelName);
if ($class === null) {
throw new \BadMethodCallException(sprintf('Unknown model %s', $modelName));
}
switch ($modelName) {
case Image::MODEL_PAYMENTMODE_VALUE:
// composite primary key,
// Doctrine requires to use the full model instead of just id.
$realId = json_decode($id, true);
......@@ -58,13 +62,13 @@ class ImageAPI extends APIHelper implements API
if ($realId['paymentMode'] === null) {
throw new \BadMethodCallException(sprintf('%s:%d not found', PaymentMode::class, $pmId));
}
$model = $this->dao->read($class, $realId);
$model = $this->dao->read(PaymentModeValue::class, $realId);
break;
default:
$model = $this->dao->read($class, $id);
$model = $this->dao->read(Image::getModelClass($modelName), $id);
}
if ($model === null) {
throw new \BadMethodCallException(sprintf('%s:%d not found', $class, $id));
throw new \BadMethodCallException(sprintf('%s:%d not found', $modelName, $id));
}
$model->setHasImage($hasImage);
$this->dao->write($model);
......@@ -76,42 +80,20 @@ class ImageAPI extends APIHelper implements API
foreach ($data as $d) {
if ($this->thumbnailer !== null) {
$thumb = $this->thumbnailer->thumbnail($d->getImage());
if ($thumb === false) {
if ($thumb !== false) {
// TODO: handle image thumbnail failure
continue;
$d->setMimeType($thumb->getMimeType());
$d->setImage($thumb->getImage());
}
$d->setMimeType($thumb->getMimeType());
$d->setImage($thumb->getImage());
}
// Set hasImage to true in associated model.
switch ($d->getModel()) {
case Image::MODEL_CATEGORY:
$this->updateHasImage(Category::class, $d->getModelId(), true);
break;
case Image::MODEL_PRODUCT:
$this->updateHasImage(Product::class, $d->getModelId(), true);
break;
case Image::MODEL_USER:
$this->updateHasImage(User::class, $d->getModelId(), true);
break;
case Image::MODEL_CUSTOMER:
$this->updateHasImage(Customer::class, $d->getModelId(), true);
break;
case Image::MODEL_PAYMENTMODE:
$this->updateHasImage(PaymentMode::class, $d->getModelId(), true);
break;
case Image::MODEL_PAYMENTMODE_VALUE:
$this->updateHasImage(PaymentModeValue::class, $d->getModelId(), true);
break;
default:
throw new \BadMethodCallException(sprintf('Unkown model %s', $d->getModel()));
}
$this->updateHasImage($d->getModel(), $d->getModelId(), true);
}
return parent::write($data);
}
public function delete($id) {
if (is_array($id) || empty($id[0])) {
if (!is_array($id) || empty($id[0])) {
$ids = array($id);
} else if (is_array($id)) {
$ids = $id;
......@@ -123,28 +105,7 @@ class ImageAPI extends APIHelper implements API
if (empty($d['model']) || empty($d['modelId'])) {
throw new \InvalidArgumentException('Incompatible id, expecting Image id');
}
switch ($d['model']) {
case Image::MODEL_CATEGORY:
$this->updateHasImage(Category::class, $d['modelId'], false);
break;
case Image::MODEL_PRODUCT:
$this->updateHasImage(Product::class, $d['modelId'], false);
break;
case Image::MODEL_USER:
$this->updateHasImage(User::class, $d['modelId'], false);
break;
case Image::MODEL_CUSTOMER:
$this->updateHasImage(Customer::class, $d['modelId'], false);
break;
case Image::MODEL_PAYMENTMODE:
$this->updateHasImage(PaymentMode::class, $d['modelId'], false);
break;
case Image::MODEL_PAYMENTMODE_VALUE:
$this->updateHasImage(PaymentModeValue::class, $d['modelId'], false);
break;
default:
throw new \BadMethodCallException(sprintf('Unkown model %s', $d['model']));
}
$this->updateHasImage($d['model'], $d['modelId'], false);
}
return parent::delete($ids);
}
......
......@@ -45,6 +45,25 @@ class Image extends DoctrineModel
const MODEL_PAYMENTMODE = 'paymentmode';
const MODEL_PAYMENTMODE_VALUE = 'paymentmodevalue';
public static function getModelClass($model) {
switch ($model) {
case Image::MODEL_CATEGORY:
return \Pasteque\Server\Model\Category::class;
case Image::MODEL_PRODUCT:
return \Pasteque\Server\Model\Product::class;
case Image::MODEL_USER:
return \Pasteque\Server\Model\User::class;
case Image::MODEL_CUSTOMER:
return \Pasteque\Server\Model\Customer::class;
case Image::MODEL_PAYMENTMODE:
return \Pasteque\Server\Model\PaymentMode::class;
case Image::MODEL_PAYMENTMODE_VALUE:
return \Pasteque\Server\Model\PaymentModeValue::class;
default:
return null;
}
}
protected function getDirectFieldNames() {
return ['model', 'modelId', 'mimeType', 'image'];
}
......
......@@ -119,7 +119,11 @@ abstract class DoctrineModel
/** Set the primitive typed fields from struct. */
protected function directFieldsFromStruct($struct, $dao) {
foreach ($this->getDirectFieldNames() as $field) {
if ($field == 'id') { continue; } // Doctrine loads it
if ($field == 'id' || $field == 'hasImage') {
// Doctrine loads the id
// and image cannot be alterated outside imageAPI
continue;
}
if (array_key_exists($field, $struct)) {
$value = $struct[$field];
call_user_func(array($this, 'set' . ucfirst($field)), $struct[$field]);
......@@ -157,7 +161,8 @@ abstract class DoctrineModel
}
}
/** Build a model linked to the DAO from raw data.
* @param $struct An associative array containing the data.
* @param $struct An associative array containing the data
* (hasImage is a special field and is ignored from the struct).
* @param $dao The DAO to link to.
* @param $embedded Only for recursive calls, don't set it.
* @throw \UnexpectedValueException If Id is set but not data is found.
......
......@@ -39,9 +39,6 @@ require_once(dirname(dirname(__FILE__)) . "/common_load.php");
class ImageAPITest extends TestCase
{
const RES_DIR = __DIR__ . '/../../res/images/';
/** Single white pixel png in base64 */
const BLANK = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAA'
. 'AADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC';
private $dao;
private $api;
private $tax;
......@@ -152,9 +149,9 @@ class ImageAPITest extends TestCase
$this->api->getDefault('cow');
}
private function blankImage($model, $id) {
private function sampleImage($model, $id) {
$img = new Image();
$img->setImage(base64_decode(static::BLANK));
$img->setImage(file_get_contents(__DIR__ . '/../res/image.png'));
$img->setMimeType('image/png');
$img->setModel($model);
$img->setModelId($id);
......@@ -163,62 +160,140 @@ class ImageAPITest extends TestCase
/** @depends testDefaultHasImageFalse */
public function testSetImage() {
$img = $this->blankImage(Image::MODEL_CATEGORY, $this->cat->getId());
// Set category image
$img = $this->sampleImage(Image::MODEL_CATEGORY, $this->cat->getId());
$this->api->write($img);
$snap = $this->dao->readSnapshot(Category::class, $this->cat->getId());
$this->assertTrue($snap->hasImage());
$img = $this->blankImage(Image::MODEL_PRODUCT, $this->prd->getId());
$snapImg = $this->dao->readSnapshot(Image::class,
['model' => Image::MODEL_CATEGORY,
'modelId' => $this->cat->getId()]);
$this->assertNotNull($snapImg);
// Set product image
$img = $this->sampleImage(Image::MODEL_PRODUCT, $this->prd->getId());
$this->api->write($img);
$snap = $this->dao->readSnapshot(Product::class, $this->prd->getId());
$this->assertTrue($snap->hasImage());
$img = $this->blankImage(Image::MODEL_USER, $this->user->getId());
$snapImg = $this->dao->readSnapshot(Image::class,
['model' => Image::MODEL_PRODUCT,
'modelId' => $this->prd->getId()]);
$this->assertNotNull($snapImg);
// Set user image
$img = $this->sampleImage(Image::MODEL_USER, $this->user->getId());
$this->api->write($img);
$snap = $this->dao->readSnapshot(User::class, $this->user->getId());
$this->assertTrue($snap->hasImage());
$img = $this->blankImage(Image::MODEL_PAYMENTMODE,
$snapImg = $this->dao->readSnapshot(Image::class,
['model' => Image::MODEL_USER,
'modelId' => $this->user->getId()]);
$this->assertNotNull($snapImg);
// Set payment mode image
$img = $this->sampleImage(Image::MODEL_PAYMENTMODE,
$this->paymentMode->getId());
$this->api->write($img);
$snap = $this->dao->readSnapshot(PaymentMode::class,
$this->paymentMode->getId());
$this->assertTrue($snap->hasImage());
$img = $this->blankImage(Image::MODEL_PAYMENTMODE_VALUE,
$snapImg = $this->dao->readSnapshot(Image::class,
['model' => Image::MODEL_PAYMENTMODE,
'modelId' => $this->paymentMode->getId()]);
$this->assertNotNull($snapImg);
// Set payment mode value image
$img = $this->sampleImage(Image::MODEL_PAYMENTMODE_VALUE,
$this->paymentMode->getValues()->get(0)->getId());
$this->api->write($img);
$snap = $this->dao->readSnapshot(PaymentModeValue::class,
$this->paymentMode->getValues()->get(0)->getId());
$this->assertTrue($snap->hasImage());
$snapImg = $this->dao->readSnapshot(Image::class,
['model' => Image::MODEL_PAYMENTMODE_VALUE,
'modelId' => json_encode($this->paymentMode->getValues()->get(0)->getId())]);
$this->assertNotNull($snapImg);
}
/** @expectedException BadMethodCallException
* @expectedExceptionMessage Unknown model cow */
public function testSetImageWrongModel() {
$img = $this->sampleImage('cow', $this->cat->getId());
$this->api->write($img);
}
/** @expectedException BadMethodCallException
* @expectedExceptionMessage category:0 not found */
public function testSetImageInexistingModel() {
$img = $this->sampleImage(Image::MODEL_CATEGORY, 0);
$this->api->write($img);
}
public function testSetNotAnImage() {
// Test thumbnailer failure
$this->markTestIncomplete();
}
/** @depends testSetImage */
public function testDeleteImage() {
$img = $this->blankImage(Image::MODEL_CATEGORY, $this->cat->getId());
// Set and delete category image
$img = $this->sampleImage(Image::MODEL_CATEGORY, $this->cat->getId());
$this->api->write($img);
$this->api->delete($img->getId());
$snap = $this->dao->readSnapshot(Category::class, $this->cat->getId());
$this->assertFalse($snap->hasImage());
$img = $this->blankImage(Image::MODEL_PRODUCT, $this->prd->getId());
$snapImg = $this->dao->readSnapshot(Image::class, ['model' => Image::MODEL_CATEGORY,
'modelId' => $this->cat->getId()]);
$this->assertNull($snapImg);
// Set and delete product image
$img = $this->sampleImage(Image::MODEL_PRODUCT, $this->prd->getId());
$this->api->write($img);
$this->api->delete($img->getId());
$snap = $this->dao->readSnapshot(Product::class, $this->prd->getId());
$this->assertFalse($snap->hasImage());
$img = $this->blankImage(Image::MODEL_USER, $this->user->getId());
$snapImg = $this->dao->readSnapshot(Image::class, ['model' => Image::MODEL_PRODUCT,
'modelId' => $this->prd->getId()]);
$this->assertNull($snapImg);
// Set and delete user image
$img = $this->sampleImage(Image::MODEL_USER, $this->user->getId());
$this->api->write($img);
$this->api->delete($img->getId());
$snap = $this->dao->readSnapshot(User::class, $this->user->getId());
$this->assertFalse($snap->hasImage());
$img = $this->blankImage(Image::MODEL_PAYMENTMODE,
$snapImg = $this->dao->readSnapshot(Image::class, ['model' => Image::MODEL_USER,
'modelId' => $this->user->getId()]);
$this->assertNull($snapImg);
// Set and delete payment mode image
$img = $this->sampleImage(Image::MODEL_PAYMENTMODE,
$this->paymentMode->getId());
$this->api->write($img);
$this->api->delete($img->getId());
$snap = $this->dao->readSnapshot(PaymentMode::class,
$this->paymentMode->getId());
$this->assertFalse($snap->hasImage());
$img = $this->blankImage(Image::MODEL_PAYMENTMODE_VALUE,
$snapImg = $this->dao->readSnapshot(Image::class, ['model' => Image::MODEL_PAYMENTMODE,
'modelId' => $this->paymentMode->getId()]);
$this->assertNull($snapImg);
// Set and delete payment mode value image
$img = $this->sampleImage(Image::MODEL_PAYMENTMODE_VALUE,
$this->paymentMode->getValues()->get(0)->getId());
$this->api->write($img);
$this->api->delete($img->getId());
$snap = $this->dao->readSnapshot(PaymentModeValue::class,
$this->paymentMode->getValues()->get(0)->getId());
$this->assertFalse($snap->hasImage());
$snapImg = $this->dao->readSnapshot(Image::class, ['model' => Image::MODEL_PAYMENTMODE_VALUE,
'modelId' => json_encode($this->paymentMode->getValues()->get(0)->getId())]);
$this->assertNull($snapImg);
}
/** @expectedException BadMethodCallException
* @expectedExceptionMessage Unknown model cow */
public function testDeleteImageWrongModel() {
$img = $this->sampleImage('cow', $this->cat->getId());
$this->api->delete($img->getId());
}
/** @expectedException BadMethodCallException
* @expectedExceptionMessage category:-1 not found */
public function testDeleteImageInexistingModel() {
$img = $this->sampleImage(Image::MODEL_CATEGORY, -1);
$this->api->delete($img->getId());
}
}
<?php
// Pasteque server testing
//
// Copyright (C)
// 2012 Scil (http://scil.coop)
// 2017 Karamel, Association Pastèque (karamel@creativekara.fr, https://pasteque.org)
//
// This file is part of Pasteque.
//
// Pasteque is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pasteque is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Pasteque. If not, see <http://www.gnu.org/licenses/>.
namespace Pasteque\Server;
use \Pasteque\Server\API\ImageAPI;
use \Pasteque\Server\Model\Category;
use \Pasteque\Server\Model\Image;
use \Pasteque\Server\System\Login;
use \Pasteque\Server\System\DAO\DAOFactory;
use \PHPUnit\Framework\TestCase;
require_once(dirname(dirname(__FILE__)) . "/common_load.php");
class HttpImageTest extends TestCase
{
const IMG_PATH = __DIR__ . '/../res/image.png';
private $curl;
private static $token;
private static $imgData;
private $dao;
private $cat;
public static function setUpBeforeClass() {
static::$token = obtainToken();
static::$imgData = file_get_contents(static::IMG_PATH);
}
public static function tearDownAfterClass() {
}
protected function setUp() {
$this->curl = curl_init();
curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($this->curl, CURLOPT_HTTPHEADER,
[Login::TOKEN_HEADER . ': ' . static::$token]);
global $dbInfo;
$this->dao = DAOFactory::getDAO($dbInfo, ['debug' => true]);
$this->cat = new Category();
$this->cat->setReference('Category');
$this->cat->setLabel('Category');
$this->dao->write($this->cat);
$this->dao->commit();
}
protected function tearDown() {
curl_close($this->curl);
foreach ([Image::class, Category::class] as $class) {
$all = $this->dao->search($class);
foreach($all as $record) {
$this->dao->delete($record);
}
}
$this->dao->commit();
$this->dao->close();
}
public function testSet() {
curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($this->curl, CURLOPT_URL,
apiUrl(sprintf('api/image/category/%d', $this->cat->getId())));
curl_setopt($this->curl, CURLOPT_POSTFIELDS, static::$imgData);
$resp = curl_exec($this->curl);
$this->assertEquals(200, curl_getinfo($this->curl, CURLINFO_HTTP_CODE));
$catSnapshot = $this->dao->readSnapshot(Category::class, $this->cat->getId());
$this->assertTrue($catSnapshot->hasImage());
$imgSnapshot = $this->dao->readSnapshot(Image::class,
['model' => Image::MODEL_CATEGORY, 'modelId' => $this->cat->getId()]);
$this->assertNotNull($imgSnapshot);
}
/** @depends testSet */
public function testSetExistent() {
$img = new Image();
$img->setModel(Image::MODEL_CATEGORY);
$img->setModelId($this->cat->getId());
$img->setImage(static::$imgData);
$img->setMimeType('image/png');
$imgApi = new ImageAPI(null, $this->dao);
$imgApi->write($img);
curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($this->curl, CURLOPT_URL,
apiUrl(sprintf('api/image/category/%d', $this->cat->getId())));
curl_setopt($this->curl, CURLOPT_POSTFIELDS, static::$imgData);
$resp = curl_exec($this->curl);
$this->assertEquals(400, curl_getinfo($this->curl, CURLINFO_HTTP_CODE));
}
public function testDelete() {
$img = new Image();
$img->setModel(Image::MODEL_CATEGORY);
$img->setModelId($this->cat->getId());
$img->setImage(static::$imgData);
$img->setMimeType('image/png');
$imgApi = new ImageAPI(null, $this->dao);
$imgApi->write($img);
curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($this->curl, CURLOPT_URL,
apiUrl(sprintf('api/image/category/%d', $this->cat->getId())));
$resp = curl_exec($this->curl);
$this->assertEquals(200, curl_getinfo($this->curl, CURLINFO_HTTP_CODE));
$this->assertEquals(1, $resp);
$this->assertEquals(null, $this->dao->readSnapshot(Image::class,
['model' => Image::MODEL_CATEGORY, 'modelId' => $this->cat->getId()]));
$cat = $this->dao->readSnapshot(Category::class, $this->cat->getId());
$this->assertEquals(false, $cat->hasImage());
}
}
......@@ -35,6 +35,7 @@
<file>http/HttpSyncTest.php</file>
<file>http/HttpTicketTest.php</file>
<file>http/HttpCustomerTest.php</file>
<file>http/HttpImageTest.php</file>
</testsuite>
</testsuites>
</phpunit>
Supports Markdown
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