diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitBadgesApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitBadgesApiController.php index 55cac8eb6..0cd99235d 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitBadgesApiController.php +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitBadgesApiController.php @@ -1,4 +1,7 @@ -repository = $repository; $this->summit_repository = $summit_repository; @@ -52,38 +57,112 @@ protected function getSummitRepository(): ISummitRepository return $this->summit_repository; } + // OpenAPI Documentation + + #[OA\Get( + path: '/api/v1/summits/{id}/badges', + operationId: 'getAllBySummit', + summary: 'Get all attendee badges for a summit', + description: 'Retrieves a paginated list of attendee badges for a specific summit. Badges are issued to attendees and contain ticket information, badge type, printing details, and feature assignments (ribbons, special access indicators, etc.).', + security: [['summit_badges_api_oauth2' => [SummitScopes::ReadAllSummitData]]], + tags: ['Summit Badges'], + parameters: [ + new OA\Parameter( + name: 'id', + in: 'path', + required: true, + description: 'Summit ID', + schema: new OA\Schema(type: 'integer') + ), + new OA\Parameter( + name: 'page', + in: 'query', + required: false, + description: 'Page number for pagination', + schema: new OA\Schema(type: 'integer', example: 1) + ), + new OA\Parameter( + name: 'per_page', + in: 'query', + required: false, + description: 'Items per page', + schema: new OA\Schema(type: 'integer', example: 10, maximum: 100) + ), + new OA\Parameter( + name: 'filter[]', + in: 'query', + required: false, + description: 'Filter expressions. Format: fieldvalue. Available fields: owner_first_name, owner_last_name, owner_full_name, owner_email, ticket_number, order_number (all support =@, ==). Operators: == (equals), =@ (contains)', + style: 'form', + explode: true, + schema: new OA\Schema( + type: 'array', + items: new OA\Items(type: 'string', example: 'owner_email==john@example.com') + ) + ), + new OA\Parameter( + name: 'order', + in: 'query', + required: false, + description: 'Order by field(s). Available fields: id, ticket_number, order_number, created. Use "-" prefix for descending order.', + schema: new OA\Schema(type: 'string', example: 'created') + ), + new OA\Parameter( + name: 'expand', + in: 'query', + required: false, + description: 'Expand relationships. Available: ticket, type, features', + schema: new OA\Schema(type: 'string', example: 'ticket,type,features') + ), + ], + responses: [ + new OA\Response( + response: 200, + description: 'Attendee badges retrieved successfully', + content: new OA\JsonContent(ref: '#/components/schemas/PaginatedSummitAttendeeBadgesResponse') + ), + 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: "Not Found"), + new OA\Response(response: Response::HTTP_PRECONDITION_FAILED, description: "Validation Error"), + new OA\Response(response: Response::HTTP_INTERNAL_SERVER_ERROR, description: "Server Error"), + ] + )] + /** * @param $summit_id * @return mixed */ - public function getAllBySummit($summit_id){ + public function getAllBySummit($summit_id) + { $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->getResourceServerContext())->find($summit_id); - if (is_null($summit)) return $this->error404(); + if (is_null($summit)) + return $this->error404(); return $this->_getAll( - function(){ + function () { return [ - 'owner_first_name' => ['=@', '=='], - 'owner_last_name' => ['=@', '=='], - 'owner_full_name' => ['=@', '=='], - 'owner_email' => ['=@', '=='], - 'ticket_number' => ['=@', '=='], - 'order_number' => ['=@', '=='], + 'owner_first_name' => ['=@', '=='], + 'owner_last_name' => ['=@', '=='], + 'owner_full_name' => ['=@', '=='], + 'owner_email' => ['=@', '=='], + 'ticket_number' => ['=@', '=='], + 'order_number' => ['=@', '=='], ]; }, - function(){ + function () { return [ - 'owner_first_name' => 'sometimes|string', - 'owner_last_name' => 'sometimes|string', - 'owner_full_name' => 'sometimes|string', - 'owner_email' => 'sometimes|string', - 'ticket_number' => 'sometimes|string', - 'order_number' => 'sometimes|string', + 'owner_first_name' => 'sometimes|string', + 'owner_last_name' => 'sometimes|string', + 'owner_full_name' => 'sometimes|string', + 'owner_email' => 'sometimes|string', + 'ticket_number' => 'sometimes|string', + 'order_number' => 'sometimes|string', ]; }, - function() - { + function () { return [ 'id', 'ticket_number', @@ -91,50 +170,107 @@ function() 'created' ]; }, - function($filter) use($summit){ - if($filter instanceof Filter){ + function ($filter) use ($summit) { + if ($filter instanceof Filter) { $filter->addFilterCondition(FilterElement::makeEqual('summit_id', $summit->getId())); } return $filter; }, - function(){ + function () { return SerializerRegistry::SerializerType_Private; } ); } + #[OA\Get( + path: '/api/v1/summits/{id}/badges/csv', + operationId: 'getAllBySummitCSV', + summary: 'Export all attendee badges for a summit to CSV', + description: 'Exports a CSV file containing all attendee badges for a specific summit. Supports the same filtering and ordering capabilities as the standard list endpoint.', + security: [['summit_badges_api_oauth2' => [SummitScopes::ReadAllSummitData]]], + tags: ['Summit Badges'], + parameters: [ + new OA\Parameter( + name: 'id', + in: 'path', + required: true, + description: 'Summit ID', + schema: new OA\Schema(type: 'integer') + ), + new OA\Parameter( + name: 'filter[]', + in: 'query', + required: false, + description: 'Filter expressions. Format: fieldvalue. Available fields: owner_first_name, owner_last_name, owner_full_name, owner_email, ticket_number, order_number (all support =@, ==)', + style: 'form', + explode: true, + schema: new OA\Schema( + type: 'array', + items: new OA\Items(type: 'string', example: 'owner_email=@john') + ) + ), + new OA\Parameter( + name: 'order', + in: 'query', + required: false, + description: 'Order by field(s). Available fields: id, ticket_number, order_number, created', + schema: new OA\Schema(type: 'string', example: '-created') + ), + ], + responses: [ + new OA\Response( + response: 200, + description: 'CSV file generated successfully', + content: new OA\MediaType( + mediaType: 'text/csv', + schema: new OA\Schema( + type: 'string', + format: 'binary' + ) + ) + ), + 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: "Not Found"), + new OA\Response(response: Response::HTTP_PRECONDITION_FAILED, description: "Validation Error"), + new OA\Response(response: Response::HTTP_INTERNAL_SERVER_ERROR, description: "Server Error"), + ] + )] + /** * @param $summit_id * @return mixed */ - public function getAllBySummitCSV($summit_id){ + public function getAllBySummitCSV($summit_id) + { $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->getResourceServerContext())->find($summit_id); - if (is_null($summit)) return $this->error404(); + if (is_null($summit)) + return $this->error404(); return $this->_getAllCSV( - function(){ + function () { return [ - 'owner_first_name' => ['=@', '=='], - 'owner_last_name' => ['=@', '=='], - 'owner_full_name' => ['=@', '=='], - 'owner_email' => ['=@', '=='], - 'ticket_number' => ['=@', '=='], - 'order_number' => ['=@', '=='], + 'owner_first_name' => ['=@', '=='], + 'owner_last_name' => ['=@', '=='], + 'owner_full_name' => ['=@', '=='], + 'owner_email' => ['=@', '=='], + 'ticket_number' => ['=@', '=='], + 'order_number' => ['=@', '=='], ]; }, - function(){ + function () { return [ - 'owner_first_name' => 'sometimes|string', - 'owner_last_name' => 'sometimes|string', - 'owner_full_name' => 'sometimes|string', - 'owner_email' => 'sometimes|string', - 'ticket_number' => 'sometimes|string', - 'order_number' => 'sometimes|string', + 'owner_first_name' => 'sometimes|string', + 'owner_last_name' => 'sometimes|string', + 'owner_full_name' => 'sometimes|string', + 'owner_email' => 'sometimes|string', + 'ticket_number' => 'sometimes|string', + 'order_number' => 'sometimes|string', ]; }, - function() - { + function () { return [ 'id', 'ticket_number', @@ -142,19 +278,19 @@ function() 'created' ]; }, - function($filter) use($summit){ - if($filter instanceof Filter){ + function ($filter) use ($summit) { + if ($filter instanceof Filter) { $filter->addFilterCondition(FilterElement::makeEqual('summit_id', $summit->getId())); } return $filter; }, - function(){ + function () { return SerializerRegistry::SerializerType_Private; }, - function(){ + function () { return []; }, - function(){ + function () { return []; }, 'attendees-badges-' @@ -163,4 +299,4 @@ function(){ -} \ No newline at end of file +} diff --git a/app/Swagger/Models/SummitBadgeFeatureTypeSchema.php b/app/Swagger/Models/SummitBadgeFeatureTypeSchema.php new file mode 100644 index 000000000..4baed7609 --- /dev/null +++ b/app/Swagger/Models/SummitBadgeFeatureTypeSchema.php @@ -0,0 +1,24 @@ + 'Read All Summit Data', + ], + ), + ], + ) +] +class SummitBadgesOAuth2Schema{} diff --git a/app/Swagger/SummitRegistrationSchemas.php b/app/Swagger/SummitRegistrationSchemas.php index 80812cf41..a18b67974 100644 --- a/app/Swagger/SummitRegistrationSchemas.php +++ b/app/Swagger/SummitRegistrationSchemas.php @@ -8,20 +8,6 @@ // Summit Badge Feature Types -#[OA\Schema( - schema: 'SummitBadgeFeatureType', - type: 'object', - properties: [ - new OA\Property(property: 'id', type: 'integer', example: 1), - new OA\Property(property: 'name', type: 'string', example: 'Speaker Ribbon'), - new OA\Property(property: 'description', type: 'string', nullable: true, example: 'Special ribbon indicating speaker status'), - new OA\Property(property: 'template_content', type: 'string', nullable: true, example: '
{{name}}
'), - new OA\Property(property: 'summit_id', type: 'integer', example: 42), - new OA\Property(property: 'image', type: 'string', nullable: true, example: 'https://example.com/images/speaker-ribbon.png'), - ] -)] -class SummitBadgeFeatureTypeSchema {} - #[OA\Schema( schema: 'PaginatedSummitBadgeFeatureTypesResponse', allOf: [ @@ -46,8 +32,8 @@ class PaginatedSummitBadgeFeatureTypesResponseSchema {} required: ['name'], properties: [ new OA\Property(property: 'name', type: 'string', example: 'Speaker Ribbon'), - new OA\Property(property: 'description', type: 'string', nullable: true, example: 'Special ribbon for speakers'), - new OA\Property(property: 'template_content', type: 'string', nullable: true, example: '
{{name}}
'), + new OA\Property(property: 'description', type: 'string', example: 'Special ribbon for speakers'), + new OA\Property(property: 'template_content', type: 'string', example: '
{{name}}
'), ] )] class SummitBadgeFeatureTypeCreateRequestSchema {} @@ -57,8 +43,8 @@ class SummitBadgeFeatureTypeCreateRequestSchema {} type: 'object', properties: [ new OA\Property(property: 'name', type: 'string', example: 'VIP Ribbon'), - new OA\Property(property: 'description', type: 'string', nullable: true, example: 'VIP attendee designation'), - new OA\Property(property: 'template_content', type: 'string', nullable: true, example: '
{{name}}
'), + new OA\Property(property: 'description', type: 'string', example: 'VIP attendee designation'), + new OA\Property(property: 'template_content', type: 'string', example: '
{{name}}
'), ] )] class SummitBadgeFeatureTypeUpdateRequestSchema {} diff --git a/app/Swagger/SummitSchemas.php b/app/Swagger/SummitSchemas.php index 3803ca47b..489d39517 100644 --- a/app/Swagger/SummitSchemas.php +++ b/app/Swagger/SummitSchemas.php @@ -4,7 +4,54 @@ use OpenApi\Attributes as OA; -// +// Summit Attendee Badges + +#[OA\Schema( + schema: 'SummitAttendeeBadge', + type: 'object', + properties: [ + new OA\Property(property: 'id', type: 'integer', example: 1), + new OA\Property(property: 'print_date', type: 'integer', nullable: true, example: 1633024800, description: 'Unix timestamp of when the badge was printed'), + new OA\Property(property: 'qr_code', type: 'string', nullable: true, example: 'QR123456789'), + new OA\Property(property: 'is_void', type: 'boolean', example: false, description: 'Whether the badge has been voided'), + new OA\Property(property: 'printed_times', type: 'integer', example: 2, description: 'Number of times this badge has been printed'), + new OA\Property(property: 'ticket_id', type: 'integer', example: 123, description: 'SummitAttendeeTicket ID, use expand=ticket for full object details'), + new OA\Property(property: 'type_id', type: 'integer', example: 5, description: 'SummitBadgeType ID, use expand=type for full object details'), + new OA\Property(property: 'type', ref: '#/components/schemas/SummitBadgeType'), + new OA\Property(property: 'print_excerpt', type: 'string', example: 'John Doe - Speaker', description: 'Short text excerpt for printing'), + new OA\Property( + property: 'features', + type: 'array', + description: 'Array of SummitBadgeFeatureType IDs assigned to this badge (use expand=features for full details)', + items: new OA\Items(oneOf: [ + new OA\Schema(type: 'integer'), + new OA\Schema(ref: '#/components/schemas/SummitBadgeFeatureType'), + ]), + example: [1, 2, 3] + ), + ], +)] +class SummitAttendeeBadgeSchema +{ +} + +#[OA\Schema( + schema: 'PaginatedSummitAttendeeBadgesResponse', + 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/SummitAttendeeBadge') + ) + ] + ) + ] +)] +class PaginatedSummitAttendeeBadgesResponseSchema {} #[OA\Schema( schema: 'SummitMediaFileType',