Added new endpoint add attendee by summit

POST /api/v1/summits/{id}/attendees

payload
* member_id ( int | required )
* share_contact_info ( boolean | optional )
* summit_hall_checked_in ( boolean | optional )
* summit_hall_checked_in_date ( epoch | optional )

Change-Id: Ie71d33941f8cc2dbe74446eefc747e306025f17f
This commit is contained in:
Sebastian Marcet 2018-01-15 11:35:25 -03:00
parent e88f886680
commit 1c0b1a24e4
15 changed files with 352 additions and 29 deletions

View File

@ -11,6 +11,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App\Services\Model\IAttendeeService;
use Exception;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Request;
@ -39,7 +40,12 @@ final class OAuth2SummitAttendeesApiController extends OAuth2ProtectedController
/**
* @var ISummitService
*/
private $service;
private $summit_service;
/**
* @var IAttendeeService
*/
private $attendee_service;
/**
* @var ISpeakerRepository
@ -61,7 +67,17 @@ final class OAuth2SummitAttendeesApiController extends OAuth2ProtectedController
*/
private $attendee_repository;
/**
* OAuth2SummitAttendeesApiController constructor.
* @param ISummitAttendeeRepository $attendee_repository
* @param ISummitRepository $summit_repository
* @param ISummitEventRepository $event_repository
* @param ISpeakerRepository $speaker_repository
* @param IEventFeedbackRepository $event_feedback_repository
* @param ISummitService $summit_service
* @param IAttendeeService $attendee_service
* @param IResourceServerContext $resource_server_context
*/
public function __construct
(
ISummitAttendeeRepository $attendee_repository,
@ -69,7 +85,8 @@ final class OAuth2SummitAttendeesApiController extends OAuth2ProtectedController
ISummitEventRepository $event_repository,
ISpeakerRepository $speaker_repository,
IEventFeedbackRepository $event_feedback_repository,
ISummitService $service,
ISummitService $summit_service,
IAttendeeService $attendee_service,
IResourceServerContext $resource_server_context
) {
parent::__construct($resource_server_context);
@ -78,7 +95,8 @@ final class OAuth2SummitAttendeesApiController extends OAuth2ProtectedController
$this->speaker_repository = $speaker_repository;
$this->event_repository = $event_repository;
$this->event_feedback_repository = $event_feedback_repository;
$this->service = $service;
$this->summit_service = $summit_service;
$this->attendee_service = $attendee_service;
}
/**
@ -192,7 +210,7 @@ final class OAuth2SummitAttendeesApiController extends OAuth2ProtectedController
$attendee = CheckAttendeeStrategyFactory::build(CheckAttendeeStrategyFactory::Own, $this->resource_server_context)->check($attendee_id, $summit);
if (is_null($attendee)) return $this->error404();
$this->service->addEventToMemberSchedule($summit, $attendee->getMember(), intval($event_id));
$this->summit_service->addEventToMemberSchedule($summit, $attendee->getMember(), intval($event_id));
return $this->created();
}
@ -234,7 +252,7 @@ final class OAuth2SummitAttendeesApiController extends OAuth2ProtectedController
$attendee = CheckAttendeeStrategyFactory::build(CheckAttendeeStrategyFactory::Own, $this->resource_server_context)->check($attendee_id, $summit);
if (is_null($attendee)) return $this->error404();
$this->service->removeEventFromMemberSchedule($summit, $attendee->getMember(), intval($event_id));
$this->summit_service->removeEventFromMemberSchedule($summit, $attendee->getMember(), intval($event_id));
return $this->deleted();
@ -282,7 +300,7 @@ final class OAuth2SummitAttendeesApiController extends OAuth2ProtectedController
$attendee = CheckAttendeeStrategyFactory::build(CheckAttendeeStrategyFactory::Own, $this->resource_server_context)->check($attendee_id, $summit);
if (is_null($attendee)) return $this->error404();
$this->service->unRSVPEvent($summit, $attendee->getMember(), $event_id);
$this->summit_service->unRSVPEvent($summit, $attendee->getMember(), $event_id);
return $this->deleted();
@ -404,4 +422,55 @@ final class OAuth2SummitAttendeesApiController extends OAuth2ProtectedController
}
}
/**
* @param int $summit_id
* @return mixed
*/
public function addAttendee($summit_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();
$rules = [
'member_id' => 'required|integer',
'share_contact_info' => 'sometimes|boolean',
'summit_hall_checked_in' => 'sometimes|boolean',
'summit_hall_checked_in_date' => 'sometimes|date_format:U',
];
// 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
);
}
$attendee = $this->attendee_service->addAttendee($summit, $data->all());
return $this->created(SerializerRegistry::getInstance()->getSerializer($attendee)->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

@ -55,17 +55,11 @@ final class UserAuthEndpoint
$http_response = Response::json(['error' => 'member not found'], 403);
return $http_response;
}
$groups = $member->getGroups();
$required_groups = explode('|', $required_groups);
foreach ($required_groups as $required_group) {
foreach ($groups as $member_group){
if (strtolower($required_group) == strtolower($member_group->getCode())) {
return $next($request);
}
}
if($member->isOnGroup($required_group))
return $next($request);
}
$http_response = Response::json(['error' => 'unauthorized member'], 403);

