Skip to content

Commit 2294d52

Browse files
authored
feat(promo-code): add new serializer for promo code apply endpoint (#509)
* feat(promo-code): add new serializer for promo code apply endpoint * chore(test): add unit test
1 parent 8188557 commit 2294d52

8 files changed

Lines changed: 137 additions & 13 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,5 @@ docker-compose/mysql/model/*.sql
3535
package.xml
3636
.env.dev
3737
rector.php
38-
public/apc.php
38+
public/apc.php
39+
.claude/

app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitPromoCodesApiController.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,7 +1417,15 @@ public function removeSpeaker($summit_id, $promo_code_id, $speaker_id)
14171417
new OA\Parameter(name: "filter", in: "query", required: false, schema: new OA\Schema(type: "string")),
14181418
],
14191419
responses: [
1420-
new OA\Response(response: Response::HTTP_OK, description: "OK"),
1420+
new OA\Response(
1421+
response: Response::HTTP_OK,
1422+
description: "Pre-validation result",
1423+
content: new OA\JsonContent(
1424+
properties: [
1425+
new OA\Property(property: 'allows_to_reassign', type: 'boolean', description: 'Whether the promo code allows ticket reassignment'),
1426+
]
1427+
)
1428+
),
14211429
new OA\Response(response: Response::HTTP_UNAUTHORIZED, description: "Unauthorized"),
14221430
new OA\Response(response: Response::HTTP_NOT_FOUND, description: "Not found"),
14231431
new OA\Response(response: Response::HTTP_PRECONDITION_FAILED, description: "Validation error"),
@@ -1462,10 +1470,12 @@ function ($attribute, $value, $fail) use ($filter) {
14621470
'ticket_type_subtype' => 'required|string|in:'.join(",", SummitTicketType::SubTypes),
14631471
]);
14641472

1465-
$this->promo_code_service
1473+
$promo_code = $this->promo_code_service
14661474
->preValidatePromoCode($summit, $this->resource_server_context->getCurrentUser(), $promo_code_val, $filter);
14671475

1468-
return $this->ok();
1476+
return $this->ok(SerializerRegistry::getInstance()
1477+
->getSerializer($promo_code, SerializerRegistry::SerializerType_PreValidation)
1478+
->serialize());
14691479
});
14701480
}
14711481

app/ModelSerializers/SerializerRegistry.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ final class SerializerRegistry
168168
const SerializerType_Admin_Voteable_CSV = "ADMIN_VOTEABLE_CSV";
169169
const SerializerType_CSV = 'CSV';
170170
const SerializerType_Admin_Registration_Stats = 'ADMIN_REG_STATS';
171+
const SerializerType_PreValidation = 'PRE_VALIDATION';
171172

172173
private function __clone()
173174
{
@@ -434,61 +435,73 @@ private function __construct()
434435
$this->registry['SummitRegistrationPromoCode'] = [
435436
self::SerializerType_Public => SummitRegistrationPromoCodeSerializer::class,
436437
self::SerializerType_CSV => SummitRegistrationPromoCodeCSVSerializer::class,
438+
self::SerializerType_PreValidation => SummitRegistrationPromoCodePreValidationSerializer::class,
437439
];
438440

439441
$this->registry['SummitRegistrationDiscountCode'] = [
440442
self::SerializerType_Public => SummitRegistrationDiscountCodeSerializer::class,
441443
self::SerializerType_CSV => SummitRegistrationDiscountCodeCSVSerializer::class,
444+
self::SerializerType_PreValidation => SummitRegistrationPromoCodePreValidationSerializer::class,
442445
];
443446

444447
$this->registry['MemberSummitRegistrationPromoCode'] = [
445448
self::SerializerType_Public => MemberSummitRegistrationPromoCodeSerializer::class,
446449
self::SerializerType_CSV => MemberSummitRegistrationPromoCodeCSVSerializer::class,
450+
self::SerializerType_PreValidation => SummitRegistrationPromoCodePreValidationSerializer::class,
447451
];
448452

449453
$this->registry['MemberSummitRegistrationDiscountCode'] = [
450454
self::SerializerType_Public => MemberSummitRegistrationDiscountCodeSerializer::class,
451455
self::SerializerType_CSV => MemberSummitRegistrationDiscountCodeCSVSerializer::class,
456+
self::SerializerType_PreValidation => SummitRegistrationPromoCodePreValidationSerializer::class,
452457
];
453458

454459
$this->registry['SpeakerSummitRegistrationPromoCode'] = [
455460
self::SerializerType_Public => SpeakerSummitRegistrationPromoCodeSerializer::class,
456461
self::SerializerType_CSV => SpeakerSummitRegistrationPromoCodeCSVSerializer::class,
462+
self::SerializerType_PreValidation => SummitRegistrationPromoCodePreValidationSerializer::class,
457463
];
458464

459465
$this->registry['SpeakerSummitRegistrationDiscountCode'] = [
460466
self::SerializerType_Public => SpeakerSummitRegistrationDiscountCodeSerializer::class,
461467
self::SerializerType_CSV => SpeakerSummitRegistrationDiscountCodeCSVSerializer::class,
468+
self::SerializerType_PreValidation => SummitRegistrationPromoCodePreValidationSerializer::class,
462469
];
463470

464471
$this->registry['SponsorSummitRegistrationPromoCode'] = [
465472
self::SerializerType_Public => SponsorSummitRegistrationPromoCodeSerializer::class,
466473
self::SerializerType_CSV => SponsorSummitRegistrationPromoCodeCSVSerializer::class,
474+
self::SerializerType_PreValidation => SummitRegistrationPromoCodePreValidationSerializer::class,
467475
];
468476

469477
$this->registry['SponsorSummitRegistrationDiscountCode'] = [
470478
self::SerializerType_Public => SponsorSummitRegistrationDiscountCodeSerializer::class,
471479
self::SerializerType_CSV => SponsorSummitRegistrationDiscountCodeCSVSerializer::class,
480+
self::SerializerType_PreValidation => SummitRegistrationPromoCodePreValidationSerializer::class,
472481
];
473482

474483
$this->registry['SpeakersSummitRegistrationPromoCode'] = [
475484
self::SerializerType_Public => SpeakersSummitRegistrationPromoCodeSerializer::class,
476485
self::SerializerType_CSV => SpeakersSummitRegistrationPromoCodeCSVSerializer::class,
486+
self::SerializerType_PreValidation => SummitRegistrationPromoCodePreValidationSerializer::class,
477487
];
478488

479489
$this->registry['SpeakersRegistrationDiscountCode'] = [
480490
self::SerializerType_Public => SpeakersRegistrationDiscountCodeSerializer::class,
481491
self::SerializerType_CSV => SpeakersRegistrationDiscountCodeCSVSerializer::class,
492+
self::SerializerType_PreValidation => SummitRegistrationPromoCodePreValidationSerializer::class,
482493
];
483494

484495
$this->registry['PrePaidSummitRegistrationPromoCode'] = [
485496
self::SerializerType_Public => SummitRegistrationPromoCodeSerializer::class,
486497
self::SerializerType_CSV => SummitRegistrationPromoCodeCSVSerializer::class,
498+
self::SerializerType_PreValidation => SummitRegistrationPromoCodePreValidationSerializer::class,
487499
];
488500

489501
$this->registry['PrePaidSummitRegistrationDiscountCode'] = [
490502
self::SerializerType_Public => SummitRegistrationDiscountCodeSerializer::class,
491503
self::SerializerType_CSV => SummitRegistrationDiscountCodeCSVSerializer::class,
504+
self::SerializerType_PreValidation => SummitRegistrationPromoCodePreValidationSerializer::class,
492505
];
493506

494507
$this->registry['PresentationSpeakerSummitAssistanceConfirmationRequest'] = PresentationSpeakerSummitAssistanceConfirmationRequestSerializer::class;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php namespace ModelSerializers;
2+
/**
3+
* Copyright 2026 OpenStack Foundation
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
**/
14+
15+
/**
16+
* Class SummitRegistrationPromoCodePreValidationSerializer
17+
* @package ModelSerializers
18+
*/
19+
class SummitRegistrationPromoCodePreValidationSerializer extends SilverStripeSerializer
20+
{
21+
protected static $array_mappings = [
22+
'AllowsToReassign' => 'allows_to_reassign:json_boolean',
23+
];
24+
}

app/Services/Model/ISummitPromoCodeService.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,10 @@ public function removePromoCodeSpeaker(SummitRegistrationPromoCode $promo_code,
148148
* @param Member $owner
149149
* @param string $promo_code_value
150150
* @param Filter $filter
151-
* @return void
151+
* @return SummitRegistrationPromoCode
152152
* @throws \Exception
153153
*/
154-
public function preValidatePromoCode(Summit $summit, Member $owner, string $promo_code_value, Filter $filter):void;
154+
public function preValidatePromoCode(Summit $summit, Member $owner, string $promo_code_value, Filter $filter):SummitRegistrationPromoCode;
155155

156156
/**
157157
* @param Summit $summit
@@ -170,4 +170,4 @@ public function triggerSendSponsorPromoCodes(Summit $summit, array $payload, $fi
170170
* @throws ValidationException
171171
*/
172172
public function sendSponsorPromoCodes(int $summit_id, array $payload, Filter $filter = null): void;
173-
}
173+
}

