commit f7801849f8f58f962feb5ac215eb0997e5b22882 Author: smarcet Date: Wed Oct 14 15:56:12 2020 -0300 Summit Metrics added new endpoints PUT /api/v1/summits/{id}/metrics/enter required scopes %s/me/summits/metrics/write s/me/summits/events/enter POST /api/v1/summits/{id}/metrics/leave payload type (string:in[GENERAL,LOBBY,EVENT,SPONSOR]) source_id (int[event id or sponsor id]) required scopes %s/me/summits/events/leave %s/me/summits/metrics/write Change-Id: I46babe9d92832da02fe175f0d6cbcfd26bb037ad Signed-off-by: smarcet diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitMembersApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitMembersApiController.php index 4b4d7c4..7fa8a19 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitMembersApiController.php +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitMembersApiController.php @@ -947,82 +947,4 @@ final class OAuth2SummitMembersApiController extends OAuth2ProtectedController } } - /** - * @param $summit_id - * @param $member_id - * @param $event_id - * @return mixed - */ - public function enterToEvent($summit_id, $member_id, $event_id){ - try { - $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->resource_server_context)->find($summit_id); - if (is_null($summit)) return $this->error404(); - - $current_member = $this->resource_server_context->getCurrentUser(); - if (is_null($current_member)) return $this->error403(); - - $event = $this->summit_service->enterTo($summit, $current_member, intval($event_id)); - - return $this->updated(SerializerRegistry::getInstance()->getSerializer($event)->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(\HTTP401UnauthorizedException $ex3) - { - Log::warning($ex3); - return $this->error401(); - } - catch (Exception $ex) { - Log::error($ex); - return $this->error500($ex); - } - } - - /** - * @param $summit_id - * @param $member_id - * @param $event_id - * @return mixed - */ - public function leaveFromEvent($summit_id, $member_id, $event_id){ - try { - $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->resource_server_context)->find($summit_id); - if (is_null($summit)) return $this->error404(); - - $current_member = $this->resource_server_context->getCurrentUser(); - if (is_null($current_member)) return $this->error403(); - - $event = $this->summit_service->leaveFrom($summit, $current_member, intval($event_id)); - - return $this->updated(SerializerRegistry::getInstance()->getSerializer($event)->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(\HTTP401UnauthorizedException $ex3) - { - Log::warning($ex3); - return $this->error401(); - } - catch (Exception $ex) { - Log::error($ex); - return $this->error500($ex); - } - } - } \ No newline at end of file diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitMetricsApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitMetricsApiController.php new file mode 100644 index 0000000..cf14849 --- /dev/null +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitMetricsApiController.php @@ -0,0 +1,233 @@ +summit_repository = $summit_repository; + $this->repository = $member_repository; + $this->service = $service; + } + + /** + * @param $summit_id + * @param $member_id + * @param $event_id + * @return mixed + */ + public function enter($summit_id){ + try { + $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->resource_server_context)->find($summit_id); + if (is_null($summit)) return $this->error404(); + + $current_member = $this->resource_server_context->getCurrentUser(); + if (is_null($current_member)) return $this->error403(); + + $payload = $this->getJsonPayload([ + 'type' => 'required|string', + 'source_id' => 'sometimes|integer', + ]); + + $metric = $this->service->enter($summit, $current_member, $payload); + + return $this->updated(SerializerRegistry::getInstance()->getSerializer($metric)->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(\HTTP401UnauthorizedException $ex3) + { + Log::warning($ex3); + return $this->error401(); + } + catch (Exception $ex) { + Log::error($ex); + return $this->error500($ex); + } + } + + /** + * @param $summit_id + * @param $member_id + * @param $event_id + * @return mixed + */ + public function leave($summit_id){ + try { + $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->resource_server_context)->find($summit_id); + if (is_null($summit)) return $this->error404(); + + $current_member = $this->resource_server_context->getCurrentUser(); + if (is_null($current_member)) return $this->error403(); + + $payload = $this->getJsonPayload([ + 'type' => 'required|string|in:'.implode(",", ISummitMetricType::ValidTypes), + 'source_id' => 'sometimes|integer', + ]); + + $metric = $this->service->leave($summit, $current_member, $payload); + + return $this->updated(SerializerRegistry::getInstance()->getSerializer($metric)->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(\HTTP401UnauthorizedException $ex3) + { + Log::warning($ex3); + return $this->error401(); + } + catch (Exception $ex) { + Log::error($ex); + return $this->error500($ex); + } + } + + /** + * @param $summit_id + * @param $member_id + * @param $event_id + * @return mixed + */ + public function enterToEvent($summit_id, $member_id, $event_id){ + try { + $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->resource_server_context)->find($summit_id); + if (is_null($summit)) return $this->error404(); + + $current_member = $this->resource_server_context->getCurrentUser(); + if (is_null($current_member)) return $this->error403(); + + $metric = $this->service->enter($summit, $current_member, [ + 'type' => 'required|string|in:'.implode(",", ISummitMetricType::ValidTypes), + 'source_id' => intval($event_id) + ]); + + return $this->updated(SerializerRegistry::getInstance()->getSerializer($metric)->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(\HTTP401UnauthorizedException $ex3) + { + Log::warning($ex3); + return $this->error401(); + } + catch (Exception $ex) { + Log::error($ex); + return $this->error500($ex); + } + } + + /** + * @param $summit_id + * @param $member_id + * @param $event_id + * @return mixed + */ + public function leaveFromEvent($summit_id, $member_id, $event_id){ + try { + $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->resource_server_context)->find($summit_id); + if (is_null($summit)) return $this->error404(); + + $current_member = $this->resource_server_context->getCurrentUser(); + if (is_null($current_member)) return $this->error403(); + + $metric = $this->service->leave($summit, $current_member, [ + 'type' => ISummitMetricType::Event, + 'source_id' => intval($event_id) + ]); + + return $this->updated(SerializerRegistry::getInstance()->getSerializer($metric)->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(\HTTP401UnauthorizedException $ex3) + { + Log::warning($ex3); + return $this->error401(); + } + 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 29208c0..4bb6f1e 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -170,6 +170,11 @@ Route::group([ Route::group(['prefix' => '{id}'], function () { + Route::group(['prefix' => 'metrics'], function () { + Route::put('enter', 'OAuth2SummitMetricsApiController@enter'); + Route::post('leave', 'OAuth2SummitMetricsApiController@leave'); + }); + Route::put('', [ 'middleware' => 'auth.user', 'uses' => 'OAuth2SummitApiController@updateSummit']); Route::post('logo', [ 'middleware' => 'auth.user', 'uses' => 'OAuth2SummitApiController@addSummitLogo']); Route::delete('logo', [ 'middleware' => 'auth.user', 'uses' => 'OAuth2SummitApiController@deleteSummitLogo']); @@ -964,8 +969,8 @@ Route::group([ Route::delete('', 'OAuth2SummitMembersApiController@removeEventFromMemberSchedule')->where('member_id', 'me'); - Route::put('enter', 'OAuth2SummitMembersApiController@enterToEvent')->where('member_id', 'me'); - Route::post('leave', 'OAuth2SummitMembersApiController@leaveFromEvent')->where('member_id', 'me'); + Route::put('enter', 'OAuth2SummitMetricsApiController@enterToEvent')->where('member_id', 'me'); + Route::post('leave', 'OAuth2SummitMetricsApiController@leaveFromEvent')->where('member_id', 'me'); }); }); }); diff --git a/app/ModelSerializers/SerializerRegistry.php b/app/ModelSerializers/SerializerRegistry.php index 2f56be0..9bea4e5 100644 --- a/app/ModelSerializers/SerializerRegistry.php +++ b/app/ModelSerializers/SerializerRegistry.php @@ -138,9 +138,13 @@ final class SerializerRegistry $this->registry['ApiEndpoint'] = ApiEndpointSerializer::class; $this->registry['ApiScope'] = ApiScopeSerializer::class; $this->registry['ApiEndpointAuthzGroup'] = ApiEndpointAuthzGroupSerializer::class; - // + // metrics + $this->registry['SummitMetric'] = SummitMetricSerializer::class; + $this->registry['SummitSponsorMetric'] = SummitSponsorMetricSerializer::class; $this->registry['SummitEventAttendanceMetric'] = SummitEventAttendanceMetricSerializer::class; + + // stripe $this->registry['StripePaymentProfile'] = [ self::SerializerType_Public => StripePaymentProfileSerializer::class, self::SerializerType_Private => AdminStripePaymentProfileSerializer::class, diff --git a/app/ModelSerializers/Summit/Metrics/SummitEventAttendanceMetricSerializer.php b/app/ModelSerializers/Summit/Metrics/SummitEventAttendanceMetricSerializer.php new file mode 100644 index 0000000..710f42e --- /dev/null +++ b/app/ModelSerializers/Summit/Metrics/SummitEventAttendanceMetricSerializer.php @@ -0,0 +1,23 @@ + 'event_id:json_int', + ]; +} \ No newline at end of file diff --git a/app/ModelSerializers/Summit/Metrics/SummitMetricSerializer.php b/app/ModelSerializers/Summit/Metrics/SummitMetricSerializer.php new file mode 100644 index 0000000..1c569ce --- /dev/null +++ b/app/ModelSerializers/Summit/Metrics/SummitMetricSerializer.php @@ -0,0 +1,30 @@ + 'member_first_name:json_string', + 'MemberLastName' => 'member_last_name:json_string', + 'MemberProfilePhotoUrl' => 'member_pic:json_url', + 'Type' => 'type:json_string', + 'Ip' => 'ip:json_string', + 'Origin' => 'origin:json_string', + 'Browser' => 'browser:json_string', + ]; +} \ No newline at end of file diff --git a/app/ModelSerializers/Summit/Metrics/SummitSponsorMetricSerializer.php b/app/ModelSerializers/Summit/Metrics/SummitSponsorMetricSerializer.php new file mode 100644 index 0000000..aa7ec4d --- /dev/null +++ b/app/ModelSerializers/Summit/Metrics/SummitSponsorMetricSerializer.php @@ -0,0 +1,24 @@ + 'sponsor_id:json_int', + ]; +} \ No newline at end of file diff --git a/app/ModelSerializers/Summit/SummitEventSerializer.php b/app/ModelSerializers/Summit/SummitEventSerializer.php index 39b82f3..7ac84a9 100644 --- a/app/ModelSerializers/Summit/SummitEventSerializer.php +++ b/app/ModelSerializers/Summit/SummitEventSerializer.php @@ -12,7 +12,6 @@ * limitations under the License. **/ -use App\Models\Foundation\Summit\Events\SummitEventAttendanceMetric; use Libs\ModelSerializers\AbstractSerializer; use models\summit\SummitEvent; /** diff --git a/app/ModelSerializers/SummitEventAttendanceMetricSerializer.php b/app/ModelSerializers/SummitEventAttendanceMetricSerializer.php deleted file mode 100644 index 657a57a..0000000 --- a/app/ModelSerializers/SummitEventAttendanceMetricSerializer.php +++ /dev/null @@ -1,27 +0,0 @@ - 'member_first_name:json_string', - 'MemberLastName' => 'member_last_name:json_string', - 'MemberProfilePhotoUrl' => 'member_pic:json_url', - ]; -} \ No newline at end of file diff --git a/app/Models/Foundation/Main/Member.php b/app/Models/Foundation/Main/Member.php index 47c5caf..8641d50 100644 --- a/app/Models/Foundation/Main/Member.php +++ b/app/Models/Foundation/Main/Member.php @@ -13,7 +13,7 @@ **/ use App\Models\Foundation\Main\IGroup; -use App\Models\Foundation\Summit\Events\SummitEventAttendanceMetric; +use models\summit\SummitMetric; use Illuminate\Support\Facades\App; use Illuminate\Support\Facades\Log; use Models\Foundation\Main\CCLA\Team; @@ -282,8 +282,8 @@ class Member extends SilverstripeBaseModel private $summit_permission_groups; /** - * @ORM\OneToMany(targetEntity="App\Models\Foundation\Summit\Events\SummitEventAttendanceMetric", mappedBy="member", cascade={"persist","remove"}, orphanRemoval=true) - * @var SummitEventAttendanceMetric[] + * @ORM\OneToMany(targetEntity="models\summit\SummitMetric", mappedBy="member", cascade={"persist","remove"}, orphanRemoval=true) + * @var SummitMetric[] */ protected $summit_attendance_metrics; diff --git a/app/Models/Foundation/Summit/Events/SummitEvent.php b/app/Models/Foundation/Summit/Events/SummitEvent.php index 98fe226..74da603 100644 --- a/app/Models/Foundation/Summit/Events/SummitEvent.php +++ b/app/Models/Foundation/Summit/Events/SummitEvent.php @@ -16,7 +16,6 @@ use App\Models\Foundation\Summit\Events\RSVP\RSVPTemplate; use App\Events\SummitEventCreated; use App\Events\SummitEventDeleted; use App\Events\SummitEventUpdated; -use App\Models\Foundation\Summit\Events\SummitEventAttendanceMetric; use Doctrine\Common\Collections\Criteria; use Doctrine\ORM\Event\PreUpdateEventArgs; use models\exceptions\ValidationException; @@ -206,7 +205,7 @@ class SummitEvent extends SilverstripeBaseModel protected $tags; /** - * @ORM\OneToMany(targetEntity="App\Models\Foundation\Summit\Events\SummitEventAttendanceMetric", mappedBy="event", cascade={"persist","remove"}, orphanRemoval=true) + * @ORM\OneToMany(targetEntity="models\summit\SummitEventAttendanceMetric", mappedBy="event", cascade={"persist","remove"}, orphanRemoval=true) * @var SummitEventAttendanceMetric[] */ protected $attendance_metrics; @@ -1144,50 +1143,6 @@ class SummitEvent extends SilverstripeBaseModel } /** - * @param Member $member - * @return SummitEventAttendanceMetric - * @throws \Exception - */ - public function enter(Member $member){ - // check if we have one - $criteria = Criteria::create(); - $criteria = $criteria - ->where(Criteria::expr()->eq('member', $member)) - ->andWhere(Criteria::expr()->isNull("outgress_date")); - - $formerMetric = $this->attendance_metrics->matching($criteria)->first(); - - if($formerMetric and $formerMetric instanceof SummitEventAttendanceMetric){ - // mark as leave - $formerMetric->abandon(); - } - - $metric = SummitEventAttendanceMetric::build($member, $this); - $this->attendance_metrics->add($metric); - return $metric; - } - - /** - * @param Member $member - * @return mixed - * @throws ValidationException - */ - public function leave(Member $member){ - $criteria = Criteria::create(); - $criteria = $criteria - ->where(Criteria::expr()->eq('member', $member)) - ->andWhere(Criteria::expr()->isNull("outgress_date")) - ->orderBy(['ingress_date' => Criteria::DESC]); - - $metric = $this->attendance_metrics->matching($criteria)->first(); - if(!$metric) - throw new ValidationException(sprintf("User %s did not enter to event yet.", $member->getId())); - $metric->abandon(); - - return $metric; - } - - /** * @return int */ public function getTotalAttendanceCount():int{ diff --git a/app/Models/Foundation/Summit/Events/SummitEventAttendanceMetric.php b/app/Models/Foundation/Summit/Events/SummitEventAttendanceMetric.php deleted file mode 100644 index 923013a..0000000 --- a/app/Models/Foundation/Summit/Events/SummitEventAttendanceMetric.php +++ /dev/null @@ -1,158 +0,0 @@ -ingress_date; - } - - /** - * @param \DateTime $ingress_date - */ - public function setIngressDate(\DateTime $ingress_date): void - { - $this->ingress_date = $ingress_date; - } - - /** - * @return \DateTime - */ - public function getOutgressDate(): ?\DateTime - { - return $this->outgress_date; - } - - /** - * @param \DateTime $outgress_date - */ - public function setOutgressDate(\DateTime $outgress_date): void - { - $this->outgress_date = $outgress_date; - } - - /** - * @return Member - */ - public function getMember(): Member - { - return $this->member; - } - - /** - * @param Member $member - */ - public function setMember(Member $member): void - { - $this->member = $member; - } - - /** - * @return SummitEvent - */ - public function getEvent(): SummitEvent - { - return $this->event; - } - - /** - * @param SummitEvent $event - */ - public function setEvent(SummitEvent $event): void - { - $this->event = $event; - } - - - public function __construct() - { - parent::__construct(); - } - - /** - * @param Member $member - * @param SummitEvent $event - * @return SummitEventAttendanceMetric - * @throws \Exception - */ - public static function build(Member $member, SummitEvent $event){ - $metric = new self(); - $metric->member = $member; - $metric->event = $event; - $metric->ingress_date = new \DateTime('now', new \DateTimeZone('UTC')); - return $metric; - } - - /** - * @throws ValidationException - */ - public function abandon(){ - if(is_null($this->ingress_date)) - throw new ValidationException('You must enter first.'); - $this->outgress_date = new \DateTime('now', new \DateTimeZone('UTC')); - } - - public function getMemberFirstName():?string{ - return $this->member->getFirstName(); - } - - public function getMemberLastName():?string{ - return $this->member->getLastName(); - } - - public function getMemberProfilePhotoUrl():?string{ - return $this->member->getProfilePhotoUrl(); - } -} \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Factories/SummitMetricFactory.php b/app/Models/Foundation/Summit/Factories/SummitMetricFactory.php new file mode 100644 index 0000000..9f19713 --- /dev/null +++ b/app/Models/Foundation/Summit/Factories/SummitMetricFactory.php @@ -0,0 +1,63 @@ +setType($data['type']); + return $metric; + } +} \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Metrics/ISummitMetricType.php b/app/Models/Foundation/Summit/Metrics/ISummitMetricType.php new file mode 100644 index 0000000..17f459f --- /dev/null +++ b/app/Models/Foundation/Summit/Metrics/ISummitMetricType.php @@ -0,0 +1,32 @@ +event; + } + + /** + * @param SummitEvent $event + */ + public function setEvent(SummitEvent $event): void + { + $this->event = $event; + } + + /** + * @return int + */ + public function getEventId(){ + try { + return is_null($this->event) ? 0 : $this->event->getId(); + } + catch(\Exception $ex){ + return 0; + } + } + +} \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Metrics/SummitMetric.php b/app/Models/Foundation/Summit/Metrics/SummitMetric.php new file mode 100644 index 0000000..6e071f6 --- /dev/null +++ b/app/Models/Foundation/Summit/Metrics/SummitMetric.php @@ -0,0 +1,234 @@ +ingress_date; + } + + /** + * @param \DateTime $ingress_date + */ + public function setIngressDate(\DateTime $ingress_date): void + { + $this->ingress_date = $ingress_date; + } + + /** + * @return \DateTime + */ + public function getOutgressDate(): ?\DateTime + { + return $this->outgress_date; + } + + /** + * @param \DateTime $outgress_date + */ + public function setOutgressDate(\DateTime $outgress_date): void + { + $this->outgress_date = $outgress_date; + } + + /** + * @return Member + */ + public function getMember(): ?Member + { + return $this->member; + } + + /** + * @param Member $member + */ + public function setMember(Member $member): void + { + $this->member = $member; + } + + /** + * @throws ValidationException + */ + public function abandon(){ + if(is_null($this->ingress_date)) + throw new ValidationException('You must enter first.'); + $this->outgress_date = new \DateTime('now', new \DateTimeZone('UTC')); + } + + public function getMemberFirstName():?string{ + return $this->member->getFirstName(); + } + + public function getMemberLastName():?string{ + return $this->member->getLastName(); + } + + public function getMemberProfilePhotoUrl():?string{ + return $this->member->getProfilePhotoUrl(); + } + + /** + * @param Member|null $member + * @return SummitMetric + * @throws \Exception + */ + public static function build(?Member $member){ + $metric = new static(); + $metric->member = $member; + $metric->ingress_date = new \DateTime('now', new \DateTimeZone('UTC')); + $metric->ip = UserClientHelper::getUserIp(); + $metric->origin = UserClientHelper::getUserOrigin(); + $metric->browser = UserClientHelper::getUserBrowser(); + return $metric; + } + + /** + * @return int + */ + public function getMemberId(){ + try { + return is_null($this->member) ? 0 : $this->member->getId(); + } + catch(\Exception $ex){ + return 0; + } + } + + /** + * @return bool + */ + public function hasMember():bool{ + return $this->getMemberId() > 0; + } + + public function clearMember(){ + $this->member = null; + } + + /** + * @return string|null + */ + public function getType(): ?string + { + return $this->type; + } + + /** + * @param string|null $type + * @throws ValidationException + */ + public function setType(?string $type): void + { + if(!in_array($type, ISummitMetricType::ValidTypes)) + throw new ValidationException(sprintf("Type %s is not a valid one.", $type)); + $this->type = $type; + } + + /** + * @return string|null + */ + public function getIp(): ?string + { + return $this->ip; + } + + /** + * @return string|null + */ + public function getOrigin(): ?string + { + return $this->origin; + } + + /** + * @return string|null + */ + public function getBrowser(): ?string + { + return $this->browser; + } +} \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Metrics/SummitSponsorMetric.php b/app/Models/Foundation/Summit/Metrics/SummitSponsorMetric.php new file mode 100644 index 0000000..5c895f1 --- /dev/null +++ b/app/Models/Foundation/Summit/Metrics/SummitSponsorMetric.php @@ -0,0 +1,66 @@ +sponsor) ? 0 : $this->sponsor->getId(); + } + catch(\Exception $ex){ + return 0; + } + } + + /** + * @return bool + */ + public function hasSponsor():bool{ + return $this->getSponsorId() > 0; + } + + /** + * @return Sponsor|null + */ + public function getSponsor(): ?Sponsor + { + return $this->sponsor; + } + + /** + * @param Sponsor|null $sponsor + */ + public function setSponsor(?Sponsor $sponsor): void + { + $this->sponsor = $sponsor; + } + +} \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Repositories/ISummitMetricRepository.php b/app/Models/Foundation/Summit/Repositories/ISummitMetricRepository.php new file mode 100644 index 0000000..42ae793 --- /dev/null +++ b/app/Models/Foundation/Summit/Repositories/ISummitMetricRepository.php @@ -0,0 +1,30 @@ +permission_groups = new ArrayCollection(); $this->media_upload_types = new ArrayCollection(); $this->featured_speakers = new ArrayCollection(); + $this->metrics = new ArrayCollection(); } /** @@ -5009,4 +5015,21 @@ SQL; return $list; } + /** + * @return SummitMetric[] + */ + public function getMetrics(): array + { + return $this->metrics; + } + + /** + * @param SummitMetric $metric + */ + public function addMetric(SummitMetric $metric){ + if($this->metrics->contains($metric)) return; + $this->metrics->add($metric); + $metric->setSummit($this); + } + } diff --git a/app/Repositories/RepositoriesProvider.php b/app/Repositories/RepositoriesProvider.php index bedd087..792c255 100644 --- a/app/Repositories/RepositoriesProvider.php +++ b/app/Repositories/RepositoriesProvider.php @@ -47,6 +47,7 @@ use App\Models\Foundation\Summit\Repositories\ISummitLocationBannerRepository; use App\Models\Foundation\Summit\Repositories\ISummitLocationRepository; use App\Models\Foundation\Summit\Repositories\ISummitMediaFileTypeRepository; use App\Models\Foundation\Summit\Repositories\ISummitMediaUploadTypeRepository; +use App\Models\Foundation\Summit\Repositories\ISummitMetricRepository; use App\Models\Foundation\Summit\Repositories\ISummitOrderExtraQuestionTypeRepository; use App\Models\Foundation\Summit\Repositories\ISummitOrderRepository; use App\Models\Foundation\Summit\Repositories\ISummitRefundPolicyTypeRepository; @@ -96,6 +97,7 @@ use models\summit\SummitDocument; use models\summit\SummitEventType; use models\summit\SummitMediaFileType; use models\summit\SummitMediaUploadType; +use models\summit\SummitMetric; use models\summit\SummitOrder; use models\summit\SummitOrderExtraQuestionType; use models\summit\SummitRefundPolicyType; @@ -601,5 +603,11 @@ final class RepositoriesProvider extends ServiceProvider } ); + App::singleton( + ISummitMetricRepository::class, + function(){ + return EntityManager::getRepository(SummitMetric::class); + } + ); } } \ No newline at end of file diff --git a/app/Repositories/Summit/DoctrineSummitMetricRepository.php b/app/Repositories/Summit/DoctrineSummitMetricRepository.php new file mode 100644 index 0000000..74b4681 --- /dev/null +++ b/app/Repositories/Summit/DoctrineSummitMetricRepository.php @@ -0,0 +1,77 @@ +getEntityManager() + ->createQueryBuilder() + ->select("e") + ->from($this->getBaseEntity(), "e") + ->where("e.type = :type"); + + if(!is_null($source_id) && $source_id > 0){ + if($type == ISummitMetricType::Event){ + $query = $query->leftJoin(SummitEventAttendanceMetric::class, 'sam', 'WITH', 'e.id = sam.id') + ->join("sam.event", "evt") + ->andWhere("evt.id = :source_id") + ->setParameter("source_id", $source_id); + } + if($type == ISummitMetricType::Sponsor){ + $query = $query->leftJoin(SummitSponsorMetric::class, 'sm', 'WITH', 'e.id = sm.id') + ->join("sm.sponsor", "sp") + ->andWhere("sp.id = :source_id") + ->setParameter("source_id", $source_id); + } + } + + return $query + ->andWhere("e.outgress_date is null") + ->andWhere("e.member = :member") + ->setParameter("member", $member) + ->setParameter("type", trim($type)) + ->setMaxResults(1) + ->orderBy('e.ingress_date', 'DESC') + ->getQuery() + ->getOneOrNullResult(); + } +} \ No newline at end of file diff --git a/app/Security/SummitScopes.php b/app/Security/SummitScopes.php index b68ac60..0ae3d0e 100644 --- a/app/Security/SummitScopes.php +++ b/app/Security/SummitScopes.php @@ -36,7 +36,8 @@ final class SummitScopes const SendMyScheduleMail = '%s/me/summits/events/schedule/mail'; const EnterEvent = '%s/me/summits/events/enter'; const LeaveEvent = '%s/me/summits/events/leave'; - + const WriteMetrics = '%s/me/summits/metrics/write'; + const ReadMetrics = '%s/me/summits/metrics/read'; // registration const CreateRegistrationOrders = '%s/summits/registration-orders/create'; diff --git a/app/Services/Model/ISummitMetricService.php b/app/Services/Model/ISummitMetricService.php new file mode 100644 index 0000000..0042325 --- /dev/null +++ b/app/Services/Model/ISummitMetricService.php @@ -0,0 +1,38 @@ +repository = $repository; + } + + /** + * @param Summit $summit + * @param Member $current_member + * @param array $payload + * @return SummitMetric + * @throws \Exception + */ + public function enter(Summit $summit, Member $current_member, array $payload): SummitMetric + { + Log::debug(sprintf("SummitMetricService::enter summit %s member %s payload %s", $summit->getId(), $current_member->getId(), json_encode($payload))); + return $this->tx_service->transaction(function () use ($summit, $current_member, $payload) { + $metric = SummitMetricFactory::build($current_member, $payload); + $metric->setMember($current_member); + + $source_id = null; + if(isset($payload['source_id'])) + $source_id = intval($payload['source_id']); + + $formerMetric = $this->repository->getNonAbandoned($current_member, $metric->getType(), $source_id); + + if(!is_null($formerMetric)){ + // mark as leave + Log::debug(sprintf("SummitMetricService::enter there is a former metric (%s)", $formerMetric->getId())); + $formerMetric->abandon(); + } + + if($metric instanceof SummitEventAttendanceMetric){ + if(!isset($payload['source_id'])){ + throw new ValidationException("source_id param is missing."); + } + $event_id = intval($payload['source_id']); + $event = $summit->getEvent($event_id); + + if (is_null($event)) { + throw new EntityNotFoundException(sprintf("Event %s does not belongs to summit %s.", $event_id, $summit->getId())); + } + + if (!$event->isPublished()) { + throw new ValidationException(sprintf("Event %s is not published.", $event->getId())); + } + + $metric->setEvent($event); + } + + if($metric instanceof SummitSponsorMetric){ + if(!isset($payload['source_id'])){ + throw new ValidationException("source_id param is missing."); + } + $sponsor_id = intval($payload['source_id']); + + $sponsor = $summit->getSummitSponsorById($sponsor_id); + + if (is_null($sponsor)) { + throw new EntityNotFoundException(sprintf("Sponsor %s does not belongs to summit %s.", $sponsor_id, $summit->getId())); + } + $metric->setSponsor($sponsor); + } + + $summit->addMetric($metric); + return $metric; + }); + } + + /** + * @param Summit $summit + * @param Member $current_member + * @param array $payload + * @return SummitMetric + * @throws \Exception + */ + public function leave(Summit $summit, Member $current_member, array $payload): SummitMetric + { + Log::debug(sprintf("SummitMetricService::leave summit %s member %s payload %s", $summit->getId(), $current_member->getId(), json_encode($payload))); + + return $this->tx_service->transaction(function () use ($summit, $current_member, $payload) { + + $source_id = null; + if(isset($payload['source_id'])) + $source_id = intval($payload['source_id']); + $formerMetric = $this->repository->getNonAbandoned($current_member, trim($payload['type']), $source_id); + if(!$formerMetric) + throw new ValidationException(sprintf("User %s has not a pending %s metric", $current_member->getId(), $payload['type'])); + + $formerMetric->abandon(); + + return $formerMetric; + }); + } +} \ No newline at end of file diff --git a/app/Services/Model/Imp/SummitService.php b/app/Services/Model/Imp/SummitService.php index 9a82628..1f29c6f 100644 --- a/app/Services/Model/Imp/SummitService.php +++ b/app/Services/Model/Imp/SummitService.php @@ -33,7 +33,6 @@ use App\Permissions\IPermissionsManager; use App\Services\Model\AbstractService; use App\Services\Model\IFolderService; use App\Services\Model\IMemberService; -use App\Services\Utils\CSVReader; use CalDAVClient\Facade\Utils\ICalTimeZoneBuilder; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use GuzzleHttp\Exception\ClientException; @@ -2506,50 +2505,6 @@ final class SummitService extends AbstractService implements ISummitService /** * @inheritDoc */ - public function enterTo(Summit $summit, Member $current_member, int $event_id): SummitEvent - { - return $this->tx_service->transaction(function () use ($summit, $current_member, $event_id) { - - $event = $summit->getEvent($event_id); - if (is_null($event)) { - throw new EntityNotFoundException(sprintf("Event %s does not belongs to summit %s.", $event_id, $summit->getId())); - } - - if (!$event->isPublished()) { - throw new ValidationException(sprintf("Event %s is not published.", $event->getId())); - } - - $event->enter($current_member); - - return $event; - - }); - } - - /** - * @inheritDoc - */ - public function leaveFrom(Summit $summit, Member $current_member, int $event_id): SummitEvent - { - return $this->tx_service->transaction(function () use ($summit, $current_member, $event_id) { - $event = $summit->getEvent($event_id); - if (is_null($event)) { - throw new EntityNotFoundException(sprintf("Event %s does not belongs to summit %s.", $event_id, $summit->getId())); - } - - if (!$event->isPublished()) { - throw new ValidationException(sprintf("Event %s is not published.", $event->getId())); - } - - $event->leave($current_member); - - return $event; - }); - } - - /** - * @inheritDoc - */ public function addEventImage(Summit $summit, $event_id, UploadedFile $file, $max_file_size = 10485760) { return $this->tx_service->transaction(function () use ($summit, $event_id, $file, $max_file_size) { diff --git a/app/Services/ModelServicesProvider.php b/app/Services/ModelServicesProvider.php index 08aeae8..f8ee1bc 100644 --- a/app/Services/ModelServicesProvider.php +++ b/app/Services/ModelServicesProvider.php @@ -30,6 +30,7 @@ use App\Services\Model\Imp\SummitDocumentService; use App\Services\Model\Imp\SummitEmailEventFlowService; use App\Services\Model\Imp\SummitMediaFileTypeService; use App\Services\Model\Imp\SummitMediaUploadTypeService; +use App\Services\Model\Imp\SummitMetricService; use App\Services\Model\Imp\SummitRegistrationInvitationService; use App\Services\Model\IOrganizationService; use App\Services\Model\IPaymentGatewayProfileService; @@ -48,6 +49,7 @@ use App\Services\Model\ISummitEmailEventFlowService; use App\Services\Model\ISummitEventTypeService; use App\Services\Model\ISummitMediaFileTypeService; use App\Services\Model\ISummitMediaUploadTypeService; +use App\Services\Model\ISummitMetricService; use App\Services\Model\ISummitOrderExtraQuestionTypeService; use App\Services\Model\ISummitPushNotificationService; use App\Services\Model\ISummitRefundPolicyTypeService; @@ -385,6 +387,12 @@ final class ModelServicesProvider extends ServiceProvider IPresentationVideoMediaUploadProcessor::class, PresentationVideoMediaUploadProcessor::class ); + + App::singleton + ( + ISummitMetricService::class, + SummitMetricService::class + ); } /** @@ -444,7 +452,8 @@ final class ModelServicesProvider extends ServiceProvider ISummitAdministratorPermissionGroupService::class, ISummitMediaFileTypeService::class, ISummitMediaUploadTypeService::class, - IPresentationVideoMediaUploadProcessor::class + IPresentationVideoMediaUploadProcessor::class, + ISummitMetricService::class, ]; } } \ No newline at end of file diff --git a/app/Services/Utils/UserClientHelper.php b/app/Services/Utils/UserClientHelper.php new file mode 100644 index 0000000..d2318d8 --- /dev/null +++ b/app/Services/Utils/UserClientHelper.php @@ -0,0 +1,40 @@ +hasTable("SummitMetric")) { + $builder->create('SummitMetric', function (Table $table) { + + $table->integer("ID", true, false); + $table->primary("ID"); + + $table->timestamp('Created'); + $table->timestamp('LastEdited'); + $table->string('ClassName'); + $table->string('Type')->setNotnull(true)->setDefault('GENERAL'); + $table->string('Ip')->setNotnull(false); + $table->string('Origin')->setNotnull(false); + $table->string('Browser')->setNotnull(false); + + $table->timestamp('IngressDate')->setNotnull(false); + $table->timestamp('OutgressDate')->setNotnull(false); + // FK + $table->integer("MemberID", false, false)->setNotnull(false)->setDefault('NULL'); + $table->index("MemberID", "MemberID"); + $table->foreign("Member", "MemberID", "ID", ["onDelete" => "CASCADE"]); + + // FK + $table->integer("SummitID", false, false)->setNotnull(false)->setDefault('NULL'); + $table->index("SummitID", "SummitID"); + $table->foreign("Summit", "SummitID", "ID", ["onDelete" => "CASCADE"]); + }); + } + + if(!$schema->hasTable("SummitSponsorMetric")) { + $builder->create('SummitSponsorMetric', function (Table $table) { + + $table->integer("ID", true, false); + $table->primary("ID"); + $table->string('ClassName'); + + // FK + $table->integer("SponsorID", false, false)->setNotnull(false)->setDefault('NULL'); + $table->index("SponsorID", "SponsorID"); + $table->foreign("Sponsor", "SponsorID", "ID", ["onDelete" => "CASCADE"]); + }); + } + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + + } +} diff --git a/database/migrations/model/Version20201014155719.php b/database/migrations/model/Version20201014155719.php new file mode 100644 index 0000000..daa766a --- /dev/null +++ b/database/migrations/model/Version20201014155719.php @@ -0,0 +1,53 @@ +addSql($sql); + + // make enum + $sql = <<addSql($sql); + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + + } +} diff --git a/database/migrations/model/Version20201014161727.php b/database/migrations/model/Version20201014161727.php new file mode 100644 index 0000000..c2a1501 --- /dev/null +++ b/database/migrations/model/Version20201014161727.php @@ -0,0 +1,89 @@ +addSql($sql); + + $sql = <<addSql($sql); + + $sql = <<addSql($sql); + + $sql = <<addSql($sql); + + $sql = <<addSql($sql); + + $sql = <<addSql($sql); + + $sql = <<addSql($sql); + + $sql = <<addSql($sql); + + + $sql = <<addSql($sql); + + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + + } +} diff --git a/database/seeds/ApiEndpointsSeeder.php b/database/seeds/ApiEndpointsSeeder.php index 8349324..272f118 100644 --- a/database/seeds/ApiEndpointsSeeder.php +++ b/database/seeds/ApiEndpointsSeeder.php @@ -6044,6 +6044,24 @@ class ApiEndpointsSeeder extends Seeder IGroup::Administrators, ] ], + [ + 'name' => 'metric-enter', + 'route' => '/api/v1/summits/{id}/metrics/enter', + 'http_method' => 'PUT', + 'scopes' => [ + sprintf(SummitScopes::EnterEvent, $current_realm), + sprintf(SummitScopes::WriteMetrics, $current_realm) + ], + ], + [ + 'name' => 'metric-leave', + 'route' => '/api/v1/summits/{id}/metrics/leave', + 'http_method' => 'POST', + 'scopes' => [ + sprintf(SummitScopes::LeaveEvent, $current_realm), + sprintf(SummitScopes::WriteMetrics, $current_realm) + ], + ], ] ); } diff --git a/database/seeds/ApiScopesSeeder.php b/database/seeds/ApiScopesSeeder.php index d646bdf..336be4c 100644 --- a/database/seeds/ApiScopesSeeder.php +++ b/database/seeds/ApiScopesSeeder.php @@ -86,6 +86,16 @@ final class ApiScopesSeeder extends Seeder 'description' => '', ], [ + 'name' => sprintf(SummitScopes::WriteMetrics, $current_realm), + 'short_description' => '', + 'description' => '', + ], + [ + 'name' => sprintf(SummitScopes::ReadMetrics, $current_realm), + 'short_description' => '', + 'description' => '', + ], + [ 'name' => sprintf(SummitScopes::AddMyRSVP, $current_realm), 'short_description' => 'Allows to add Summit events as RSVP', 'description' => 'Allows to add Summit events as RSVP', diff --git a/tests/OAuth2SummitMetricsApiControllerTest.php b/tests/OAuth2SummitMetricsApiControllerTest.php new file mode 100644 index 0000000..d2789f7 --- /dev/null +++ b/tests/OAuth2SummitMetricsApiControllerTest.php @@ -0,0 +1,91 @@ + $summit_id, + ]; + + $data = [ + 'type' => \models\summit\ISummitMetricType::General, + ]; + + $headers = [ + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json", + 'REMOTE_ADDR' => '10.1.0.1', + 'HTTP_REFERER' => 'https://www.test.com' + ]; + + $response = $this->action( + "PUT", + "OAuth2SummitMetricsApiController@enter", + $params, + [], + [], + [], + $headers, + json_encode($data) + ); + + $content = $response->getContent(); + $this->assertResponseStatus(201); + $metric = json_decode($content); + $this->assertTrue(!is_null($metric)); + return $metric; + } + + /** + * @param int $summit_id + * @return mixed + */ + public function testEnterEvent($summit_id = 18, $event_id = 706){ + + $params = [ + 'id' => $summit_id, + 'event_id' => $event_id, + 'member_id' => 'me' + ]; + + $headers = [ + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json", + 'REMOTE_ADDR' => '10.1.0.1', + 'HTTP_REFERER' => 'https://www.test.com' + ]; + + $response = $this->action( + "PUT", + "OAuth2SummitMetricsApiController@enterToEvent", + $params, + [], + [], + [], + $headers + ); + + $content = $response->getContent(); + $this->assertResponseStatus(201); + $metric = json_decode($content); + $this->assertTrue(!is_null($metric)); + return $metric; + } +} \ No newline at end of file diff --git a/tests/ProtectedApiTest.php b/tests/ProtectedApiTest.php index faf683a..c503465 100644 --- a/tests/ProtectedApiTest.php +++ b/tests/ProtectedApiTest.php @@ -109,6 +109,8 @@ class AccessTokenServiceStub implements IAccessTokenService sprintf(SummitScopes::WriteSummitMediaFileTypes, $url), sprintf(CompanyScopes::Write, $url), sprintf(CompanyScopes::Read, $url), + sprintf(SummitScopes::WriteMetrics, $url), + sprintf(SummitScopes::ReadMetrics, $url), ); return AccessToken::createFromParams( @@ -202,6 +204,8 @@ class AccessTokenServiceStub2 implements IAccessTokenService sprintf(SummitScopes::LeaveEvent, $url), sprintf(SummitScopes::ReadSummitMediaFileTypes, $url), sprintf(SummitScopes::WriteSummitMediaFileTypes, $url), + sprintf(SummitScopes::WriteMetrics, $url), + sprintf(SummitScopes::ReadMetrics, $url), sprintf(CompanyScopes::Write, $url), sprintf(CompanyScopes::Read, $url), );