diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitAttendeesApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitAttendeesApiController.php index 592c991d6..9059252e4 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitAttendeesApiController.php +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitAttendeesApiController.php @@ -21,13 +21,17 @@ use App\Jobs\Emails\SummitAttendeeRegistrationIncompleteReminderEmail; use App\Jobs\Emails\SummitAttendeeTicketRegenerateHashEmail; use App\Jobs\SynchAllAttendeesStatus; +use App\Models\Foundation\Main\IGroup; use App\ModelSerializers\SerializerUtils; use App\Rules\Boolean; +use App\Security\SummitScopes; use App\Services\Model\IAttendeeService; use App\Services\Model\ISummitOrderService; use Exception; +use Illuminate\Http\Response; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Request; +use OpenApi\Attributes as OA; use models\exceptions\EntityNotFoundException; use models\exceptions\ValidationException; use models\main\IMemberRepository; @@ -142,10 +146,34 @@ public function __construct * Attendees endpoints */ - /** - * @param $summit_id - * @return mixed - */ + #[OA\Get( + path: '/api/v1/summits/{id}/attendees/me', + operationId: 'getCurrentAttendee', + summary: 'Get current user attendee profile', + description: 'Returns the attendee profile for the currently authenticated user in the specified summit', + tags: ['Summit Attendees'], + security: [ + [ + 'summit_attendees_oauth2' => [ + SummitScopes::ReadSummitData, + SummitScopes::ReadAllSummitData, + ] + ] + ], + parameters: [ + new OA\Parameter(name: 'id', in: 'path', required: true, description: 'Summit ID', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'expand', in: 'query', required: false, description: 'Expand relationships (extra_questions, tickets, presentation_votes, ticket_types, allowed_access_levels, allowed_features, tags)', schema: new OA\Schema(type: 'string')), + ], + responses: [ + new OA\Response( + response: Response::HTTP_OK, + description: 'Successful operation', + content: new OA\JsonContent(ref: '#/components/schemas/SummitAttendee') + ), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: 'Unauthorized'), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: 'Summit or attendee not found'), + ] + )] public function getOwnAttendee($summit_id) { try { @@ -170,11 +198,43 @@ public function getOwnAttendee($summit_id) } } - /** - * @param $summit_id - * @param $attendee_id - * @return mixed - */ + #[OA\Get( + path: '/api/v1/summits/{id}/attendees/{attendee_id}', + operationId: 'getAttendee', + summary: 'Get attendee by ID', + description: 'Returns a specific attendee profile from the summit', + tags: ['Summit Attendees'], + x: [ + 'required-groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::SummitRegistrationAdmins, + ] + ], + security: [ + [ + 'summit_attendees_oauth2' => [ + SummitScopes::ReadSummitData, + SummitScopes::ReadAllSummitData, + ] + ] + ], + parameters: [ + new OA\Parameter(name: 'id', in: 'path', required: true, description: 'Summit ID', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'attendee_id', in: 'path', required: true, description: 'Attendee ID', schema: new OA\Schema(type: 'integer')), + new OA\Parameter(name: 'expand', in: 'query', required: false, description: 'Expand relationships', schema: new OA\Schema(type: 'string')), + ], + responses: [ + new OA\Response( + response: Response::HTTP_OK, + description: 'Successful operation', + content: new OA\JsonContent(ref: '#/components/schemas/SummitAttendee') + ), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: 'Unauthorized'), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: 'Summit or attendee not found'), + ] + )] public function getAttendee($summit_id, $attendee_id) { return $this->processRequest(function() use($summit_id, $attendee_id){ @@ -201,11 +261,37 @@ public function getAttendee($summit_id, $attendee_id) }); } - /** - * @param $summit_id - * @param $attendee_id - * @return mixed - */ + #[OA\Get( + path: '/api/v1/summits/{id}/attendees/{attendee_id}/schedule', + operationId: 'getAttendeeSchedule', + summary: 'Get attendee schedule', + description: 'Returns the personal schedule for a specific attendee', + tags: ['Summit Attendees'], + security: [ + [ + 'summit_attendees_oauth2' => [ + SummitScopes::ReadSummitData, + SummitScopes::ReadAllSummitData, + ] + ] + ], + parameters: [ + new OA\Parameter(name: 'id', in: 'path', required: true, description: 'Summit ID', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'attendee_id', in: 'path', required: true, description: 'Attendee ID', schema: new OA\Schema(type: 'integer')), + ], + responses: [ + new OA\Response( + response: Response::HTTP_OK, + description: 'Successful operation', + content: new OA\JsonContent( + type: 'array', + items: new OA\Items(type: 'object') + ) + ), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: 'Unauthorized'), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: 'Summit or attendee not found'), + ] + )] public function getAttendeeSchedule($summit_id, $attendee_id) { try { @@ -232,12 +318,31 @@ public function getAttendeeSchedule($summit_id, $attendee_id) } } - /** - * @param $summit_id - * @param $attendee_id - * @param $event_id - * @return mixed - */ + #[OA\Post( + path: '/api/v1/summits/{id}/attendees/{attendee_id}/schedule/{event_id}', + operationId: 'addEventToAttendeeSchedule', + summary: 'Add event to attendee schedule', + description: 'Adds an event to the attendee\'s personal schedule', + tags: ['Summit Attendees'], + security: [ + [ + 'summit_attendees_oauth2' => [ + SummitScopes::WriteSummitData, + ] + ] + ], + parameters: [ + new OA\Parameter(name: 'id', in: 'path', required: true, description: 'Summit ID', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'attendee_id', in: 'path', required: true, description: 'Attendee ID', schema: new OA\Schema(type: 'integer')), + new OA\Parameter(name: 'event_id', in: 'path', required: true, description: 'Event ID', schema: new OA\Schema(type: 'integer')), + ], + responses: [ + new OA\Response(response: Response::HTTP_CREATED, description: 'Event added to schedule successfully'), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: 'Unauthorized'), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: 'Summit, attendee or event not found'), + new OA\Response(response: Response::HTTP_PRECONDITION_FAILED, description: 'Validation Error'), + ] + )] public function addEventToAttendeeSchedule($summit_id, $attendee_id, $event_id) { try { @@ -266,12 +371,31 @@ public function addEventToAttendeeSchedule($summit_id, $attendee_id, $event_id) } } - /** - * @param $summit_id - * @param $attendee_id - * @param $event_id - * @return mixed - */ + #[OA\Delete( + path: '/api/v1/summits/{id}/attendees/{attendee_id}/schedule/{event_id}', + operationId: 'removeEventFromAttendeeSchedule', + summary: 'Remove event from attendee schedule', + description: 'Removes an event from the attendee\'s personal schedule', + tags: ['Summit Attendees'], + security: [ + [ + 'summit_attendees_oauth2' => [ + SummitScopes::WriteSummitData, + ] + ] + ], + parameters: [ + new OA\Parameter(name: 'id', in: 'path', required: true, description: 'Summit ID', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'attendee_id', in: 'path', required: true, description: 'Attendee ID', schema: new OA\Schema(type: 'integer')), + new OA\Parameter(name: 'event_id', in: 'path', required: true, description: 'Event ID', schema: new OA\Schema(type: 'integer')), + ], + responses: [ + new OA\Response(response: Response::HTTP_NO_CONTENT, description: 'Event removed from schedule successfully'), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: 'Unauthorized'), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: 'Summit, attendee or event not found'), + new OA\Response(response: Response::HTTP_PRECONDITION_FAILED, description: 'Validation Error'), + ] + )] public function removeEventFromAttendeeSchedule($summit_id, $attendee_id, $event_id) { try { @@ -301,12 +425,32 @@ public function removeEventFromAttendeeSchedule($summit_id, $attendee_id, $event } } - /** - * @param $summit_id - * @param $attendee_id - * @param $event_id - * @return mixed - */ + #[OA\Delete( + path: '/api/v1/summits/{id}/attendees/{attendee_id}/schedule/{event_id}/rsvp', + operationId: 'deleteAttendeeEventRsvp', + summary: 'Delete RSVP for event', + description: 'Deletes the attendee\'s RSVP for a specific event', + tags: ['Summit Attendees'], + security: [ + [ + 'summit_attendees_oauth2' => [ + SummitScopes::WriteSummitData, + SummitScopes::DeleteMyRSVP, + ] + ] + ], + parameters: [ + new OA\Parameter(name: 'id', in: 'path', required: true, description: 'Summit ID', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'attendee_id', in: 'path', required: true, description: 'Attendee ID', schema: new OA\Schema(type: 'integer')), + new OA\Parameter(name: 'event_id', in: 'path', required: true, description: 'Event ID', schema: new OA\Schema(type: 'integer')), + ], + responses: [ + new OA\Response(response: Response::HTTP_NO_CONTENT, description: 'RSVP deleted successfully'), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: 'Unauthorized'), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: 'Summit, attendee or event not found'), + new OA\Response(response: Response::HTTP_PRECONDITION_FAILED, description: 'Validation Error'), + ] + )] public function deleteEventRSVP($summit_id, $attendee_id, $event_id) { try { @@ -342,10 +486,70 @@ public function deleteEventRSVP($summit_id, $attendee_id, $event_id) } } - /** - * @param $summit_id - * @return mixed - */ + #[OA\Put( + path: '/api/v1/summits/{id}/attendees/{attendee_id}/schedule/{event_id}/check-in', + operationId: 'checkInAttendeeToEvent', + summary: 'Check-in attendee to event', + description: 'Performs check-in for an attendee at a specific event on their schedule', + tags: ['Summit Attendees'], + security: [ + [ + 'summit_attendees_oauth2' => [ + SummitScopes::WriteSummitData, + ] + ] + ], + parameters: [ + new OA\Parameter(name: 'id', in: 'path', required: true, description: 'Summit ID', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'attendee_id', in: 'path', required: true, description: 'Attendee ID or "me"', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'event_id', in: 'path', required: true, description: 'Event ID', schema: new OA\Schema(type: 'integer')), + ], + responses: [ + new OA\Response(response: Response::HTTP_NOT_IMPLEMENTED, description: 'Not Implemented'), + ] + )] + + #[OA\Get( + path: '/api/v1/summits/{id}/attendees', + operationId: 'getAllAttendees', + summary: 'Get all attendees for a summit', + description: 'Returns a paginated list of attendees for the specified summit with filtering, sorting and search capabilities', + tags: ['Summit Attendees'], + x: [ + 'required-groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::SummitRegistrationAdmins, + ] + ], + security: [ + [ + 'summit_attendees_oauth2' => [ + SummitScopes::ReadSummitData, + SummitScopes::ReadAllSummitData, + ] + ] + ], + parameters: [ + new OA\Parameter(name: 'id', in: 'path', required: true, description: 'Summit ID', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'page', in: 'query', required: false, description: 'Page number', schema: new OA\Schema(type: 'integer', default: 1)), + new OA\Parameter(name: 'per_page', in: 'query', required: false, description: 'Items per page', schema: new OA\Schema(type: 'integer', default: 10, maximum: 100)), + new OA\Parameter(name: 'filter', in: 'query', required: false, description: 'Filter by id, first_name, last_name, full_name, company, email, member_id, ticket_type, badge_type, status, has_member, has_tickets, etc.', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'order', in: 'query', required: false, description: 'Order by first_name, last_name, email, company, id, status, etc.', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'expand', in: 'query', required: false, description: 'Expand relationships', schema: new OA\Schema(type: 'string')), + ], + responses: [ + new OA\Response( + response: Response::HTTP_OK, + description: 'Successful operation', + content: new OA\JsonContent(ref: '#/components/schemas/PaginatedSummitAttendeesResponse') + ), + 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_NOT_FOUND, description: 'Summit not found'), + ] + )] public function getAttendeesBySummit($summit_id) { @@ -489,10 +693,46 @@ function () { ); } - /** - * @param $summit_id - * @return mixed - */ + #[OA\Get( + path: '/api/v1/summits/{id}/attendees/csv', + operationId: 'getAllAttendeesCSV', + summary: 'Export attendees to CSV', + description: 'Returns a CSV file with all attendees for the specified summit', + tags: ['Summit Attendees'], + x: [ + 'required-groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::SummitRegistrationAdmins, + ] + ], + security: [ + [ + 'summit_attendees_oauth2' => [ + SummitScopes::ReadSummitData, + SummitScopes::ReadAllSummitData, + ] + ] + ], + parameters: [ + new OA\Parameter(name: 'id', in: 'path', required: true, description: 'Summit ID', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'filter', in: 'query', required: false, description: 'Filter by id, first_name, last_name, full_name, company, email, member_id, ticket_type, badge_type, status, has_member, has_tickets, etc.', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'order', in: 'query', required: false, description: 'Order by first_name, last_name, email, company, id, status, etc.', schema: new OA\Schema(type: 'string')), + ], + responses: [ + new OA\Response( + response: Response::HTTP_OK, + description: 'CSV file with attendees data', + content: new OA\MediaType( + mediaType: 'text/csv', + schema: new OA\Schema(type: 'string', format: 'binary') + ) + ), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: 'Unauthorized'), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: 'Summit not found'), + ] + )] public function getAttendeesBySummitCSV($summit_id) { @@ -610,10 +850,47 @@ function () { ); } - /** - * @param int $summit_id - * @return mixed - */ + #[OA\Post( + path: '/api/v1/summits/{id}/attendees', + operationId: 'createAttendee', + summary: 'Create a new attendee', + description: 'Creates a new attendee for the specified summit', + tags: ['Summit Attendees'], + x: [ + 'required-groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::SummitRegistrationAdmins, + ] + ], + security: [ + [ + 'summit_attendees_oauth2' => [ + SummitScopes::WriteSummitData, + SummitScopes::WriteAttendeesData, + ] + ] + ], + parameters: [ + new OA\Parameter(name: 'id', in: 'path', required: true, description: 'Summit ID', schema: new OA\Schema(type: 'string')), + ], + requestBody: new OA\RequestBody( + required: true, + content: new OA\JsonContent(ref: '#/components/schemas/AttendeeRequest') + ), + responses: [ + new OA\Response( + response: Response::HTTP_CREATED, + description: 'Attendee created successfully', + content: new OA\JsonContent(ref: '#/components/schemas/SummitAttendee') + ), + 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_NOT_FOUND, description: 'Summit not found'), + new OA\Response(response: Response::HTTP_PRECONDITION_FAILED, description: 'Validation Error'), + ] + )] public function addAttendee($summit_id) { return $this->processRequest(function() use($summit_id){ @@ -668,11 +945,38 @@ public function addAttendee($summit_id) }); } - /** - * @param $summit_id - * @param $attendee_id - * @return mixed - */ + #[OA\Delete( + path: '/api/v1/summits/{id}/attendees/{attendee_id}', + operationId: 'deleteAttendee', + summary: 'Delete an attendee', + description: 'Deletes a specific attendee from the summit', + tags: ['Summit Attendees'], + x: [ + 'required-groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::SummitRegistrationAdmins, + ] + ], + security: [ + [ + 'summit_attendees_oauth2' => [ + SummitScopes::WriteSummitData, + SummitScopes::WriteAttendeesData, + ] + ] + ], + parameters: [ + new OA\Parameter(name: 'id', in: 'path', required: true, description: 'Summit ID', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'attendee_id', in: 'path', required: true, description: 'Attendee ID', schema: new OA\Schema(type: 'integer')), + ], + responses: [ + new OA\Response(response: Response::HTTP_NO_CONTENT, description: 'Attendee deleted successfully'), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: 'Unauthorized'), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: 'Summit or attendee not found'), + ] + )] public function deleteAttendee($summit_id, $attendee_id) { return $this->processRequest(function() use($summit_id, $attendee_id){ @@ -690,11 +994,48 @@ public function deleteAttendee($summit_id, $attendee_id) }); } - /** - * @param int $summit_id - * @param int $attendee_id - * @return mixed - */ + #[OA\Put( + path: '/api/v1/summits/{id}/attendees/{attendee_id}', + operationId: 'updateAttendee', + summary: 'Update an attendee', + description: 'Updates a specific attendee in the summit', + tags: ['Summit Attendees'], + x: [ + 'required-groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::SummitRegistrationAdmins, + ] + ], + security: [ + [ + 'summit_attendees_oauth2' => [ + SummitScopes::WriteSummitData, + SummitScopes::WriteAttendeesData, + ] + ] + ], + parameters: [ + new OA\Parameter(name: 'id', in: 'path', required: true, description: 'Summit ID', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'attendee_id', in: 'path', required: true, description: 'Attendee ID', schema: new OA\Schema(type: 'integer')), + ], + requestBody: new OA\RequestBody( + required: true, + content: new OA\JsonContent(ref: '#/components/schemas/AttendeeRequest') + ), + responses: [ + new OA\Response( + response: Response::HTTP_OK, + description: 'Attendee updated successfully', + content: new OA\JsonContent(ref: '#/components/schemas/SummitAttendee') + ), + 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_NOT_FOUND, description: 'Summit or attendee not found'), + new OA\Response(response: Response::HTTP_PRECONDITION_FAILED, description: 'Validation Error'), + ] + )] public function updateAttendee($summit_id, $attendee_id) { return $this->processRequest(function() use($summit_id, $attendee_id){ @@ -752,11 +1093,48 @@ public function updateAttendee($summit_id, $attendee_id) }); } - /** - * @param $summit_id - * @param $attendee_id - * @return mixed - */ + #[OA\Post( + path: '/api/v1/summits/{id}/attendees/{attendee_id}/tickets', + operationId: 'addAttendeeTicket', + summary: 'Add ticket to attendee', + description: 'Creates a new ticket for a specific attendee', + tags: ['Summit Attendees'], + x: [ + 'required-groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::SummitRegistrationAdmins, + ] + ], + security: [ + [ + 'summit_attendees_oauth2' => [ + SummitScopes::WriteSummitData, + SummitScopes::WriteAttendeesData, + ] + ] + ], + parameters: [ + new OA\Parameter(name: 'id', in: 'path', required: true, description: 'Summit ID', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'attendee_id', in: 'path', required: true, description: 'Attendee ID', schema: new OA\Schema(type: 'integer')), + ], + requestBody: new OA\RequestBody( + required: true, + content: new OA\JsonContent(ref: '#/components/schemas/AddAttendeeTicketRequest') + ), + responses: [ + new OA\Response( + response: Response::HTTP_CREATED, + description: 'Ticket created successfully', + content: new OA\JsonContent(type: 'object') + ), + 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_NOT_FOUND, description: 'Summit or attendee not found'), + new OA\Response(response: Response::HTTP_PRECONDITION_FAILED, description: 'Validation Error'), + ] + )] public function addAttendeeTicket($summit_id, $attendee_id) { return $this->processRequest(function() use($summit_id, $attendee_id){ @@ -808,12 +1186,39 @@ public function addAttendeeTicket($summit_id, $attendee_id) }); } - /** - * @param $summit_id - * @param $attendee_id - * @param $ticket_id - * @return mixed - */ + #[OA\Delete( + path: '/api/v1/summits/{id}/attendees/{attendee_id}/tickets/{ticket_id}', + operationId: 'deleteAttendeeTicket', + summary: 'Delete attendee ticket', + description: 'Deletes a specific ticket from an attendee', + tags: ['Summit Attendees'], + x: [ + 'required-groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::SummitRegistrationAdmins, + ] + ], + security: [ + [ + 'summit_attendees_oauth2' => [ + SummitScopes::WriteSummitData, + SummitScopes::WriteAttendeesData, + ] + ] + ], + parameters: [ + new OA\Parameter(name: 'id', in: 'path', required: true, description: 'Summit ID', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'attendee_id', in: 'path', required: true, description: 'Attendee ID', schema: new OA\Schema(type: 'integer')), + new OA\Parameter(name: 'ticket_id', in: 'path', required: true, description: 'Ticket ID', schema: new OA\Schema(type: 'integer')), + ], + responses: [ + new OA\Response(response: Response::HTTP_NO_CONTENT, description: 'Ticket deleted successfully'), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: 'Unauthorized'), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: 'Summit, attendee or ticket not found'), + ] + )] public function deleteAttendeeTicket($summit_id, $attendee_id, $ticket_id) { return $this->processRequest(function() use($summit_id, $attendee_id, $ticket_id){ @@ -830,13 +1235,44 @@ public function deleteAttendeeTicket($summit_id, $attendee_id, $ticket_id) }); } - /** - * @param $summit_id - * @param $attendee_id - * @param $ticket_id - * @param $other_member_id - * @return mixed - */ + #[OA\Put( + path: '/api/v1/summits/{id}/attendees/{attendee_id}/tickets/{ticket_id}/reassign/{other_member_id}', + operationId: 'reassignAttendeeTicketByMember', + summary: 'Reassign ticket to another member', + description: 'Reassigns a ticket from one attendee to another member', + tags: ['Summit Attendees'], + x: [ + 'required-groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::SummitRegistrationAdmins, + ] + ], + security: [ + [ + 'summit_attendees_oauth2' => [ + SummitScopes::WriteSummitData, + SummitScopes::WriteAttendeesData, + ] + ] + ], + parameters: [ + new OA\Parameter(name: 'id', in: 'path', required: true, description: 'Summit ID', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'attendee_id', in: 'path', required: true, description: 'Attendee ID', schema: new OA\Schema(type: 'integer')), + new OA\Parameter(name: 'ticket_id', in: 'path', required: true, description: 'Ticket ID', schema: new OA\Schema(type: 'integer')), + new OA\Parameter(name: 'other_member_id', in: 'path', required: true, description: 'Target Member ID', schema: new OA\Schema(type: 'integer')), + ], + responses: [ + new OA\Response( + response: Response::HTTP_OK, + description: 'Ticket reassigned successfully', + content: new OA\JsonContent(type: 'object') + ), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: 'Unauthorized'), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: 'Summit, attendee, ticket or member not found'), + ] + )] public function reassignAttendeeTicketByMember($summit_id, $attendee_id, $ticket_id, $other_member_id) { return $this->processRequest(function() use($summit_id, $attendee_id, $ticket_id, $other_member_id){ @@ -862,12 +1298,48 @@ public function reassignAttendeeTicketByMember($summit_id, $attendee_id, $ticket }); } - /** - * @param $summit_id - * @param $attendee_id - * @param $ticket_id - * @return \Illuminate\Http\JsonResponse|mixed - */ + #[OA\Put( + path: '/api/v1/summits/{id}/attendees/{attendee_id}/tickets/{ticket_id}/reassign', + operationId: 'reassignAttendeeTicket', + summary: 'Reassign ticket to another attendee', + description: 'Reassigns a ticket to a different attendee by email', + tags: ['Summit Attendees'], + x: [ + 'required-groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::SummitRegistrationAdmins, + ] + ], + security: [ + [ + 'summit_attendees_oauth2' => [ + SummitScopes::WriteSummitData, + SummitScopes::WriteAttendeesData, + ] + ] + ], + parameters: [ + new OA\Parameter(name: 'id', in: 'path', required: true, description: 'Summit ID', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'attendee_id', in: 'path', required: true, description: 'Attendee ID', schema: new OA\Schema(type: 'integer')), + new OA\Parameter(name: 'ticket_id', in: 'path', required: true, description: 'Ticket ID', schema: new OA\Schema(type: 'integer')), + ], + requestBody: new OA\RequestBody( + required: true, + content: new OA\JsonContent(ref: '#/components/schemas/ReassignAttendeeTicketRequest') + ), + responses: [ + new OA\Response( + response: Response::HTTP_OK, + description: 'Ticket reassigned successfully', + content: new OA\JsonContent(type: 'object') + ), + 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_NOT_FOUND, description: 'Summit, attendee or ticket not found'), + ] + )] public function reassignAttendeeTicket($summit_id, $attendee_id, $ticket_id) { return $this->processRequest(function() use($summit_id, $attendee_id, $ticket_id){ @@ -905,10 +1377,44 @@ protected function getSummitRepository(): ISummitRepository return $this->summit_repository; } - /** - * @param $summit_id - * @return \Illuminate\Http\JsonResponse|mixed - */ + #[OA\Put( + path: '/api/v1/summits/{id}/attendees/all/send', + operationId: 'sendAttendeesEmail', + summary: 'Send email to attendees', + description: 'Sends email notifications to filtered attendees', + tags: ['Summit Attendees'], + x: [ + 'required-groups' => [ + IGroup::SuperAdmins, + IGroup::Administrators, + IGroup::SummitAdministrators, + IGroup::SummitRegistrationAdmins, + ] + ], + security: [ + [ + 'summit_attendees_oauth2' => [ + SummitScopes::WriteSummitData, + SummitScopes::WriteAttendeesData, + ] + ] + ], + parameters: [ + new OA\Parameter(name: 'id', in: 'path', required: true, description: 'Summit ID', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'filter', in: 'query', required: false, description: 'Filter attendees', schema: new OA\Schema(type: 'string')), + ], + requestBody: new OA\RequestBody( + required: true, + content: new OA\JsonContent(ref: '#/components/schemas/SendAttendeesEmailRequest') + ), + responses: [ + new OA\Response(response: Response::HTTP_OK, description: 'Email send process initiated successfully'), + 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_NOT_FOUND, description: 'Summit not found'), + new OA\Response(response: Response::HTTP_PRECONDITION_FAILED, description: 'Validation Error'), + ] + )] public function send($summit_id) { return $this->processRequest(function() use($summit_id){ @@ -1035,11 +1541,34 @@ public function send($summit_id) }); } - /** - * @param int $summit_id - * @param int $attendee_id - * @return mixed - */ + #[OA\Put( + path: '/api/v1/summits/{id}/attendees/{attendee_id}/virtual-check-in', + operationId: 'doAttendeeVirtualCheckIn', + summary: 'Perform virtual check-in', + description: 'Performs virtual check-in for a specific attendee', + tags: ['Summit Attendees'], + security: [ + [ + 'summit_attendees_oauth2' => [ + SummitScopes::WriteSummitData, + SummitScopes::DoVirtualCheckIn, + ] + ] + ], + parameters: [ + new OA\Parameter(name: 'id', in: 'path', required: true, description: 'Summit ID', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'attendee_id', in: 'path', required: true, description: 'Attendee ID', schema: new OA\Schema(type: 'integer')), + ], + responses: [ + new OA\Response( + response: Response::HTTP_OK, + description: 'Virtual check-in completed successfully', + content: new OA\JsonContent(ref: '#/components/schemas/SummitAttendee') + ), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: 'Unauthorized'), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: 'Summit or attendee not found'), + ] + )] public function doVirtualCheckin($summit_id, $attendee_id) { return $this->processRequest(function() use($summit_id, $attendee_id){ @@ -1060,11 +1589,36 @@ public function doVirtualCheckin($summit_id, $attendee_id) }); } - /** - * @param $summit_id - * @param $attendee_id - * @return mixed - */ + #[OA\Get( + path: '/api/v1/summits/{id}/attendees/{attendee_id}/me', + operationId: 'getMyRelatedAttendee', + summary: 'Get related attendee for current user', + description: 'Returns attendee information if current user owns the tickets or is the attendee', + tags: ['Summit Attendees'], + security: [ + [ + 'summit_attendees_oauth2' => [ + SummitScopes::ReadSummitData, + SummitScopes::ReadAllSummitData, + ] + ] + ], + parameters: [ + new OA\Parameter(name: 'id', in: 'path', required: true, description: 'Summit ID', schema: new OA\Schema(type: 'string')), + new OA\Parameter(name: 'attendee_id', in: 'path', required: true, description: 'Attendee ID', schema: new OA\Schema(type: 'integer')), + new OA\Parameter(name: 'expand', in: 'query', required: false, description: 'Expand relationships', schema: new OA\Schema(type: 'string')), + ], + responses: [ + new OA\Response( + response: Response::HTTP_OK, + description: 'Successful operation', + content: new OA\JsonContent(ref: '#/components/schemas/SummitAttendee') + ), + 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 or attendee not found'), + ] + )] public function getMyRelatedAttendee($summit_id, $attendee_id) { return $this->processRequest(function() use($summit_id, $attendee_id){ @@ -1117,4 +1671,4 @@ public function getMyRelatedAttendee($summit_id, $attendee_id) }); } -} \ No newline at end of file +} diff --git a/app/Swagger/Models/SummitAttendeeSchema.php b/app/Swagger/Models/SummitAttendeeSchema.php new file mode 100644 index 000000000..78e8e95cb --- /dev/null +++ b/app/Swagger/Models/SummitAttendeeSchema.php @@ -0,0 +1,49 @@ + 'Read Summit Data', + SummitScopes::ReadAllSummitData => 'Read All Summit Data', + SummitScopes::WriteSummitData => 'Write Summit Data', + SummitScopes::WriteAttendeesData => 'Write Attendees Data', + SummitScopes::DoVirtualCheckIn => 'Do Virtual Check-In', + SummitScopes::DeleteMyRSVP => 'Delete My RSVP', + ], + ), + ], +) +] +class SummitAttendeesAuthSchema +{ +} \ No newline at end of file diff --git a/app/Swagger/SummitRegistrationSchemas.php b/app/Swagger/SummitRegistrationSchemas.php index 8e5afc512..3822c7e08 100644 --- a/app/Swagger/SummitRegistrationSchemas.php +++ b/app/Swagger/SummitRegistrationSchemas.php @@ -53,6 +53,129 @@ class SummitTaxTypeUpdateRequestSchema // Badge Types +#[OA\Schema( + schema: 'PaginatedSummitAttendeesResponse', + allOf: [ + new OA\Schema(ref: '#/components/schemas/PaginateDataSchemaResponse'), + new OA\Schema( + type: 'object', + properties: [ + new OA\Property( + property: 'data', + type: 'array', + items: new OA\Items(ref: '#/components/schemas/SummitAttendee') + ) + ] + ) + ] +)] +class PaginatedSummitAttendeesResponseSchema +{ +} + +#[OA\Schema( + schema: 'AttendeeRequest', + type: 'object', + properties: [ + new OA\Property(property: 'shared_contact_info', type: 'boolean'), + new OA\Property(property: 'summit_hall_checked_in', type: 'boolean'), + new OA\Property(property: 'disclaimer_accepted', type: 'boolean'), + new OA\Property(property: 'first_name', type: 'string', maxLength: 255), + new OA\Property(property: 'surname', type: 'string', maxLength: 255), + new OA\Property(property: 'company', type: 'string', maxLength: 255), + new OA\Property(property: 'email', type: 'string', maxLength: 255), + new OA\Property(property: 'member_id', type: 'integer'), + new OA\Property(property: 'admin_notes', type: 'string', maxLength: 1024), + new OA\Property(property: 'tags', type: 'array', items: new OA\Items(type: 'string')), + new OA\Property(property: 'manager_id', type: 'integer'), + new OA\Property( + property: 'extra_questions', + type: 'array', + items: new OA\Items( + type: 'object', + properties: [ + new OA\Property(property: 'question_id', type: 'integer'), + new OA\Property(property: 'answer', type: 'string') + ] + ) + ), + ] +)] +class AttendeeRequestSchema +{ +} + +#[OA\Schema( + schema: 'AddAttendeeTicketRequest', + type: 'object', + required: ['ticket_type_id'], + properties: [ + new OA\Property(property: 'ticket_type_id', type: 'integer'), + new OA\Property(property: 'promo_code', type: 'string'), + new OA\Property(property: 'external_order_id', type: 'string'), + new OA\Property(property: 'external_attendee_id', type: 'string'), + ] +)] +class AddAttendeeTicketRequestSchema +{ +} + +#[OA\Schema( + schema: 'ReassignAttendeeTicketRequest', + type: 'object', + required: ['attendee_email'], + properties: [ + new OA\Property(property: 'attendee_first_name', type: 'string', maxLength: 255), + new OA\Property(property: 'attendee_last_name', type: 'string', maxLength: 255), + new OA\Property(property: 'attendee_email', type: 'string', maxLength: 255), + new OA\Property(property: 'attendee_company', type: 'string', maxLength: 255), + new OA\Property( + property: 'extra_questions', + type: 'array', + items: new OA\Items( + type: 'object', + properties: [ + new OA\Property(property: 'question_id', type: 'integer'), + new OA\Property(property: 'answer', type: 'string') + ] + ) + ), + ] +)] +class ReassignAttendeeTicketRequestSchema +{ +} + +#[OA\Schema( + schema: 'SendAttendeesEmailRequest', + type: 'object', + required: ['email_flow_event'], + properties: [ + new OA\Property( + property: 'email_flow_event', + type: 'string', + enum: ['SUMMIT_ATTENDEE_TICKET_REGENERATE_HASH', 'SUMMIT_ATTENDEE_INVITE_TICKET_EDITION', 'SUMMIT_ATTENDEE_ALL_TICKETS_EDITION', 'SUMMIT_ATTENDEE_REGISTRATION_INCOMPLETE_REMINDER', 'SUMMIT_ATTENDEE_GENERIC'] + ), + new OA\Property( + property: 'attendees_ids', + type: 'array', + items: new OA\Items(type: 'integer') + ), + new OA\Property( + property: 'excluded_attendees_ids', + type: 'array', + items: new OA\Items(type: 'integer') + ), + new OA\Property(property: 'test_email_recipient', type: 'string', format: 'email'), + new OA\Property(property: 'outcome_email_recipient', type: 'string', format: 'email'), + ] +)] +class SendAttendeesEmailRequestSchema +{ +} + +// Summit Badge Types + #[OA\Schema( schema: "PaginatedSummitBadgeTypesResponse", description: "Paginated list of summit badge types", diff --git a/app/Swagger/schemas.php b/app/Swagger/schemas.php index b87fdc324..5fa00a088 100644 --- a/app/Swagger/schemas.php +++ b/app/Swagger/schemas.php @@ -152,25 +152,8 @@ class RSVPInvitationSchema new OA\Property(property: 'event_id', type: 'integer', example: 123), ] )] -class RSVPInvitationCSVSchema -{ -} +class RSVPInvitationCSVSchema {} -#[OA\Schema( - schema: 'SummitAttendee', - type: 'object', - properties: [ - new OA\Property(property: 'id', type: 'integer'), - new OA\Property(property: 'created', type: 'integer', example: 1630500518), - new OA\Property(property: 'last_edited', type: 'integer', example: 1630500518), - new OA\Property(property: 'first_name', type: 'string', example: 'John'), - new OA\Property(property: 'last_name', type: 'string', example: 'Doe'), - new OA\Property(property: 'status', type: 'string', example: 'Complete'), - ] -)] -class SummitAttendeeSchema -{ -} #[OA\Schema( schema: 'SummitEvent',