From 2fb068b181ab0ac395e6424547bd9a418abda04f Mon Sep 17 00:00:00 2001 From: Matias Perrone Date: Wed, 10 Dec 2025 21:04:45 +0000 Subject: [PATCH] feat: Extend Swagger Coverage for controller OAuth2SummitPromoCodesApiController --- .../OAuth2SummitPromoCodesApiController.php | 497 +++++++++++++++++- .../Security/SummitPromoCodesAuthSchema.php | 26 + .../SummitRegistrationPromoCodesSchemas.php | 155 +++++- 3 files changed, 676 insertions(+), 2 deletions(-) create mode 100644 app/Swagger/Security/SummitPromoCodesAuthSchema.php diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitPromoCodesApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitPromoCodesApiController.php index f019931a7..86ed9509d 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitPromoCodesApiController.php +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitPromoCodesApiController.php @@ -17,12 +17,15 @@ use App\Http\Utils\EpochCellFormatter; use App\Http\Utils\Filters\FiltersParams; use App\Jobs\Emails\Registration\PromoCodes\SponsorPromoCodeEmail; +use App\Models\Foundation\Main\IGroup; use App\Models\Foundation\Summit\PromoCodes\PromoCodesConstants; use App\Models\Foundation\Summit\Repositories\ISpeakersRegistrationDiscountCodeRepository; use App\Models\Foundation\Summit\Repositories\ISpeakersSummitRegistrationPromoCodeRepository; use App\ModelSerializers\SerializerUtils; use App\Rules\Boolean; +use App\Security\SummitScopes; use Illuminate\Http\Request as LaravelRequest; +use Illuminate\Http\Response; use Illuminate\Support\Facades\Request; use models\main\IMemberRepository; use models\oauth2\IResourceServerContext; @@ -35,6 +38,7 @@ use models\summit\SummitRegistrationPromoCode; use models\summit\SummitTicketType; use ModelSerializers\SerializerRegistry; +use OpenApi\Attributes as OA; use services\model\ISummitPromoCodeService; use utils\Filter; use utils\FilterElement; @@ -111,6 +115,28 @@ public function __construct $this->speakers_discount_code_repository = $speakers_discount_code_repository; } + #[OA\Get( + path: "/api/v1/summits/{id}/promo-codes", + summary: "Get all promo codes for a summit", + description: "Get all promo codes for a summit with filtering and pagination", + operationId: "getAllPromoCodesBySummit", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::ReadAllSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators, IGroup::SummitRegistrationAdmins]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "page", in: "query", required: false, schema: new OA\Schema(type: "integer", default: 1)), + new OA\Parameter(name: "per_page", in: "query", required: false, schema: new OA\Schema(type: "integer", default: 10)), + new OA\Parameter(name: "filter", in: "query", required: false, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "order", in: "query", required: false, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "expand", in: "query", required: false, schema: new OA\Schema(type: "string")), + ], + responses: [ + new OA\Response(response: Response::HTTP_OK, description: "OK"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Summit not found"), + ] + )] /** * @param $summit_id * @return mixed @@ -205,6 +231,29 @@ function ($page, $per_page, $filter, $order, $applyExtraFilters) use ($summit) { ); } + #[OA\Get( + path: "/api/v1/summits/{id}/sponsor-promo-codes", + summary: "Get all sponsor promo codes for a summit", + description: "Get all sponsor promo codes for a summit with filtering and pagination", + operationId: "getAllSponsorPromoCodesBySummit", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::ReadAllSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators, IGroup::SummitRegistrationAdmins]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "page", in: "query", required: false, schema: new OA\Schema(type: "integer", default: 1)), + new OA\Parameter(name: "per_page", in: "query", required: false, schema: new OA\Schema(type: "integer", default: 10)), + new OA\Parameter(name: "filter", in: "query", required: false, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "order", in: "query", required: false, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "expand", in: "query", required: false, schema: new OA\Schema(type: "string")), + ], + responses: [ + new OA\Response(response: Response::HTTP_OK, description: "OK"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Summit not found"), + ] + )] /** * @param $summit_id * @return mixed @@ -292,6 +341,25 @@ function ($page, $per_page, $filter, $order, $applyExtraFilters) use ($summit) { ); } + #[OA\Get( + path: "/api/v1/summits/{id}/promo-codes/csv", + summary: "Export promo codes to CSV", + description: "Export all promo codes for a summit to CSV format", + operationId: "getAllPromoCodesBySummitCSV", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::ReadAllSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators, IGroup::SummitRegistrationAdmins]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "filter", in: "query", required: false, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "order", in: "query", required: false, schema: new OA\Schema(type: "string")), + ], + responses: [ + new OA\Response(response: Response::HTTP_OK, description: "CSV file"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Summit not found"), + ] + )] /** * @param $summit_id * @return \Illuminate\Http\Response|mixed @@ -408,6 +476,26 @@ function ($page, $per_page, $filter, $order, $applyExtraFilters) use ($summit) { ); } + #[OA\Get( + path: "/api/v1/summits/{id}/sponsor-promo-codes/csv", + summary: "Export sponsor promo codes to CSV", + description: "Export all sponsor promo codes for a summit to CSV format", + operationId: "getSponsorPromoCodesAllBySummitCSV", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::ReadAllSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators, IGroup::SummitRegistrationAdmins]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "filter", in: "query", required: false, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "order", in: "query", required: false, schema: new OA\Schema(type: "string")), + ], + responses: [ + new OA\Response(response: Response::HTTP_OK, description: "CSV file"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Summit not found"), + ] + )] public function getSponsorPromoCodesAllBySummitCSV($summit_id) { @@ -511,6 +599,23 @@ function ($page, $per_page, $filter, $order, $applyExtraFilters) use ($summit) { ); } + #[OA\Get( + path: "/api/v1/summits/{id}/promo-codes/metadata", + summary: "Get promo codes metadata", + description: "Get metadata about promo codes for a summit", + operationId: "getPromoCodesMetadata", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::ReadAllSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators, IGroup::SummitRegistrationAdmins]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + ], + responses: [ + new OA\Response(response: Response::HTTP_OK, description: "OK"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Summit not found"), + ] + )] /** * @param $summit_id * @return mixed @@ -526,6 +631,25 @@ public function getMetadata($summit_id) ); } + #[OA\Post( + path: "/api/v1/summits/{id}/promo-codes", + summary: "Create a promo code", + description: "Create a new promo code for a summit", + operationId: "addPromoCodeBySummit", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::WritePromoCodeData, SummitScopes::WriteSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators, IGroup::SummitRegistrationAdmins]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + ], + requestBody: new OA\RequestBody(required: true, content: new OA\JsonContent(ref: '#/components/schemas/PromoCodeAddRequest')), + responses: [ + new OA\Response(response: Response::HTTP_CREATED, description: "Created"), + 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"), + ] + )] /** * @param $summit_id * @return mixed @@ -549,6 +673,26 @@ public function addPromoCodeBySummit($summit_id) }); } + #[OA\Put( + path: "/api/v1/summits/{id}/promo-codes/{promo_code_id}", + summary: "Update a promo code", + description: "Update an existing promo code", + operationId: "updatePromoCodeBySummit", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::WritePromoCodeData, SummitScopes::WriteSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators, IGroup::SummitRegistrationAdmins]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "promo_code_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + ], + requestBody: new OA\RequestBody(required: true, content: new OA\JsonContent(ref: '#/components/schemas/PromoCodeUpdateRequest')), + responses: [ + new OA\Response(response: Response::HTTP_OK, description: "OK"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not found"), + new OA\Response(response: Response::HTTP_PRECONDITION_FAILED, description: "Validation error"), + ] + )] /** * @param $summit_id * @param $promo_code_id @@ -574,6 +718,24 @@ public function updatePromoCodeBySummit($summit_id, $promo_code_id) }); } + #[OA\Delete( + path: "/api/v1/summits/{id}/promo-codes/{promo_code_id}", + summary: "Delete a promo code", + description: "Delete a promo code from a summit", + operationId: "deletePromoCodeBySummit", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::WritePromoCodeData, SummitScopes::WriteSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators, IGroup::SummitRegistrationAdmins]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "promo_code_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + ], + responses: [ + new OA\Response(response: Response::HTTP_NO_CONTENT, description: "Deleted"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not found"), + ] + )] /** * @param $summit_id * @param $promo_code_id @@ -592,6 +754,24 @@ public function deletePromoCodeBySummit($summit_id, $promo_code_id) }); } + #[OA\Post( + path: "/api/v1/summits/{id}/promo-codes/{promo_code_id}/mail", + summary: "Send promo code email", + description: "Send an email with the promo code", + operationId: "sendPromoCodeMail", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::WritePromoCodeData, SummitScopes::WriteSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators, IGroup::SummitRegistrationAdmins]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "promo_code_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + ], + responses: [ + new OA\Response(response: Response::HTTP_OK, description: "OK"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not found"), + ] + )] /** * @param $summit_id * @param $promo_code_id @@ -607,6 +787,25 @@ public function sendPromoCodeMail($summit_id, $promo_code_id) }); } + #[OA\Get( + path: "/api/v1/summits/{id}/promo-codes/{promo_code_id}", + summary: "Get a promo code", + description: "Get a specific promo code by ID", + operationId: "getPromoCodeBySummit", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::ReadAllSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators, IGroup::SummitRegistrationAdmins]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "promo_code_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "expand", in: "query", required: false, schema: new OA\Schema(type: "string")), + ], + responses: [ + new OA\Response(response: Response::HTTP_OK, description: "OK"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not found"), + ] + )] /** * @param $summit_id * @param $promo_code_id @@ -628,6 +827,25 @@ public function getPromoCodeBySummit($summit_id, $promo_code_id) }); } + #[OA\Put( + path: "/api/v1/summits/{id}/promo-codes/{promo_code_id}/badge-features/{badge_feature_id}", + summary: "Add badge feature to promo code", + description: "Add a badge feature to a promo code", + operationId: "addBadgeFeatureToPromoCode", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::WritePromoCodeData, SummitScopes::WriteSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators, IGroup::SummitRegistrationAdmins]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "promo_code_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "badge_feature_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + ], + responses: [ + new OA\Response(response: Response::HTTP_OK, description: "OK"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not found"), + ] + )] /** * @param $summit_id * @param $promo_code_id @@ -650,6 +868,25 @@ public function addBadgeFeatureToPromoCode($summit_id, $promo_code_id, $badge_fe } + #[OA\Delete( + path: "/api/v1/summits/{id}/promo-codes/{promo_code_id}/badge-features/{badge_feature_id}", + summary: "Remove badge feature from promo code", + description: "Remove a badge feature from a promo code", + operationId: "removeBadgeFeatureFromPromoCode", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::WritePromoCodeData, SummitScopes::WriteSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators, IGroup::SummitRegistrationAdmins]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "promo_code_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "badge_feature_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + ], + responses: [ + new OA\Response(response: Response::HTTP_OK, description: "OK"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not found"), + ] + )] /** * @param $summit_id * @param $promo_code_id @@ -671,6 +908,26 @@ public function removeBadgeFeatureFromPromoCode($summit_id, $promo_code_id, $bad }); } + #[OA\Put( + path: "/api/v1/summits/{id}/promo-codes/{promo_code_id}/ticket-types/{ticket_type_id}", + summary: "Add ticket type to promo code", + description: "Add a ticket type rule to a promo code", + operationId: "addTicketTypeToPromoCode", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::WritePromoCodeData, SummitScopes::WriteSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators, IGroup::SummitRegistrationAdmins]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "promo_code_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "ticket_type_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + ], + requestBody: new OA\RequestBody(required: false, content: new OA\JsonContent(ref: '#/components/schemas/PromoCodeTicketTypeRuleRequest')), + responses: [ + new OA\Response(response: Response::HTTP_OK, description: "OK"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not found"), + ] + )] /** * @param $summit_id * @param $promo_code_id @@ -702,6 +959,25 @@ public function addTicketTypeToPromoCode($summit_id, $promo_code_id, $ticket_typ } + #[OA\Delete( + path: "/api/v1/summits/{id}/promo-codes/{promo_code_id}/ticket-types/{ticket_type_id}", + summary: "Remove ticket type from promo code", + description: "Remove a ticket type rule from a promo code", + operationId: "removeTicketTypeFromPromoCode", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::WritePromoCodeData, SummitScopes::WriteSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators, IGroup::SummitRegistrationAdmins]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "promo_code_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "ticket_type_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + ], + responses: [ + new OA\Response(response: Response::HTTP_OK, description: "OK"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not found"), + ] + )] /** * @param $summit_id * @param $promo_code_id @@ -722,6 +998,32 @@ public function removeTicketTypeFromPromoCode($summit_id, $promo_code_id, $ticke }); } + #[OA\Post( + path: "/api/v1/summits/{id}/promo-codes/csv", + summary: "Import promo codes from CSV", + description: "Import promo codes from a CSV file", + operationId: "ingestPromoCodes", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::WritePromoCodeData, SummitScopes::WriteSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators, IGroup::SummitRegistrationAdmins]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + ], + requestBody: new OA\RequestBody( + required: true, + content: new OA\MediaType( + mediaType: "multipart/form-data", + schema: new OA\Schema(properties: [new OA\Property(property: "file", type: "string", format: "binary")]) + ) + ), + responses: [ + new OA\Response(response: Response::HTTP_OK, description: "OK"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Summit not found"), + new OA\Response(response: Response::HTTP_PRECONDITION_FAILED, description: "Validation error"), + ] + )] /** * @param LaravelRequest $request * @param $summit_id @@ -748,6 +1050,32 @@ public function ingestPromoCodes(LaravelRequest $request, $summit_id) }); } + #[OA\Post( + path: "/api/v1/summits/{id}/sponsor-promo-codes/csv", + summary: "Import sponsor promo codes from CSV", + description: "Import sponsor promo codes from a CSV file", + operationId: "ingestSponsorPromoCodes", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::WritePromoCodeData, SummitScopes::WriteSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators, IGroup::SummitRegistrationAdmins]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + ], + requestBody: new OA\RequestBody( + required: true, + content: new OA\MediaType( + mediaType: "multipart/form-data", + schema: new OA\Schema(properties: [new OA\Property(property: "file", type: "string", format: "binary")]) + ) + ), + responses: [ + new OA\Response(response: Response::HTTP_OK, description: "OK"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Summit not found"), + new OA\Response(response: Response::HTTP_PRECONDITION_FAILED, description: "Validation error"), + ] + )] /** * @param LaravelRequest $request * @param $summit_id @@ -774,6 +1102,28 @@ public function ingestSponsorPromoCodes(LaravelRequest $request, $summit_id) }); } + #[OA\Get( + path: "/api/v1/summits/{id}/speakers-promo-codes/{promo_code_id}/speakers", + summary: "Get speakers for a promo code", + description: "Get all speakers associated with a promo code", + operationId: "getPromoCodeSpeakers", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::ReadAllSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "promo_code_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "page", in: "query", required: false, schema: new OA\Schema(type: "integer", default: 1)), + new OA\Parameter(name: "per_page", in: "query", required: false, schema: new OA\Schema(type: "integer", default: 10)), + new OA\Parameter(name: "filter", in: "query", required: false, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "order", in: "query", required: false, schema: new OA\Schema(type: "string")), + ], + responses: [ + new OA\Response(response: Response::HTTP_OK, description: "OK"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not found"), + ] + )] /** * @param $summit_id * @param $promo_code_id @@ -840,6 +1190,28 @@ function ($page, $per_page, $filter, $order, $applyExtraFilters) use ($promo_cod ); } + #[OA\Get( + path: "/api/v1/summits/{id}/speakers-discount-codes/{discount_code_id}/speakers", + summary: "Get speakers for a discount code", + description: "Get all speakers associated with a discount code", + operationId: "getDiscountCodeSpeakers", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::ReadAllSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "discount_code_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "page", in: "query", required: false, schema: new OA\Schema(type: "integer", default: 1)), + new OA\Parameter(name: "per_page", in: "query", required: false, schema: new OA\Schema(type: "integer", default: 10)), + new OA\Parameter(name: "filter", in: "query", required: false, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "order", in: "query", required: false, schema: new OA\Schema(type: "string")), + ], + responses: [ + new OA\Response(response: Response::HTTP_OK, description: "OK"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not found"), + ] + )] /** * @param $summit_id * @param $discount_code_id @@ -906,6 +1278,46 @@ function ($page, $per_page, $filter, $order, $applyExtraFilters) use ($discount_ ); } + #[OA\Post( + path: "/api/v1/summits/{id}/speakers-promo-codes/{promo_code_id}/speakers/{speaker_id}", + summary: "Add speaker to promo code", + description: "Associate a speaker with a promo code", + operationId: "addPromoCodeSpeaker", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::WritePromoCodeData, SummitScopes::WriteSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "promo_code_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "speaker_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + ], + responses: [ + new OA\Response(response: Response::HTTP_CREATED, description: "Created"), + 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"), + ] + )] + #[OA\Post( + path: "/api/v1/summits/{id}/speakers-discount-codes/{discount_code_id}/speakers/{speaker_id}", + summary: "Add speaker to promo code", + description: "Associate a speaker with a promo code", + operationId: "addDiscountCodeSpeaker", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::WritePromoCodeData, SummitScopes::WriteSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "discount_code_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "speaker_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + ], + responses: [ + new OA\Response(response: Response::HTTP_CREATED, description: "Created"), + 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"), + ] + )] /** * @param $summit_id * @param $promo_code_id @@ -929,6 +1341,46 @@ public function addSpeaker($summit_id, $promo_code_id, $speaker_id) }); } + #[OA\Delete( + path: "/api/v1/summits/{id}/speakers-promo-codes/{promo_code_id}/speakers/{speaker_id}", + summary: "Remove speaker from promo code", + description: "Remove a speaker from a promo code", + operationId: "removePromoCodeSpeaker", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::WritePromoCodeData, SummitScopes::WriteSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "promo_code_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "speaker_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + ], + responses: [ + new OA\Response(response: Response::HTTP_NO_CONTENT, description: "Deleted"), + 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"), + ] + )] + #[OA\Delete( + path: "/api/v1/summits/{id}/speakers-discount-codes/{discount_code_id}/speakers/{speaker_id}", + summary: "Remove speaker from promo code", + description: "Remove a speaker from a promo code", + operationId: "removeDiscountCodeSpeaker", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::WritePromoCodeData, SummitScopes::WriteSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "discount_code_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "speaker_id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + ], + responses: [ + new OA\Response(response: Response::HTTP_NO_CONTENT, description: "Deleted"), + 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"), + ] + )] /** * @param $summit_id * @param $promo_code_id @@ -952,6 +1404,25 @@ public function removeSpeaker($summit_id, $promo_code_id, $speaker_id) }); } + #[OA\Get( + path: "/api/v1/summits/{id}/promo-codes/{promo_code_val}/apply", + summary: "Pre-validate promo code", + description: "Pre-validate a promo code before applying it", + operationId: "preValidatePromoCode", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::ReadSummitData, SummitScopes::ReadAllSummitData]]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "promo_code_val", in: "path", required: true, schema: new OA\Schema(type: "string")), + new OA\Parameter(name: "filter", in: "query", required: false, schema: new OA\Schema(type: "string")), + ], + responses: [ + new OA\Response(response: Response::HTTP_OK, description: "OK"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not found"), + new OA\Response(response: Response::HTTP_PRECONDITION_FAILED, description: "Validation error"), + ] + )] /** * @param $summit_id * @param $promo_code_val @@ -998,6 +1469,30 @@ function ($attribute, $value, $fail) use ($filter) { }); } + #[OA\Put( + path: "/api/v1/summits/{id}/sponsors/all/promo-codes/all/send", + summary: "Send sponsor promo codes emails", + description: "Trigger sending sponsor promo codes via email", + operationId: "sendSponsorPromoCodesMail", + tags: ["Promo Codes"], + security: [['summit_promo_codes_oauth2' => [SummitScopes::WritePromoCodeData, SummitScopes::WriteSummitData]]], + x: ['required-groups' => [IGroup::SuperAdmins, IGroup::Administrators, IGroup::SummitAdministrators]], + parameters: [ + new OA\Parameter(name: "id", in: "path", required: true, schema: new OA\Schema(type: "integer")), + new OA\Parameter(name: "filter", in: "query", required: false, schema: new OA\Schema(type: "string")), + ], + requestBody: new OA\RequestBody( + required: true, + content: new OA\JsonContent(ref: '#/components/schemas/SendSponsorPromoCodesRequest') + ), + responses: [ + new OA\Response(response: Response::HTTP_OK, description: "OK"), + new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"), + new OA\Response(response: Response::HTTP_FORBIDDEN, description: "Forbidden"), + new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Summit not found"), + new OA\Response(response: Response::HTTP_PRECONDITION_FAILED, description: "Validation error"), + ] + )] /** * @param $summit_id * @return \Illuminate\Http\JsonResponse|mixed @@ -1070,4 +1565,4 @@ public function sendSponsorPromoCodes($summit_id) return $this->ok(); }); } -} \ No newline at end of file +} diff --git a/app/Swagger/Security/SummitPromoCodesAuthSchema.php b/app/Swagger/Security/SummitPromoCodesAuthSchema.php new file mode 100644 index 000000000..998c84f10 --- /dev/null +++ b/app/Swagger/Security/SummitPromoCodesAuthSchema.php @@ -0,0 +1,26 @@ + 'Read Summit Data', + SummitScopes::ReadAllSummitData => 'Read All Summit Data', + SummitScopes::WriteSummitData => 'Write Summit Data', + SummitScopes::WritePromoCodeData => 'Write Promo Code Data', + ], + ), + ], + ) +] +class SummitPromoCodesAuthSchema {} diff --git a/app/Swagger/SummitRegistrationPromoCodesSchemas.php b/app/Swagger/SummitRegistrationPromoCodesSchemas.php index 6b3e2f895..2d2e6dca1 100644 --- a/app/Swagger/SummitRegistrationPromoCodesSchemas.php +++ b/app/Swagger/SummitRegistrationPromoCodesSchemas.php @@ -4,4 +4,157 @@ use OpenApi\Attributes as OA; -// +/** + * Base Promo Code Request Schema - Common properties for all promo code types + */ +#[OA\Schema( + schema: 'PromoCodeBaseRequest', + title: 'Promo Code Base Request', + description: 'Base request schema for promo codes with common properties', + required: ['class_name', 'code'], + properties: [ + new OA\Property( + property: 'class_name', + type: 'string', + description: 'The type of promo code to create', + enum: [ + 'SUMMIT_PROMO_CODE', + 'SUMMIT_DISCOUNT_CODE', + 'SPEAKER_PROMO_CODE', + 'SPEAKERS_PROMO_CODE', + 'SPONSOR_PROMO_CODE', + 'MEMBER_PROMO_CODE', + 'MEMBER_DISCOUNT_CODE', + 'SPEAKER_DISCOUNT_CODE', + 'SPEAKERS_DISCOUNT_CODE', + 'SPONSOR_DISCOUNT_CODE', + 'PRE_PAID_PROMO_CODE', + 'PRE_PAID_DISCOUNT_CODE' + ] + ), + new OA\Property(property: 'code', type: 'string', maxLength: 255, description: 'The promo code string'), + new OA\Property(property: 'description', type: 'string', maxLength: 1024, description: 'Description of the promo code'), + new OA\Property(property: 'notes', type: 'string', maxLength: 2048, description: 'Internal notes about the promo code'), + new OA\Property(property: 'quantity_available', type: 'integer', minimum: 0, description: 'Number of times this code can be used'), + new OA\Property(property: 'valid_since_date', type: 'integer', format: 'int64', description: 'Start validity date (epoch timestamp)'), + new OA\Property(property: 'valid_until_date', type: 'integer', format: 'int64', description: 'End validity date (epoch timestamp)'), + new OA\Property(property: 'allowed_ticket_types', type: 'array', items: new OA\Items(type: 'integer'), description: 'Array of allowed ticket type IDs'), + new OA\Property(property: 'badge_features', type: 'array', items: new OA\Items(type: 'integer'), description: 'Array of badge feature IDs to apply'), + new OA\Property(property: 'allows_to_delegate', type: 'boolean', description: 'Whether the code holder can delegate to others'), + new OA\Property(property: 'allows_to_reassign', type: 'boolean', description: 'Whether the code can be reassigned'), + ] +)] +class PromoCodeBaseRequestSchema {} + +/** + * Add Promo Code Request Schema + */ +#[OA\Schema( + schema: 'PromoCodeAddRequest', + title: 'Add Promo Code Request', + description: 'Request schema for creating a new promo code. Additional properties depend on class_name.', + allOf: [ + new OA\Schema(ref: '#/components/schemas/PromoCodeBaseRequest'), + new OA\Schema( + properties: [ + new OA\Property(property: 'type', type: 'string', description: 'Promo code type (VIP, ATC, MEDIA ANALYST for Member codes; ACCEPTED, ALTERNATE for Speaker codes)'), + new OA\Property(property: 'first_name', type: 'string', description: 'Owner first name (for Member codes, required without owner_id)'), + new OA\Property(property: 'last_name', type: 'string', description: 'Owner last name (for Member codes, required without owner_id)'), + new OA\Property(property: 'email', type: 'string', format: 'email', maxLength: 254, description: 'Owner email (for Member codes, required without owner_id)'), + new OA\Property(property: 'owner_id', type: 'integer', description: 'Owner member ID (for Member codes, required without first_name/last_name/email)'), + new OA\Property(property: 'speaker_id', type: 'integer', description: 'Speaker ID (for Speaker codes)'), + new OA\Property(property: 'sponsor_id', type: 'integer', description: 'Sponsor ID (for Sponsor codes, required)'), + new OA\Property(property: 'contact_email', type: 'string', format: 'email', maxLength: 254, description: 'Contact email (for Sponsor codes, required)'), + new OA\Property(property: 'amount', type: 'number', description: 'Discount amount (for discount codes, required without rate)'), + new OA\Property(property: 'rate', type: 'number', description: 'Discount rate percentage (for discount codes, required without amount)'), + ] + ) + ] +)] +class PromoCodeAddRequestSchema {} + +/** + * Update Promo Code Request Schema + */ +#[OA\Schema( + schema: 'PromoCodeUpdateRequest', + title: 'Update Promo Code Request', + description: 'Request schema for updating an existing promo code. All properties are optional.', + required: ['class_name'], + properties: [ + new OA\Property( + property: 'class_name', + type: 'string', + description: 'The type of promo code', + enum: [ + 'SUMMIT_PROMO_CODE', + 'SUMMIT_DISCOUNT_CODE', + 'SPEAKER_PROMO_CODE', + 'SPEAKERS_PROMO_CODE', + 'SPONSOR_PROMO_CODE', + 'MEMBER_PROMO_CODE', + 'MEMBER_DISCOUNT_CODE', + 'SPEAKER_DISCOUNT_CODE', + 'SPEAKERS_DISCOUNT_CODE', + 'SPONSOR_DISCOUNT_CODE', + 'PRE_PAID_PROMO_CODE', + 'PRE_PAID_DISCOUNT_CODE' + ] + ), + new OA\Property(property: 'code', type: 'string', maxLength: 255, description: 'The promo code string'), + new OA\Property(property: 'description', type: 'string', maxLength: 1024, description: 'Description of the promo code'), + new OA\Property(property: 'notes', type: 'string', maxLength: 2048, description: 'Internal notes about the promo code'), + new OA\Property(property: 'quantity_available', type: 'integer', minimum: 0, description: 'Number of times this code can be used'), + new OA\Property(property: 'valid_since_date', type: 'integer', format: 'int64', description: 'Start validity date (epoch timestamp)'), + new OA\Property(property: 'valid_until_date', type: 'integer', format: 'int64', description: 'End validity date (epoch timestamp)'), + new OA\Property(property: 'allowed_ticket_types', type: 'array', items: new OA\Items(type: 'integer'), description: 'Array of allowed ticket type IDs'), + new OA\Property(property: 'badge_features', type: 'array', items: new OA\Items(type: 'integer'), description: 'Array of badge feature IDs to apply'), + new OA\Property(property: 'badge_features_apply_to_all_tix_retroactively', type: 'boolean', description: 'Apply badge features retroactively to all tickets'), + new OA\Property(property: 'tags', type: 'array', items: new OA\Items(type: 'string'), description: 'Array of tag strings'), + new OA\Property(property: 'allows_to_delegate', type: 'boolean', description: 'Whether the code holder can delegate to others'), + new OA\Property(property: 'allows_to_reassign', type: 'boolean', description: 'Whether the code can be reassigned'), + new OA\Property(property: 'type', type: 'string', description: 'Promo code type'), + new OA\Property(property: 'first_name', type: 'string', description: 'Owner first name (for Member codes)'), + new OA\Property(property: 'last_name', type: 'string', description: 'Owner last name (for Member codes)'), + new OA\Property(property: 'email', type: 'string', format: 'email', maxLength: 254, description: 'Owner email (for Member codes)'), + new OA\Property(property: 'owner_id', type: 'integer', description: 'Owner member ID (for Member codes)'), + new OA\Property(property: 'speaker_id', type: 'integer', description: 'Speaker ID (for Speaker codes)'), + new OA\Property(property: 'sponsor_id', type: 'integer', description: 'Sponsor ID (for Sponsor codes)'), + new OA\Property(property: 'contact_email', type: 'string', format: 'email', maxLength: 254, description: 'Contact email (for Sponsor codes)'), + new OA\Property(property: 'amount', type: 'number', description: 'Discount amount (for discount codes)'), + new OA\Property(property: 'rate', type: 'number', description: 'Discount rate percentage (for discount codes)'), + ] +)] +class PromoCodeUpdateRequestSchema {} + +/** + * Promo Code Ticket Type Rule Request Schema + */ +#[OA\Schema( + schema: 'PromoCodeTicketTypeRuleRequest', + title: 'Promo Code Ticket Type Rule Request', + description: 'Request schema for adding a ticket type rule to a promo code (discount codes only)', + properties: [ + new OA\Property(property: 'amount', type: 'number', minimum: 0, description: 'Fixed discount amount (required without rate)'), + new OA\Property(property: 'rate', type: 'number', minimum: 0, description: 'Discount rate percentage (required without amount)'), + ] +)] +class PromoCodeTicketTypeRuleRequestSchema {} + +/** + * Send Sponsor Promo Codes Email Request Schema + */ +#[OA\Schema( + schema: 'SendSponsorPromoCodesRequest', + title: 'Send Sponsor Promo Codes Email Request', + description: 'Request schema for sending sponsor promo codes via email', + required: ['email_flow_event'], + properties: [ + new OA\Property(property: 'email_flow_event', type: 'string', description: 'Email flow event slug', enum: ['SUMMIT_REGISTRATION_SPONSOR_PROMO_CODE']), + new OA\Property(property: 'promo_code_ids', type: 'array', items: new OA\Items(type: 'integer'), description: 'Specific promo code IDs to send'), + new OA\Property(property: 'excluded_promo_code_ids', type: 'array', items: new OA\Items(type: 'integer'), description: 'Promo code IDs to exclude'), + new OA\Property(property: 'test_email_recipient', type: 'string', format: 'email', description: 'Test email recipient'), + new OA\Property(property: 'outcome_email_recipient', type: 'string', format: 'email', description: 'Email recipient for outcome notification'), + ] +)] +class SendSponsorPromoCodesRequestSchema {}