New External Orders Endpoints

added 2 new endpoints

* /api/v1/summits/{id}/external-orders/{external_order_id}
  to read external orders ( from eventbrite api)
* /api/v1/summits/{id}/external-orders/{external_order_id}/external-attendees/{external_attendee_id}/confirm
  to confirm, and become an attendee

Change-Id: I1de5cf51fffdb45a9220190f412b3a121377b023
This commit is contained in:
Sebastian Marcet 2016-03-23 13:08:15 -03:00
parent 2ef930a1d8
commit 8113fefaff
16 changed files with 500 additions and 21 deletions

View File

@ -53,4 +53,6 @@ API_RESPONSE_CACHE_LIFETIME=600
LOG_EMAIL_TO=smarcet@gmail.com
LOG_EMAIL_FROM=smarcet@gmail.com
LOG_LEVEL=info
LOG_LEVEL=info
EVENTBRITE_OAUTH2_PERSONAL_TOKEN=

View File

@ -59,10 +59,10 @@ class OAuth2SummitApiController extends OAuth2ProtectedController
) {
parent::__construct($resource_server_context);
$this->repository = $summit_repository;
$this->speaker_repository = $speaker_repository;
$this->event_repository = $event_repository;
$this->service = $service;
$this->repository = $summit_repository;
$this->speaker_repository = $speaker_repository;
$this->event_repository = $event_repository;
$this->service = $service;
}
public function getSummits()
@ -1432,4 +1432,55 @@ class OAuth2SummitApiController extends OAuth2ProtectedController
}
}
public function getExternalOrder($summit_id, $external_order_id){
try {
$summit = SummitFinderStrategyFactory::build($this->repository)->find($summit_id);
if (is_null($summit)) return $this->error404();
$order = $this->service->getExternalOrder($summit, $external_order_id);
return $this->ok($order);
}
catch (EntityNotFoundException $ex1) {
Log::warning($ex1);
return $this->error404(array('message' => $ex1->getMessage()));
}
catch (ValidationException $ex2) {
Log::warning($ex2);
return $this->error412($ex2->getMessages());
}
catch (Exception $ex) {
Log::error($ex);
return $this->error500($ex);
}
}
public function confirmExternalOrderAttendee($summit_id, $external_order_id, $external_attendee_id){
try {
$summit = SummitFinderStrategyFactory::build($this->repository)->find($summit_id);
if (is_null($summit)) return $this->error404();
$member_id = $this->resource_server_context->getCurrentUserExternalId();
if (is_null($member_id)) {
throw new \HTTP401UnauthorizedException;
}
$attendee = $this->service->confirmExternalOrderAttendee($summit, $member_id, $external_order_id, $external_attendee_id);
return $this->ok($attendee);
}
catch (EntityNotFoundException $ex1) {
Log::warning($ex1);
return $this->error404(array('message' => $ex1->getMessage()));
}
catch (ValidationException $ex2) {
Log::warning($ex2);
return $this->error412($ex2->getMessages());
}
catch (\HTTP401UnauthorizedException $ex3) {
Log::warning($ex3);
return $this->error401();
}
catch (Exception $ex) {
Log::error($ex);
return $this->error500($ex);
}
}
}

View File

@ -124,6 +124,12 @@ Route::group(array(
Route::get('', 'OAuth2SummitApiController@getSummitTypes');
});
// summit types
Route::group(array('prefix' => 'external-orders'), function () {
Route::get('{external_order_id}', 'OAuth2SummitApiController@getExternalOrder');
Route::post('{external_order_id}/external-attendees/{external_attendee_id}/confirm', 'OAuth2SummitApiController@confirmExternalOrderAttendee');
});
});
});
});

View File

@ -73,8 +73,10 @@ class BaseModelEloquent extends Eloquent
{
case 'datetime_epoch':
{
$datetime = new \DateTime($value);
$value = $datetime->getTimestamp();
if(!is_null($value)) {
$datetime = new \DateTime($value);
$value = $datetime->getTimestamp();
}
}
break;
case 'json_string':

View File

@ -37,6 +37,10 @@ class ValidationException extends Exception
public function getMessages()
{
$this->messages;
if(is_null($this->messages))
{
$this->messages = array($this->getMessage());
}
return $this->messages;
}
}

View File

