Added Track Icon endpoints

POST /api/v1/summits/{id}/tracks/{track_id}/icon

param

file

Scopes
%s/summits/write

DELETE /api/v1/summits/{id}/tracks/{track_id}/icon

Scopes

%s/summits/write

Change-Id: Iefc9570a7b2f3b06174445b2f661c080c0e91699
Signed-off-by: smarcet <smarcet@gmail.com>
This commit is contained in:
smarcet 2020-10-22 16:24:52 -03:00
parent 7b27b05d72
commit 4addbe0c94
9 changed files with 356 additions and 1 deletions

View File

@ -16,6 +16,7 @@ use App\Http\Utils\EpochCellFormatter;
use App\Http\Utils\PagingConstants;
use App\Models\Foundation\Summit\Repositories\ISummitTrackRepository;
use App\Services\Model\ISummitTrackService;
use Illuminate\Http\Request as LaravelRequest;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Log;
@ -608,4 +609,59 @@ final class OAuth2SummitTracksApiController extends OAuth2ProtectedController
return $this->error500($ex);
}
}
public function addTrackIcon(LaravelRequest $request, $summit_id, $track_id){
try {
$summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->resource_server_context)->find($summit_id);
if (is_null($summit)) return $this->error404();
$file = $request->file('file');
if (is_null($file)) {
return $this->error412(array('file param not set!'));
}
$image = $this->track_service->addTrackIcon($summit, $track_id, $file);
return $this->created(SerializerRegistry::getInstance()->getSerializer($image)->serialize());
}
catch (ValidationException $ex1)
{
Log::warning($ex1);
return $this->error412(array($ex1->getMessage()));
}
catch(EntityNotFoundException $ex2)
{
Log::warning($ex2);
return $this->error404(array('message'=> $ex2->getMessage()));
}
catch (Exception $ex) {
Log::error($ex);
return $this->error500($ex);
}
}
public function deleteTrackIcon($summit_id, $track_id) {
try {
$summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->resource_server_context)->find($summit_id);
if (is_null($summit)) return $this->error404();
$this->track_service->removeTrackIcon($summit, $track_id);
return $this->deleted();
}
catch (ValidationException $ex1)
{
Log::warning($ex1);
return $this->error412(array($ex1->getMessage()));
}
catch(EntityNotFoundException $ex2)
{
Log::warning($ex2);
return $this->error404(array('message'=> $ex2->getMessage()));
}
catch (Exception $ex) {
Log::error($ex);
return $this->error500($ex);
}
}
}

View File