app/Services/Model/Imp/SummitPromoCodeService.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -897,12 +897,12 @@ public function removePromoCodeSpeaker(SummitRegistrationPromoCode $promo_code,
897897
* @param Member $owner
898898
* @param string $promo_code_value
899899
* @param Filter $filter
900-
* @return void
900+
* @return SummitRegistrationPromoCode
901901
* @throws \Exception
902902
*/
903-
public function preValidatePromoCode(Summit $summit, Member $owner, string $promo_code_value, Filter $filter):void
903+
public function preValidatePromoCode(Summit $summit, Member $owner, string $promo_code_value, Filter $filter):SummitRegistrationPromoCode
904904
{
905-
$this->tx_service->transaction(function () use ($summit, $owner, $promo_code_value, $filter) {
905+
return $this->tx_service->transaction(function () use ($summit, $owner, $promo_code_value, $filter) {
906906

907907
$ticket_type_id = intval($filter->getUniqueFilter('ticket_type_id')->getValue());
908908

@@ -921,7 +921,7 @@ public function preValidatePromoCode(Summit $summit, Member $owner, string $prom
921921
if (!$promo_code instanceof SummitRegistrationPromoCode || $promo_code->getSummitId() != $summit->getId() || !$validator->isValid($promo_code)){
922922
throw new ValidationException(sprintf('The Promo Code "%s" is not a valid code.', $promo_code_value));
923923
}
924-
924+
return $promo_code;
925925
});
926926
}
927927

@@ -1008,4 +1008,4 @@ function ($summit, $flow_event, $promocode_id, $test_email_recipient, $announcem
10081008
$filter
10091009
);
10101010
}
1011-
}
1011+
}

tests/OAuth2SummitPromoCodesApiTest.php

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,41 @@ public function testPreValidatePromoCodeSuccess()
527527

528528
$headers = ["HTTP_Authorization" => " Bearer " . $this->access_token];
529529

530-
$this->action(
530+
$response = $this->action(
531+
"GET",
532+
"OAuth2SummitPromoCodesApiController@preValidatePromoCode",
533+
$params,
534+
[],
535+
[],
536+
[],
537+
$headers
538+
);
539+
540+
$this->assertResponseStatus(200);
541+
$content = json_decode($response->getContent(), true);
542+
$this->assertArrayHasKey('allows_to_reassign', $content);
543+
$this->assertTrue($content['allows_to_reassign']);
544+
}
545+
546+
public function testPreValidatePromoCodeReturnsAllowsToReassignFalse()
547+
{
548+
self::$default_prepaid_discount_code->setAllowsToReassign(false);
549+
550+
$type_id = self::$default_ticket_type->getId();
551+
552+
$params = [
553+
'id' => self::$summit->getId(),
554+
'promo_code_val' => self::$default_prepaid_discount_code->getCode(),
555+
'filter' => [
556+
'ticket_type_id==' . $type_id,
557+
'ticket_type_qty==1',
558+
'ticket_type_subtype==' . SummitTicketType::Subtype_PrePaid
559+
]
560+
];
561+
562+
$headers = ["HTTP_Authorization" => " Bearer " . $this->access_token];
563+
564+
$response = $this->action(
531565
"GET",
532566
"OAuth2SummitPromoCodesApiController@preValidatePromoCode",
533567
$params,
@@ -538,6 +572,9 @@ public function testPreValidatePromoCodeSuccess()
538572
);
539573

540574
$this->assertResponseStatus(200);
575+
$content = json_decode($response->getContent(), true);
576+
$this->assertArrayHasKey('allows_to_reassign', $content);
577+
$this->assertFalse($content['allows_to_reassign']);
541578
}
542579

543580
public function testPreValidatePromoCodeInvalid()

tests/SerializerTests.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
use models\oauth2\IResourceServerContext;
1616
use models\summit\Summit;
1717
use models\summit\SummitAttendee;
18+
use models\summit\SummitRegistrationPromoCode;
1819
use ModelSerializers\SerializerDecorator;
1920
use ModelSerializers\SummitAttendeeAdminSerializer;
21+
use ModelSerializers\SummitRegistrationPromoCodePreValidationSerializer;
2022
use Mockery;
2123

2224
/**
@@ -30,6 +32,43 @@ public function tearDown():void
3032
Mockery::close();
3133
}
3234

35+
public function testPreValidationSerializerReturnsAllowsToReassignTrue()
36+
{
37+
$promo_code = Mockery::mock(SummitRegistrationPromoCode::class)->makePartial();
38+
$promo_code->shouldReceive('isAllowsToReassign')->andReturn(true);
39+
$promo_code->shouldReceive('getIdentifier')->andReturn(1);
40+
41+
$resource_server_context = Mockery::mock(IResourceServerContext::class);
42+
$serializer = new SerializerDecorator(
43+
new SummitRegistrationPromoCodePreValidationSerializer($promo_code, $resource_server_context)
44+
);
45+
$values = $serializer->serialize();
46+
47+
$this->assertIsArray($values);
48+
$this->assertArrayHasKey('allows_to_reassign', $values);
49+
$this->assertTrue($values['allows_to_reassign']);
50+
// should only contain allows_to_reassign and inherited id fields
51+
$this->assertArrayNotHasKey('code', $values);
52+
$this->assertArrayNotHasKey('redeemed', $values);
53+
}
54+
55+
public function testPreValidationSerializerReturnsAllowsToReassignFalse()
56+
{
57+
$promo_code = Mockery::mock(SummitRegistrationPromoCode::class)->makePartial();
58+
$promo_code->shouldReceive('isAllowsToReassign')->andReturn(false);
59+
$promo_code->shouldReceive('getIdentifier')->andReturn(1);
60+
61+
$resource_server_context = Mockery::mock(IResourceServerContext::class);
62+
$serializer = new SerializerDecorator(
63+
new SummitRegistrationPromoCodePreValidationSerializer($promo_code, $resource_server_context)
64+
);
65+
$values = $serializer->serialize();
66+
67+
$this->assertIsArray($values);
68+
$this->assertArrayHasKey('allows_to_reassign', $values);
69+
$this->assertFalse($values['allows_to_reassign']);
70+
}
71+
3372
public function testAdminSummitAttendeeSerilizer(){
3473
$summit = Mockery::mock(Summit::class);
3574
$attendee = Mockery::mock(SummitAttendee::class)->makePartial();

0 commit comments

Comments
 (0)