@ -14,7 +14,9 @@
**/
use App;
use Config;
use Illuminate\Support\ServiceProvider;
use services\apis\EventbriteAPI;
/***
* Class ServicesProvider
@ -33,5 +35,10 @@ class ServicesProvider extends ServiceProvider
App::singleton('libs\utils\ICacheService', 'services\utils\RedisCacheService');
App::singleton('libs\utils\ITransactionService', 'services\utils\EloquentTransactionService');
App::singleton('services\model\ISummitService', 'services\model\SummitService');
App::singleton('services\apis\IEventbriteAPI', function(){
$api = new EventbriteAPI();
$api->setCredentials(array('token' => Config::get("server.eventbrite_oauth2_personal_token", null)));
return $api;
});
}
}

View File

@ -0,0 +1,79 @@
<?php
namespace services\apis;
use GuzzleHttp\Client;
/**
* Copyright 2016 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.
**/
final class EventbriteAPI implements IEventbriteAPI
{
const BaseUrl = 'https://www.eventbriteapi.com/v3';
private $auth_info;
/**
* @param array $auth_info
* @return $this
*/
public function setCredentials(array $auth_info)
{
$this->auth_info = $auth_info;
}
/**
* @param string $api_url
* @param array $params
* @return mixed
* @throws Exception
*/
public function getEntity($api_url, array $params)
{
if(strstr($api_url, self::BaseUrl) === false) throw new Exception('invalid base url!');
$client = new Client();
$query = array
(
'token' => $this->auth_info['token']
);
foreach($params as $param => $value)
{
$query[$param] = $value;
}
$response = $client->get($api_url, array
(
'query' => $query
)
);
if($response->getStatusCode() !== 200) throw new Exception('invalid status code!');
$content_type = $response->getHeader('content-type');
if(empty($content_type)) throw new Exception('invalid content type!');
if($content_type !== 'application/json') throw new Exception('invalid content type!');
$json = $response->getBody()->getContents();
return json_decode($json, true);
}
/**
* @param string $order_id
* @return mixed
*/
public function getOrder($order_id)
{
$order_id = intval($order_id);
$url = sprintf('%s/orders/%s', self::BaseUrl, $order_id);
return $this->getEntity($url, array('expand' => 'attendees'));
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace services\apis;
/**
* Copyright 2016 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.
**/
interface IEventbriteAPI
{
/**
* @param array $auth_info
* @return $this
*/
public function setCredentials(array $auth_info);
/**
* @param string $order_id
* @return mixed
*/
public function getOrder($order_id);
}

View File

@ -97,4 +97,21 @@ interface ISummitService
* @return array
*/
public function getSummitEntityEvents(Summit $summit, $member_id = null, \DateTime $from_date = null, $from_id = null);
/**
* @param Summit $summit
* @param $external_order_id
* @return array
*/
public function getExternalOrder(Summit $summit, $external_order_id);
/**
* @param Summit $summit
* @param int $me_id
* @param int $external_order_id
* @param int $external_attendee_id
* @return SummitAttendee
*/
public function confirmExternalOrderAttendee(Summit $summit, $me_id, $external_order_id, $external_attendee_id);
}

View File

@ -2,6 +2,7 @@
use App\Events\MyScheduleAdd;
use App\Events\MyScheduleRemove;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Support\Facades\Event;
use models\exceptions\EntityNotFoundException;
use libs\utils\ITransactionService;
@ -17,6 +18,7 @@ use models\summit\PresentationVideo;
use models\summit\Summit;
use models\summit\SummitAirport;
use models\summit\SummitAttendee;
use models\summit\SummitAttendeeTicket;
use models\summit\SummitEntityEvent;
use models\summit\SummitEvent;
use models\summit\SummitEventFactory;
@ -30,6 +32,8 @@ use models\summit\SummitType;
use models\summit\SummitVenue;
use models\summit\SummitVenueRoom;
use Log;
use services\apis\IEventbriteAPI;
/**
* Copyright 2015 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
@ -59,14 +63,26 @@ final class SummitService implements ISummitService
*/
private $event_repository;
/**
* @var IEventbriteAPI
*/
private $eventbrite_api;
/**
* SummitService constructor.
* @param ISummitEventRepository $event_repository
* @param IEventbriteAPI $eventbrite_api
* @param ITransactionService $tx_service
*/
public function __construct(ISummitEventRepository $event_repository, ITransactionService $tx_service)
public function __construct
(
ISummitEventRepository $event_repository,
IEventbriteAPI $eventbrite_api,
ITransactionService $tx_service
)
{
$this->event_repository = $event_repository;
$this->eventbrite_api = $eventbrite_api;
$this->tx_service = $tx_service;
}
@ -907,4 +923,149 @@ final class SummitService implements ISummitService
return true;
});
}
/**
* @param Summit $summit
* @param $external_order_id
* @return array
* @throws ValidationException
* @throws \Exception
*/
public function getExternalOrder(Summit $summit, $external_order_id)
{
try{
$external_order = $this->eventbrite_api->getOrder($external_order_id);
if (isset($external_order['attendees']))
{
$status = $external_order['status'];
$summit_external_id = $external_order['event_id'];
$summit = Summit::where('ExternalEventId', '=', $summit_external_id)->first();
if(is_null($summit)) throw new EntityNotFoundException('summit does not exists!');
if(intval($summit->ID) !== intval($summit->ID)) throw new ValidationException('order does not belongs to current summit!');
if($status !== 'placed') throw new ValidationException($status);
$attendees = array();
foreach($external_order['attendees'] as $a)
{
$ticket_external_id = intval($a['ticket_class_id']);
$ticket_type = SummitTicketType::where('ExternalId', '=', $ticket_external_id)->first();
if(is_null($ticket_type)) continue;
array_push($attendees, array(
'external_id' => intval($a['id']),
'first_name' => $a['profile']['first_name'],
'last_name' => $a['profile']['last_name'],
'company' => $a['profile']['company'],
'email' => $a['profile']['email'],
'job_title' => $a['profile']['job_title'],
'status' => $a['status'],
'ticket_type' => array
(
'id' => intval($ticket_type->ID),
'name' => $ticket_type->Name,
'external_id' => $ticket_external_id,
)
));
}
return array('id' => intval($external_order_id), 'attendees' => $attendees);
}
}
catch(ClientException $ex1){
if($ex1->getCode() === 400)
throw new ValidationException('external order does not exists!');
throw $ex1;
}
catch(\Exception $ex){
throw $ex;
}
}
/**
* @param Summit $summit
* @param int $me_id
* @param int $external_order_id
* @param int $external_attendee_id
* @return SummitAttendee
*/
public function confirmExternalOrderAttendee(Summit $summit, $me_id, $external_order_id, $external_attendee_id)
{
return $this->tx_service->transaction(function () use ($summit, $me_id, $external_order_id, $external_attendee_id){
try{
$external_order = $this->eventbrite_api->getOrder($external_order_id);
if (isset($external_order['attendees']))
{
$external_attendee = null;
foreach($external_order['attendees'] as $a)
{
if(intval($a['id']) === intval($external_attendee_id)) {
$external_attendee = $a;
break;
}
}
if(is_null($external_attendee)) throw new EntityNotFoundException('Attendee not found!');
$ticket_external_id = intval($external_attendee['ticket_class_id']);
$ticket_type = SummitTicketType::where('ExternalId', '=', $ticket_external_id)->first();
if(is_null($ticket_type)) throw new EntityNotFoundException('Ticket Type not found!');;
$status = $external_order['status'];
$summit_external_id = $external_order['event_id'];
$summit = Summit::where('ExternalEventId', '=', $summit_external_id)->first();
if(is_null($summit)) throw new EntityNotFoundException('summit does not exists!');
if(intval($summit->ID) !== intval($summit->ID)) throw new ValidationException('order does not belongs to current summit!');
if($status !== 'placed') throw new ValidationException($status);
$old_attendee = SummitAttendee::where('MemberID', '=', $me_id)->where('SummitID','=', $summit->ID)->first();
if(!is_null($old_attendee))
throw new ValidationException
(
'Attendee Already Exist for current summit!'
);
$old_ticket = SummitAttendeeTicket
::where('ExternalOrderId','=', $external_order_id)
->where('ExternalAttendeeId','=', $external_attendee_id)->first();
if(!is_null($old_ticket))
throw new ValidationException
(
sprintf
(
'Ticket already redeem for attendee id %s !',
$old_ticket->OwnerID
)
);
$attendee = new SummitAttendee;
$attendee->MemberID = $me_id;
$attendee->SummitID = $summit->ID;
$attendee->save();
$ticket = new SummitAttendeeTicket;
$ticket->ExternalOrderId = intval($external_order_id);
$ticket->ExternalAttendeeId = intval($external_attendee_id);
$ticket->TicketBoughtDate = $external_attendee['created'];
$ticket->TicketChangedDate = $external_attendee['changed'];
$ticket->TicketTypeID = $ticket_type->getIdentifier();
$ticket->OwnerID = $attendee->ID;
$ticket->save();
return $attendee;
}
}
catch(ClientException $ex1){
if($ex1->getCode() === 400)
throw new ValidationException('external order does not exists!');
throw $ex1;
}
catch(\Exception $ex){
throw $ex;
}
});
}
}

