diff --git a/app/Http/Controllers/apis/protected/main/OAuth2TeamInvitationsApiController.php b/app/Http/Controllers/apis/protected/main/OAuth2TeamInvitationsApiController.php index 5cc81113..f17e6061 100644 --- a/app/Http/Controllers/apis/protected/main/OAuth2TeamInvitationsApiController.php +++ b/app/Http/Controllers/apis/protected/main/OAuth2TeamInvitationsApiController.php @@ -1,6 +1,6 @@ service = $service; } + /** + * @return mixed + */ public function getMyInvitations(){ try { @@ -83,6 +86,80 @@ final class OAuth2TeamInvitationsApiController extends OAuth2ProtectedController } } + /** + * @return mixed + */ + public function getMyPendingInvitations(){ + + try { + $current_member_id = $this->resource_server_context->getCurrentUserExternalId(); + if (is_null($current_member_id)) return $this->error403(); + + $invitations = $this->repository->getPendingInvitationsByInvitee($current_member_id); + + $response = new PagingResponse + ( + count($invitations), + count($invitations), + 1, + 1, + $invitations + ); + + return $this->ok($response->toArray($expand = Input::get('expand',''))); + } + 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); + } + } + + /** + * @return mixed + */ + public function getMyAcceptedInvitations(){ + + try { + $current_member_id = $this->resource_server_context->getCurrentUserExternalId(); + if (is_null($current_member_id)) return $this->error403(); + + $invitations = $this->repository->getAcceptedInvitationsByInvitee($current_member_id); + + $response = new PagingResponse + ( + count($invitations), + count($invitations), + 1, + 1, + $invitations + ); + + return $this->ok($response->toArray($expand = Input::get('expand',''))); + } + 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 $invitation_id * @return mixed diff --git a/app/Http/routes.php b/app/Http/routes.php index 0e6b00a7..441b4150 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -53,6 +53,8 @@ Route::group(array( // invitations Route::group(['prefix'=>'team-invitations'], function(){ Route::get('', 'OAuth2TeamInvitationsApiController@getMyInvitations'); + Route::get('pending', 'OAuth2TeamInvitationsApiController@getMyPendingInvitations'); + Route::get('accepted', 'OAuth2TeamInvitationsApiController@getMyAcceptedInvitations'); Route::group(['prefix'=>'{invitation_id}'], function() { Route::put('', 'OAuth2TeamInvitationsApiController@acceptInvitation'); Route::delete('', 'OAuth2TeamInvitationsApiController@declineInvitation'); diff --git a/app/Models/Foundation/Main/ChatTeams/ChatTeam.php b/app/Models/Foundation/Main/ChatTeams/ChatTeam.php index aeca5de2..53fd7eef 100644 --- a/app/Models/Foundation/Main/ChatTeams/ChatTeam.php +++ b/app/Models/Foundation/Main/ChatTeams/ChatTeam.php @@ -258,6 +258,18 @@ class ChatTeam extends SilverstripeBaseModel return $this->getOwnerId() == $member->getId(); } + /** + * @param Member $member + * @return bool + */ + public function isAlreadyInvited(Member $member){ + $res = $this->invitations->filter(function ($i) use($member){ + return $i->getInvitee()->getId() == $member->getId() && $i->isPending(); + }); + + return $res->count() > 0; + } + /** * @param Member $member * @return bool @@ -276,10 +288,19 @@ class ChatTeam extends SilverstripeBaseModel $res = $this->members->filter(function ($e) use($member){ return $e->getMember()->getId() == $member->getId(); }); - if($res->count() == 0) return; + if($res->count() == 0) return; + // remove member $team_member = $res->first(); $this->members->removeElement($team_member); + $res2 = $this->invitations->filter(function ($i) use($member){ + return $i->getInvitee()->getId() == $member->getId(); + }); + // remove invitation + if($res2->count() > 0) { + $this->invitations->removeElement($res2->first()); + } + $team_member->setTeam(null); } } \ No newline at end of file diff --git a/app/Models/Foundation/Main/ChatTeams/ChatTeamInvitation.php b/app/Models/Foundation/Main/ChatTeams/ChatTeamInvitation.php index f930e85b..7c0168b6 100644 --- a/app/Models/Foundation/Main/ChatTeams/ChatTeamInvitation.php +++ b/app/Models/Foundation/Main/ChatTeams/ChatTeamInvitation.php @@ -164,6 +164,13 @@ class ChatTeamInvitation extends SilverstripeBaseModel return $this->getIsAccepted(); } + /** + * @return bool + */ + public function isPending(){ + return !$this->getIsAccepted(); + } + public function accept() { $this->is_accepted = true; diff --git a/app/Models/Foundation/Main/Repositories/IChatTeamInvitationRepository.php b/app/Models/Foundation/Main/Repositories/IChatTeamInvitationRepository.php index 69db1fd9..81b8877a 100644 --- a/app/Models/Foundation/Main/Repositories/IChatTeamInvitationRepository.php +++ b/app/Models/Foundation/Main/Repositories/IChatTeamInvitationRepository.php @@ -23,4 +23,8 @@ interface IChatTeamInvitationRepository extends IBaseRepository * @return ChatTeamInvitation[] */ function getInvitationsByInvitee($invitee_id); + + function getPendingInvitationsByInvitee($invitee_id); + + function getAcceptedInvitationsByInvitee($invitee_id); } \ No newline at end of file diff --git a/app/Repositories/Main/Doctrine/DoctrineChatTeamInvitationRepository.php b/app/Repositories/Main/Doctrine/DoctrineChatTeamInvitationRepository.php index 3a22886a..dbc30f1c 100644 --- a/app/Repositories/Main/Doctrine/DoctrineChatTeamInvitationRepository.php +++ b/app/Repositories/Main/Doctrine/DoctrineChatTeamInvitationRepository.php @@ -37,4 +37,34 @@ final class DoctrineChatTeamInvitationRepository ->innerJoin('i.invitee', 'm', Join::WITH, " m.id = :member_id") ->setParameter('member_id', $invitee_id)->getQuery()->getResult(); } + + /** + * @param int $invitee_id + * @return ChatTeamInvitation[] + */ + function getPendingInvitationsByInvitee($invitee_id) + { + return $this->getEntityManager() + ->createQueryBuilder() + ->select("i") + ->from(\models\main\ChatTeamInvitation::class, "i") + ->innerJoin('i.invitee', 'm', Join::WITH, " m.id = :member_id") + ->where('i.is_accepted = false') + ->setParameter('member_id', $invitee_id)->getQuery()->getResult(); + } + + /** + * @param int $invitee_id + * @return ChatTeamInvitation[] + */ + function getAcceptedInvitationsByInvitee($invitee_id) + { + return $this->getEntityManager() + ->createQueryBuilder() + ->select("i") + ->from(\models\main\ChatTeamInvitation::class, "i") + ->innerJoin('i.invitee', 'm', Join::WITH, " m.id = :member_id") + ->where('i.is_accepted = true') + ->setParameter('member_id', $invitee_id)->getQuery()->getResult(); + } } \ No newline at end of file diff --git a/app/Repositories/Main/Doctrine/DoctrineChatTeamRepository.php b/app/Repositories/Main/Doctrine/DoctrineChatTeamRepository.php index 1cda3048..95319b1f 100644 --- a/app/Repositories/Main/Doctrine/DoctrineChatTeamRepository.php +++ b/app/Repositories/Main/Doctrine/DoctrineChatTeamRepository.php @@ -45,7 +45,7 @@ final class DoctrineChatTeamRepository extends SilverStripeDoctrineRepository im { $result = $this ->getEntityManager() - ->createQuery("select t.id from \models\main\ChatTeam t join t.messages m where exists (select m2.id from \models\main\ChatTeamPushNotificationMessage m2 where m2.id = m.id and m2.is_sent = 0 )") + ->createQuery("select distinct t.id from \models\main\ChatTeam t join t.messages m where exists (select m2.id from \models\main\ChatTeamPushNotificationMessage m2 where m2.id = m.id and m2.is_sent = 0 )") ->getScalarResult(); $ids = array_map('current', $result); return $ids; diff --git a/app/Services/Model/ChatTeamService.php b/app/Services/Model/ChatTeamService.php index 69a6bcd1..331c7d5f 100644 --- a/app/Services/Model/ChatTeamService.php +++ b/app/Services/Model/ChatTeamService.php @@ -1,6 +1,6 @@ tx_service->transaction(function() use($team_id, $invitee_id, $permission){ + $team = $this->repository->getById($team_id); if(is_null($team)) throw new EntityNotFoundException(); @@ -182,15 +183,16 @@ final class ChatTeamService implements IChatTeamService if (is_null($inviter)) throw new EntityNotFoundException(); $invitee = $this->member_repository->getById($invitee_id); - if(is_null($invitee)) - throw new EntityNotFoundException(); + if(is_null($invitee)) throw new EntityNotFoundException(); - if(!$team->isAdmin($inviter)) - throw new EntityNotFoundException(); + if(!$team->isAdmin($inviter)) throw new EntityNotFoundException(); if($team->isMember($invitee)) throw new ValidationException(sprintf('member id %s already is a member of team id %s', $invitee_id, $team_id)); + if($team->isAlreadyInvited($invitee)) + throw new ValidationException(sprintf('member id %s has a pending invitation on team id %s', $invitee_id, $team_id)); + $invitation = $team->createInvitation($inviter, $invitee, $permission); $team->addInvitation($invitation); @@ -343,17 +345,21 @@ final class ChatTeamService implements IChatTeamService function sendMessages($batch_size = 1000) { return $this->tx_service->transaction(function() use($batch_size){ + echo(sprintf('calling ChatTeamService.sendMessages(%s)', $batch_size)).PHP_EOL; $teams_ids = $this->repository->getAllTeamsIdsWithPendingMessages2Sent(); $qty = 0; + foreach($teams_ids as $team_id) { + echo(sprintf('processing messages for team id %s', $team_id)).PHP_EOL; $messages = $this->chat_message_repository->getAllNotSentByTeamPaginated ( $team_id, new PagingInfo(1, $batch_size) ); - + echo(sprintf('found %s messages for team id %s, send them...', $team_id, $messages->getTotal())).PHP_EOL; + $team_messages_counter = 0; foreach ($messages->getItems() as $message){ $data = [ @@ -369,7 +375,10 @@ final class ChatTeamService implements IChatTeamService $this->push_sender_service->sendPush([sprintf('team_%s', $team_id)], $data); $message->markSent(); ++$qty; + ++$team_messages_counter; } + + echo(sprintf('sent %s messages for team id %s', $team_messages_counter, $team_id)).PHP_EOL; } return $qty; }); diff --git a/database/seeds/ApiEndpointsSeeder.php b/database/seeds/ApiEndpointsSeeder.php index c4347bea..7c10884a 100644 --- a/database/seeds/ApiEndpointsSeeder.php +++ b/database/seeds/ApiEndpointsSeeder.php @@ -552,6 +552,18 @@ class ApiEndpointsSeeder extends Seeder 'http_method' => 'GET', 'scopes' => [sprintf('%s/members/invitations/read', $current_realm)], ), + array( + 'name' => 'get-pending-invitations', + 'route' => '/api/v1/members/me/team-invitations/pending', + 'http_method' => 'GET', + 'scopes' => [sprintf('%s/members/invitations/read', $current_realm)], + ), + array( + 'name' => 'get-accepted-invitations', + 'route' => '/api/v1/members/me/team-invitations/accepted', + 'http_method' => 'GET', + 'scopes' => [sprintf('%s/members/invitations/read', $current_realm)], + ), array( 'name' => 'accept-invitation', 'route' => '/api/v1/members/me/team-invitations/{invitation_id}', diff --git a/tests/OAuth2ChatTeamApiTest.php b/tests/OAuth2ChatTeamApiTest.php index 8433e297..314d5098 100644 --- a/tests/OAuth2ChatTeamApiTest.php +++ b/tests/OAuth2ChatTeamApiTest.php @@ -329,9 +329,61 @@ final class OAuth2ChatTeamApiTest extends ProtectedApiTest ); $content = $response->getContent(); - $messages = json_decode($content); - $this->assertTrue(!is_null($messages)); + $invitations = json_decode($content); + $this->assertTrue(!is_null($invitations)); $this->assertResponseStatus(200); - return $messages; + return $invitations; + } + + public function testGetMyPendingInvitations(){ + + $headers = [ + "HTTP_Authorization" => " Bearer " . $this->access_token, + ]; + + $params = [ + ]; + + $response = $this->action( + "GET", + "OAuth2TeamInvitationsApiController@getMyPendingInvitations", + $params, + array(), + array(), + array(), + $headers + ); + + $content = $response->getContent(); + $invitations = json_decode($content); + $this->assertTrue(!is_null($invitations)); + $this->assertResponseStatus(200); + return $invitations; + } + + public function testGetMyAcceptedInvitations(){ + + $headers = [ + "HTTP_Authorization" => " Bearer " . $this->access_token, + ]; + + $params = [ + ]; + + $response = $this->action( + "GET", + "OAuth2TeamInvitationsApiController@getMyAcceptedInvitations", + $params, + array(), + array(), + array(), + $headers + ); + + $content = $response->getContent(); + $invitations = json_decode($content); + $this->assertTrue(!is_null($invitations)); + $this->assertResponseStatus(200); + return $invitations; } } \ No newline at end of file