@ -989,10 +989,16 @@ Route::group([
Route::get('', 'OAuth2SummitTracksApiController@getTrackBySummit');
Route::put('', [ 'middleware' => 'auth.user', 'uses' => 'OAuth2SummitTracksApiController@updateTrackBySummit']);
Route::delete('', [ 'middleware' => 'auth.user', 'uses' => 'OAuth2SummitTracksApiController@deleteTrackBySummit']);
Route::group(['prefix' => 'allowed-tags'], function () {
Route::get('', 'OAuth2SummitTracksApiController@getTrackAllowedTagsBySummit');
});
Route::group(['prefix' => 'icon'], function () {
Route::post('', [ 'middleware' => 'auth.user', 'uses' => 'OAuth2SummitTracksApiController@addTrackIcon']);
Route::delete('', [ 'middleware' => 'auth.user', 'uses' => 'OAuth2SummitTracksApiController@deleteTrackIcon']);
});
Route::group(['prefix' => 'extra-questions'], function () {
Route::get('', 'OAuth2SummitTracksApiController@getTrackExtraQuestionsBySummit');
Route::group(['prefix' => '{question_id}'], function () {

View File

@ -34,6 +34,7 @@ final class PresentationCategorySerializer extends SilverStripeSerializer
'ChairVisible' => 'chair_visible:json_boolean',
'SummitId' => 'summit_id:json_int',
'Color' => 'color:json_color',
'IconUrl' => 'icon_url:json_url',
];
/**

View File

@ -16,6 +16,7 @@ use Doctrine\ORM\Event\PreUpdateEventArgs;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
use App\Models\Foundation\Summit\Events\Presentations\TrackQuestions\TrackQuestionTemplate;
use Doctrine\Common\Collections\Criteria;
use models\main\File;
use models\main\Tag;
use models\utils\SilverstripeBaseModel;
use Doctrine\Common\Collections\ArrayCollection;
@ -177,6 +178,12 @@ class PresentationCategory extends SilverstripeBaseModel
*/
protected $extra_questions;
/**
* @ORM\ManyToOne(targetEntity="models\main\File", cascade={"persist","remove"})
* @ORM\JoinColumn(name="IconID", referencedColumnName="ID")
* @var File
*/
protected $icon;
/**
* @return TrackQuestionTemplate[]|ArrayCollection
@ -504,4 +511,57 @@ SQL;
{
$this->color = $color;
}
/**
* @return File|null
*/
public function getIcon(): ?File
{
return $this->icon;
}
/**
* @param File $icon
*/
public function setIcon(File $icon): void
{
$this->icon = $icon;
}
public function clearIcon():void{
$this->icon = null;
}
/**
* @return bool
*/
public function hasIcon(){
return $this->getIconId() > 0;
}
/**
* @return int
*/
public function getIconId()
{
try{
if(is_null($this->icon)) return 0;
return $this->icon->getId();
}
catch(\Exception $ex){
return 0;
}
}
/**
* @return string|null
*/
public function getIconUrl():?string{
$photoUrl = null;
if($this->hasIcon() && $photo = $this->getIcon()){
$photoUrl = $photo->getUrl();
}
return $photoUrl;
}
}

View File

@ -11,10 +11,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Illuminate\Http\UploadedFile;
use models\exceptions\EntityNotFoundException;
use models\exceptions\ValidationException;
use models\summit\PresentationCategory;
use models\summit\Summit;
use models\summit\SummitEvent;
/**
* Interface ISummitTrackService
* @package App\Services\Model
@ -77,4 +81,24 @@ interface ISummitTrackService
*/
public function removeTrackExtraQuestion($track_id, $question_id);
/**
* @param Summit $summit
* @param int $track_id
* @param UploadedFile $file
* @param int $max_file_size
* @throws ValidationException
* @throws EntityNotFoundException
* @return SummitEvent
*/
public function addTrackIcon(Summit $summit, $track_id, UploadedFile $file, $max_file_size = 10485760);
/**
* @param Summit $summit
* @param int $track_id
* @throws ValidationException
* @throws EntityNotFoundException
* @return void
*/
public function removeTrackIcon(Summit $summit, $track_id):void;
}

View File

@ -15,9 +15,11 @@
use App\Events\TrackDeleted;
use App\Events\TrackInserted;
use App\Events\TrackUpdated;
use App\Http\Utils\IFileUploader;
use App\Models\Foundation\Summit\Factories\PresentationCategoryFactory;
use App\Models\Foundation\Summit\Repositories\ISummitTrackRepository;
use App\Models\Foundation\Summit\Repositories\ITrackQuestionTemplateRepository;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Event;
use libs\utils\ITransactionService;
use models\exceptions\EntityNotFoundException;
@ -25,6 +27,8 @@ use models\exceptions\ValidationException;
use models\main\ITagRepository;
use models\summit\PresentationCategory;
use models\summit\Summit;
use models\summit\SummitEvent;
/**
* Class SummitTrackService
* @package App\Services\Model
@ -48,11 +52,17 @@ final class SummitTrackService
*/
private $track_question_template_repository;
/**
* @var IFileUploader
*/
private $file_uploader;
/**
* SummitTrackService constructor.
* @param ISummitTrackRepository $track_repository
* @param ITagRepository $tag_repository
* @param ITrackQuestionTemplateRepository $track_question_template_repository
* @param IFileUploader $file_uploader
* @param ITransactionService $tx_service
*/
public function __construct
@ -60,6 +70,7 @@ final class SummitTrackService
ISummitTrackRepository $track_repository,
ITagRepository $tag_repository,
ITrackQuestionTemplateRepository $track_question_template_repository,
IFileUploader $file_uploader,
ITransactionService $tx_service
)
{
@ -67,6 +78,7 @@ final class SummitTrackService
$this->tag_repository = $tag_repository;
$this->track_repository = $track_repository;
$this->track_question_template_repository = $track_question_template_repository;
$this->file_uploader = $file_uploader;
}
/**
@ -329,4 +341,50 @@ final class SummitTrackService
$track->removeExtraQuestion($track_question_template);
});
}
/**
* @inheritDoc
*/
public function addTrackIcon(Summit $summit, $track_id, UploadedFile $file, $max_file_size = 10485760)
{
return $this->tx_service->transaction(function () use ($summit, $track_id, $file, $max_file_size) {
$allowed_extensions = ['png', 'jpg', 'jpeg', 'gif'];
$track = $summit->getPresentationCategory($track_id);
if (is_null($track) || !$track instanceof PresentationCategory) {
throw new EntityNotFoundException('track not found on summit!');
}
if (!in_array($file->extension(), $allowed_extensions)) {
throw new ValidationException("file does not has a valid extension ('png','jpg','jpeg','gif').");
}
if ($file->getSize() > $max_file_size) {
throw new ValidationException(sprintf("file exceeds max_file_size (%s MB).", ($max_file_size / 1024) / 1024));
}
$file = $this->file_uploader->build($file, 'summit-track-icon', true);
$track->setIcon($file);
return $file;
});
}
/**
* @inheritDoc
*/
public function removeTrackIcon(Summit $summit, $track_id): void
{
$this->tx_service->transaction(function () use ($summit, $track_id) {
$track = $summit->getPresentationCategory($track_id);
if (is_null($track) || !$track instanceof PresentationCategory) {
throw new EntityNotFoundException('track not found on summit!');
}
$track->clearIcon();
});
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace Database\Migrations\Model;
use Doctrine\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema as Schema;
use LaravelDoctrine\Migrations\Schema\Builder;
use LaravelDoctrine\Migrations\Schema\Table;
class Version20201022181641 extends AbstractMigration
{
/**
* @param Schema $schema
*/
public function up(Schema $schema)
{
$builder = new Builder($schema);
if($schema->hasTable("PresentationCategory") && !$builder->hasColumn("PresentationCategory", "IconID")) {
$builder->table('PresentationCategory', function (Table $table) {
$table->integer("IconID", false, false)->setNotnull(false)->setDefault('NULL');
$table->index("IconID", "IconID");
$table->foreign("File", "IconID", "ID", ["onDelete" => "CASCADE"]);
});
}
}
/**
* @param Schema $schema
*/
public function down(Schema $schema)
{
$builder = new Builder($schema);
if($schema->hasTable("PresentationCategory") && $builder->hasColumn("PresentationCategory", "IconID")) {
$builder->table('PresentationCategory', function (Table $table) {
$table->dropColumn("IconID");
});
}
}
}

View File

@ -3766,6 +3766,34 @@ class ApiEndpointsSeeder extends Seeder
sprintf(SummitScopes::ReadAllSummitData, $current_realm)
],
],
[
'name' => 'add-track-icon',
'route' => '/api/v1/summits/{id}/tracks/{track_id}/icon',
'http_method' => 'POST',
'scopes' => [
sprintf(SummitScopes::WriteTracksData, $current_realm),
sprintf(SummitScopes::WriteSummitData, $current_realm)
],
'authz_groups' => [
IGroup::SuperAdmins,
IGroup::Administrators,
IGroup::SummitAdministrators,
]
],
[
'name' => 'remove-track-icon',
'route' => '/api/v1/summits/{id}/tracks/{track_id}/icon',
'http_method' => 'DELETE',
'scopes' => [
sprintf(SummitScopes::WriteTracksData, $current_realm),
sprintf(SummitScopes::WriteSummitData, $current_realm)
],
'authz_groups' => [
IGroup::SuperAdmins,
IGroup::Administrators,
IGroup::SummitAdministrators,
]
],
[
'name' => 'add-track-extra-questions',
'route' => '/api/v1/summits/{id}/tracks/{track_id}/extra-questions/{question_id}',

View File

@ -11,7 +11,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use LaravelDoctrine\ORM\Facades\EntityManager;
use Illuminate\Http\UploadedFile;
/**
* Class OAuth2TracksApiTest
*/
@ -360,4 +361,82 @@ final class OAuth2TracksApiTest extends ProtectedApiTest
$added_tracks = json_decode($content);
$this->assertTrue(!is_null($added_tracks));
}
public function testAddTrackIcon($summit_id=25){
$repo = EntityManager::getRepository(\models\summit\Summit::class);
$summit = $repo->getById($summit_id);
if(!$summit instanceof \models\summit\Summit)
throw new Exception();
$track = $summit->getPresentationCategories()[0];
$params = array
(
'id' => $summit_id,
'track_id' => $track->getId(),
);
$headers = array
(
"HTTP_Authorization" => " Bearer " . $this->access_token,
// "CONTENT_TYPE" => "multipart/form-data; boundary=----WebKitFormBoundaryBkSYnzBIiFtZu4pb"
);
$response = $this->action
(
"POST",
"OAuth2SummitTracksApiController@addTrackIcon",
$params,
array(),
array(),
[
'file' => UploadedFile::fake()->image('icon.jpg')
],
$headers,
[]
);
$video_id = $response->getContent();
$this->assertResponseStatus(201);
return intval($video_id);
}
public function testRemoveTrackIcon($summit_id=25){
$repo = EntityManager::getRepository(\models\summit\Summit::class);
$summit = $repo->getById($summit_id);
if(!$summit instanceof \models\summit\Summit)
throw new Exception();
$track = $summit->getPresentationCategories()[0];
$params = array
(
'id' => $summit_id,
'track_id' => $track->getId(),
);
$headers = array
(
"HTTP_Authorization" => " Bearer " . $this->access_token,
// "CONTENT_TYPE" => "multipart/form-data; boundary=----WebKitFormBoundaryBkSYnzBIiFtZu4pb"
);
$response = $this->action
(
"DELETE",
"OAuth2SummitTracksApiController@deleteTrackIcon",
$params,
array(),
array(),
[
],
$headers,
[]
);
$video_id = $response->getContent();
$this->assertResponseStatus(204);
return intval($video_id);
}
}