View File

@ -11,7 +11,7 @@
"laravel/framework": "5.0.*",
"predis/predis": "1.0.1",
"php": ">=5.4.0",
"guzzlehttp/guzzle": "5.2.0"
"guzzlehttp/guzzle": "5.3.0"
},
"require-dev": {
"phpunit/phpunit": "4.6.6",

View File

@ -14,9 +14,10 @@
return array
(
'ssl_enabled' => env('SSL_ENABLED', false),
'db_log_enabled' => env('DB_LOG_ENABLED', false),
'access_token_cache_lifetime' => env('ACCESS_TOKEN_CACHE_LIFETIME', 300),
'assets_base_url' => env('ASSETS_BASE_URL', null),
'response_cache_lifetime' => env('API_RESPONSE_CACHE_LIFETIME', 300),
'ssl_enabled' => env('SSL_ENABLED', false),
'db_log_enabled' => env('DB_LOG_ENABLED', false),
'access_token_cache_lifetime' => env('ACCESS_TOKEN_CACHE_LIFETIME', 300),
'assets_base_url' => env('ASSETS_BASE_URL', null),
'response_cache_lifetime' => env('API_RESPONSE_CACHE_LIFETIME', 300),
'eventbrite_oauth2_personal_token' => env('EVENTBRITE_OAUTH2_PERSONAL_TOKEN', ''),
);

View File

@ -496,11 +496,35 @@ class ApiEndpointsSeeder extends Seeder
)
);
$summit_read_scope = ApiScope::where('name', '=', sprintf('%s/summits/read', $current_realm))->first();
$summit_write_scope = ApiScope::where('name', '=', sprintf('%s/summits/write', $current_realm))->first();
$summit_write_event_scope = ApiScope::where('name', '=', sprintf('%s/summits/write-event', $current_realm))->first();
$summit_publish_event_scope = ApiScope::where('name', '=', sprintf('%s/summits/publish-event', $current_realm))->first();
$summit_delete_event_scope = ApiScope::where('name', '=', sprintf('%s/summits/delete-event', $current_realm))->first();
//external orders
ApiEndpoint::create(
array(
'name' => 'get-external-order',
'active' => true,
'api_id' => $summit->id,
'route' => '/api/v1/summits/{id}/external-orders/{external_order_id}',
'http_method' => 'GET'
)
);
ApiEndpoint::create(
array(
'name' => 'confirm-external-order',
'active' => true,
'api_id' => $summit->id,
'route' => '/api/v1/summits/{id}/external-orders/{external_order_id}/external-attendees/{external_attendee_id}/confirm',
'http_method' => 'POST'
)
);
$summit_read_scope = ApiScope::where('name', '=', sprintf('%s/summits/read', $current_realm))->first();
$summit_write_scope = ApiScope::where('name', '=', sprintf('%s/summits/write', $current_realm))->first();
$summit_write_event_scope = ApiScope::where('name', '=', sprintf('%s/summits/write-event', $current_realm))->first();
$summit_publish_event_scope = ApiScope::where('name', '=', sprintf('%s/summits/publish-event', $current_realm))->first();
$summit_delete_event_scope = ApiScope::where('name', '=', sprintf('%s/summits/delete-event', $current_realm))->first();
$summit_external_order_read = ApiScope::where('name', '=', sprintf('%s/summits/read-external-orders', $current_realm))->first();
$summit_external_order_confirm = ApiScope::where('name', '=', sprintf('%s/summits/confirm-external-orders', $current_realm))->first();
// read
$endpoint = ApiEndpoint::where('name', '=', 'get-summits')->first();
@ -543,9 +567,11 @@ class ApiEndpointsSeeder extends Seeder
$endpoint->scopes()->attach($summit_read_scope->id);
$endpoint = ApiEndpoint::where('name', '=', 'get-summit-types')->first();
$endpoint->scopes()->attach($summit_read_scope->id);
$endpoint = ApiEndpoint::where('name', '=', 'get-external-order')->first();
$endpoint->scopes()->attach($summit_external_order_read->id);
// write
$endpoint->scopes()->attach($summit_write_scope->id);
$endpoint = ApiEndpoint::where('name', '=', 'delete-event-attendee-schedule')->first();
$endpoint->scopes()->attach($summit_write_scope->id);
$endpoint = ApiEndpoint::where('name', '=', 'checking-event-attendee-schedule')->first();
@ -554,6 +580,7 @@ class ApiEndpointsSeeder extends Seeder
$endpoint->scopes()->attach($summit_write_scope->id);
$endpoint = ApiEndpoint::where('name', '=', 'add-event-feedback')->first();
$endpoint->scopes()->attach($summit_write_scope->id);
// write events
$endpoint = ApiEndpoint::where('name', '=', 'add-event')->first();
$endpoint->scopes()->attach($summit_write_event_scope->id);
@ -569,6 +596,11 @@ class ApiEndpointsSeeder extends Seeder
$endpoint = ApiEndpoint::where('name', '=', 'delete-event')->first();
$endpoint->scopes()->attach($summit_delete_event_scope->id);
//confirm external order
$endpoint = ApiEndpoint::where('name', '=', 'confirm-external-order')->first();
$endpoint->scopes()->attach($summit_external_order_confirm->id);
}
}

