From 694135c21412a9c0df6f28f3a1fa74fdbdc18bcb Mon Sep 17 00:00:00 2001 From: smarcet Date: Wed, 26 Jun 2019 12:28:42 -0300 Subject: [PATCH] * fixed bugs * added filter owner_name and owner_email to get reservations endpoint * added endpoint get reservation per id GET /api/v1/summits/all/locations/bookable-rooms/all/reservations/{id} * added endpoint get all reservations by summit CSV GET /api/v1/summits/{id}/locations/bookable-rooms/all/reservations/csv Change-Id: I44f658998f4b81c5d6b5d568eb0e2f2fef2b04a9 --- .../Traits/SummitBookableVenueRoomApi.php | 144 ++++++++++++++++++ app/Http/routes.php | 9 +- .../BookableRoomReservationCanceledEmail.php | 2 +- .../SummitRoomReservationSerializer.php | 21 ++- ...octrineSummitRoomReservationRepository.php | 13 +- database/seeds/ApiEndpointsSeeder.php | 120 ++++++++------- tests/OAuth2SummitLocationsApiTest.php | 88 +++++++++++ 7 files changed, 332 insertions(+), 65 deletions(-) diff --git a/app/Http/Controllers/Apis/Protected/Summit/Traits/SummitBookableVenueRoomApi.php b/app/Http/Controllers/Apis/Protected/Summit/Traits/SummitBookableVenueRoomApi.php index d33cc293..df3d0d22 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/Traits/SummitBookableVenueRoomApi.php +++ b/app/Http/Controllers/Apis/Protected/Summit/Traits/SummitBookableVenueRoomApi.php @@ -11,6 +11,9 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ + +use App\Http\Utils\BooleanCellFormatter; +use App\Http\Utils\EpochCellFormatter; use App\Http\Utils\PagingConstants; use Exception; use Illuminate\Support\Facades\Input; @@ -37,6 +40,41 @@ use Illuminate\Http\Request as LaravelRequest; */ trait SummitBookableVenueRoomApi { + + /** + * @param $id + * @return mixed + */ + public function getReservationById($id){ + try { + + $expand = Request::input('expand', ''); + $relations = Request::input('relations', ''); + $relations = !empty($relations) ? explode(',', $relations) : []; + + $reservation = $this->reservation_repository->getById($id); + + if (is_null($reservation)) { + return $this->error404(); + } + + return $this->ok(SerializerRegistry::getInstance()->getSerializer($reservation)->serialize($expand,[], $relations)); + } + 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); + } + } + /** * @param $summit_id * @return \Illuminate\Http\JsonResponse|mixed @@ -178,6 +216,8 @@ trait SummitBookableVenueRoomApi 'room_name' => ['==', '=@'], 'room_id' => ['=='], 'owner_id' => ['=='], + 'owner_name' => ['==', '=@'], + 'owner_email' => ['==', '=@'], 'status' => ['=='], 'start_datetime' => ['>', '<', '<=', '>=', '=='], 'end_datetime' => ['>', '<', '<=', '>=', '=='], @@ -188,6 +228,8 @@ trait SummitBookableVenueRoomApi $filter->validate([ 'status' => sprintf('sometimes|in:%s',implode(',', SummitRoomReservation::$valid_status)), 'room_name' => 'sometimes|string', + 'owner_name' => 'sometimes|string', + 'owner_email' => 'sometimes|string', 'summit_id' => 'sometimes|integer', 'room_id' => 'sometimes|integer', 'owner_id' => 'sometimes|string', @@ -247,6 +289,108 @@ trait SummitBookableVenueRoomApi return $this->error500($ex); } } + + /** + * @param $summit_id + * @return mixed + */ + public function getAllReservationsBySummitCSV($summit_id){ + + try { + + $summit = SummitFinderStrategyFactory::build($this->repository, $this->resource_server_context)->find($summit_id); + if (is_null($summit)) return $this->error404(); + + // default values + $page = 1; + $per_page = PHP_INT_MAX; + + $filter = null; + + if (Input::has('filter')) { + $filter = FilterParser::parse(Input::get('filter'), [ + 'summit_id' => ['=='], + 'room_name' => ['==', '=@'], + 'room_id' => ['=='], + 'owner_id' => ['=='], + 'owner_name' => ['==', '=@'], + 'owner_email' => ['==', '=@'], + 'status' => ['=='], + 'start_datetime' => ['>', '<', '<=', '>=', '=='], + 'end_datetime' => ['>', '<', '<=', '>=', '=='], + ]); + } + if(is_null($filter)) $filter = new Filter(); + + $filter->validate([ + 'status' => sprintf('sometimes|in:%s',implode(',', SummitRoomReservation::$valid_status)), + 'room_name' => 'sometimes|string', + 'owner_name' => 'sometimes|string', + 'owner_email' => 'sometimes|string', + 'summit_id' => 'sometimes|integer', + 'room_id' => 'sometimes|integer', + 'owner_id' => 'sometimes|string', + 'start_datetime' => 'sometimes|required|date_format:U', + 'end_datetime' => 'sometimes|required_with:start_datetime|date_format:U|after:start_datetime', + + ], [ + 'status.in' => sprintf + ( + ":attribute has an invalid value ( valid values are %s )", + implode(", ", SummitRoomReservation::$valid_status) + ) + ]); + + $order = null; + + if (Input::has('order')) + { + $order = OrderParser::parse(Input::get('order'), [ + 'id', + 'start_datetime', + 'end_datetime', + ]); + } + + + $data = $this->reservation_repository->getAllBySummitByPage($summit, new PagingInfo($page, $per_page), $filter, $order); + + $filename = "bookable-rooms-reservations-" . date('Ymd'); + $list = $data->toArray(); + return $this->export + ( + 'csv', + $filename, + $list['data'], + [ + 'created' => new EpochCellFormatter, + 'last_edited' => new EpochCellFormatter, + 'start_datetime' => new EpochCellFormatter, + 'end_datetime' => new EpochCellFormatter, + ] + ); + } + 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(\HTTP401UnauthorizedException $ex3) + { + Log::warning($ex3); + return $this->error401(); + } + catch (Exception $ex) { + Log::error($ex); + return $this->error500($ex); + } + } + /** * @param $summit_id * @param $venue_id diff --git a/app/Http/routes.php b/app/Http/routes.php index d5ce0ae5..e5b3cfec 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -94,6 +94,11 @@ Route::group([ Route::group(['prefix' => 'selection-plans'], function () { Route::get('current/{status}', ['uses' => 'OAuth2SummitSelectionPlansApiController@getCurrentSelectionPlanByStatus'])->where('status', 'submission|selection|voting'); }); + Route::group(['prefix' => 'locations'], function () { + // GET /api/v1/summits/all/locations/bookable-rooms/all/reservations/{id} + Route::get('bookable-rooms/all/reservations/{id}', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitLocationsApiController@getReservationById']); + }); + }); Route::post('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitApiController@addSummit']); @@ -326,6 +331,8 @@ Route::group([ Route::group(['prefix' => 'reservations'], function () { // GET /api/v1/summits/{id}/locations/bookable-rooms/all/reservations Route::get('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitLocationsApiController@getAllReservationsBySummit']); + // GET /api/v1/summits/{id}/locations/bookable-rooms/all/reservations/csv + Route::get('csv', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitLocationsApiController@getAllReservationsBySummitCSV']); // GET /api/v1/summits/{id}/locations/bookable-rooms/all/reservations/me Route::get('me', 'OAuth2SummitLocationsApiController@getMyBookableVenueRoomReservations'); Route::group(['prefix' => '{reservation_id}'], function () { @@ -495,7 +502,7 @@ Route::group([ Route::group(['prefix' => '{value_id}'], function () { Route::get('', 'OAuth2SummitBookableRoomsAttributeTypeApiController@getBookableRoomAttributeValue'); Route::put('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitBookableRoomsAttributeTypeApiController@updateBookableRoomAttributeValue']); - Route::delete('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitBookableRoomsAttributeTypeApiController@udeleteBookableRoomAttributeValue']); + Route::delete('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitBookableRoomsAttributeTypeApiController@deleteBookableRoomAttributeValue']); }); }); }); diff --git a/app/Mail/BookableRoomReservationCanceledEmail.php b/app/Mail/BookableRoomReservationCanceledEmail.php index d4ce3e7e..884ef0f1 100644 --- a/app/Mail/BookableRoomReservationCanceledEmail.php +++ b/app/Mail/BookableRoomReservationCanceledEmail.php @@ -27,7 +27,7 @@ final class BookableRoomReservationCanceledEmail extends AbstractBookableRoomRes { $subject = Config::get("mail.bookable_room_reservation_canceled_email_subject"); if(empty($subject)) - $subject = sprintf("[%s] Your Reservation had canceled", Config::get('app.app_name')); + $subject = sprintf("[%s] Your Room Reservation had been canceled", Config::get('app.app_name')); return $this->from(Config::get("mail.from")) ->to($this->reservation->getOwner()->getEmail()) diff --git a/app/ModelSerializers/Locations/SummitRoomReservationSerializer.php b/app/ModelSerializers/Locations/SummitRoomReservationSerializer.php index 657d3746..91e3d05a 100644 --- a/app/ModelSerializers/Locations/SummitRoomReservationSerializer.php +++ b/app/ModelSerializers/Locations/SummitRoomReservationSerializer.php @@ -21,17 +21,16 @@ use ModelSerializers\SilverStripeSerializer; final class SummitRoomReservationSerializer extends SilverStripeSerializer { protected static $array_mappings = [ - 'RoomId' => 'room_id:json_int', - 'OwnerId' => 'owner_id:json_int', - 'Amount' => 'amount:json_int', - 'Currency' => 'currency:json_string', - 'Status' => 'status:json_string', - 'StartDatetime' => 'start_datetime:datetime_epoch', - 'EndDatetime' => 'end_datetime:datetime_epoch', - 'LocalStartDatetime' => 'local_start_datetime:datetime_epoch', - 'LocalEndDatetime' => 'local_end_datetime:datetime_epoch', - 'ApprovedPaymentDate' => 'approved_payment_date:datetime_epoch', - 'LastError' => 'last_error:json_string', + 'RoomId' => 'room_id:json_int', + 'OwnerId' => 'owner_id:json_int', + 'Amount' => 'amount:json_int', + 'RefundedAmount' => 'refunded_amount:json_int', + 'Currency' => 'currency:json_string', + 'Status' => 'status:json_string', + 'StartDatetime' => 'start_datetime:datetime_epoch', + 'EndDatetime' => 'end_datetime:datetime_epoch', + 'ApprovedPaymentDate' => 'approved_payment_date:datetime_epoch', + 'LastError' => 'last_error:json_string', 'PaymentGatewayClientToken' => 'payment_gateway_client_token:json_string' ]; diff --git a/app/Repositories/Summit/DoctrineSummitRoomReservationRepository.php b/app/Repositories/Summit/DoctrineSummitRoomReservationRepository.php index 5632b39a..785fac04 100644 --- a/app/Repositories/Summit/DoctrineSummitRoomReservationRepository.php +++ b/app/Repositories/Summit/DoctrineSummitRoomReservationRepository.php @@ -22,7 +22,6 @@ use utils\Filter; use utils\Order; use utils\PagingInfo; use utils\PagingResponse; - /** * Class DoctrineSummitRoomReservationRepository * @package App\Repositories\Summit @@ -68,6 +67,18 @@ class DoctrineSummitRoomReservationRepository 'o', "o.id :operator :value" ), + 'owner_name' => new DoctrineJoinFilterMapping + ( + 'e.owner', + 'o', + "LOWER(CONCAT(o.first_name, ' ', o.last_name)) :operator ':value'" + ), + 'owner_email' => new DoctrineJoinFilterMapping + ( + 'e.owner', + 'o', + "o.email :operator :value" + ), ]; } diff --git a/database/seeds/ApiEndpointsSeeder.php b/database/seeds/ApiEndpointsSeeder.php index a94d294d..0ac3eed1 100644 --- a/database/seeds/ApiEndpointsSeeder.php +++ b/database/seeds/ApiEndpointsSeeder.php @@ -38,38 +38,6 @@ class ApiEndpointsSeeder extends Seeder $this->seedTrackQuestionTemplateEndpoints(); } - /** - * @param string $api_name - * @param array $endpoints_info - */ - private function seedApiEndpoints($api_name, array $endpoints_info){ - - $api = EntityManager::getRepository(\App\Models\ResourceServer\Api::class)->findOneBy(['name' => $api_name]); - if(is_null($api)) return; - - foreach($endpoints_info as $endpoint_info){ - - $endpoint = new ApiEndpoint(); - $endpoint->setName($endpoint_info['name']); - $endpoint->setRoute($endpoint_info['route']); - $endpoint->setHttpMethod($endpoint_info['http_method']); - $endpoint->setActive(true); - $endpoint->setAllowCors(true); - $endpoint->setAllowCredentials(true); - $endpoint->setApi($api); - - foreach($endpoint_info['scopes'] as $scope_name){ - $scope = EntityManager::getRepository(\App\Models\ResourceServer\ApiScope::class)->findOneBy(['name' => $scope_name]); - if(is_null($scope)) continue; - $endpoint->addScope($scope); - } - - EntityManager::persist($endpoint); - } - - EntityManager::flush(); - } - private function seedSummitEndpoints() { $current_realm = Config::get('app.scope_base_realm'); @@ -1180,6 +1148,15 @@ class ApiEndpointsSeeder extends Seeder sprintf(SummitScopes::ReadMyBookableRoomsReservationData, $current_realm), ], ], + [ + 'name' => 'get-bookable-venue-room-reservations-by-id', + 'route' => '/api/v1/summits/all/locations/bookable-rooms/all/reservations/{id}', + 'http_method' => 'GET', + 'scopes' => [ + sprintf(SummitScopes::ReadBookableRoomsData, $current_realm), + sprintf(SummitScopes::ReadAllSummitData, $current_realm) + ], + ], [ 'name' => 'cancel-my-bookable-venue-room-reservation', 'route' => '/api/v1/summits/{id}/locations/bookable-rooms/all/reservations/{reservation_id}', @@ -1206,6 +1183,15 @@ class ApiEndpointsSeeder extends Seeder sprintf(SummitScopes::ReadSummitData, $current_realm), ], ], + [ + 'name' => 'get-bookable-venue-room-reservations-by-summit-csv', + 'route' => '/api/v1/summits/{id}/locations/bookable-rooms/all/reservations/csv', + 'http_method' => 'GET', + 'scopes' => [ + sprintf(SummitScopes::ReadAllSummitData, $current_realm), + sprintf(SummitScopes::ReadSummitData, $current_realm), + ], + ], [ 'name' => 'get-bookable-venue-room-reservation', 'route' => '/api/v1/summits/{id}/locations/bookable-rooms/{room_id}/reservations/{reservation_id}', @@ -2313,6 +2299,38 @@ class ApiEndpointsSeeder extends Seeder ]); } + /** + * @param string $api_name + * @param array $endpoints_info + */ + private function seedApiEndpoints($api_name, array $endpoints_info){ + + $api = EntityManager::getRepository(\App\Models\ResourceServer\Api::class)->findOneBy(['name' => $api_name]); + if(is_null($api)) return; + + foreach($endpoints_info as $endpoint_info){ + + $endpoint = new ApiEndpoint(); + $endpoint->setName($endpoint_info['name']); + $endpoint->setRoute($endpoint_info['route']); + $endpoint->setHttpMethod($endpoint_info['http_method']); + $endpoint->setActive(true); + $endpoint->setAllowCors(true); + $endpoint->setAllowCredentials(true); + $endpoint->setApi($api); + + foreach($endpoint_info['scopes'] as $scope_name){ + $scope = EntityManager::getRepository(\App\Models\ResourceServer\ApiScope::class)->findOneBy(['name' => $scope_name]); + if(is_null($scope)) continue; + $endpoint->addScope($scope); + } + + EntityManager::persist($endpoint); + } + + EntityManager::flush(); + } + private function seedMemberEndpoints(){ $current_realm = Config::get('app.scope_base_realm'); @@ -2451,6 +2469,25 @@ class ApiEndpointsSeeder extends Seeder ); } + private function seedGroupsEndpoints(){ + $current_realm = Config::get('app.scope_base_realm'); + + $this->seedApiEndpoints('groups', [ + // members + [ + 'name' => 'get-groups', + 'route' => '/api/v1/groups', + 'http_method' => 'GET', + 'scopes' => [ + sprintf(SummitScopes::ReadAllSummitData, $current_realm), + sprintf(SummitScopes::ReadSummitData, $current_realm), + sprintf('%s/groups/read', $current_realm) + ], + ] + ] + ); + } + private function seedOrganizationsEndpoints(){ $current_realm = Config::get('app.scope_base_realm'); @@ -2481,25 +2518,6 @@ class ApiEndpointsSeeder extends Seeder ); } - private function seedGroupsEndpoints(){ - $current_realm = Config::get('app.scope_base_realm'); - - $this->seedApiEndpoints('groups', [ - // members - [ - 'name' => 'get-groups', - 'route' => '/api/v1/groups', - 'http_method' => 'GET', - 'scopes' => [ - sprintf(SummitScopes::ReadAllSummitData, $current_realm), - sprintf(SummitScopes::ReadSummitData, $current_realm), - sprintf('%s/groups/read', $current_realm) - ], - ] - ] - ); - } - public function seedTrackQuestionTemplateEndpoints(){ $current_realm = Config::get('app.scope_base_realm'); diff --git a/tests/OAuth2SummitLocationsApiTest.php b/tests/OAuth2SummitLocationsApiTest.php index bd2ef167..beb24ccd 100644 --- a/tests/OAuth2SummitLocationsApiTest.php +++ b/tests/OAuth2SummitLocationsApiTest.php @@ -1708,4 +1708,92 @@ final class OAuth2SummitLocationsApiTest extends ProtectedApiTest $reservations = json_decode($content); $this->assertTrue(!is_null($reservations)); } + + public function testGetAllReservationsBySummitAndOwnerName($summit_id =27){ + $params = [ + 'id' => $summit_id, + 'filter' => 'status==Canceled,owner_name=@Sebastian' + ]; + + $headers = + [ + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ]; + + $response = $this->action + ( + "GET", + "OAuth2SummitLocationsApiController@getAllReservationsBySummit", + $params, + [], + [], + [], + $headers + ); + + $content = $response->getContent(); + $this->assertResponseStatus(200); + + $reservations = json_decode($content); + $this->assertTrue(!is_null($reservations)); + } + + public function testGetAllReservationsBySummitAndOwnerNameCSV($summit_id =27){ + $params = [ + 'id' => $summit_id, + 'filter' => 'status==Canceled,owner_name=@Sebastian' + ]; + + $headers = + [ + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ]; + + $response = $this->action + ( + "GET", + "OAuth2SummitLocationsApiController@getAllReservationsBySummitCSV", + $params, + [], + [], + [], + $headers + ); + + $csv = $response->getContent(); + $this->assertResponseStatus(200); + $this->assertTrue(!empty($csv)); + } + + public function testGetReservationById($id = 2){ + $params = [ + 'id' => $id, + 'filter' => 'status==Canceled,owner_name=@Sebastian' + ]; + + $headers = + [ + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ]; + + $response = $this->action + ( + "GET", + "OAuth2SummitLocationsApiController@getReservationById", + $params, + [], + [], + [], + $headers + ); + + $content = $response->getContent(); + $this->assertResponseStatus(200); + + $reservation = json_decode($content); + $this->assertTrue(!is_null($reservation)); + } } \ No newline at end of file