mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-13 21:13:26 +00:00
PHRAS-29 #fix Add creation story in API
This commit is contained in:
@@ -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());
|
||||
|
||||
|
@@ -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/
|
||||
*
|
||||
|
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
@@ -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
|
||||
*
|
||||
|
@@ -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);
|
||||
|
||||
|
25
lib/conf.d/json_schema/story.json
Normal file
25
lib/conf.d/json_schema/story.json
Normal 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"]
|
||||
}
|
21
lib/conf.d/json_schema/story_record.json
Normal file
21
lib/conf.d/json_schema/story_record.json
Normal 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"]
|
||||
}
|
@@ -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
|
||||
|
@@ -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']));
|
||||
|
||||
|
Reference in New Issue
Block a user