Added new Endpoint Update Speaker

PUT /api/v1/summits/{summit_id}/speakers/{speaker_id}

Change-Id: I8d071826c5e039a7fea1d033113c64de4ffbdf73
This commit is contained in:
Sebastian Marcet 2017-12-12 08:10:40 -03:00
parent cf9d1ca5a4
commit 5dd7f6e4fc
13 changed files with 549 additions and 21 deletions

View File

@ -0,0 +1,22 @@
<?php namespace App\Events;
/**
* Copyright 2017 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
/**
* Class PresentationSpeakerCreated
* @package App\Events
*/
class PresentationSpeakerCreated extends PresentationSpeakerEntityStateChanged
{
}

View File

@ -0,0 +1,21 @@
<?php namespace App\Events;
/**
* Copyright 2017 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
/**
* Class PresentationSpeakerDeleted
* @package App\Events
*/
class PresentationSpeakerDeleted extends PresentationSpeakerEntityStateChanged
{
}

View File

@ -0,0 +1,62 @@
<?php namespace App\Events;
/**
* Copyright 2017 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Doctrine\ORM\Event\LifecycleEventArgs;
use Illuminate\Queue\SerializesModels;
use models\summit\PresentationSpeaker;
/**
* Class PresentationSpeakerEntityStateChanged
* @package App\Events
*/
class PresentationSpeakerEntityStateChanged extends Event
{
use SerializesModels;
/**
* @var PresentationSpeaker
*/
protected $speaker;
/**
* @var LifecycleEventArgs
*/
protected $args;
/**
* SummitEventEntityStateChanged constructor.
* @param PresentationSpeaker $speaker
* @param LifecycleEventArgs $args
*/
public function __construct(PresentationSpeaker $speaker, LifecycleEventArgs $args)
{
$this->speaker = $speaker;
$this->args = $args;
}
/**
* @return PresentationSpeaker
*/
public function getPresentationSpeaker()
{
return $this->speaker;
}
/**
* @return LifecycleEventArgs
*/
public function getArgs()
{
return $this->args;
}
}

View File

@ -0,0 +1,21 @@
<?php namespace App\Events;
/**
* Copyright 2017 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
/**
* Class PresentationSpeakerUpdated
* @package App\Events
*/
class PresentationSpeakerUpdated extends PresentationSpeakerEntityStateChanged
{
}

View File

@ -254,7 +254,6 @@ final class OAuth2SummitSpeakersApiController extends OAuth2ProtectedController
}
}
public function addSpeaker($summit_id){
try {
if(!Request::isJson()) return $this->error403();
@ -315,4 +314,68 @@ final class OAuth2SummitSpeakersApiController extends OAuth2ProtectedController
return $this->error500($ex);
}
}
public function updateSpeaker($summit_id, $speaker_id){
try {
if(!Request::isJson()) return $this->error403();
$data = Input::json();
$summit = SummitFinderStrategyFactory::build($this->repository, $this->resource_server_context)->find($summit_id);
if (is_null($summit)) return $this->error404();
$speaker = CheckSpeakerStrategyFactory::build(CheckSpeakerStrategyFactory::Me, $this->resource_server_context)->check($speaker_id, $summit);
if (is_null($speaker)) return $this->error404();
$rules = array
(
'title' => 'sometimes|string|max:100',
'first_name' => 'sometimes|string|max:100',
'last_name' => 'sometimes|string|max:100',
'bio' => 'sometimes|string',
'irc' => 'sometimes|string|max:50',
'twitter' => 'sometimes|string|max:50',
'member_id' => 'sometimes|integer',
'email' => 'sometimes|string|max:50',
'on_site_phone' => 'sometimes|string|max:50',
'registered' => 'sometimes|boolean',
'confirmed' => 'sometimes|boolean',
'checked_in' => 'sometimes|boolean',
'registration_code' => 'sometimes|string',
);
// Creates a Validator instance and validates the data.
$validation = Validator::make($data->all(), $rules);
if ($validation->fails()) {
$messages = $validation->messages()->toArray();
return $this->error412
(
$messages
);
}
$fields = [
'title',
'bio',
];
$speaker = $this->service->updateSpeaker($summit, $speaker, HTMLCleaner::cleanData($data->all(), $fields));
return $this->created(SerializerRegistry::getInstance()->getSerializer($speaker)->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);
}
}
}