View File

@ -155,6 +155,7 @@ Route::group([
Route::get('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitAttendeesApiController@getAttendeesBySummit']);
Route::get('me', 'OAuth2SummitAttendeesApiController@getOwnAttendee');
Route::post('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitAttendeesApiController@addAttendee']);
Route::group(array('prefix' => '{attendee_id}'), function () {
Route::get('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitAttendeesApiController@getAttendee']);

View File

@ -11,7 +11,6 @@
* 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 Doctrine\Common\Collections\ArrayCollection;
@ -20,7 +19,6 @@ use models\exceptions\ValidationException;
use models\main\Member;
use models\main\SummitMemberSchedule;
use models\utils\SilverstripeBaseModel;
/**
* @ORM\Entity
* @ORM\Table(name="SummitAttendee")
@ -70,6 +68,20 @@ class SummitAttendee extends SilverstripeBaseModel
return (bool)$this->summit_hall_checked_in;
}
/**
* @param bool $summit_hall_checked_in
*/
public function setSummitHallCheckedIn($summit_hall_checked_in){
$this->summit_hall_checked_in = $summit_hall_checked_in;
}
/**
* @param \DateTime $summit_hall_checked_in_date
*/
public function setSummitHallCheckedInDate(\DateTime $summit_hall_checked_in_date){
$this->summit_hall_checked_in_date = $summit_hall_checked_in_date;
}
/**
* @return boolean
*/
@ -225,4 +237,5 @@ class SummitAttendee extends SilverstripeBaseModel
return $this->member->getRsvpByEvent($event_id);
}
}

View File

@ -0,0 +1,49 @@
<?php namespace models\summit\factories;
/**
* Copyright 2018 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 models\main\Member;
use models\summit\Summit;
use models\summit\SummitAttendee;
/**
* Class SummitAttendeeFactory
* @package models\summit\factories
*/
final class SummitAttendeeFactory
{
/**
* @param Summit $summit
* @param Member $member
* @param array $data
* @return SummitAttendee
*/
public static function build(Summit $summit, Member $member, array $data){
$attendee = new SummitAttendee();
$attendee->setMember($member);
$attendee->setSummit($summit);
if(isset($data['share_contact_info']))
$attendee->setShareContactInfo($data['share_contact_info']);
if(isset($data['summit_hall_checked_in']))
$attendee->setSummitHallCheckedIn($data['summit_hall_checked_in']);
if(isset($data['summit_hall_checked_in_date']))
$attendee->setSummitHallCheckedInDate
(
new \DateTime(intval($data['summit_hall_checked_in_date']))
);
return $attendee;
}
}

View File

