From d292a9478df8c2d67023221fba49df3ef592458c Mon Sep 17 00:00:00 2001 From: Sebastian Marcet Date: Thu, 18 Jan 2018 23:22:34 -0300 Subject: [PATCH] Endpoint add attendee ticket POST /api/v1/summits/{id}/attendees/{attendee_id}/tickets Payload { ticket_type_id: 1234 external_order_id: "1234", external_attendee_id: "1234" } Change-Id: Icfbf9cc07b4261829c74f5fe493025e785850300 --- .../OAuth2SummitAttendeesApiController.php | 52 +++++++++ app/Http/routes.php | 4 + .../Summit/SummitAttendeeTicketSerializer.php | 16 ++- .../Summit/Attendees/SummitAttendeeTicket.php | 23 +++- .../Factories/SummitAttendeeTicketFactory.php | 42 +++++++ .../Summit/Repositories/ISummitRepository.php | 8 +- .../ISummitTicketTypeRepository.php | 22 ++++ .../Foundation/Summit/SummitTicketType.php | 4 +- app/Repositories/RepositoriesProvider.php | 8 ++ ...DoctrineSummitAttendeeTicketRepository.php | 12 ++ .../Summit/DoctrineSummitRepository.php | 3 - .../DoctrineSummitTicketTypeRepository.php | 32 +++++ app/Services/Model/AttendeeService.php | 109 +++++++++++++++++- app/Services/Model/IAttendeeService.php | 10 ++ database/seeds/ApiEndpointsSeeder.php | 8 ++ tests/OAuth2AttendeesApiTest.php | 35 ++++++ 16 files changed, 373 insertions(+), 15 deletions(-) create mode 100644 app/Models/Foundation/Summit/Factories/SummitAttendeeTicketFactory.php create mode 100644 app/Models/Foundation/Summit/Repositories/ISummitTicketTypeRepository.php create mode 100644 app/Repositories/Summit/DoctrineSummitTicketTypeRepository.php diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitAttendeesApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitAttendeesApiController.php index 904ee8a7..3c027fec 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitAttendeesApiController.php +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitAttendeesApiController.php @@ -563,4 +563,56 @@ final class OAuth2SummitAttendeesApiController extends OAuth2ProtectedController } } + /** + * @param $summit_id + * @param $attendee_id + * @return mixed + */ + public function addAttendeeTicket($summit_id, $attendee_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(); + + $attendee = $this->attendee_repository->getById($attendee_id); + if(is_null($attendee)) return $this->error404(); + + $rules = [ + 'ticket_type_id' => 'required|integer', + 'external_order_id' => 'required|string', + 'external_attendee_id' => 'required|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 + ); + } + + $ticket = $this->attendee_service->addAttendeeTicket($attendee, $data->all()); + + return $this->created(SerializerRegistry::getInstance()->getSerializer($ticket)->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); + } + } } \ No newline at end of file diff --git a/app/Http/routes.php b/app/Http/routes.php index cdb41896..73505018 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -172,6 +172,10 @@ Route::group([ Route::put('/check-in', 'OAuth2SummitAttendeesApiController@checkingAttendeeOnEvent')->where('attendee_id', 'me|[0-9]+'); }); }); + Route::group(array('prefix' => 'tickets'), function () + { + Route::post('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitAttendeesApiController@addAttendeeTicket']); + }); }); }); diff --git a/app/ModelSerializers/Summit/SummitAttendeeTicketSerializer.php b/app/ModelSerializers/Summit/SummitAttendeeTicketSerializer.php index 38df3636..dfbf6088 100644 --- a/app/ModelSerializers/Summit/SummitAttendeeTicketSerializer.php +++ b/app/ModelSerializers/Summit/SummitAttendeeTicketSerializer.php @@ -1,6 +1,4 @@ 'external_attendee_id:json_string', 'BoughtDate' => 'bought_date:datetime_epoch', 'TicketTypeId' => 'ticket_type_id:json_int', + 'OwnerId' => 'owner_id:json_int', ]; /** @@ -39,11 +43,19 @@ final class SummitAttendeeTicketSerializer extends SilverStripeSerializer foreach ($exp_expand as $relation) { switch (trim($relation)) { case 'ticket_type': { + if(!$ticket->hasTicketType()) continue; unset($values['ticket_type_id']); $values['ticket_type'] = SerializerRegistry::getInstance()->getSerializer($ticket->getTicketType())->serialize(); } break; + case 'owner_id': { + if(!$ticket->hasOwner()) continue; + unset($values['owner_id']); + $values['owner'] = SerializerRegistry::getInstance()->getSerializer($ticket->getOwner())->serialize(); + } + break; } + } } diff --git a/app/Models/Foundation/Summit/Attendees/SummitAttendeeTicket.php b/app/Models/Foundation/Summit/Attendees/SummitAttendeeTicket.php index 65122b38..544821c8 100644 --- a/app/Models/Foundation/Summit/Attendees/SummitAttendeeTicket.php +++ b/app/Models/Foundation/Summit/Attendees/SummitAttendeeTicket.php @@ -136,7 +136,7 @@ class SummitAttendeeTicket extends SilverstripeBaseModel */ public function getTicketTypeId(){ try{ - return $this->ticket_type->getId(); + return is_null($this->ticket_type) ? 0 : $this->ticket_type->getId(); } catch(\Exception $ex){ return 0; @@ -173,4 +173,25 @@ class SummitAttendeeTicket extends SilverstripeBaseModel { $this->owner = $owner; } + + /** + * @return int + */ + public function getOwnerId(){ + try{ + return is_null($this->owner) ? 0 : $this->owner->getId(); + } + catch(\Exception $ex){ + return 0; + } + } + + /** + * @return bool + */ + public function hasOwner(){ + return $this->getOwnerId() > 0; + } + + } \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Factories/SummitAttendeeTicketFactory.php b/app/Models/Foundation/Summit/Factories/SummitAttendeeTicketFactory.php new file mode 100644 index 00000000..4857bfff --- /dev/null +++ b/app/Models/Foundation/Summit/Factories/SummitAttendeeTicketFactory.php @@ -0,0 +1,42 @@ +addTicket($ticket); + if(isset($data['external_order_id'])) + $ticket->setExternalOrderId($data['external_order_id']); + if(isset($data['external_attendee_id'])) + $ticket->setExternalAttendeeId($data['external_attendee_id']); + + $ticket->setTicketType($type); + + return $ticket; + } +} \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Repositories/ISummitRepository.php b/app/Models/Foundation/Summit/Repositories/ISummitRepository.php index 48f5b85f..5ac323ea 100644 --- a/app/Models/Foundation/Summit/Repositories/ISummitRepository.php +++ b/app/Models/Foundation/Summit/Repositories/ISummitRepository.php @@ -1,7 +1,4 @@ _em->getConnection()->delete(" + SummitAttendeeTicket + ", ["ID" => $entity->getIdentifier()]); + } } \ No newline at end of file diff --git a/app/Repositories/Summit/DoctrineSummitRepository.php b/app/Repositories/Summit/DoctrineSummitRepository.php index b95b1915..f067a603 100644 --- a/app/Repositories/Summit/DoctrineSummitRepository.php +++ b/app/Repositories/Summit/DoctrineSummitRepository.php @@ -1,5 +1,4 @@ attendee_repository = $attendee_repository; - $this->member_repository = $member_repository; - $this->tx_service = $tx_service; + $this->attendee_repository = $attendee_repository; + $this->ticket_repository = $ticket_repository; + $this->member_repository = $member_repository; + $this->ticket_type_repository = $ticket_type_repository; + $this->eventbrite_api = $eventbrite_api; + $this->tx_service = $tx_service; } /** @@ -139,4 +168,78 @@ final class AttendeeService implements IAttendeeService }); } + + /** + * @param SummitAttendee $attendee + * @param array $data + * @throws ValidationException + * @throws EntityNotFoundException + * @return SummitAttendeeTicket + */ + public function addAttendeeTicket(SummitAttendee $attendee, array $data){ + return $this->tx_service->transaction(function() use($attendee, $data){ + + if(!isset($data['ticket_type_id'])) + throw new ValidationException("ticket_type_id is mandatory!"); + + $type = $this->ticket_type_repository->getById(intval($data['ticket_type_id'])); + + if(is_null($type)) + throw new EntityNotFoundException(sprintf("ticket type %s not found!", $data['ticket_type_id'])); + + $old_ticket = $this->ticket_repository->getByExternalOrderIdAndExternalAttendeeId + ( + $data['external_order_id'], + $data['external_attendee_id'] + ); + + if(!is_null($old_ticket)) { + if ($old_ticket->hasOwner()) + throw new ValidationException + ( + sprintf + ( + "external_order_id %s - external_attendee_id %s already assigned to attendee id %s", + $data['external_order_id'], + $data['external_attendee_id'], + $old_ticket->getOwner()->getId() + ) + ); + $this->ticket_repository->delete($old_ticket); + } + + // validate with external api ... + + try { + $external_order = $this->eventbrite_api->getOrder($data['external_order_id']); + $external_attendee_found = false; + $summit_external_id = $external_order['event_id']; + + if (intval($attendee->getSummit()->getSummitExternalId()) !== intval($summit_external_id)) + throw new ValidationException('order %s does not belongs to current summit!', $external_order_id); + + foreach ($external_order['attendees'] as $external_attendee){ + if($data['external_attendee_id'] == $external_attendee['id']){ + $external_attendee_found = true; + break; + } + } + if(!$external_attendee_found){ + throw new ValidationException + ( + sprintf("external_attendee_id %s does not belongs to external_order_id %s", $data['external_attendee_id'], $data['external_order_id']) + ); + } + } + catch (ClientException $ex1) { + if ($ex1->getCode() === 400) + throw new EntityNotFoundException('external order does not exists!'); + if ($ex1->getCode() === 403) + throw new EntityNotFoundException('external order does not exists!'); + throw $ex1; + } + + return SummitAttendeeTicketFactory::build($attendee, $type, $data); + }); + } } \ No newline at end of file diff --git a/app/Services/Model/IAttendeeService.php b/app/Services/Model/IAttendeeService.php index bbd9a174..6ecf62a5 100644 --- a/app/Services/Model/IAttendeeService.php +++ b/app/Services/Model/IAttendeeService.php @@ -15,6 +15,7 @@ use models\exceptions\EntityNotFoundException; use models\exceptions\ValidationException; use models\summit\Summit; use models\summit\SummitAttendee; +use models\summit\SummitAttendeeTicket; /** * Interface IAttendeeService * @package App\Services\Model @@ -48,4 +49,13 @@ interface IAttendeeService * @throws EntityNotFoundException */ public function updateAttendee(Summit $summit, $attendee_id, array $data); + + /** + * @param SummitAttendee $attendee + * @param array $data + * @throws ValidationException + * @throws EntityNotFoundException + * @return SummitAttendeeTicket + */ + public function addAttendeeTicket(SummitAttendee $attendee, array $data); } \ No newline at end of file diff --git a/database/seeds/ApiEndpointsSeeder.php b/database/seeds/ApiEndpointsSeeder.php index 584329ea..0bd2ac09 100644 --- a/database/seeds/ApiEndpointsSeeder.php +++ b/database/seeds/ApiEndpointsSeeder.php @@ -180,6 +180,14 @@ class ApiEndpointsSeeder extends Seeder sprintf(SummitScopes::WriteAttendeesData, $current_realm), ], ), + array( + 'name' => 'add-attendee-ticket', + 'route' => '/api/v1/summits/{id}/attendees/{attendee_id}/tickets', + 'http_method' => 'POST', + 'scopes' => [ + sprintf(SummitScopes::WriteAttendeesData, $current_realm), + ], + ), // speakers array( 'name' => 'get-speakers', diff --git a/tests/OAuth2AttendeesApiTest.php b/tests/OAuth2AttendeesApiTest.php index c129c0fd..54a6a9a8 100644 --- a/tests/OAuth2AttendeesApiTest.php +++ b/tests/OAuth2AttendeesApiTest.php @@ -232,4 +232,39 @@ class OAuth2AttendeesApiTest extends ProtectedApiTest $this->assertTrue(!is_null($attendee)); return $attendee; } + + public function testAddAttendeeTicket(){ + $params = [ + 'id' => 23, + 'attendee_id' => 12642 + ]; + + $data = [ + 'ticket_type_id' => 50, + 'external_order_id' => '617372932', + 'external_attendee_id' => '774078887', + ]; + + $headers = [ + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ]; + + $response = $this->action( + "POST", + "OAuth2SummitAttendeesApiController@addAttendeeTicket", + $params, + [], + [], + [], + $headers, + json_encode($data) + ); + + $content = $response->getContent(); + $this->assertResponseStatus(201); + $ticket = json_decode($content); + $this->assertTrue(!is_null($ticket)); + return $ticket; + } } \ No newline at end of file