-
Notifications
You must be signed in to change notification settings - Fork 2
feat(promo-codes): domain-authorized promo codes for early registration access #525
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 17 commits
24f1e3d
05c889c
a556412
fe32435
6a12e47
2967746
5dd1ad7
82a28c3
a5809af
b38e434
a9ece25
138c1f8
b87cefd
ed2064d
19e5f53
ae261a7
c3f8df7
c2719e1
c4bcdef
e2ca6b5
d824974
93bc180
283f576
1ac95a0
b9f2b2a
f464339
13e13a2
8ad56fc
a0a9ba4
ff81217
b909683
310455a
f12af1b
d2fbcb1
39e452f
a1bd4ea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1575,4 +1575,66 @@ public function sendSponsorPromoCodes($summit_id) | |
| return $this->ok(); | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Discover qualifying promo codes for the current user. | ||
| * Returns domain-authorized codes (matched by email domain) and existing email-linked | ||
| * codes (member/speaker, matched by associated email) with auto_apply flag. | ||
| * Email is always derived from the authenticated principal — no email query parameter accepted. | ||
| */ | ||
| #[OA\Get( | ||
| path: "/api/v1/summits/{id}/promo-codes/all/discover", | ||
| summary: "Discover qualifying promo codes for the current user", | ||
| description: "Returns domain-authorized promo codes (matched by email domain) and existing email-linked promo codes (member/speaker, matched by associated email) for the current user", | ||
| operationId: "discoverPromoCodesBySummit", | ||
| tags: ["Promo Codes"], | ||
| security: [['summit_promo_codes_oauth2' => [SummitScopes::ReadSummitData]]], | ||
| parameters: [ | ||
| new OA\Parameter(name: "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_FORBIDDEN, description: "Forbidden"), | ||
| new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Summit not found"), | ||
| ] | ||
| )] | ||
| public function discover($summit_id) | ||
| { | ||
| return $this->processRequest(function () use ($summit_id) { | ||
|
|
||
| $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->resource_server_context)->find(intval($summit_id)); | ||
| if (is_null($summit)) | ||
| return $this->error404(); | ||
|
|
||
| $current_member = $this->resource_server_context->getCurrentUser(); | ||
| if (is_null($current_member)) | ||
| return $this->error403(); | ||
|
|
||
| $codes = $this->promo_code_service->discoverPromoCodes($summit, $current_member); | ||
|
|
||
| $expand = Request::input('expand', ''); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @caseylocker this not follow api patterns please check other controllers to see how we are doing it SerializerUtils::getExpand(),
SerializerUtils::getFields(),
SerializerUtils::getRelations()
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated in c4bcdef. The |
||
| $fields = Request::input('fields', ''); | ||
| $relations = Request::input('relations', ''); | ||
|
|
||
| $relations = !empty($relations) ? explode(',', $relations) : ['allowed_ticket_types', 'badge_features', 'tags', 'ticket_types_rules']; | ||
| $fields = !empty($fields) ? explode(',', $fields) : []; | ||
|
|
||
| $data = []; | ||
| foreach ($codes as $code) { | ||
| $serializer = SerializerRegistry::getInstance()->getSerializer($code); | ||
| $data[] = $serializer->serialize($expand, $fields, $relations); | ||
| } | ||
|
|
||
| $total = count($data); | ||
| return $this->ok([ | ||
| 'total' => $total, | ||
| 'per_page' => $total, | ||
| 'current_page' => 1, | ||
| 'last_page' => 1, | ||
| 'data' => $data, | ||
| ]); | ||
| }); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| <?php namespace ModelSerializers; | ||
| /** | ||
| * Copyright 2026 OpenStack Foundation | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| **/ | ||
|
|
||
| use models\summit\DomainAuthorizedSummitRegistrationDiscountCode; | ||
|
|
||
| /** | ||
| * Class DomainAuthorizedSummitRegistrationDiscountCodeSerializer | ||
| * @package ModelSerializers | ||
| */ | ||
| class DomainAuthorizedSummitRegistrationDiscountCodeSerializer | ||
| extends SummitRegistrationDiscountCodeSerializer | ||
| { | ||
| protected static $array_mappings = [ | ||
| 'AllowedEmailDomains' => 'allowed_email_domains:json_string_array', | ||
| 'QuantityPerAccount' => 'quantity_per_account:json_int', | ||
| 'AutoApply' => 'auto_apply:json_boolean', | ||
| ]; | ||
|
|
||
| protected static $allowed_relations = [ | ||
| 'allowed_ticket_types', | ||
| ]; | ||
|
|
||
| /** | ||
| * @param null $expand | ||
| * @param array $fields | ||
| * @param array $relations | ||
| * @param array $params | ||
| * @return array | ||
| */ | ||
| public function serialize($expand = null, array $fields = [], array $relations = [], array $params = []) | ||
| { | ||
| $code = $this->object; | ||
| if (!$code instanceof DomainAuthorizedSummitRegistrationDiscountCode) return []; | ||
| $values = parent::serialize($expand, $fields, $relations, $params); | ||
|
|
||
| // RE-ADD allowed_ticket_types (parent discount serializer unsets it). | ||
| // Check both relations (default serialization) and expand (explicit ?expand= request). | ||
| $needs_allowed_ticket_types = in_array('allowed_ticket_types', $relations) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @caseylocker This silently compensates for a parent behavior that's undocumented. If the parent serializer changes, this will break without any error. Consider either overriding the parent behaviour more explicitly (resetting the parent's unset) or documenting why the parent SummitRegistrationDiscountCodeSerializer removes allowed_ticket_types and whether that should be changed there instead.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair flag. I dug into this — the parent's What was sloppy was that three subclasses (
Wire output is byte-identical; the contract is now explicit so a future edit to the parent can't silently break the children. |
||
| || (!empty($expand) && str_contains($expand, 'allowed_ticket_types')); | ||
| if ($needs_allowed_ticket_types && !isset($values['allowed_ticket_types'])) { | ||
| $ticket_types = []; | ||
| foreach ($code->getAllowedTicketTypes() as $ticket_type) { | ||
| $ticket_types[] = $ticket_type->getId(); | ||
| } | ||
| $values['allowed_ticket_types'] = $ticket_types; | ||
| } | ||
|
|
||
| // Transient remaining_quantity_per_account (set by service layer) | ||
| $values['remaining_quantity_per_account'] = $code->getRemainingQuantityPerAccount(); | ||
|
|
||
| return $values; | ||
| } | ||
|
|
||
| protected static $expand_mappings = [ | ||
| 'allowed_ticket_types' => [ | ||
| 'type' => \Libs\ModelSerializers\Many2OneExpandSerializer::class, | ||
| 'getter' => 'getAllowedTicketTypes', | ||
| ], | ||
| ]; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| <?php namespace ModelSerializers; | ||
| /** | ||
| * Copyright 2026 OpenStack Foundation | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| **/ | ||
|
|
||
| use models\summit\DomainAuthorizedSummitRegistrationPromoCode; | ||
|
|
||
| /** | ||
| * Class DomainAuthorizedSummitRegistrationPromoCodeSerializer | ||
| * @package ModelSerializers | ||
| */ | ||
| class DomainAuthorizedSummitRegistrationPromoCodeSerializer | ||
| extends SummitRegistrationPromoCodeSerializer | ||
| { | ||
| protected static $array_mappings = [ | ||
| 'AllowedEmailDomains' => 'allowed_email_domains:json_string_array', | ||
| 'QuantityPerAccount' => 'quantity_per_account:json_int', | ||
| 'AutoApply' => 'auto_apply:json_boolean', | ||
| ]; | ||
|
|
||
| /** | ||
| * @param null $expand | ||
| * @param array $fields | ||
| * @param array $relations | ||
| * @param array $params | ||
| * @return array | ||
| */ | ||
| public function serialize($expand = null, array $fields = [], array $relations = [], array $params = []) | ||
| { | ||
| $code = $this->object; | ||
| if (!$code instanceof DomainAuthorizedSummitRegistrationPromoCode) return []; | ||
| $values = parent::serialize($expand, $fields, $relations, $params); | ||
|
|
||
| // Transient remaining_quantity_per_account (set by service layer) | ||
| $values['remaining_quantity_per_account'] = $code->getRemainingQuantityPerAccount(); | ||
|
|
||
| return $values; | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.