View File

@ -181,13 +181,14 @@ Route::group([
});
// speakers
Route::group(array('prefix' => 'speakers'), function () {
Route::group(['prefix' => 'speakers'], function () {
Route::post('', [ 'middleware' => 'auth.user:administrators', 'uses' => 'OAuth2SummitSpeakersApiController@addSpeaker']);
Route::get('', 'OAuth2SummitSpeakersApiController@getSpeakers');
Route::group(array('prefix' => '{speaker_id}'), function () {
Route::group(['prefix' => '{speaker_id}'], function () {
Route::get('', 'OAuth2SummitSpeakersApiController@getSpeaker')->where('speaker_id', 'me|[0-9]+');
Route::put('',[ 'middleware' => 'auth.user:administrators', 'uses' => 'OAuth2SummitSpeakersApiController@updateSpeaker'])->where('speaker_id', 'me|[0-9]+');
});
});

View File

@ -11,18 +11,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Mapping AS ORM;
use App\Events\PresentationSpeakerCreated;
use App\Events\PresentationSpeakerDeleted;
use App\Events\PresentationSpeakerUpdated;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
use models\main\File;
use models\main\Member;
use models\utils\PreRemoveEventArgs;
use models\utils\SilverstripeBaseModel;
use Doctrine\ORM\Mapping AS ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Illuminate\Support\Facades\Event;
use Doctrine\Common\Collections\Criteria;
/**
* @ORM\Entity
* @ORM\Table(name="PresentationSpeaker")
* @ORM\Entity(repositoryClass="App\Repositories\Summit\DoctrineSpeakerRepository")
* @ORM\HasLifecycleCallbacks
* Class PresentationSpeaker
* @package models\summit
*/
@ -420,6 +426,17 @@ class PresentationSpeaker extends SilverstripeBaseModel
return $this;
}
/**
* @param Summit $summit
* @return PresentationSpeakerSummitAssistanceConfirmationRequest
*/
public function getAssistanceFor(Summit $summit)
{
$criteria = Criteria::create();
$criteria->where(Criteria::expr()->eq('summit', $summit));
return $this->summit_assistances->matching($criteria)->first();
}
/**
* @return mixed
*/
@ -447,4 +464,98 @@ class PresentationSpeaker extends SilverstripeBaseModel
$request->setSpeaker($this);
return $request;
}
/**
* @return Summit[]
*/
public function getRelatedSummits(){
$query = <<<SQL
SELECT DISTINCT Summit.* FROM Presentation_Speakers
INNER JOIN Presentation ON Presentation.ID = Presentation_Speakers.PresentationID
INNER JOIN SummitEvent ON SummitEvent.ID = Presentation.ID
INNER JOIN Summit ON Summit.ID = SummitEvent.SummitID
WHERE SummitEvent.Published = 1 AND Presentation_Speakers.PresentationSpeakerID = :speaker_id;
SQL;
$rsm = new ResultSetMappingBuilder($this->getEM());
$rsm->addRootEntityFromClassMetadata(\models\summit\Summit::class, 's');
// build rsm here
$native_query = $this->getEM()->createNativeQuery($query, $rsm);
$native_query->setParameter("speaker_id", $this->id);
$summits = $native_query->getResult();
if(count($summits) == 0){
$assistance = $this->getLatestAssistance();
if(is_null($assistance)) return [];
return [ $assistance->getSummit() ];
}
return $summits;
}
/**
* @return PresentationSpeakerSummitAssistanceConfirmationRequest
*/
public function getLatestAssistance(){
return $this->summit_assistances->last();
}
// life cycle events
/**
* @var PreRemoveEventArgs
*/
private $pre_remove_events;
/**
* @ORM\PreRemove:
*/
public function deleting($args){
$this->pre_remove_events = new PreRemoveEventArgs
(
[
'id' => $this->id,
'class_name' => "PresentationSpeaker",
'summits' => $this->getRelatedSummits(),
]
);
}
/**
* @ORM\PostRemove:
*/
public function deleted($args){
Event::fire(new PresentationSpeakerDeleted($this, $this->pre_remove_events));
$this->pre_remove_events = null;
}
/**
* @var PreUpdateEventArgs
*/
private $pre_update_args;
/**
* @ORM\PreUpdate:
*/
public function updating(PreUpdateEventArgs $args){
$this->pre_update_args = $args;
}
/**
* @ORM\PostUpdate:
*/
public function updated($args)
{
Event::fire(new PresentationSpeakerUpdated($this, $this->pre_update_args));
$this->pre_update_args = null;
}
/**
* @ORM\PostPersist
*/
public function inserted($args){
Event::fire(new PresentationSpeakerCreated($this, $args));
}
}

View File

@ -19,7 +19,6 @@ use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use models\exceptions\ValidationException;
use models\main\Company;
use models\main\Member;
use models\main\Tag;
use models\utils\PreRemoveEventArgs;
use models\utils\SilverstripeBaseModel;

View File

@ -12,6 +12,9 @@
* limitations under the License.
**/
use App\Events\MyFavoritesAdd;
use App\Events\PresentationSpeakerCreated;
use App\Events\PresentationSpeakerDeleted;
use App\Events\PresentationSpeakerUpdated;
use App\Events\SummitEventCreated;
use App\Events\SummitEventDeleted;
use App\Events\SummitEventUpdated;
@ -49,7 +52,6 @@ class EventServiceProvider extends ServiceProvider
],
];
/**
* Register any other events for your application.
* @return void
@ -359,5 +361,103 @@ class EventServiceProvider extends ServiceProvider
});
Event::listen(\App\Events\PresentationSpeakerCreated::class, function($event)
{
if(!$event instanceof PresentationSpeakerCreated) return;
$resource_server_context = App::make(\models\oauth2\IResourceServerContext::class);
$member_repository = App::make(\models\main\IMemberRepository::class);
$owner_id = $resource_server_context->getCurrentUserExternalId();
if(is_null($owner_id)) $owner_id = 0;
$em = Registry::getManager('ss');
foreach($event->getPresentationSpeaker()->getRelatedSummits() as $summit) {
$entity_event = new SummitEntityEvent;
$entity_event->setEntityClassName("PresentationSpeaker");
$entity_event->setEntityId($event->getPresentationSpeaker()->getId());
$entity_event->setType('INSERT');
if ($owner_id > 0) {
$member = $member_repository->getById($owner_id);
$entity_event->setOwner($member);
}
$entity_event->setSummit($summit);
$entity_event->setMetadata('');
$em->persist($entity_event);
$em->flush();
}
});
Event::listen(\App\Events\PresentationSpeakerUpdated::class, function($event)
{
if(!$event instanceof PresentationSpeakerUpdated) return;
$resource_server_context = App::make(\models\oauth2\IResourceServerContext::class);
$member_repository = App::make(\models\main\IMemberRepository::class);
$owner_id = $resource_server_context->getCurrentUserExternalId();
if(is_null($owner_id)) $owner_id = 0;
$em = Registry::getManager('ss');
foreach($event->getPresentationSpeaker()->getRelatedSummits() as $summit) {
$entity_event = new SummitEntityEvent;
$entity_event->setEntityClassName("PresentationSpeaker");
$entity_event->setEntityId($event->getPresentationSpeaker()->getId());
$entity_event->setType('UPDATE');
if ($owner_id > 0) {
$member = $member_repository->getById($owner_id);
$entity_event->setOwner($member);
}
$entity_event->setSummit($summit);
$entity_event->setMetadata('');
$em->persist($entity_event);
$em->flush();
}
});
Event::listen(\App\Events\PresentationSpeakerDeleted::class, function($event)
{
if(!$event instanceof PresentationSpeakerDeleted) return;
$args = $event->getArgs();
if(!$args instanceof PreRemoveEventArgs) return;
$resource_server_context = App::make(\models\oauth2\IResourceServerContext::class);
$member_repository = App::make(\models\main\IMemberRepository::class);
$owner_id = $resource_server_context->getCurrentUserExternalId();
if(is_null($owner_id)) $owner_id = 0;
$params = $args->getParams();
$em = Registry::getManager('ss');
foreach($params['summits'] as $summit) {
$entity_event = new SummitEntityEvent;
$entity_event->setEntityClassName($params['class_name']);
$entity_event->setEntityId($params['id']);
$entity_event->setType('DELETE');
if ($owner_id > 0) {
$member = $member_repository->getById($owner_id);
$entity_event->setOwner($member);
}
$entity_event->setSummit($summit);
$entity_event->setMetadata('');
$em->persist($entity_event);
$em->flush();
}
});
}
}

View File

@ -29,6 +29,15 @@ interface ISpeakerService
*/
public function addSpeaker(Summit $summit, array $data);
/**
* @param Summit $summit
* @param array $data
* @param PresentationSpeaker $speaker
* @return PresentationSpeaker
* @throws ValidationException
*/
public function updateSpeaker(Summit $summit, PresentationSpeaker $speaker, array $data);
/**
* @param PresentationSpeaker $speaker
* @param Summit $summit

View File

@ -22,6 +22,7 @@ use models\summit\ISpeakerRegistrationRequestRepository;
use models\summit\ISpeakerRepository;
use models\summit\ISpeakerSummitRegistrationPromoCodeRepository;
use models\summit\PresentationSpeaker;
use models\summit\PresentationSpeakerSummitAssistanceConfirmationRequest;
use models\summit\SpeakerRegistrationRequest;
use models\summit\SpeakerSummitRegistrationPromoCode;
use models\summit\Summit;
@ -67,6 +68,16 @@ final class SpeakerService implements ISpeakerService
private $tx_service;
/**
* SpeakerService constructor.
* @param ISpeakerRepository $speaker_repository
* @param IMemberRepository $member_repository
* @param ISpeakerRegistrationRequestRepository $speaker_registration_request_repository
* @param ISpeakerSummitRegistrationPromoCodeRepository $registration_code_repository
* @param IEmailCreationRequestRepository $email_creation_request_repository
* @param IFolderRepository $folder_repository
* @param ITransactionService $tx_service
*/
public function __construct
(
ISpeakerRepository $speaker_repository,
@ -150,18 +161,9 @@ final class SpeakerService implements ISpeakerService
}
}
$on_site_phone = isset($data['on_site_phone']) ? trim($data['on_site_phone']) : null;
$registered = isset($data['registered']) ? 1 : 0;
$checked_in = isset($data['checked_in']) ? 1 : 0;
$confirmed = isset($data['confirmed']) ? 1 : 0;
$summit_assistance = $speaker->buildAssistanceFor($summit);
$summit_assistance->setOnSitePhone($on_site_phone);
$summit_assistance->setRegistered($registered);
$summit_assistance->setIsConfirmed($confirmed);
$summit_assistance->setCheckedIn($checked_in);
$speaker->addSummitAssistance($summit_assistance);
$speaker->addSummitAssistance(
$this->updateSummitAssistance($speaker->buildAssistanceFor($summit), $data)
);
$reg_code = isset($data['registration_code']) ? trim($data['registration_code']) : null;
if(!empty($reg_code)){
@ -177,6 +179,25 @@ final class SpeakerService implements ISpeakerService
});
}
/**
* @param PresentationSpeakerSummitAssistanceConfirmationRequest $summit_assistance
* @param array $data
* @return PresentationSpeakerSummitAssistanceConfirmationRequest
*/
private function updateSummitAssistance(PresentationSpeakerSummitAssistanceConfirmationRequest $summit_assistance, array $data){
$on_site_phone = isset($data['on_site_phone']) ? trim($data['on_site_phone']) : null;
$registered = isset($data['registered']) ? 1 : 0;
$checked_in = isset($data['checked_in']) ? 1 : 0;
$confirmed = isset($data['confirmed']) ? 1 : 0;
$summit_assistance->setOnSitePhone($on_site_phone);
$summit_assistance->setRegistered($registered);
$summit_assistance->setIsConfirmed($confirmed);
$summit_assistance->setCheckedIn($checked_in);
return $summit_assistance;
}
/**
* @param PresentationSpeaker $speaker
* @param string $email
@ -239,6 +260,10 @@ final class SpeakerService implements ISpeakerService
});
}
/**
* @param PresentationSpeaker $speaker
* @param array $data
*/
private function updateSpeakerMainData(PresentationSpeaker $speaker, array $data){
if(isset($data['title']))
$speaker->setTitle(trim($data['title']));
@ -260,4 +285,54 @@ final class SpeakerService implements ISpeakerService
}
/**
* @param Summit $summit
* @param array $data
* @param PresentationSpeaker $speaker
* @return PresentationSpeaker
* @throws ValidationException
* @throws EntityNotFoundException
*/
public function updateSpeaker(Summit $summit, PresentationSpeaker $speaker, array $data)
{
return $this->tx_service->transaction(function() use ($summit, $speaker, $data){
$member_id = isset($data['member_id']) ? intval($data['member_id']) : null;
if($member_id > 0)
{
$member = $this->member_repository->getById($member_id);
if(is_null($member))
throw new EntityNotFoundException;
$existent_speaker = $this->speaker_repository->getByMember($member);
if($existent_speaker && $existent_speaker->getId() !== $speaker->getId())
throw new ValidationException
(
sprintf
(
"member_id %s already has assigned another speaker id (%s)",
$member_id,
$existent_speaker->getId()
)
);
$speaker->setMember($member);
}
$this->updateSpeakerMainData($speaker, $data);
$summit_assistance = $speaker->getAssistanceFor($summit);
if(is_null($summit_assistance)){
$speaker->addSummitAssistance(
$this->updateSummitAssistance($speaker->buildAssistanceFor($summit), $data)
);
}
$reg_code = isset($data['registration_code']) ? trim($data['registration_code']) : null;
if(!empty($reg_code)){
$this->registerSummitPromoCodeByValue($speaker, $summit, $reg_code);
}
return $speaker;
});
}
}