View File

@ -140,6 +140,26 @@ class ApiScopesSeeder extends Seeder
'system' => false
)
);
ApiScope::create(
array(
'name' => sprintf('%s/summits/read-external-orders', $current_realm),
'short_description' => 'Allow to read External Orders',
'description' => 'Allow to read External Orders',
'api_id' => $summits->id,
'system' => false
)
);
ApiScope::create(
array(
'name' => sprintf('%s/summits/confirm-external-orders', $current_realm),
'short_description' => 'Allow to confirm External Orders',
'description' => 'Allow to confirm External Orders',
'api_id' => $summits->id,
'system' => false
)
);
}
}

View File

@ -1146,4 +1146,69 @@ class OAuth2SummitApiTest extends ProtectedApiTest
$this->assertTrue(!is_null($locations));
}
public function testGetCurrentSummitExternalOrder()
{
$params = array
(
'id' => 'current',
'external_order_id' => 484446336
);
$headers = array
(
"HTTP_Authorization" => " Bearer " .$this->access_token,
"CONTENT_TYPE" => "application/json"
);
$response = $this->action
(
"GET",
"OAuth2SummitApiController@getExternalOrder",
$params,
array(),
array(),
array(),
$headers
);
$content = $response->getContent();
$this->assertResponseStatus(200);
$order = json_decode($content);
$this->assertTrue(!is_null($order));
}
public function testGetCurrentSummitConfirmExternalOrder()
{
$params = array
(
'id' => 'current',
'external_order_id' => 484446336,
'external_attendee_id' => 611227262
);
$headers = array
(
"HTTP_Authorization" => " Bearer " .$this->access_token,
"CONTENT_TYPE" => "application/json"
);
$response = $this->action
(
"POST",
"OAuth2SummitApiController@confirmExternalOrderAttendee",
$params,
array(),
array(),
array(),
$headers
);
$content = $response->getContent();
$this->assertResponseStatus(200);
$attendee = json_decode($content);
$this->assertTrue(!is_null($attendee));
}
}

View File

@ -42,6 +42,8 @@ class AccessTokenServiceStub implements IAccessTokenService
$url . '/summits/write-event',
$url . '/summits/publish-event',
$url . '/summits/delete-event',
$url . '/summits/read-external-orders',
$url . '/summits/confirm-external-orders',
);
return AccessToken::createFromParams('123456789', implode(' ', $scopes), '1', $realm, '1','11624', 3600, 'WEB_APPLICATION', '', '');
@ -71,6 +73,8 @@ class AccessTokenServiceStub2 implements IAccessTokenService
$url . '/summits/write-event',
$url . '/summits/publish-event',
$url . '/summits/delete-event',
$url . '/summits/read-external-orders',
$url . '/summits/confirm-external-orders',
);
return AccessToken::createFromParams('123456789', implode(' ', $scopes), '1', $realm, null,null, 3600, 'SERVICE', '', '');