From d2f7d110ece3f499b6c7ed2ea55422eb079888bd Mon Sep 17 00:00:00 2001 From: Sebastian Marcet Date: Tue, 4 Sep 2018 12:30:28 -0300 Subject: [PATCH] added new endpoint ADD Organization POST /api/v1/organizations payload Change-Id: I713fcb70f380693b8b20a21a569064a0b3c3ed84 name: required|string|max:255 --- .../Main/OAuth2OrganizationsApiController.php | 59 +++++++++++- app/Http/routes.php | 1 + .../Repositories/IOrganizationRepository.php | 6 +- .../Main/DoctrineOrganizationRepository.php | 19 ++++ app/Security/OrganizationScopes.php | 23 +++++ app/Services/Model/IOrganizationService.php | 46 +++++++++ app/Services/Model/OrganizationService.php | 94 +++++++++++++++++++ app/Services/ServicesProvider.php | 7 ++ database/seeds/ApiEndpointsSeeder.php | 18 +++- database/seeds/ApiScopesSeeder.php | 38 +++++++- resources/lang/en/validation_errors.php | 2 + tests/OAuth2OrganizationsApiTest.php | 30 ++++++ tests/ProtectedApiTest.php | 5 + 13 files changed, 338 insertions(+), 10 deletions(-) create mode 100644 app/Security/OrganizationScopes.php create mode 100644 app/Services/Model/IOrganizationService.php create mode 100644 app/Services/Model/OrganizationService.php diff --git a/app/Http/Controllers/Apis/Protected/Main/OAuth2OrganizationsApiController.php b/app/Http/Controllers/Apis/Protected/Main/OAuth2OrganizationsApiController.php index 0f406ddc..1fbffcd0 100644 --- a/app/Http/Controllers/Apis/Protected/Main/OAuth2OrganizationsApiController.php +++ b/app/Http/Controllers/Apis/Protected/Main/OAuth2OrganizationsApiController.php @@ -11,8 +11,10 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ +use App\Services\Model\IOrganizationService; use models\main\IOrganizationRepository; use models\oauth2\IResourceServerContext; +use ModelSerializers\SerializerRegistry; use utils\Filter; use utils\FilterParser; use utils\FilterParserException; @@ -31,18 +33,26 @@ use Illuminate\Support\Facades\Validator; final class OAuth2OrganizationsApiController extends OAuth2ProtectedController { /** - * OAuth2MembersApiController constructor. - * @param IOrganizationRepository $organization_repository + * @var IOrganizationService + */ + private $service; + + /** + * OAuth2OrganizationsApiController constructor. + * @param IOrganizationRepository $company_repository * @param IResourceServerContext $resource_server_context + * @param IOrganizationService $service */ public function __construct ( IOrganizationRepository $company_repository, - IResourceServerContext $resource_server_context + IResourceServerContext $resource_server_context, + IOrganizationService $service ) { parent::__construct($resource_server_context); $this->repository = $company_repository; + $this->service = $service; } public function getAll(){ @@ -128,4 +138,47 @@ final class OAuth2OrganizationsApiController extends OAuth2ProtectedController return $this->error500($ex); } } + + + public function addOrganization(){ + try { + + if(!Request::isJson()) return $this->error400(); + + $data = Input::json(); + + $rules = [ + 'name' => 'required|string|max:255', + ]; + + // 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 + ); + } + + $organization = $this->service->addOrganization($data->all()); + + return $this->created(SerializerRegistry::getInstance()->getSerializer($organization)->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 c3d0a958..f667adb8 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -74,6 +74,7 @@ Route::group([ // organizations Route::group(['prefix'=>'organizations'], function(){ Route::get('', 'OAuth2OrganizationsApiController@getAll'); + Route::post('', 'OAuth2OrganizationsApiController@addOrganization'); }); // groups diff --git a/app/Models/Foundation/Main/Repositories/IOrganizationRepository.php b/app/Models/Foundation/Main/Repositories/IOrganizationRepository.php index a26864bf..cc9f5e30 100644 --- a/app/Models/Foundation/Main/Repositories/IOrganizationRepository.php +++ b/app/Models/Foundation/Main/Repositories/IOrganizationRepository.php @@ -18,5 +18,9 @@ use models\utils\IBaseRepository; */ interface IOrganizationRepository extends IBaseRepository { - + /** + * @param string $name + * @return Organization|null + */ + public function getByName($name); } \ No newline at end of file diff --git a/app/Repositories/Main/DoctrineOrganizationRepository.php b/app/Repositories/Main/DoctrineOrganizationRepository.php index 0799e81d..ce004d4c 100644 --- a/app/Repositories/Main/DoctrineOrganizationRepository.php +++ b/app/Repositories/Main/DoctrineOrganizationRepository.php @@ -51,4 +51,23 @@ final class DoctrineOrganizationRepository 'name' => 'e.name', ]; } + + /** + * @param string $name + * @return Organization|null + */ + public function getByName($name) + { + try { + return $this->getEntityManager()->createQueryBuilder() + ->select("t") + ->from(\models\main\Organization::class, "o") + ->where('UPPER(TRIM(o.name)) = UPPER(TRIM(:name))') + ->setParameter('name', $name) + ->getQuery()->getOneOrNullResult(); + } + catch(\Exception $ex){ + return null; + } + } } \ No newline at end of file diff --git a/app/Security/OrganizationScopes.php b/app/Security/OrganizationScopes.php new file mode 100644 index 00000000..25d67684 --- /dev/null +++ b/app/Security/OrganizationScopes.php @@ -0,0 +1,23 @@ +organization_repository = $organization_repository; + } + + /** + * @param array $data + * @return Organization + * @throws EntityNotFoundException + * @throws ValidationException + */ + public function addOrganization(array $data) + { + return $this->tx_service->transaction(function () use($data){ + + $name = trim( $data['name']); + $old_organization = $this->organization_repository->getByName($name); + if(!is_null($old_organization)) + throw new ValidationException(trans("validation_errors.OrganizationService.addOrganization.alreadyExistName", ["name" => $name])); + + $new_organization = new Organization(); + $new_organization->setName($name); + $this->organization_repository->add($new_organization); + return $new_organization; + }); + } + + /** + * @param array $data + * @param int $organization_id + * @return Organization + * @throws EntityNotFoundException + * @throws ValidationException + */ + public function updateOrganization(array $data, $organization_id) + { + // TODO: Implement updateOrganization() method. + } + + /** + * @param int $organization_id + * @return void + * @throws EntityNotFoundException + */ + public function deleteOrganization($organization_id) + { + // TODO: Implement deleteOrganization() method. + } +} \ No newline at end of file diff --git a/app/Services/ServicesProvider.php b/app/Services/ServicesProvider.php index 5e56f261..1be212a1 100644 --- a/app/Services/ServicesProvider.php +++ b/app/Services/ServicesProvider.php @@ -22,6 +22,7 @@ use App\Services\Model\IAttendeeService; use App\Services\Model\IFolderService; use App\Services\Model\ILocationService; use App\Services\Model\IMemberService; +use App\Services\Model\IOrganizationService; use App\Services\Model\IPresentationCategoryGroupService; use App\Services\Model\IRSVPTemplateService; use App\Services\Model\ISummitEventTypeService; @@ -29,6 +30,7 @@ use App\Services\Model\ISummitPushNotificationService; use App\Services\Model\ISummitSelectionPlanService; use App\Services\Model\ISummitTicketTypeService; use App\Services\Model\ISummitTrackService; +use App\Services\Model\OrganizationService; use App\Services\Model\PresentationCategoryGroupService; use App\Services\Model\SummitLocationService; use App\Services\Model\MemberService; @@ -236,5 +238,10 @@ final class ServicesProvider extends ServiceProvider ISummitSelectionPlanService::class, SummitSelectionPlanService::class ); + + App::singleton( + IOrganizationService::class, + OrganizationService::class + ); } } \ No newline at end of file diff --git a/database/seeds/ApiEndpointsSeeder.php b/database/seeds/ApiEndpointsSeeder.php index 6d604460..66edb391 100644 --- a/database/seeds/ApiEndpointsSeeder.php +++ b/database/seeds/ApiEndpointsSeeder.php @@ -16,6 +16,7 @@ use Illuminate\Support\Facades\Config; use App\Models\ResourceServer\ApiEndpoint; use LaravelDoctrine\ORM\Facades\EntityManager; use App\Security\SummitScopes; +use App\Security\OrganizationScopes; /** * Class ApiEndpointsSeeder */ @@ -1866,9 +1867,20 @@ class ApiEndpointsSeeder extends Seeder 'route' => '/api/v1/organizations', 'http_method' => 'GET', 'scopes' => [ - sprintf(SummitScopes::ReadAllSummitData, $current_realm), - sprintf(SummitScopes::ReadSummitData, $current_realm), - sprintf('%s/organizations/read', $current_realm) + sprintf(OrganizationScopes::ReadOrganizationData, $current_realm) + ], + ] + ] + ); + + $this->seedApiEndpoints('organizations', [ + // organizations + [ + 'name' => 'add-organizations', + 'route' => '/api/v1/organizations', + 'http_method' => 'POST', + 'scopes' => [ + sprintf(OrganizationScopes::WriteOrganizationData, $current_realm) ], ] ] diff --git a/database/seeds/ApiScopesSeeder.php b/database/seeds/ApiScopesSeeder.php index e6f629ff..08354ae4 100644 --- a/database/seeds/ApiScopesSeeder.php +++ b/database/seeds/ApiScopesSeeder.php @@ -17,6 +17,7 @@ use App\Models\ResourceServer\ApiScope; use LaravelDoctrine\ORM\Facades\EntityManager; use Illuminate\Support\Facades\DB; use App\Security\SummitScopes; +use App\Security\OrganizationScopes; /** * Class ApiScopesSeeder */ @@ -34,6 +35,7 @@ final class ApiScopesSeeder extends Seeder $this->seedTagsScopes(); $this->seedCompaniesScopes(); $this->seedGroupsScopes(); + $this->seedOrganizationScopes(); } private function seedSummitScopes() @@ -208,14 +210,14 @@ final class ApiScopesSeeder extends Seeder private function seedTagsScopes(){ $current_realm = Config::get('app.url'); - $api = EntityManager::getRepository(\App\Models\ResourceServer\Api::class)->findOneBy(['name' => 'tags']); + $api = EntityManager::getRepository(\App\Models\ResourceServer\Api::class)->findOneBy(['name' => 'organizations']); $scopes = [ - array( + [ 'name' => sprintf('%s/tags/read', $current_realm), 'short_description' => 'Get Tags Data', 'description' => 'Grants read only access for Tags Data', - ), + ], ]; foreach ($scopes as $scope_info) { @@ -232,6 +234,36 @@ final class ApiScopesSeeder extends Seeder EntityManager::flush(); } + private function seedOrganizationScopes(){ + $current_realm = Config::get('app.url'); + $api = EntityManager::getRepository(\App\Models\ResourceServer\Api::class)->findOneBy(['name' => 'companies']); + + $scopes = [ + [ + 'name' => sprintf(OrganizationScopes::ReadOrganizationData, $current_realm), + 'short_description' => 'Get Organizations Data', + 'description' => 'Grants read only access for Organization Data', + ], + [ + 'name' => sprintf(OrganizationScopes::WriteOrganizationData, $current_realm), + 'short_description' => 'Write Companies Data', + 'description' => 'Grants write access for Organization Data', + ], + ]; + + foreach ($scopes as $scope_info) { + $scope = new ApiScope(); + $scope->setName($scope_info['name']); + $scope->setShortDescription($scope_info['short_description']); + $scope->setDescription($scope_info['description']); + $scope->setActive(true); + $scope->setDefault(false); + $scope->setApi($api); + EntityManager::persist($scope); + } + + EntityManager::flush(); + } private function seedCompaniesScopes(){ $current_realm = Config::get('app.url'); diff --git a/resources/lang/en/validation_errors.php b/resources/lang/en/validation_errors.php index 2d26ebde..710adecf 100644 --- a/resources/lang/en/validation_errors.php +++ b/resources/lang/en/validation_errors.php @@ -83,4 +83,6 @@ return [ 'PresentationService.saveOrUpdatePresentation.notAvailableCFP' => 'type id :type_id is not a available for CFP', 'PresentationService.saveOrUpdatePresentation.trackDontBelongToSelectionPlan' => 'track :track_id does not belongs to selection plan :selection_plan_id', 'PresentationService.submitPresentation.limitReached' => 'You reached the limit :limit of presentations.', + // organizations + 'OrganizationService.addOrganization.alreadyExistName' => 'Organization name :name already exists!', ]; \ No newline at end of file diff --git a/tests/OAuth2OrganizationsApiTest.php b/tests/OAuth2OrganizationsApiTest.php index a1721219..67da5d44 100644 --- a/tests/OAuth2OrganizationsApiTest.php +++ b/tests/OAuth2OrganizationsApiTest.php @@ -41,4 +41,34 @@ class OAuth2OrganizationsApiTest extends ProtectedApiTest $this->assertResponseStatus(200); } + public function testAddOrganization(){ + + $name = str_random(16).'_org_name'; + $data = [ + 'name' => $name, + ]; + + $headers = [ + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ]; + + $response = $this->action( + "POST", + "OAuth2OrganizationsApiController@addOrganization", + [], + [], + [], + [], + $headers, + json_encode($data) + ); + + $content = $response->getContent(); + $this->assertResponseStatus(201); + $org = json_decode($content); + $this->assertTrue(!is_null($org)); + return $org; + } + } \ No newline at end of file diff --git a/tests/ProtectedApiTest.php b/tests/ProtectedApiTest.php index 881f8323..48fca5ce 100644 --- a/tests/ProtectedApiTest.php +++ b/tests/ProtectedApiTest.php @@ -16,6 +16,7 @@ use Illuminate\Support\Facades\Config; use models\oauth2\AccessToken; use App\Models\ResourceServer\IAccessTokenService; use App\Security\SummitScopes; +use App\Security\OrganizationScopes; /** * Class AccessTokenServiceStub */ @@ -61,6 +62,8 @@ class AccessTokenServiceStub implements IAccessTokenService sprintf(SummitScopes::WriteAttendeesData, $url), sprintf(SummitScopes::WriteMemberData, $url), sprintf(SummitScopes::WritePromoCodeData, $url), + sprintf(OrganizationScopes::WriteOrganizationData, $url), + sprintf(OrganizationScopes::ReadOrganizationData, $url), ); return AccessToken::createFromParams('123456789', implode(' ', $scopes), '1', $realm, '1','11624', 3600, 'WEB_APPLICATION', '', ''); @@ -110,6 +113,8 @@ class AccessTokenServiceStub2 implements IAccessTokenService sprintf(SummitScopes::WriteAttendeesData, $url), sprintf(SummitScopes::WriteMemberData, $url), sprintf(SummitScopes::WritePromoCodeData, $url), + sprintf(OrganizationScopes::WriteOrganizationData, $url), + sprintf(OrganizationScopes::ReadOrganizationData, $url), ); return AccessToken::createFromParams('123456789', implode(' ', $scopes), '1', $realm, null,null, 3600, 'SERVICE', '', '');