PHRAS-29 #fix Add creation story in API

This commit is contained in:
Nicolas Le Goff
2015-02-16 18:10:31 +01:00
parent b6f6fe3217
commit 938a83b967
9 changed files with 322 additions and 2 deletions

View File

@@ -21,6 +21,7 @@ use Alchemy\Phrasea\Core\Event\ApiLoadEndEvent;
use Alchemy\Phrasea\Core\Event\ApiLoadStartEvent;
use Alchemy\Phrasea\Core\Event\Subscriber\ApiOauth2ErrorsSubscriber;
use Alchemy\Phrasea\Core\Event\Subscriber\ApiExceptionHandlerSubscriber;
use Alchemy\Phrasea\Core\Provider\JsonSchemaServiceProvider;
use Monolog\Logger;
use Monolog\Processor\WebProcessor;
use Silex\Application as SilexApplication;
@@ -86,6 +87,7 @@ return call_user_func(function ($environment = PhraseaApplication::ENV_PROD) {
}
});
$app->register(new JsonSchemaServiceProvider());
$app->register(new \API_V1_Timer());
$app['dispatcher']->dispatch(PhraseaEvents::API_LOAD_START, new ApiLoadStartEvent());

View File

@@ -480,6 +480,30 @@ class V1 implements ControllerProviderInterface
return $app['api']->substitute_subdef($app, $request)->get_response();
});
/**
* Route : /stories/add/
*
* Method : POST
*
* Parameters :
*
*/
$controllers->post('/stories', function (SilexApplication $app, Request $request) {
return $app['api']->add_story($app, $request)->get_response();
});
/**
* Route : /stories/{story_id}/records
*
* Method : POST
*
* Parameters :
*
*/
$controllers->post('/stories/{databox_id}/{story_id}/records', function (SilexApplication $app, Request $request, $databox_id, $story_id) {
return $app['api']->add_records_to_story($app, $request, $databox_id, $story_id)->get_response();
});
/**
* Route : /search/
*

View File

@@ -0,0 +1,35 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2015 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Core\Provider;
use JsonSchema\Uri\UriRetriever;
use JsonSchema\Validator;
use Silex\Application;
use Silex\ServiceProviderInterface;
class JsonSchemaServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['json-schema.retriever'] = $app->share(function (Application $app) {
return new UriRetriever();
});
$app['json-schema.validator'] = $app->share(function (Application $app) {
return new Validator();
});
}
public function boot(Application $app)
{
}
}

View File

@@ -2182,6 +2182,135 @@ class API_V1_adapter extends API_V1_Abstract
);
}
public function add_story(Application $app, Request $request)
{
$content = $request->getContent();
$data = @json_decode($content);
if (JSON_ERROR_NONE !== json_last_error()) {
$app->abort(400, 'Json response cannot be decoded or the encoded data is deeper than the recursion limit');
}
if (!isset($data->{'stories'})) {
$app->abort(400, 'Missing "stories" property');
}
$schemaStory = $app['json-schema.retriever']->retrieve('file://'.$app['root.path'].'/lib/conf.d/json_schema/story.json');
$schemaRecordStory = $app['json-schema.retriever']->retrieve('file://'.$app['root.path'].'/lib/conf.d/json_schema/story_record.json');
$storyData = $data->{'stories'};
if (!is_array($storyData)) {
$storyData = array($storyData);
}
$stories = array();
foreach ($storyData as $data) {
$stories[] = $this->create_story($app, $data, $schemaStory, $schemaRecordStory);
}
$result = new API_V1_result($app, $request, $this);
$result->set_datas(array('stories' => array_map(function($story) {
return sprintf('/stories/%s/%s/', $story->get_sbas_id(), $story->get_record_id());
}, $stories)));
return $result;
}
public function add_records_to_story(Application $app, Request $request, $databox_id, $story_id)
{
$content = $request->getContent();
$data = @json_decode($content);
if (JSON_ERROR_NONE !== json_last_error()) {
$app->abort(400, 'Json response cannot be decoded or the encoded data is deeper than the recursion limit');
}
if (!isset($data->{'story_records'})) {
$app->abort(400, 'Missing "story_records" property');
}
$recordsData = $data->{'story_records'};
if (!is_array($recordsData)) {
$recordsData = array($recordsData);
}
$story = new \record_adapter($app, $databox_id, $story_id);
$schema = $app['json-schema.retriever']->retrieve('file://'.$app['root.path'].'/lib/conf.d/json_schema/story_record.json');
$records = array();
foreach ($recordsData as $data) {
$records[] = $this->add_record_to_story($app, $story, $data, $schema);
}
$app['dispatcher']->dispatch(PhraseaEvents::RECORD_EDIT, new RecordEdit($story));
$result = new API_V1_result($this->app, $request, $this);
$result->set_datas(array('records' => $records));
return $result;
}
protected function create_story(Application $app, $data, $schemaStory, $schemaRecordStory)
{
$app['json-schema.validator']->check($data, $schemaStory);
if (false === $app['json-schema.validator']->isValid()) {
$app->abort(400, 'Request body does not contains a valid "story" object');
}
$collection = \collection::get_from_base_id($app, $data->{'collection_id'});
if (!$app['authentication']->getUser()->ACL()->has_right_on_base($collection->get_base_id(), 'canaddrecord')) {
$app->abort(403, sprintf('You can not create a story on this collection %s', $collection->get_base_id()));
}
$story = \record_adapter::createStory($app, $collection);
if (isset($data->{'title'})) {
$story->set_original_name((string) $data->{'title'});
}
if (isset($data->{'story_records'})) {
$recordsData = (array) $data->{'story_records'};
foreach ($recordsData as $data) {
$this->add_record_to_story($app, $story, $data, $schemaRecordStory);
}
}
return $story;
}
protected function add_record_to_story(Application $app, record_adapter $story, $data, $jsonSchema)
{
$app['json-schema.validator']->check($data, $jsonSchema);
if (false === $app['json-schema.validator']->isValid()) {
$app->abort(400, 'Request body contains not a valid "record story" object');
}
$databox_id = $data->{'databox_id'};
$record_id = $data->{'record_id'};
try {
$record = new \record_adapter($app, $databox_id, $record_id);
} catch (Exception_Record_AdapterNotFound $e) {
$app->abort(404, sprintf('Record identified by databox_is %s and record_id %s could not be found', $databox_id, $record_id));
}
if (!$story->hasChild($record)) {
$story->appendChild($record);
}
return $record->get_serialize_key();
}
/**
* Provide phraseanet global values
*

View File

@@ -1409,7 +1409,8 @@ class record_adapter implements record_Interface, cache_cacheableInterface
$app['filesystem']->copy($file->getFile()->getRealPath(), $pathhd . $newname, true);
$media = $app['mediavorus']->guess($pathhd . $newname);
$subdef = media_subdef::create($app, $record, 'document', $media);
media_subdef::create($app, $record, 'document', $media);
$record->delete_data_from_cache(\record_adapter::CACHE_SUBDEFS);

View File

@@ -0,0 +1,25 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Story",
"description": "A story from Phraseanet",
"type": "object",
"properties": {
"id": {
"description": "The unique identifier for a story",
"type": "string"
},
"story_id": {
"description": "The identifier for a record contextualized by the databox",
"type": "integer"
},
"databox_id": {
"description": "The databox identifier where the record is localized",
"type": "integer"
},
"collection_id": {
"description": "The collection identifier where the record is localized",
"type": "integer"
}
},
"required": ["collection_id"]
}

View File

@@ -0,0 +1,21 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Record",
"description": "A record in a story",
"type": "object",
"properties": {
"id": {
"description": "The unique identifier for a record",
"type": "string"
},
"record_id": {
"description": "The identifier for a story contextualized by the databox",
"type": "integer"
},
"databox_id": {
"description": "The databox identifier where the story is localized",
"type": "integer"
}
},
"required": ["databox_id", "record_id"]
}

View File

@@ -1589,6 +1589,80 @@ abstract class ApiAbstract extends \PhraseanetWebTestCaseAbstract
}
}
public function testAddStory()
{
$this->setToken(self::$token);
$route = '/api/v1/stories';
$story['collection_id'] = self::$DI['collection']->get_base_id();
$story['title'] = uniqid('story');
$file = new File(self::$DI['app'], self::$DI['app']['mediavorus']->guess(__DIR__ . '/../../../../files/p4logo.jpg'), self::$DI['collection']);
$record = \record_adapter::createFromFile($file, self::$DI['app']);
$story['story_records'] = array(array(
'databox_id' => $record->get_sbas_id(),
'record_id' => $record->get_record_id()
));
self::$DI['client']->request(
'POST',
$route,
$this->getParameters(),
$this->getAddRecordFile(),
array('HTTP_Accept' => $this->getAcceptMimeType()),
json_encode(array('stories' => array($story)))
);
$content = $this->unserialize(self::$DI['client']->getResponse()->getContent());
$this->evaluateResponse200(self::$DI['client']->getResponse());
$this->evaluateMeta200($content);
$data = $content['response'];
$this->assertArrayHasKey('stories', $data);
$this->assertCount(1, $data['stories']);
list($empty, $path, $databox_id, $story_id) = explode('/', current($data['stories']));
$databox = self::$DI['app']['phraseanet.appbox']->get_databox($databox_id);
$story = $databox->get_record($story_id);
$story->delete();
$record->delete();
}
public function testAddRecordToStory()
{
$this->setToken(self::$token);
$story = \record_adapter::createStory(self::$DI['app'], self::$DI['collection']);
$route = sprintf('/api/v1/stories/%s/%s/records', $story->get_sbas_id(), $story->get_record_id());
$file = new File(self::$DI['app'], self::$DI['app']['mediavorus']->guess(__DIR__ . '/../../../../files/extractfile.jpg'), self::$DI['collection']);
$record = \record_adapter::createFromFile($file, self::$DI['app']);
$records = array(
'databox_id' => $record->get_sbas_id(),
'record_id' => $record->get_record_id()
);
self::$DI['client']->request(
'POST',
$route,
$this->getParameters(),
$this->getAddRecordFile(),
array('HTTP_Accept' => $this->getAcceptMimeType()),
json_encode(array('story_records' => array($records)))
);
$content = $this->unserialize(self::$DI['client']->getResponse()->getContent());
$this->evaluateResponse200(self::$DI['client']->getResponse());
$this->evaluateMeta200($content);
$data = $content['response'];
$this->assertArrayHasKey('records', $data);
$this->assertCount(1, $data['records']);
$story->delete();
$record->delete();
}
/**
* @covers \API_V1_adapter::add_record
* @covers \API_V1_adapter::list_record

View File

@@ -41,7 +41,16 @@ class Sha256Test extends \PhraseanetPHPUnitAbstract
$session = new \Entities\LazaretSession();
self::$DI['app']['EM']->persist($session);
self::$DI['app']['border-manager']->process($session, File::buildFromPathfile($this->media->getFile()->getPathname(), self::$DI['collection'], self::$DI['app']), null, Manager::FORCE_RECORD);
self::$DI['app']['border-manager']->process(
$session,
File::buildFromPathfile(
$this->media->getFile()->getPathname(),
self::$DI['collection'],
self::$DI['app']
),
null,
Manager::FORCE_RECORD
);
$mock = $this->getMock('\\Alchemy\\Phrasea\\Border\\File', array('getSha256'), array(self::$DI['app'], $this->media, self::$DI['collection']));