diff --git a/.env.example b/.env.example index a117d6925..599f4b25c 100644 --- a/.env.example +++ b/.env.example @@ -38,8 +38,8 @@ QUEUE_DATABASE= MAIL_DRIVER=sendgrid SENDGRID_API_KEY='YOUR_SENDGRID_API_KEY' -CORS_ALLOWED_HEADERS=origin, content-type, accept, authorization, x-requested-with -CORS_ALLOWED_METHODS=GET, POST, OPTIONS, PUT, DELETE +CORS_ALLOWED_HEADERS="origin, content-type, accept, authorization, x-requested-with" +CORS_ALLOWED_METHODS="GET, POST, OPTIONS, PUT, DELETE" CORS_USE_PRE_FLIGHT_CACHING=true CORS_MAX_AGE=3200 CORS_EXPOSED_HEADERS= diff --git a/.gitignore b/.gitignore index e90b4d509..1af1e0a00 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,4 @@ public/apc.php .nvmrc .codegraph docs/ +docker-compose.override.yml diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSpeakersApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSpeakersApiController.php index 01a169c6b..2aa4e53ad 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSpeakersApiController.php +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSpeakersApiController.php @@ -392,6 +392,110 @@ function ($page, $per_page, $filter, $order, $applyExtraFilters) use ($summit) { ); } + #[OA\Get( + path: '/api/v1/summits/{id}/speakers/all/events/count', + operationId: 'getSpeakersActivitiesCount', + description: 'Get the count of unique activities associated with speakers matching the filter criteria', + tags: ['Summit Speakers'], + security: [['summit_speakers_oauth2' => [ + SummitScopes::ReadSummitData, + SummitScopes::ReadAllSummitData + ]]], + parameters: [ + new OA\Parameter( + name: 'id', + description: 'Summit ID', + in: 'path', + required: true, + schema: new OA\Schema(type: 'integer') + ), + new OA\Parameter( + name: 'filter', + description: 'Filter by id, first_name, last_name, email, full_name, member_id, has_accepted_presentations, has_alternate_presentations, has_rejected_presentations, presentations_track_id, presentations_track_group_id, presentations_selection_plan_id, presentations_type_id, has_media_upload_with_type, has_not_media_upload_with_type, etc.', + in: 'query', + required: false, + schema: new OA\Schema(type: 'string') + ), + ], + responses: [ + new OA\Response( + response: Response::HTTP_OK, + description: 'Unique activities count', + content: new OA\JsonContent( + properties: [new OA\Property(property: 'count', type: 'integer')] + ) + ), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: 'Not Found'), + new OA\Response(response: Response::HTTP_INTERNAL_SERVER_ERROR, description: 'Server Error'), + ] + )] + public function getSpeakersActivitiesCount($summit_id) + { + return $this->processRequest(function () use ($summit_id) { + + $summit = SummitFinderStrategyFactory::build($this->getRepository(), $this->getResourceServerContext())->find($summit_id); + if (is_null($summit)) return $this->error404(); + + $filter = null; + + if (Request::has('filter')) { + $filter = FilterParser::parse(Request::input('filter'), [ + 'id' => ['=='], + 'not_id' => ['=='], + 'first_name' => ['=@', '@@', '=='], + 'last_name' => ['=@', '@@', '=='], + 'email' => ['=@', '@@', '=='], + 'full_name' => ['=@', '@@', '=='], + 'member_id' => ['=='], + 'member_user_external_id' => ['=='], + 'has_accepted_presentations' => ['=='], + 'has_alternate_presentations' => ['=='], + 'has_rejected_presentations' => ['=='], + 'presentations_track_id' => ['=='], + 'presentations_track_group_id' => ['=='], + 'presentations_selection_plan_id' => ['=='], + 'presentations_type_id' => ['=='], + 'presentations_title' => ['=@', '@@', '=='], + 'presentations_abstract' => ['=@', '@@', '=='], + 'presentations_submitter_full_name' => ['=@', '@@', '=='], + 'presentations_submitter_email' => ['=@', '@@', '=='], + 'has_media_upload_with_type' => ['=='], + 'has_not_media_upload_with_type' => ['=='], + ]); + } + + if (!is_null($filter)) { + $filter->validate([ + 'id' => 'sometimes|integer', + 'not_id' => 'sometimes|integer', + 'first_name' => 'sometimes|string', + 'last_name' => 'sometimes|string', + 'email' => 'sometimes|string', + 'full_name' => 'sometimes|string', + 'member_id' => 'sometimes|integer', + 'member_user_external_id' => 'sometimes|integer', + 'has_accepted_presentations' => 'sometimes|required|string|in:true,false', + 'has_alternate_presentations' => 'sometimes|required|string|in:true,false', + 'has_rejected_presentations' => 'sometimes|required|string|in:true,false', + 'presentations_track_id' => 'sometimes|integer', + 'presentations_track_group_id' => 'sometimes|integer', + 'presentations_selection_plan_id' => 'sometimes|integer', + 'presentations_type_id' => 'sometimes|integer', + 'presentations_title' => 'sometimes|string', + 'presentations_abstract' => 'sometimes|string', + 'presentations_submitter_full_name' => 'sometimes|string', + 'presentations_submitter_email' => 'sometimes|string', + 'has_media_upload_with_type' => 'sometimes|integer', + 'has_not_media_upload_with_type' => 'sometimes|integer', + ]); + } + + $count = $this->speaker_repository->getUniqueActivitiesCountBySummit($summit, $filter); + + return $this->ok(['count' => $count]); + }); + } + /** * @param $summit_id * @return mixed diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSubmittersApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSubmittersApiController.php index 0a4a84827..7f886bed0 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSubmittersApiController.php +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitSubmittersApiController.php @@ -495,4 +495,111 @@ public function send($summit_id) return $this->ok(); }); } + + #[OA\Get( + path: "/api/v1/summits/{id}/submitters/all/events/count", + summary: "Get unique activities count for submitters", + operationId: "getSubmittersActivitiesCount", + tags: ["Summit Submitters"], + security: [['summit_submitters_oauth2' => [ + SummitScopes::ReadSummitData, + SummitScopes::ReadAllSummitData, + ]]], + parameters: [ + new OA\Parameter( + name: "id", + in: "path", + required: true, + description: "Summit ID or slug", + schema: new OA\Schema(type: "string") + ), + new OA\Parameter( + name: "filter", + in: "query", + required: false, + description: "Filter query (supports multiple operators). Filterable fields: id, first_name, last_name, email, full_name, member_id, has_accepted_presentations, has_alternate_presentations, has_rejected_presentations, presentations_track_id, presentations_selection_plan_id, presentations_type_id, is_speaker, has_media_upload_with_type, has_not_media_upload_with_type.", + schema: new OA\Schema(type: "string", example: "has_accepted_presentations==true") + ), + ], + responses: [ + new OA\Response( + response: Response::HTTP_OK, + description: "Unique activities count", + content: new OA\JsonContent( + properties: [new OA\Property(property: "count", type: "integer")] + ) + ), + new OA\Response(response: Response::HTTP_BAD_REQUEST, description: "Bad Request"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Summit not found"), + new OA\Response(response: Response::HTTP_INTERNAL_SERVER_ERROR, description: "Server Error"), + ] + )] + public function getSubmittersActivitiesCount($summit_id) + { + return $this->processRequest(function () use ($summit_id) { + + $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->getResourceServerContext())->find(intval($summit_id)); + if (is_null($summit)) return $this->error404(); + + $filter = null; + + if (Request::has('filter')) { + $filter = FilterParser::parse(Request::input('filter'), [ + 'id' => ['=='], + 'not_id' => ['=='], + 'first_name' => ['=@', '@@', '=='], + 'last_name' => ['=@', '@@', '=='], + 'email' => ['=@', '@@', '=='], + 'full_name' => ['=@', '@@', '=='], + 'member_id' => ['=='], + 'member_user_external_id' => ['=='], + 'has_accepted_presentations' => ['=='], + 'has_alternate_presentations' => ['=='], + 'has_rejected_presentations' => ['=='], + 'presentations_track_id' => ['=='], + 'presentations_selection_plan_id' => ['=='], + 'presentations_type_id' => ['=='], + 'presentations_title' => ['=@', '@@', '=='], + 'presentations_abstract' => ['=@', '@@', '=='], + 'presentations_submitter_full_name' => ['=@', '@@', '=='], + 'presentations_submitter_email' => ['=@', '@@', '=='], + 'is_speaker' => ['=='], + 'has_media_upload_with_type' => ['=='], + 'has_not_media_upload_with_type' => ['=='], + ]); + } + + if (!is_null($filter)) { + $filter->validate([ + 'id' => 'sometimes|integer', + 'not_id' => 'sometimes|integer', + 'first_name' => 'sometimes|string', + 'last_name' => 'sometimes|string', + 'email' => 'sometimes|string', + 'full_name' => 'sometimes|string', + 'member_id' => 'sometimes|integer', + 'member_user_external_id' => 'sometimes|integer', + 'has_accepted_presentations' => 'sometimes|string|in:true,false', + 'has_alternate_presentations' => 'sometimes|string|in:true,false', + 'has_rejected_presentations' => 'sometimes|string|in:true,false', + 'presentations_track_id' => 'sometimes|integer', + 'presentations_selection_plan_id' => 'sometimes|integer', + 'presentations_type_id' => 'sometimes|integer', + 'presentations_title' => 'sometimes|string', + 'presentations_abstract' => 'sometimes|string', + 'presentations_submitter_full_name' => 'sometimes|string', + 'presentations_submitter_email' => 'sometimes|string', + 'is_speaker' => 'sometimes|string|in:true,false', + 'has_media_upload_with_type' => 'sometimes|integer', + 'has_not_media_upload_with_type' => 'sometimes|integer', + ]); + } + + $count = $this->repository->getUniqueActivitiesCountBySummit($summit, $filter); + + return $this->ok(['count' => $count]); + }); + } } diff --git a/app/Http/Middleware/OAuth2BearerAccessTokenRequestValidator.php b/app/Http/Middleware/OAuth2BearerAccessTokenRequestValidator.php index 502bb64eb..902a93acd 100644 --- a/app/Http/Middleware/OAuth2BearerAccessTokenRequestValidator.php +++ b/app/Http/Middleware/OAuth2BearerAccessTokenRequestValidator.php @@ -173,7 +173,7 @@ public function handle($request, Closure $next) } if ( $token_info->getApplicationType() === 'JS_CLIENT' - && (is_null($origin) || empty($origin)|| str_contains($token_info->getAllowedOrigins(), $origin) === false ) + && (is_null($origin) || empty($origin)|| str_contains($token_info->getAllowedOrigins(), rtrim($origin, '/')) === false ) ) { //check origins throw new OAuth2ResourceServerException( diff --git a/app/ModelSerializers/Summit/SummitSerializer.php b/app/ModelSerializers/Summit/SummitSerializer.php index 45f71321c..b285d9291 100644 --- a/app/ModelSerializers/Summit/SummitSerializer.php +++ b/app/ModelSerializers/Summit/SummitSerializer.php @@ -320,24 +320,26 @@ public function serialize($expand = null, array $fields = [], array $relations = if (!$has_registration_profile && !is_null($build_default_payment_gateway_profile_strategy) ) { - - $values['payment_profiles'][] = - SerializerRegistry::getInstance()->getSerializer - ( - $build_default_payment_gateway_profile_strategy->build(IPaymentConstants::ApplicationTypeRegistration), - $this->getSerializerType() - )->serialize(AbstractSerializer::filterExpandByPrefix($expand, 'payment_profiles')); - + try { + $profile = $build_default_payment_gateway_profile_strategy->build(IPaymentConstants::ApplicationTypeRegistration); + $serializer = SerializerRegistry::getInstance()->getSerializer($profile, $this->getSerializerType()); + if (!is_null($serializer)) + $values['payment_profiles'][] = $serializer->serialize(AbstractSerializer::filterExpandByPrefix($expand, 'payment_profiles')); + } catch (\Exception $ex) { + Log::warning($ex->getMessage()); + } } if (!$has_bookable_rooms_profile && !is_null($build_default_payment_gateway_profile_strategy)) { - $values['payment_profiles'][] = - SerializerRegistry::getInstance()->getSerializer - ( - $build_default_payment_gateway_profile_strategy->build(IPaymentConstants::ApplicationTypeBookableRooms), - $this->getSerializerType() - )->serialize(AbstractSerializer::filterExpandByPrefix($expand, 'payment_profiles')); + try { + $profile = $build_default_payment_gateway_profile_strategy->build(IPaymentConstants::ApplicationTypeBookableRooms); + $serializer = SerializerRegistry::getInstance()->getSerializer($profile, $this->getSerializerType()); + if (!is_null($serializer)) + $values['payment_profiles'][] = $serializer->serialize(AbstractSerializer::filterExpandByPrefix($expand, 'payment_profiles')); + } catch (\Exception $ex) { + Log::warning($ex->getMessage()); + } } } diff --git a/app/Models/Foundation/Main/Repositories/IMemberRepository.php b/app/Models/Foundation/Main/Repositories/IMemberRepository.php index 94b1802cc..324273c71 100644 --- a/app/Models/Foundation/Main/Repositories/IMemberRepository.php +++ b/app/Models/Foundation/Main/Repositories/IMemberRepository.php @@ -90,4 +90,12 @@ public function getSubmittersBySummit(Summit $summit, PagingInfo $paging_info, F * @throws \Doctrine\DBAL\Exception */ public function getSubmittersIdsBySummit(Summit $summit, PagingInfo $paging_info, Filter $filter = null, Order $order = null); + + /** + * @param Summit $summit + * @param Filter|null $filter + * @return int + * @throws \Doctrine\DBAL\Exception + */ + public function getUniqueActivitiesCountBySummit(Summit $summit, Filter $filter = null): int; } \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Repositories/ISpeakerRepository.php b/app/Models/Foundation/Summit/Repositories/ISpeakerRepository.php index 7bc8c7496..e6587be3e 100644 --- a/app/Models/Foundation/Summit/Repositories/ISpeakerRepository.php +++ b/app/Models/Foundation/Summit/Repositories/ISpeakerRepository.php @@ -93,4 +93,12 @@ public function getSpeakersIdsBySummit(Summit $summit, PagingInfo $paging_info, * @return PagingResponse */ public function getAllCompaniesByPage(PagingInfo $paging_info, Filter $filter = null, Order $order = null); + + /** + * @param Summit $summit + * @param Filter|null $filter + * @return int + * @throws \Doctrine\DBAL\Exception + */ + public function getUniqueActivitiesCountBySummit(Summit $summit, Filter $filter = null): int; } \ No newline at end of file diff --git a/app/Repositories/Summit/DoctrineMemberRepository.php b/app/Repositories/Summit/DoctrineMemberRepository.php index 8bc166e14..c4b15ccca 100644 --- a/app/Repositories/Summit/DoctrineMemberRepository.php +++ b/app/Repositories/Summit/DoctrineMemberRepository.php @@ -58,7 +58,7 @@ protected function getBaseEntity() */ protected function applyExtraJoins(QueryBuilder $query, ?Filter $filter = null, ?Order $order = null): QueryBuilder { - if($filter->hasFilter("summit_id") || $filter->hasFilter("schedule_event_id")){ + if(!is_null($filter) && ($filter->hasFilter("summit_id") || $filter->hasFilter("schedule_event_id"))){ $query ->leftJoin("e.schedule","sch") ->leftJoin("sch.event", "evt") @@ -638,6 +638,56 @@ function ($query) { }); } + /** + * @param Summit $summit + * @param Filter|null $filter + * @return int + * @throws \Doctrine\DBAL\Exception + */ + public function getUniqueActivitiesCountBySummit(Summit $summit, Filter $filter = null): int + { + // Step 1: collect distinct member IDs matching the summit + filter using the + // same base query / filter mappings as getSubmittersBySummit. + $qb = $this->getEntityManager()->createQueryBuilder() + ->distinct(true) + ->select("e.id") + ->from($this->getBaseEntity(), "e") + ->where(" + EXISTS ( + SELECT __p.id FROM models\summit\Presentation __p + JOIN __p.created_by __cb93 WITH __cb93 = e.id + WHERE __p.summit = :summit + )") + ->setParameter("summit", $summit); + + $qb = $this->applyExtraJoins($qb, $filter); + + if (!is_null($filter)) { + $filter->apply2Query($qb, $this->getFilterMappings($filter)); + } + + $memberIds = array_column($qb->getQuery()->getArrayResult(), 'id'); + + if (empty($memberIds)) return 0; + + // Step 2: count distinct presentations submitted by those members. + $sql = <<getEntityManager()->getConnection()->executeQuery( + $sql, + ['summit_id' => $summit->getId(), 'member_ids' => $memberIds], + ['summit_id' => \Doctrine\DBAL\ParameterType::INTEGER, 'member_ids' => \Doctrine\DBAL\ArrayParameterType::INTEGER] + ); + + return intval($stm->fetchOne()); + } + /** * @param PagingInfo $paging_info * @param Filter|null $filter diff --git a/app/Repositories/Summit/DoctrineSpeakerRepository.php b/app/Repositories/Summit/DoctrineSpeakerRepository.php index 272f7c86e..5eefef8e1 100644 --- a/app/Repositories/Summit/DoctrineSpeakerRepository.php +++ b/app/Repositories/Summit/DoctrineSpeakerRepository.php @@ -697,6 +697,66 @@ function ($query) { } + /** + * @param Summit $summit + * @param Filter|null $filter + * @return int + * @throws \Doctrine\DBAL\Exception + */ + public function getUniqueActivitiesCountBySummit(Summit $summit, Filter $filter = null): int + { + // Step 1: collect distinct speaker IDs matching the summit + filter using the + // same base query / filter mappings as getSpeakersBySummit. + $qb = $this->getEntityManager()->createQueryBuilder() + ->distinct(true) + ->select("e.id") + ->from($this->getBaseEntity(), "e") + ->leftJoin("e.registration_request", "rr") + ->leftJoin("e.member", "m") + ->where(" + EXISTS ( + SELECT __p.id FROM models\summit\Presentation __p JOIN __p.speakers __spk WITH __spk.speaker = e.id + WHERE __p.summit = :summit + ) OR + EXISTS ( + SELECT __p1.id FROM models\summit\Presentation __p1 JOIN __p1.moderator __md WITH __md.id = e.id + WHERE __p1.summit = :summit + )") + ->setParameter("summit", $summit); + + if (!is_null($filter)) { + $filter->apply2Query($qb, $this->getFilterMappings($filter)); + } + + $speakerIds = array_column($qb->getQuery()->getArrayResult(), 'id'); + + if (empty($speakerIds)) return 0; + + // Step 2: count distinct presentations linked to those speakers (as speaker or moderator). + $sql = <<getEntityManager()->getConnection()->executeQuery( + $sql, + ['summit_id' => $summit->getId(), 'speaker_ids' => $speakerIds], + ['summit_id' => \Doctrine\DBAL\ParameterType::INTEGER, 'speaker_ids' => \Doctrine\DBAL\ArrayParameterType::INTEGER] + ); + + return intval($stm->fetchOne()); + } + /** * @param Summit $summit * @param PagingInfo $paging_info diff --git a/database/seeders/ApiEndpointsSeeder.php b/database/seeders/ApiEndpointsSeeder.php index fb94f015d..66145da8b 100644 --- a/database/seeders/ApiEndpointsSeeder.php +++ b/database/seeders/ApiEndpointsSeeder.php @@ -3733,6 +3733,20 @@ private function seedSummitEndpoints() IGroup::SummitRegistrationAdmins ] ], + [ + 'name' => 'get-submitters-activities-count', + 'route' => '/api/v1/summits/{id}/submitters/all/events/count', + 'http_method' => 'GET', + 'scopes' => [ + SummitScopes::ReadSummitData, + SummitScopes::ReadAllSummitData, + ], + 'authz_groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + ] + ], // speakers [ 'name' => 'get-speakers', @@ -4085,6 +4099,20 @@ private function seedSummitEndpoints() IGroup::SummitRegistrationAdmins ] ], + [ + 'name' => 'get-speakers-activities-count', + 'route' => '/api/v1/summits/{id}/speakers/all/events/count', + 'http_method' => 'GET', + 'scopes' => [ + SummitScopes::ReadSummitData, + SummitScopes::ReadAllSummitData, + ], + 'authz_groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + ] + ], // events [ 'name' => 'get-events', diff --git a/database/seeders/ConfigSeeder.php b/database/seeders/ConfigSeeder.php index 2137dceb9..04d16bd22 100644 --- a/database/seeders/ConfigSeeder.php +++ b/database/seeders/ConfigSeeder.php @@ -31,6 +31,16 @@ public function run() // clear all $em = Registry::getManager(ResourceServerEntity::EntityManager); $em->clear(); + + // evict the L2 (second-level) cache for all resource-server entities so that + // stale cached IDs from previous seeder runs don't cause FK violations after + // the raw SQL DELETEs below reset the auto-increment sequence. + $l2Cache = $em->getCache(); + if ($l2Cache !== null) { + $l2Cache->evictEntityRegions(); + $l2Cache->evictQueryRegions(); + } + $connection = $em->getConnection(); $connection->beginTransaction(); $statements = [ diff --git a/docker-compose.override.testing.yml b/docker-compose.override.testing.yml new file mode 100644 index 000000000..9e495adca --- /dev/null +++ b/docker-compose.override.testing.yml @@ -0,0 +1,16 @@ +# This file is used to run this project locally alongside openstackid without port conflicts. +services: + otel-collector: + ports: !reset + - "1889:1888" # pprof extension + - "8890:8888" # Prometheus metrics exposed by the Collector + - "8891:8889" # Prometheus exporter metrics + - "13134:13133" # health_check extension + - "4319:4317" # OTLP gRPC receiver + - "4320:4318" # OTLP http receiver + - "55680:55679" # zpages extension + + elasticsearch: + ports: !reset + - "9202:9200" + - "9302:9300" diff --git a/routes/api_v1.php b/routes/api_v1.php index fad69a60c..98c312312 100644 --- a/routes/api_v1.php +++ b/routes/api_v1.php @@ -591,6 +591,7 @@ Route::get('csv', ['middleware' => 'auth.user', 'uses' => 'OAuth2SummitSubmittersApiController@getAllBySummitCSV']); Route::group(['prefix' => 'all'], function () { Route::put('send', ['middleware' => 'auth.user', 'uses' => 'OAuth2SummitSubmittersApiController@send']); + Route::get('events/count', ['middleware' => 'auth.user', 'uses' => 'OAuth2SummitSubmittersApiController@getSubmittersActivitiesCount']); }); }); @@ -604,6 +605,7 @@ Route::get('me', 'OAuth2SummitSpeakersApiController@getMySummitSpeaker'); Route::group(['prefix' => 'all'], function () { Route::put('send', ['middleware' => 'auth.user', 'uses' => 'OAuth2SummitSpeakersApiController@send']); + Route::get('events/count', ['middleware' => 'auth.user', 'uses' => 'OAuth2SummitSpeakersApiController@getSpeakersActivitiesCount']); }); Route::group(['prefix' => '{speaker_id}'], function () { Route::get('', ['middleware' => diff --git a/tests/SubmitterRepositoryTest.php b/tests/SubmitterRepositoryTest.php index 357c09635..7b7ebc56f 100644 --- a/tests/SubmitterRepositoryTest.php +++ b/tests/SubmitterRepositoryTest.php @@ -33,6 +33,7 @@ public function testGetSubmittersBySummit(){ $summit_repository = EntityManager::getRepository(Summit::class); $summit = $summit_repository->find(3401); + if (is_null($summit)) $this->markTestSkipped('Summit 3401 not in test DB'); $filter = FilterParser::parse( ["filter" => "is_speaker==false"], @@ -62,6 +63,7 @@ public function testGetSubmittersIdsBySummit(){ $summit_repository = EntityManager::getRepository(Summit::class); $summit = $summit_repository->find(3363); + if (is_null($summit)) $this->markTestSkipped('Summit 3363 not in test DB'); $filter = FilterParser::parse( ["filter" => "has_rejected_presentations==false"], @@ -76,4 +78,26 @@ public function testGetSubmittersIdsBySummit(){ self::assertNotEmpty($submitterIds); } + + public function testGetUniqueActivitiesCountBySummit(){ + $submitter_repository = EntityManager::getRepository(Member::class); + $summit_repository = EntityManager::getRepository(Summit::class); + + $summit = $summit_repository->find(3401); + if (is_null($summit)) $this->markTestSkipped('Summit 3401 not in test DB'); + + $totalCount = $submitter_repository->getUniqueActivitiesCountBySummit($summit, null); + self::assertIsInt($totalCount); + self::assertGreaterThan(0, $totalCount); + + $filter = FilterParser::parse( + ["filter" => "is_speaker==false"], + ["is_speaker" => ['==']] + ); + + $filteredCount = $submitter_repository->getUniqueActivitiesCountBySummit($summit, $filter); + + self::assertIsInt($filteredCount); + self::assertLessThanOrEqual($totalCount, $filteredCount); + } } \ No newline at end of file diff --git a/tests/oauth2/OAuth2SummitSpeakersApiTest.php b/tests/oauth2/OAuth2SummitSpeakersApiTest.php index 6003096e6..36ae7f01a 100644 --- a/tests/oauth2/OAuth2SummitSpeakersApiTest.php +++ b/tests/oauth2/OAuth2SummitSpeakersApiTest.php @@ -1871,4 +1871,100 @@ public function testDeclineSpeakerEditPermission() $this->assertEquals(200, $response->getStatusCode()); } + public function testGetCurrentSummitSpeakersActivitiesCount() + { + $params = [ + 'id' => self::$summit->getId(), + ]; + + $headers = [ + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ]; + + $response = $this->action( + "GET", + "OAuth2SummitSpeakersApiController@getSpeakersActivitiesCount", + $params, + [], + [], + [], + $headers + ); + + $content = $response->getContent(); + $this->assertResponseStatus(200); + $data = json_decode($content); + $this->assertNotNull($data); + $this->assertTrue(isset($data->count)); + $this->assertGreaterThan(0, $data->count); + } + + public function testGetCurrentSummitSpeakersActivitiesCountFilteredBySelPlan() + { + $headers = [ + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ]; + + $unfilteredResponse = $this->action( + "GET", + "OAuth2SummitSpeakersApiController@getSpeakersActivitiesCount", + ['id' => self::$summit->getId()], + [], [], [], $headers + ); + $this->assertResponseStatus(200); + $unfilteredData = json_decode($unfilteredResponse->getContent()); + $this->assertGreaterThan(0, $unfilteredData->count); + + $filteredResponse = $this->action( + "GET", + "OAuth2SummitSpeakersApiController@getSpeakersActivitiesCount", + [ + 'id' => self::$summit->getId(), + 'filter' => [ + sprintf('presentations_selection_plan_id==%s', self::$default_selection_plan->getId()), + ], + ], + [], [], [], $headers + ); + $this->assertResponseStatus(200); + $filteredData = json_decode($filteredResponse->getContent()); + $this->assertNotNull($filteredData); + $this->assertTrue(isset($filteredData->count)); + $this->assertLessThanOrEqual($unfilteredData->count, $filteredData->count); + } + + public function testGetCurrentSummitSpeakersActivitiesCountWithAcceptedPresentations() + { + $params = [ + 'id' => self::$summit->getId(), + 'filter' => [ + 'has_accepted_presentations==true', + ], + ]; + + $headers = [ + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ]; + + $response = $this->action( + "GET", + "OAuth2SummitSpeakersApiController@getSpeakersActivitiesCount", + $params, + [], + [], + [], + $headers + ); + + $content = $response->getContent(); + $this->assertResponseStatus(200); + $data = json_decode($content); + $this->assertNotNull($data); + $this->assertTrue(isset($data->count)); + $this->assertGreaterThan(0, $data->count); + } + } \ No newline at end of file diff --git a/tests/oauth2/OAuth2SummitSubmittersApiTest.php b/tests/oauth2/OAuth2SummitSubmittersApiTest.php index 13547f8c4..eb9c8a5eb 100644 --- a/tests/oauth2/OAuth2SummitSubmittersApiTest.php +++ b/tests/oauth2/OAuth2SummitSubmittersApiTest.php @@ -239,4 +239,65 @@ public function testGetSubmittersWithSubmittedMediaUploadsWithType() $submitters = json_decode($content); $this->assertTrue(!is_null($submitters)); } + + public function testGetCurrentSummitSubmittersActivitiesCount() + { + $params = [ + 'id' => self::$summit->getId(), + ]; + + $headers = [ + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ]; + + $response = $this->action( + "GET", + "OAuth2SummitSubmittersApiController@getSubmittersActivitiesCount", + $params, + [], + [], + [], + $headers + ); + + $content = $response->getContent(); + $this->assertResponseStatus(200); + $data = json_decode($content); + $this->assertNotNull($data); + $this->assertTrue(isset($data->count)); + $this->assertGreaterThanOrEqual(0, $data->count); + } + + public function testGetCurrentSummitSubmittersActivitiesCountWithAcceptedPresentations() + { + $params = [ + 'id' => self::$summit->getId(), + 'filter' => [ + 'has_accepted_presentations==true', + ], + ]; + + $headers = [ + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ]; + + $response = $this->action( + "GET", + "OAuth2SummitSubmittersApiController@getSubmittersActivitiesCount", + $params, + [], + [], + [], + $headers + ); + + $content = $response->getContent(); + $this->assertResponseStatus(200); + $data = json_decode($content); + $this->assertNotNull($data); + $this->assertTrue(isset($data->count)); + $this->assertGreaterThanOrEqual(0, $data->count); + } } \ No newline at end of file