From e6af97c7e8723d640c87dfb461e752e1e3cfc2f5 Mon Sep 17 00:00:00 2001 From: Sebastian Marcet Date: Thu, 29 Mar 2018 11:06:47 -0300 Subject: [PATCH] Added endpoints to create/update RSVP templates by summit POST /api/v1/summits/{id}/rsvp-templates Payload * title (required|string|max:255) * is_enabled (required|boolean) PUT /api/v1/summits/{id}/rsvp-templates/{template_id} Payload * title (sometimes|string|max:255) * is_enabled (sometimes|boolean) Change-Id: I8d379bd796382ec75fef238ad2e2b9681d51e909 --- ...mmitTicketTypeActionEntityEventFactory.php | 2 +- ...mmitRSVPTemplateValidationRulesFactory.php | 33 ++++++ ...OAuth2SummitRSVPTemplatesApiController.php | 111 +++++++++++++++++- app/Http/routes.php | 2 + .../RSVP/Templates/RSVPTemplateSerializer.php | 3 +- .../Factories/SummitRSVPTemplateFactory.php | 44 +++++++ app/Models/Foundation/Summit/Summit.php | 24 ++++ app/Services/Model/IRSVPTemplateService.php | 22 ++++ app/Services/Model/RSVPTemplateService.php | 86 ++++++++++++++ resources/lang/en/not_found_errors.php | 1 + resources/lang/en/validation_errors.php | 4 + tests/OAuth2SummitRSVPTemplateApiTest.php | 77 ++++++++++++ 12 files changed, 405 insertions(+), 4 deletions(-) create mode 100644 app/Http/Controllers/Apis/Protected/Summit/Factories/SummitRSVPTemplateValidationRulesFactory.php create mode 100644 app/Models/Foundation/Summit/Factories/SummitRSVPTemplateFactory.php diff --git a/app/Factories/EntityEvents/SummitTicketTypeActionEntityEventFactory.php b/app/Factories/EntityEvents/SummitTicketTypeActionEntityEventFactory.php index 15e0d06a..02406857 100644 --- a/app/Factories/EntityEvents/SummitTicketTypeActionEntityEventFactory.php +++ b/app/Factories/EntityEvents/SummitTicketTypeActionEntityEventFactory.php @@ -11,8 +11,8 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ - use App\Events\SummitTicketTypeAction; +use Illuminate\Support\Facades\App; use models\main\IMemberRepository; use models\oauth2\IResourceServerContext; use models\summit\ISummitRepository; diff --git a/app/Http/Controllers/Apis/Protected/Summit/Factories/SummitRSVPTemplateValidationRulesFactory.php b/app/Http/Controllers/Apis/Protected/Summit/Factories/SummitRSVPTemplateValidationRulesFactory.php new file mode 100644 index 00000000..3b16e9c9 --- /dev/null +++ b/app/Http/Controllers/Apis/Protected/Summit/Factories/SummitRSVPTemplateValidationRulesFactory.php @@ -0,0 +1,33 @@ + 'sometimes|string|max:255', + 'is_enabled' => 'sometimes|boolean', + ]; + } + return [ + 'title' => 'required|string|max:255', + 'is_enabled' => 'required|boolean', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitRSVPTemplatesApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitRSVPTemplatesApiController.php index bc2d3a35..13191dd4 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitRSVPTemplatesApiController.php +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitRSVPTemplatesApiController.php @@ -21,6 +21,7 @@ use Illuminate\Support\Facades\Request; use Illuminate\Support\Facades\Validator; use models\exceptions\EntityNotFoundException; use models\exceptions\ValidationException; +use models\main\IMemberRepository; use models\oauth2\IResourceServerContext; use models\summit\ISummitRepository; use ModelSerializers\SerializerRegistry; @@ -51,10 +52,16 @@ final class OAuth2SummitRSVPTemplatesApiController extends OAuth2ProtectedContro */ private $rsvp_template_service; + /** + * @var IMemberRepository + */ + private $member_repository; + /** * OAuth2SummitRSVPTemplatesApiController constructor. * @param ISummitRepository $summit_repository * @param IRSVPTemplateRepository $rsvp_template_repository + * @param IMemberRepository $member_repository * @param IRSVPTemplateService $rsvp_template_service * @param IResourceServerContext $resource_server_context */ @@ -62,16 +69,22 @@ final class OAuth2SummitRSVPTemplatesApiController extends OAuth2ProtectedContro ( ISummitRepository $summit_repository, IRSVPTemplateRepository $rsvp_template_repository, + IMemberRepository $member_repository, IRSVPTemplateService $rsvp_template_service, IResourceServerContext $resource_server_context ) { parent::__construct($resource_server_context); $this->summit_repository = $summit_repository; + $this->member_repository = $member_repository; $this->rsvp_template_service = $rsvp_template_service; $this->rsvp_template_repository = $rsvp_template_repository; } + /** + * Template endpoints + */ + /** * @param $summit_id * @return mixed @@ -235,6 +248,102 @@ final class OAuth2SummitRSVPTemplatesApiController extends OAuth2ProtectedContro } } + /** + * @param $summit_id + * @return mixed + */ + public function addRSVPTemplate($summit_id){ + try { + + if(!Request::isJson()) return $this->error400(); + $payload = Input::json()->all(); + + $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->resource_server_context)->find($summit_id); + if (is_null($summit)) return $this->error404(); + + $rules = SummitRSVPTemplateValidationRulesFactory::build($payload); + // Creates a Validator instance and validates the data. + $validation = Validator::make($payload, $rules); + + if ($validation->fails()) { + $messages = $validation->messages()->toArray(); + + return $this->error412 + ( + $messages + ); + } + + $current_member = null; + if(!is_null($this->resource_server_context->getCurrentUserExternalId())){ + $current_member = $this->member_repository->getById($this->resource_server_context->getCurrentUserExternalId()); + } + + $template = $this->rsvp_template_service->addTemplate($summit, $current_member, $payload); + + return $this->created(SerializerRegistry::getInstance()->getSerializer($template)->serialize()); + } + catch (ValidationException $ex1) { + Log::warning($ex1); + return $this->error412([$ex1->getMessage()]); + } + catch(EntityNotFoundException $ex2) + { + Log::warning($ex2); + return $this->error404(['message'=> $ex2->getMessage()]); + } + catch (Exception $ex) { + Log::error($ex); + return $this->error500($ex); + } + } + + /** + * @param $summit_id + * @param $template_id + * @return mixed + */ + public function updateRSVPTemplate($summit_id, $template_id){ + try { + + if(!Request::isJson()) return $this->error400(); + $payload = Input::json()->all(); + + $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->resource_server_context)->find($summit_id); + if (is_null($summit)) return $this->error404(); + + $rules = SummitRSVPTemplateValidationRulesFactory::build($payload, true); + // Creates a Validator instance and validates the data. + $validation = Validator::make($payload, $rules); + + if ($validation->fails()) { + $messages = $validation->messages()->toArray(); + + return $this->error412 + ( + $messages + ); + } + + $template = $this->rsvp_template_service->updateTemplate($summit, $template_id, $payload); + + return $this->updated(SerializerRegistry::getInstance()->getSerializer($template)->serialize()); + } + catch (ValidationException $ex1) { + Log::warning($ex1); + return $this->error412([$ex1->getMessage()]); + } + catch(EntityNotFoundException $ex2) + { + Log::warning($ex2); + return $this->error404(['message'=> $ex2->getMessage()]); + } + catch (Exception $ex) { + Log::error($ex); + return $this->error500($ex); + } + } + /** * Questions endpoints */ @@ -457,7 +566,7 @@ final class OAuth2SummitRSVPTemplatesApiController extends OAuth2ProtectedContro $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->resource_server_context)->find($summit_id); if (is_null($summit)) return $this->error404(); - $rules = SummitRSVPTemplateQuestionValueValidationRulesFactory::build($payload, true); + $rules = SummitRSVPTemplateQuestionValueValidationRulesFactory::build($payload); // Creates a Validator instance and validates the data. $validation = Validator::make($payload, $rules); diff --git a/app/Http/routes.php b/app/Http/routes.php index 074a075c..e6e26666 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -170,8 +170,10 @@ Route::group([ // rsvp templates Route::group(['prefix' => 'rsvp-templates'], function () { Route::get('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitRSVPTemplatesApiController@getAllBySummit']); + Route::post('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitRSVPTemplatesApiController@addRSVPTemplate']); Route::group(['prefix' => '{template_id}'], function () { Route::get('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitRSVPTemplatesApiController@getRSVPTemplate']); + Route::put('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitRSVPTemplatesApiController@updateRSVPTemplate']); Route::delete('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitRSVPTemplatesApiController@deleteRSVPTemplate']); Route::group(['prefix' => 'questions'], function () { Route::post('', [ 'middleware' => 'auth.user:administrators|summit-front-end-administrators', 'uses' => 'OAuth2SummitRSVPTemplatesApiController@addRSVPTemplateQuestion']); diff --git a/app/ModelSerializers/Summit/RSVP/Templates/RSVPTemplateSerializer.php b/app/ModelSerializers/Summit/RSVP/Templates/RSVPTemplateSerializer.php index 0e0bd9af..dddcbf7e 100644 --- a/app/ModelSerializers/Summit/RSVP/Templates/RSVPTemplateSerializer.php +++ b/app/ModelSerializers/Summit/RSVP/Templates/RSVPTemplateSerializer.php @@ -21,9 +21,8 @@ use ModelSerializers\SilverStripeSerializer; final class RSVPTemplateSerializer extends SilverStripeSerializer { protected static $array_mappings = [ - 'Title' => 'title:json_string', - 'Enabled' => 'is_enable:json_boolean', + 'Enabled' => 'is_enabled:json_boolean', 'CreatedById' => 'created_by_id:json_int', 'SummitId' => 'summit_id:json_int', ]; diff --git a/app/Models/Foundation/Summit/Factories/SummitRSVPTemplateFactory.php b/app/Models/Foundation/Summit/Factories/SummitRSVPTemplateFactory.php new file mode 100644 index 00000000..5c1d0ca4 --- /dev/null +++ b/app/Models/Foundation/Summit/Factories/SummitRSVPTemplateFactory.php @@ -0,0 +1,44 @@ +setTitle(trim($data['title'])); + + if(isset($data['is_enabled'])) + $template->setIsEnabled(boolval($data['is_enabled'])); + + return $template; + } +} \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Summit.php b/app/Models/Foundation/Summit/Summit.php index f82b3908..ca40f550 100644 --- a/app/Models/Foundation/Summit/Summit.php +++ b/app/Models/Foundation/Summit/Summit.php @@ -1751,11 +1751,35 @@ SQL; return $rsvp_template === false ? null : $rsvp_template; } + /** + * @param string $rsvp_template_title + * @return RSVPTemplate|null + */ + public function getRSVPTemplateByTitle($rsvp_template_title){ + $criteria = Criteria::create(); + $criteria->where(Criteria::expr()->eq('title', trim($rsvp_template_title))); + $rsvp_template = $this->rsvp_templates->matching($criteria)->first(); + return $rsvp_template === false ? null : $rsvp_template; + } + + + /** + * @param RSVPTemplate $template + * @return $this + */ + public function addRSVPTemplate(RSVPTemplate $template){ + if($this->rsvp_templates->contains($template)) return; + $this->rsvp_templates->add($template); + $template->setSummit($this); + return $this; + } + /** * @param RSVPTemplate $template * @return $this */ public function removeRSVPTemplate(RSVPTemplate $template){ + if(!$this->rsvp_templates->contains($template)) return; $this->rsvp_templates->removeElement($template); $template->clearSummit(); return $this; diff --git a/app/Services/Model/IRSVPTemplateService.php b/app/Services/Model/IRSVPTemplateService.php index da2a97fb..85e96c54 100644 --- a/app/Services/Model/IRSVPTemplateService.php +++ b/app/Services/Model/IRSVPTemplateService.php @@ -13,8 +13,10 @@ **/ use App\Models\Foundation\Summit\Events\RSVP\RSVPQuestionTemplate; use App\Models\Foundation\Summit\Events\RSVP\RSVPQuestionValueTemplate; +use App\Models\Foundation\Summit\Events\RSVP\RSVPTemplate; use models\exceptions\EntityNotFoundException; use models\exceptions\ValidationException; +use models\main\Member; use models\summit\Summit; /** * Interface IRSVPTemplateService @@ -22,6 +24,26 @@ use models\summit\Summit; */ interface IRSVPTemplateService { + /** + * @param Summit $summit + * @param Member|null $creator + * @param array $payload + * @return RSVPTemplate + * @throws EntityNotFoundException + * @throws ValidationException + */ + public function addTemplate(Summit $summit, Member $creator, array $payload); + + /** + * @param Summit $summit + * @param int $template_id + * @param array $payload + * @return RSVPTemplate + * @throws EntityNotFoundException + * @throws ValidationException + */ + public function updateTemplate(Summit $summit, $template_id, array $payload); + /** * @param Summit $summit * @param int $template_id diff --git a/app/Services/Model/RSVPTemplateService.php b/app/Services/Model/RSVPTemplateService.php index b2e5cb3b..9f2fab92 100644 --- a/app/Services/Model/RSVPTemplateService.php +++ b/app/Services/Model/RSVPTemplateService.php @@ -14,12 +14,15 @@ use App\Models\Foundation\Summit\Events\RSVP\RSVPMultiValueQuestionTemplate; use App\Models\Foundation\Summit\Events\RSVP\RSVPQuestionTemplate; use App\Models\Foundation\Summit\Events\RSVP\RSVPQuestionValueTemplate; +use App\Models\Foundation\Summit\Events\RSVP\RSVPTemplate; +use App\Models\Foundation\Summit\Factories\SummitRSVPTemplateFactory; use App\Models\Foundation\Summit\Factories\SummitRSVPTemplateQuestionFactory; use App\Models\Foundation\Summit\Factories\SummitRSVPTemplateQuestionValueFactory; use App\Models\Foundation\Summit\Repositories\IRSVPTemplateRepository; use libs\utils\ITransactionService; use models\exceptions\EntityNotFoundException; use models\exceptions\ValidationException; +use models\main\Member; use models\summit\Summit; /** * Class RSVPTemplateService @@ -45,6 +48,88 @@ final class RSVPTemplateService $this->rsvp_template_repository = $rsvp_template_repository; } + /** + * @param Summit $summit + * @param Member|null $creator + * @param array $payload + * @return RSVPTemplate + * @throws EntityNotFoundException + * @throws ValidationException + */ + public function addTemplate(Summit $summit, Member $creator, array $payload) + { + return $this->tx_service->transaction(function() use($summit, $creator, $payload){ + + $former_template = $summit->getRSVPTemplateByTitle($payload['title']); + + if(!is_null($former_template)){ + throw new ValidationException + ( + trans('validation_errors.RSVPTemplateService.addTemplate.TitleAlreadyExists'), + [ + 'title' => $payload['title'], + 'summit_id' => $summit->getId() + ] + ); + } + + $template = SummitRSVPTemplateFactory::build($payload); + + if(!is_null($creator)) + $template->setCreatedBy($creator); + + $summit->addRSVPTemplate($template); + + return $template; + }); + } + + /** + * @param Summit $summit + * @param int $template_id + * @param array $payload + * @return RSVPTemplate + * @throws EntityNotFoundException + * @throws ValidationException + */ + public function updateTemplate(Summit $summit, $template_id, array $payload) + { + return $this->tx_service->transaction(function() use($summit, $template_id, $payload){ + + if(isset($payload['title'])) { + $former_template = $summit->getRSVPTemplateByTitle($payload['title']); + + if (!is_null($former_template) && $former_template->getId() != $template_id) { + throw new ValidationException + ( + trans('validation_errors.RSVPTemplateService.updateTemplate.TitleAlreadyExists', + [ + 'title' => $payload['title'], + 'summit_id' => $summit->getId() + ] + ) + ); + } + } + + $template = $summit->getRSVPTemplateById($template_id); + + if(is_null($template)){ + throw new EntityNotFoundException + ( + trans('not_found_errors.RSVPTemplateService.updateTemplate.TemplateNotFound', + [ + 'template_id' => $template_id, + 'summit_id' => $summit->getId() + ] + ) + ); + } + + return SummitRSVPTemplateFactory::populate($template, $payload); + }); + } + /** * @param Summit $summit * @param int $template_id @@ -508,4 +593,5 @@ final class RSVPTemplateService }); } + } \ No newline at end of file diff --git a/resources/lang/en/not_found_errors.php b/resources/lang/en/not_found_errors.php index d4c11301..f9033dc8 100644 --- a/resources/lang/en/not_found_errors.php +++ b/resources/lang/en/not_found_errors.php @@ -42,6 +42,7 @@ return [ 'LocationService.deleteLocationImage.LocationNotFound' => 'location :location_id not found on summit :summit_id', 'LocationService.deleteLocationImage.ImageNotFound' => 'image :image_id not found on location :location_id', // RSVPTemplateService + 'RSVPTemplateService.updateTemplate.TemplateNotFound' => 'template :template_id not found on summit :summit_id', 'RSVPTemplateService.deleteTemplate.TemplateNotFound' => 'template :template_id not found on summit :summit_id', 'RSVPTemplateService.addQuestion.TemplateNotFound' => 'template :template_id not found on summit :summit_id', 'RSVPTemplateService.updateQuestion.TemplateNotFound' => 'template :template_id not found on summit :summit_id', diff --git a/resources/lang/en/validation_errors.php b/resources/lang/en/validation_errors.php index 4d56e6e2..63063dcf 100644 --- a/resources/lang/en/validation_errors.php +++ b/resources/lang/en/validation_errors.php @@ -55,6 +55,8 @@ return [ 'RSVPTemplateService.addQuestion.QuestionNameAlreadyExists' => 'question name :name already exists for template :template_id', 'RSVPTemplateService.updateQuestion.QuestionNameAlreadyExists' => 'question name :name already exists for template :template_id', 'RSVPTemplateService.addQuestionValue.ValueAlreadyExist' => 'value :value already exists on question :question_id', + 'RSVPTemplateService.addTemplate.TitleAlreadyExists' => 'title :title already exists on summit :summit_id', + 'RSVPTemplateService.updateTemplate.TitleAlreadyExists' => 'title :title already exists on summit :summit_id', // SummitTicketTypeService 'SummitTicketTypeService.addTicketType.NameAlreadyExists' => 'ticket name :name already exists on summit :summit_id', 'SummitTicketTypeService.addTicketType.ExternalIdAlreadyExists' => 'ticket external id :external_id already exists on summit :summit_id', @@ -63,4 +65,6 @@ return [ 'SummitTicketTypeService.seedSummitTicketTypesFromEventBrite.MissingExternalId' => 'summit :summit_is has not set external id (eventbrite)', // PresentationCategoryGroupService 'PresentationCategoryGroupService.addTrackGroup.NameAlreadyExists' => 'name :name already exists for summit :summit_id', + + ]; \ No newline at end of file diff --git a/tests/OAuth2SummitRSVPTemplateApiTest.php b/tests/OAuth2SummitRSVPTemplateApiTest.php index a6972774..2b841426 100644 --- a/tests/OAuth2SummitRSVPTemplateApiTest.php +++ b/tests/OAuth2SummitRSVPTemplateApiTest.php @@ -82,6 +82,83 @@ final class OAuth2SummitRSVPTemplateApiTest extends ProtectedApiTest return $rsvp_template; } + public function testAddRSVPTemplate($summit_id = 24){ + + $params = [ + 'id' => $summit_id, + ]; + + $title = str_random(16).'_rsvp_template_title'; + + $data = [ + 'title' => $title, + 'is_enabled' => false, + ]; + + $headers = [ + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ]; + + $response = $this->action( + "POST", + "OAuth2SummitRSVPTemplatesApiController@addRSVPTemplate", + $params, + [], + [], + [], + $headers, + json_encode($data) + ); + + $content = $response->getContent(); + $this->assertResponseStatus(201); + + $template = json_decode($content); + $this->assertTrue(!is_null($template)); + $this->assertTrue($template->title == $title); + return $template; + } + + public function testUpdateRSVPTemplate($summit_id = 24){ + + $template = $this->testAddRSVPTemplate($summit_id); + + $params = [ + 'id' => $summit_id, + 'template_id' => $template->id + ]; + + $data = [ + 'is_enabled' => true, + ]; + + $headers = [ + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ]; + + $response = $this->action( + "PUT", + "OAuth2SummitRSVPTemplatesApiController@updateRSVPTemplate", + $params, + [], + [], + [], + $headers, + json_encode($data) + ); + + $content = $response->getContent(); + $this->assertResponseStatus(201); + + $template = json_decode($content); + $this->assertTrue(!is_null($template)); + $this->assertTrue($template->is_enabled == true); + return $template; + } + + public function testDeleteRSVPTemplate($summit_id = 23){ $template = $this->testGetRSVPTemplateById($summit_id);