View File

@ -165,6 +165,14 @@ class ApiEndpointsSeeder extends Seeder
sprintf(SummitScopes::WriteSpeakersData, $current_realm),
],
),
array(
'name' => 'update-speaker',
'route' => '/api/v1/summits/{id}/speakers/{speaker_id}',
'http_method' => 'PUT',
'scopes' => [
sprintf(SummitScopes::WriteSpeakersData, $current_realm),
],
),
array(
'name' => 'get-all-speakers',
'route' => '/api/v1/speakers',

View File

@ -130,4 +130,40 @@ class OAuth2SpeakersApiTest extends ProtectedApiTest
$this->assertTrue($speaker->id > 0);
return $speaker;
}
public function testUpdateSpeaker($summit_id = 23)
{
$params = [
'id' => $summit_id,
'speaker_id' => 1
];
$headers = [
"HTTP_Authorization" => " Bearer " . $this->access_token,
"CONTENT_TYPE" => "application/json"
];
$data = [
'title' => 'Legend!',
];
$response = $this->action
(
"PUT",
"OAuth2SummitSpeakersApiController@updateSpeaker",
$params,
[],
[],
[],
$headers,
json_encode($data)
);
$this->assertResponseStatus(201);
$content = $response->getContent();
$speaker = json_decode($content);
$this->assertTrue($speaker->id > 0);
return $speaker;
}
}