From d518cb083b51448976c664f3263023d56688929a Mon Sep 17 00:00:00 2001 From: Casey Hungler Date: Thu, 18 Jan 2024 14:33:50 -0700 Subject: [PATCH] [Slack][Equipment Authorization] Refactor batch authorization into queuable action(s) (#48) * Add AddUserMembership Action * Add BatchAuthorizeEquipment Action * Use BatchAuthorizeEquipment in `EquipmentAuthorization->handle` --- app/Actions/WordPress/AddUserMembership.php | 33 ++++++++++ .../WordPress/BatchAuthorizeEquipment.php | 66 +++++++++++++++++++ .../Slack/Modals/EquipmentAuthorization.php | 45 ++++++------- 3 files changed, 120 insertions(+), 24 deletions(-) create mode 100644 app/Actions/WordPress/AddUserMembership.php create mode 100644 app/Actions/WordPress/BatchAuthorizeEquipment.php diff --git a/app/Actions/WordPress/AddUserMembership.php b/app/Actions/WordPress/AddUserMembership.php new file mode 100644 index 00000000..b09fb7e6 --- /dev/null +++ b/app/Actions/WordPress/AddUserMembership.php @@ -0,0 +1,33 @@ +wpApi = $wpApi; + } + + /** + * Execute the action. + * + * @return mixed + */ + public function execute($actorId, $memberId, $planId) + { + $this->wpApi->members->addMembership($memberId, $planId); + Log::info('AddUserMembership: Customer '.$actorId.' granted user plan id '.$planId.' to Customer '.$memberId); + } +} diff --git a/app/Actions/WordPress/BatchAuthorizeEquipment.php b/app/Actions/WordPress/BatchAuthorizeEquipment.php new file mode 100644 index 00000000..e666c87b --- /dev/null +++ b/app/Actions/WordPress/BatchAuthorizeEquipment.php @@ -0,0 +1,66 @@ +authorizeAction = $addUserMembership; + } + + /** + * Execute the action. + * @param Customer $trainer - The person who is submitting the authorization + * @param Collection $members - The members to be authorized on the equipment + * @param Collection $equipment - The pieces of equipment for which the members will be authorized + * @param Boolean $makeTrainers - Default false. Whether the members should be authorized to trainers others on this equipment. + * @return mixed + */ + public function execute(Customer $trainer, $members, $equipment, $makeTrainers=false) + { + // Validate that trainer is an authorized trainer for all equipment + foreach($equipment as $e) { + if (!$trainer->hasMembership($e->trainer_plan_id)) { + Log::info('BatchAuthorizeEquipment: Customer attempted to submit authorization without trainer role. '.json_encode([ + 'trainer_id' => $trainer->id, + 'equipment_id' => $e->id + ])); + + throw new \Exception('NotAuthorized'); + } + } + + Log::info('BatchAuthorizeEquipment: Submitting batch: '.json_encode([ + 'trainer_id' => $trainer->id, + 'member_ids' => $members->map(fn($member) => $member->id), + 'equipment_ids' => $equipment->map(fn($e) => $e->id), + 'make_trainers' => $makeTrainers + ])); + + $planIds = $equipment->map(fn($e) => $e->user_plan_id); + + if ($makeTrainers) { + $planIds = $planIds->concat($equipment->map(fn ($e) => $e->trainer_plan_id)); + } + + foreach($members->crossjoin($planIds) as [$member, $planId]){ + if ($member->hasMembership($planId)) { + continue; + } + $this->authorizeAction->onQueue()->execute($trainer->id, $member->id, $planId); + } + return 'ok'; + } +} diff --git a/app/External/Slack/Modals/EquipmentAuthorization.php b/app/External/Slack/Modals/EquipmentAuthorization.php index c001a627..88b854a0 100644 --- a/app/External/Slack/Modals/EquipmentAuthorization.php +++ b/app/External/Slack/Modals/EquipmentAuthorization.php @@ -14,6 +14,7 @@ use SlackPhp\BlockKit\Partials\Option; use SlackPhp\BlockKit\Surfaces\Modal; use Illuminate\Support\Collection; +use App\Actions\WordPress\BatchAuthorizeEquipment; class EquipmentAuthorization implements ModalInterface { @@ -84,7 +85,6 @@ public static function handle(SlackRequest $request) } $state = self::getStateValues($request); - $makeUsers = true; $makeTrainers = ! is_null($state[self::TRAINER_CHECK][self::TRAINER_CHECK] ?? null); /** @var WooCommerceApi $api */ @@ -93,26 +93,19 @@ public static function handle(SlackRequest $request) $selectedEquipment = self::equipmentFromState($state); $selectedMembers = self::peopleFromState($state); - $actorId = $request->customer()->id; - - foreach($selectedMembers->crossJoin($selectedEquipment)->all() as [$person, $equipment]) { - if (!$person->hasMembership($equipment->user_plan_id)) { - Log::info('EquipmentAuthorization: Customer '.$actorId.' authorized Customer '.$person->id.' to use equipment under plan id '.$equipment->user_plan_id); - $api->members->addMembership($person->id, $equipment->user_plan_id); - } - - if ($makeTrainers && !$person->hasMembership($equipment->trainer_plan_id)) { - Log::info('EquipmentAuthorization: Customer '.$actorId.' authorized Customer '.$person->id.' to train on equipment with plan id '.$equipment->trainer_plan_id); - $api->members->addMembership($person->id, $equipment->trainer_plan_id); + $actor = $request->customer(); + + try { + app()->make(BatchAuthorizeEquipment::class)->execute($actor, $selectedMembers, $selectedEquipment, $makeTrainers); + } catch (\Exception $e) { + if ($e->getMessage() == 'NotAuthorized') { + return response()->json([ + 'response_action' => 'errors', + 'errors' => [self::EQUIPMENT_DROPDOWN => "You don't have permission to authorize members for this equipment."] + ]); } } - - if (! $makeTrainers && ! $makeUsers) { - return (new FailureModal('Neither user nor trainer appear to have been selected. Try again?')) - ->update(); - } - // TODO actually check error before sending success return (new SuccessModal())->update(); } @@ -132,16 +125,20 @@ public static function getBlockActions(): array public static function equipmentFromState($state): Collection { - return collect($state[self::EQUIPMENT_DROPDOWN][self::EQUIPMENT_DROPDOWN] ?? []) - -> map(fn($formValue) => str_replace('equipment-', '', $formValue)) - -> map(fn($equipmentId) => TrainableEquipment::find($equipmentId)); + $equipmentIds = array_map( + fn($formValue) => str_replace('equipment-', '', $formValue), + $state[self::EQUIPMENT_DROPDOWN][self::EQUIPMENT_DROPDOWN] ?? [] + ); + return TrainableEquipment::whereIn('id', $equipmentIds)->get(); } public static function peopleFromState($state): Collection { - return collect($state[self::PERSON_DROPDOWN][self::PERSON_DROPDOWN] ?? []) - -> map(fn($formValue) => str_replace('customer-', '', $formValue)) - -> map(fn($customerId) => Customer::find($customerId)); + $customerIds = array_map( + fn($formValue) => str_replace('customer-', '', $formValue), + $state[self::PERSON_DROPDOWN][self::PERSON_DROPDOWN] ?? [] + ); + return Customer::with('memberships')->whereIn('id', $customerIds)->get(); } public static function getOptions(SlackRequest $request)