Commit 02cee256 authored by Karamel's avatar Karamel
Browse files

Update image routes, add error responses, add tests.

parent c9817dec
<?php
use \Pasteque\Server\Exception\InvalidFieldException;
use \Pasteque\Server\Exception\RecordNotFoundException;
use \Pasteque\Server\Model\Image;
use \Pasteque\Server\System\API\APICaller;
use \Pasteque\Server\System\API\APIResult;
......@@ -35,13 +37,13 @@ $app->GET('/api/image/{model}/default',
$app->GET('/api/image/{model}/{id}',
function($request, $response, $args) {
$ptApp = $this->get('settings')['ptApp'];
$imgCall = APICaller::run($ptApp, 'image', 'get',
[['model' => $args['model'], 'modelId' =>$args['id']]]);
$key = ['model' => $args['model'], 'modelId' => $args['id']];
$imgCall = APICaller::run($ptApp, 'image', 'get', [$key]);
if ($imgCall->getStatus() == APIResult::STATUS_CALL_OK) {
$img = $imgCall->getContent();
if ($img === null) {
$response->withStatus(404, "Image not found");
return $response;
$e = new RecordNotFoundException(Image::class, $key);
return $response->notFound($e, "Image not found");
}
$mime = $img->getMimeType();
$data = $img->getImage();
......@@ -66,13 +68,12 @@ $app->DELETE('/api/image/{model}/{id}',
$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');
$key = ['model' => $args['model'], 'modelId' => $args['id']];
$image = Image::load($key, $ptApp->getDao());
if ($image !== null) {
$e = new InvalidFieldException(InvalidFieldException::CSTR_UNIQUE,
Image::class, 'modelId', $key, $key['modelId']);
return $response->reject($e);
}
$data = $request->getParsedBody();
$image = new Image();
......
......@@ -22,6 +22,7 @@
namespace Pasteque\Server\API;
use \Pasteque\Server\Exception\InvalidFieldException;
use \Pasteque\Server\Model\Category;
use \Pasteque\Server\Model\Customer;
use \Pasteque\Server\Model\Image;
......@@ -49,7 +50,9 @@ class ImageAPI extends APIHelper implements API
$model = null;
$class = Image::getModelClass($modelName);
if ($class === null) {
throw new \BadMethodCallException(sprintf('Unknown model %s', $modelName));
throw new InvalidFieldException(InvalidFieldException::CSTR_ENUM,
static::MODEL_NAME, 'model',
['model' => $modelName, 'modelId' => $id], $modelName);
}
switch ($modelName) {
case Image::MODEL_PAYMENTMODE_VALUE:
......@@ -60,7 +63,11 @@ class ImageAPI extends APIHelper implements API
$realId['paymentMode'] = $this->dao->read(PaymentMode::class,
$pmId);
if ($realId['paymentMode'] === null) {
throw new \BadMethodCallException(sprintf('%s:%d not found', PaymentMode::class, $pmId));
throw new InvalidFieldException(
InvalidFieldException::CSTR_ASSOCIATION_NOT_FOUND,
static::MODEL_NAME, 'modelId',
['model' => $modelName, 'modelId' => $id],
$id);
}
$model = $this->dao->read(PaymentModeValue::class, $realId);
break;
......@@ -69,7 +76,11 @@ class ImageAPI extends APIHelper implements API
}
if ($model === null && $hasImage == true) {
// If the model is not found but the image was removed, it's not a problem
throw new \BadMethodCallException(sprintf('%s:%d not found', $modelName, $id));
throw new InvalidFieldException(
InvalidFieldException::CSTR_ASSOCIATION_NOT_FOUND,
static::MODEL_NAME, 'modelId',
['model' => $modelName, 'modelId' => $id],
$id);
}
if ($model !== null) {
$model->setHasImage($hasImage);
......@@ -77,6 +88,38 @@ class ImageAPI extends APIHelper implements API
}
}
/**
* Get a single entry from it's ID.
* @throws InvalidFieldException When the model is not set or is invalid.
*/
public function get($id) {
$model = null;
$modelId = null;
if (is_array($id)) {
if (array_key_exists('model', $id)) {
$model = $id['model'];
}
if (array_key_exists('modelId', $id)) {
$modelId = $id['modelId'];
}
}
$checkModel = Image::getModelClass($model);
if ($checkModel == null) {
throw new InvalidFieldException(InvalidFieldException::CSTR_ENUM,
static::MODEL_NAME, 'model',
['model' => $model, 'modelId' => $modelId], $model);
}
return parent::get($id);
}
/**
* Write images and update 'hasImage' in associated records.
* @param Image|Image[] The images to write.
* @return Image|Image[] The updated images.
* @throws InvalidFieldException with constraint ENUM when the model is not
* a valid one or with constraint ASSOCIATION_NOT_FOUND when the associated
* record was not found.
*/
public function write($data) {
$this->supportOrDie($data);
$arrayArgs = is_array($data);
......@@ -149,7 +192,9 @@ class ImageAPI extends APIHelper implements API
$img->setMimeType('image/png');
return $img;
default:
throw new \BadMethodCallException(sprintf('Unknown model %s', $model));
throw new InvalidFieldException(InvalidFieldException::CSTR_ENUM,
static::MODEL_NAME, 'model',
['model' => $model, 'modelId' => 'default'], $model);
}
}
......
......@@ -45,6 +45,8 @@ class InvalidFieldException extends \Exception implements PastequeException
* default anymore.
*/
const CSTR_DEFAULT_REQUIRED = 'DefaultRequired';
/** The field is an enum and the value is not within it. */
const CSTR_ENUM = 'Enum';
private $constraint;
private $class;
......
......@@ -73,9 +73,8 @@ class Image extends DoctrineMainModel
return [];
}
protected static function getReferenceKey() {
return 'id';
return ['model', 'modelId'];
}
/** Images don't have reference yet. This is the same as getId(). */
public function getReference() {
return $this->getId();
}
......
......@@ -21,8 +21,7 @@
namespace Pasteque\Server\System\API;
use \Pasteque\Server\Exception\APINotFoundException;
use \Pasteque\Server\Exception\InvalidFieldException;
use \Pasteque\Server\Exception\InvalidRecordException;
use \Pasteque\Server\Exception\PastequeException;
use \Pasteque\Server\System\Login;
/** Utility class to call API methods. */
......@@ -88,9 +87,7 @@ class APICaller {
$realArgs = $methodParser->buildArgsArray($args);
return APIResult::success($method->invokeArgs($api,
$realArgs));
} catch (InvalidRecordException $e) {
return APIResult::reject($e);
} catch (InvalidFieldException $e) {
} catch (PastequeException $e) {
return APIResult::reject($e);
} catch (\BadMethodCallException $e) {
return APIResult::reject($e->getMessage());
......
......@@ -22,6 +22,8 @@
namespace Pasteque\Server;
use \Pasteque\Server\API\ImageAPI;
use \Pasteque\Server\Exception\InvalidFieldException;
use \Pasteque\Server\Exception\RecordNotFoundException;
use \Pasteque\Server\Model\Category;
use \Pasteque\Server\Model\Customer;
use \Pasteque\Server\Model\Image;
......@@ -144,9 +146,18 @@ class ImageAPITest extends TestCase
}
public function testDefaultUnknownModel() {
$this->expectException(\BadMethodCallException::class);
$this->expectExceptionMessage('Unknown model cow');
$this->api->getDefault('cow');
$exceptionThrown = false;
try {
$this->api->getDefault('cow');
} catch (InvalidFieldException $e) {
$exceptionThrown = true;
$this->assertEquals('Enum', $e->getConstraint());
$this->assertEquals('model', $e->getField());
$this->assertEquals('cow', $e->getValue());
$this->assertEquals('cow', $e->getId()['model']);
$this->assertEquals('default', $e->getId()['modelId']);
}
$this->assertTrue($exceptionThrown);
}
private function sampleImage($model, $id) {
......@@ -212,17 +223,38 @@ class ImageAPITest extends TestCase
}
public function testSetImageWrongModel() {
$this->expectException(\BadMethodCallException::class);
$this->expectExceptionMessage('Unknown model cow');
$img = $this->sampleImage('cow', $this->cat->getId());
$this->api->write($img);
$exceptionThrown = false;
try {
$this->api->write($img);
} catch (InvalidFieldException $e) {
$exceptionThrown = true;
$this->assertEquals('Enum', $e->getConstraint());
$this->assertEquals('model', $e->getField());
$this->assertEquals('cow', $e->getValue());
$this->assertEquals('cow', $e->getId()['model']);
$this->assertEquals($this->cat->getId(), $e->getId()['modelId']);
}
$this->assertTrue($exceptionThrown);
}
public function testSetImageInexistingModel() {
$this->expectException(\BadMethodCallException::class);
$this->expectExceptionMessage('category:0 not found');
$img = $this->sampleImage(Image::MODEL_CATEGORY, 0);
$this->api->write($img);
$exceptionThrown = false;
try {
$this->api->write($img);
} catch (InvalidFieldException $e) {
$exceptionThrown = true;
$this->assertEquals(
InvalidFieldException::CSTR_ASSOCIATION_NOT_FOUND,
$e->getConstraint());
$this->assertEquals(Image::class, $e->getClass());
$this->assertEquals('modelId', $e->getField());
$this->assertEquals(Image::MODEL_CATEGORY, $e->getId()['model']);
$this->assertEquals(0, $e->getId()['modelId']);
$this->assertEquals(0, $e->getValue());
}
$this->assertTrue($exceptionThrown);
}
public function testSetNotAnImage() {
......@@ -285,10 +317,19 @@ class ImageAPITest extends TestCase
}
public function testDeleteImageWrongModel() {
$this->expectException(\BadMethodCallException::class);
$this->expectExceptionMessage('Unknown model cow');
$img = $this->sampleImage('cow', $this->cat->getId());
$this->api->delete($img->getId());
$exceptionThrown = false;
try {
$this->api->delete($img->getId());
} catch (InvalidFieldException $e) {
$exceptionThrown = true;
$this->assertEquals('Enum', $e->getConstraint());
$this->assertEquals('model', $e->getField());
$this->assertEquals('cow', $e->getValue());
$this->assertEquals('cow', $e->getId()['model']);
$this->assertEquals($this->cat->getId(), $e->getId()['modelId']);
}
$this->assertTrue($exceptionThrown);
}
public function testDeleteImageInexistingModel() {
......
......@@ -73,22 +73,80 @@ class HttpImageTest extends TestCase
$this->dao->close();
}
public function testSet() {
private function wrongModelCheck($method, $id = null) {
curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $method);
if ($id == null) {
curl_setopt($this->curl, CURLOPT_URL,
apiUrl('api/image/cat/default'));
} else {
curl_setopt($this->curl, CURLOPT_URL,
apiUrl(sprintf('api/image/cat/%d', $id)));
}
if ($method == 'PUT' || $method == 'PATCH') {
curl_setopt($this->curl, CURLOPT_POSTFIELDS, static::$imgData);
}
$resp = curl_exec($this->curl);
$this->assertEquals(400, curl_getinfo($this->curl, CURLINFO_HTTP_CODE));
$jsResp = json_decode($resp, true);
$this->assertNotEquals(false, $jsResp);
$this->assertEquals('InvalidField', $jsResp['error']);
$this->assertEquals('Enum', $jsResp['constraint']);
$this->assertEquals(Image::class, $jsResp['class']);
$this->assertEquals('model', $jsResp['field']);
$this->assertEquals('cat', $jsResp['key']['model']);
if ($id === null) {
$this->assertEquals('default', $jsResp['key']['modelId']);
} else {
$this->assertEquals($id, $jsResp['key']['modelId']);
}
$this->assertEquals('cat', $jsResp['value']);
}
public function testGetDefaultWrongModel() {
$this->wrongModelCheck('GET');
}
public function testGetNoImage() {
curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, 'GET');
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(404, curl_getinfo($this->curl, CURLINFO_HTTP_CODE));
$jsResp = json_decode($resp, true);
$this->assertNotEquals(false, $jsResp);
$this->assertEquals('RecordNotFound', $jsResp['error']);
$this->assertEquals(Image::class, $jsResp['class']);
$this->assertEquals(Image::MODEL_CATEGORY, $jsResp['key']['model']);
$this->assertEquals($this->cat->getId(), $jsResp['key']['modelId']);
}
public function testGetWrongModel() {
$this->wrongModelCheck('GET', $this->cat->getId());
}
public function testPutOk() {
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());
$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()]);
['model' => Image::MODEL_CATEGORY,
'modelId' => $this->cat->getId()]);
$this->assertNotNull($imgSnapshot);
}
/** @depends testSet */
public function testSetExistent() {
public function testPutWrongModel() {
$this->wrongModelCheck('PUT', $this->cat->getId());
}
public function testPutExisting() {
$img = new Image();
$img->setModel(Image::MODEL_CATEGORY);
$img->setModelId($this->cat->getId());
......@@ -102,6 +160,41 @@ class HttpImageTest extends TestCase
curl_setopt($this->curl, CURLOPT_POSTFIELDS, static::$imgData);
$resp = curl_exec($this->curl);
$this->assertEquals(400, curl_getinfo($this->curl, CURLINFO_HTTP_CODE));
$jsResp = json_decode($resp, true);
$this->assertNotEquals(false, $jsResp);
$this->assertEquals('InvalidField', $jsResp['error']);
$this->assertEquals('UniqueValue', $jsResp['constraint']);
$this->assertEquals(Image::class, $jsResp['class']);
$this->assertEquals('modelId', $jsResp['field']);
$this->assertEquals($this->cat->getId(), $jsResp['value']);
$this->assertEquals(Image::MODEL_CATEGORY, $jsResp['key']['model']);
$this->assertEquals($this->cat->getId(), $jsResp['key']['modelId']);
}
public function testPutNotFound() {
$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() + 1)));
curl_setopt($this->curl, CURLOPT_POSTFIELDS, static::$imgData);
$resp = curl_exec($this->curl);
$this->assertEquals(400, curl_getinfo($this->curl, CURLINFO_HTTP_CODE));
$jsResp = json_decode($resp, true);
$this->assertNotEquals(false, $jsResp);
$this->assertEquals('InvalidField', $jsResp['error']);
$this->assertEquals('AssociationNotFound', $jsResp['constraint']);
$this->assertEquals(Image::class, $jsResp['class']);
$this->assertEquals('modelId', $jsResp['field']);
$this->assertEquals($this->cat->getId() + 1, $jsResp['value']);
$this->assertEquals(Image::MODEL_CATEGORY, $jsResp['key']['model']);
$this->assertEquals($this->cat->getId() + 1, $jsResp['key']['modelId']);
}
public function testDelete() {
......@@ -123,4 +216,17 @@ class HttpImageTest extends TestCase
$cat = $this->dao->readSnapshot(Category::class, $this->cat->getId());
$this->assertEquals(false, $cat->hasImage());
}
public function testDeleteNotFound() {
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(0, $resp);
}
public function testDeleteWrongModel() {
$this->wrongModelCheck('DELETE', $this->cat->getId());
}
}
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