@ -11,13 +11,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use models\main\Member;
use models\utils\IBaseRepository;
use utils\Filter;
use utils\Order;
use utils\PagingInfo;
use utils\PagingResponse;
/**
* Class ISummitAttendeeRepository
* @package models\summit
@ -32,4 +31,11 @@ interface ISummitAttendeeRepository extends IBaseRepository
* @return PagingResponse
*/
public function getBySummit(Summit $summit, PagingInfo $paging_info, Filter $filter = null, Order $order = null);
/**
* @param Summit $summit
* @param Member $member
* @return SummitAttendee
*/
public function getBySummitAndMember(Summit $summit, Member $member);
}

View File

@ -12,6 +12,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use models\main\Member;
use models\summit\ISummitAttendeeRepository;
use models\summit\Summit;
use models\summit\SummitAttendee;
@ -130,4 +131,26 @@ final class DoctrineSummitAttendeeRepository
$data
);
}
/**
* @param Summit $summit
* @param Member $member
* @return SummitAttendee
*/
public function getBySummitAndMember(Summit $summit, Member $member)
{
$query = $this->getEntityManager()
->createQueryBuilder()
->select("a")
->from(SummitAttendee::class, "a")
->leftJoin('a.summit', 's')
->leftJoin('a.member', 'm')
->where("s.id = :summit_id")->andWhere("m.id = :member_id")
->setParameter("summit_id", $summit->getId())
->setParameter("member_id", $member->getId());
$res = $query->getQuery()->getOneOrNullResult();
return $res;
}
}

View File

@ -12,20 +12,21 @@
* limitations under the License.
**/
/**
* Class SummitScopes
* @package App\Security
*/
final class SummitScopes
{
const ReadSummitData = '%s/summits/read';
const ReadAllSummitData = '%s/summits/read/all';
const ReadSummitData = '%s/summits/read';
const ReadAllSummitData = '%s/summits/read/all';
const WriteSummitData = '%s/summits/write';
const WriteSpeakersData = '%s/speakers/write';
const WriteSummitData = '%s/summits/write';
const WriteSpeakersData = '%s/speakers/write';
const PublishEventData = '%s/summits/publish-event';
const WriteEventData = '%s/summits/write-event';
const WriteVideoData = '%s/summits/write-videos';
const PublishEventData = '%s/summits/publish-event';
const WriteEventData = '%s/summits/write-event';
const WriteVideoData = '%s/summits/write-videos';
const WriteAttendeesData = '%s/attendees/write';
}

View File

@ -0,0 +1,87 @@
<?php namespace App\Services\Model;
/**
* Copyright 2018 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 libs\utils\ITransactionService;
use models\exceptions\EntityNotFoundException;
use models\exceptions\ValidationException;
use models\main\IMemberRepository;
use models\summit\factories\SummitAttendeeFactory;
use models\summit\ISummitAttendeeRepository;
use models\summit\Summit;
use models\summit\SummitAttendee;
/**
* Class AttendeeService
* @package App\Services\Model
*/
class AttendeeService implements IAttendeeService
{
/**
* @var ISummitAttendeeRepository
*/
private $attendee_repository;
/**
* @var IMemberRepository
*/
private $member_repository;
/**
* @var ITransactionService
*/
private $tx_service;
public function __construct
(
ISummitAttendeeRepository $attendee_repository,
IMemberRepository $member_repository,
ITransactionService $tx_service
)
{
$this->attendee_repository = $attendee_repository;
$this->member_repository = $member_repository;
$this->tx_service = $tx_service;
}
/**
* @param Summit $summit
* @param array $data
* @return SummitAttendee
*/
public function addAttendee(Summit $summit, array $data)
{
return $this->tx_service->transaction(function() use($summit, $data){
if(!isset($data['member_id']))
throw new ValidationException("member_id is required");
$member_id = intval($data['member_id']);
$member = $this->member_repository->getById($member_id);
if(is_null($member))
throw new EntityNotFoundException("member not found");
// check if attendee already exist for this summit
$old_attendee = $this->attendee_repository->getBySummitAndMember($summit, $member);
if(!is_null($old_attendee))
throw new ValidationException(sprintf("attendee already exist for summit id %s and member id %s", $summit->getId(), $member->getIdentifier()));
$attendee = SummitAttendeeFactory::build($summit, $member, $data);
$this->attendee_repository->add($attendee);
return $attendee;
});
}
}

View File

@ -0,0 +1,28 @@
<?php namespace App\Services\Model;
/**
* Copyright 2018 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 models\summit\Summit;
use models\summit\SummitAttendee;
/**
* Interface IAttendeeService
* @package App\Services\Model
*/
interface IAttendeeService
{
/**
* @param Summit $summit
* @param array $data
* @return SummitAttendee
*/
public function addAttendee(Summit $summit, array $data);
}

View File

@ -11,6 +11,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App\Services\Model\AttendeeService;
use App\Services\Model\IAttendeeService;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\ServiceProvider;
@ -74,6 +76,12 @@ class ServicesProvider extends ServiceProvider
return $api;
});
App::singleton
(
IAttendeeService::class,
AttendeeService::class
);
// work request pre processors
App::singleton

View File

@ -156,6 +156,14 @@ class ApiEndpointsSeeder extends Seeder
'http_method' => 'PUT',
'scopes' => [sprintf(SummitScopes::WriteSummitData, $current_realm)],
),
array(
'name' => 'add-attendee',
'route' => '/api/v1/summits/{id}/attendees',
'http_method' => 'POST',
'scopes' => [
sprintf(SummitScopes::WriteAttendeesData, $current_realm),
],
),
// speakers
array(
'name' => 'get-speakers',

View File

@ -11,14 +11,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Config;;
use App\Models\ResourceServer\ApiScope;
use LaravelDoctrine\ORM\Facades\EntityManager;
use Illuminate\Support\Facades\DB;
use App\Security\SummitScopes;
/**
* Class ApiScopesSeeder
*/
@ -115,6 +113,11 @@ final class ApiScopesSeeder extends Seeder
'short_description' => 'Write Speakers Data',
'description' => 'Grants write access for Speakers Data',
),
array(
'name' => sprintf(SummitScopes::WriteAttendeesData, $current_realm),
'short_description' => 'Write Attendees Data',
'description' => 'Grants write access for Attendees Data',
),
];
foreach ($scopes as $scope_info) {

View File

@ -135,4 +135,35 @@ class OAuth2AttendeesApiTest extends ProtectedApiTest
$attendees = json_decode($content);
$this->assertTrue(!is_null($attendees));
}
public function testAddAttendee(){
$params = [
'id' => 23,
];
$data = [
'member_id' => 1
];
$headers = [
"HTTP_Authorization" => " Bearer " . $this->access_token,
"CONTENT_TYPE" => "application/json"
];
$response = $this->action(
"POST",
"OAuth2SummitAttendeesApiController@addAttendee",
$params,
[],
[],
[],
$headers,
json_encode($data)
);
$content = $response->getContent();
$this->assertResponseStatus(201);
$attendee = json_decode($content);
$this->assertTrue(!is_null($attendee));
}
}

View File

@ -57,6 +57,7 @@ class AccessTokenServiceStub implements IAccessTokenService
$url . '/me/summits/events/favorites/add',
$url . '/me/summits/events/favorites/delete',
sprintf(SummitScopes::WriteSpeakersData, $url),
sprintf(SummitScopes::WriteAttendeesData, $url),
);
return AccessToken::createFromParams('123456789', implode(' ', $scopes), '1', $realm, '1','11624', 3600, 'WEB_APPLICATION', '', '');
@ -102,6 +103,7 @@ class AccessTokenServiceStub2 implements IAccessTokenService
$url . '/me/summits/events/favorites/add',
$url . '/me/summits/events/favorites/delete',
sprintf(SummitScopes::WriteSpeakersData, $url),
sprintf(SummitScopes::WriteAttendeesData, $url),
);
return AccessToken::createFromParams('123456789', implode(' ', $scopes), '1', $realm, null,null, 3600, 'SERVICE', '', '');