Skip to content

Commit f35737b

Browse files
feat: add new formatters for PurchaseOrders
1 parent a87252f commit f35737b

9 files changed

+790
-0
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
namespace App\Audit\ConcreteFormatters;
4+
5+
/**
6+
* Copyright 2026 OpenStack Foundation
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
**/
17+
18+
use App\Audit\AbstractAuditLogFormatter;
19+
use App\Audit\Interfaces\IAuditStrategy;
20+
use App\Models\Foundation\ExtraQuestions\ExtraQuestionTypeValue;
21+
use Illuminate\Support\Facades\Log;
22+
23+
class ExtraQuestionTypeValueAuditLogFormatter extends AbstractAuditLogFormatter
24+
{
25+
public function format($subject, array $change_set): ?string
26+
{
27+
if (!$subject instanceof ExtraQuestionTypeValue) {
28+
return null;
29+
}
30+
31+
try {
32+
$id = $subject->getId() ?? 'unknown';
33+
$label = $subject->getLabel() ?? 'Unknown Value';
34+
$question = $subject->getQuestion();
35+
$question_label = $question ? ($question->getLabel() ?? 'Unknown Question') : 'Unknown Question';
36+
37+
switch ($this->event_type) {
38+
case IAuditStrategy::EVENT_ENTITY_CREATION:
39+
return sprintf("Extra Question Value '%s' (%s) for Question '%s' created by user %s", $label, $id, $question_label, $this->getUserInfo());
40+
case IAuditStrategy::EVENT_ENTITY_UPDATE:
41+
$details = $this->buildChangeDetails($change_set);
42+
return sprintf("Extra Question Value '%s' (%s) for Question '%s' updated: %s by user %s", $label, $id, $question_label, $details, $this->getUserInfo());
43+
case IAuditStrategy::EVENT_ENTITY_DELETION:
44+
return sprintf("Extra Question Value '%s' (%s) for Question '%s' deleted by user %s", $label, $id, $question_label, $this->getUserInfo());
45+
}
46+
} catch (\Exception $ex) {
47+
Log::warning("ExtraQuestionTypeValueAuditLogFormatter error: " . $ex->getMessage());
48+
}
49+
50+
return null;
51+
}
52+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
namespace App\Audit\ConcreteFormatters;
4+
5+
/**
6+
* Copyright 2026 OpenStack Foundation
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
**/
17+
18+
use App\Audit\AbstractAuditLogFormatter;
19+
use App\Audit\Interfaces\IAuditStrategy;
20+
use models\summit\SummitOrder;
21+
use Illuminate\Support\Facades\Log;
22+
23+
class SummitOrderAuditLogFormatter extends AbstractAuditLogFormatter
24+
{
25+
public function format($subject, array $change_set): ?string
26+
{
27+
if (!$subject instanceof SummitOrder) {
28+
return null;
29+
}
30+
31+
try {
32+
$id = $subject->getId() ?? 'unknown';
33+
$order_number = $subject->getNumber() ?? 'Unknown Order';
34+
$status = $subject->getStatus() ?? 'Unknown';
35+
$summit = $subject->getSummit();
36+
$summit_name = $summit ? ($summit->getName() ?? 'Unknown Summit') : 'Unknown Summit';
37+
$owner = $subject->getOwner();
38+
$owner_email = $owner ? ($owner->getEmail() ?? 'Unknown Owner') : 'No Owner';
39+
40+
switch ($this->event_type) {
41+
case IAuditStrategy::EVENT_ENTITY_CREATION:
42+
return sprintf("Order '%s' (%s) created for '%s' in Summit '%s' with status '%s' by user %s", $order_number, $id, $owner_email, $summit_name, $status, $this->getUserInfo());
43+
case IAuditStrategy::EVENT_ENTITY_UPDATE:
44+
$details = $this->buildChangeDetails($change_set);
45+
return sprintf("Order '%s' (%s) in Summit '%s' updated: %s by user %s", $order_number, $id, $summit_name, $details, $this->getUserInfo());
46+
case IAuditStrategy::EVENT_ENTITY_DELETION:
47+
return sprintf("Order '%s' (%s) in Summit '%s' deleted by user %s", $order_number, $id, $summit_name, $this->getUserInfo());
48+
}
49+
} catch (\Exception $ex) {
50+
Log::warning("SummitOrderAuditLogFormatter error: " . $ex->getMessage());
51+
}
52+
53+
return null;
54+
}
55+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
namespace App\Audit\ConcreteFormatters;
4+
5+
/**
6+
* Copyright 2026 OpenStack Foundation
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
**/
17+
18+
use App\Audit\AbstractAuditLogFormatter;
19+
use App\Audit\Interfaces\IAuditStrategy;
20+
use models\summit\SummitOrderExtraQuestionType;
21+
use Illuminate\Support\Facades\Log;
22+
23+
class SummitOrderExtraQuestionTypeAuditLogFormatter extends AbstractAuditLogFormatter
24+
{
25+
public function format($subject, array $change_set): ?string
26+
{
27+
if (!$subject instanceof SummitOrderExtraQuestionType) {
28+
return null;
29+
}
30+
31+
try {
32+
$id = $subject->getId() ?? 'unknown';
33+
$label = $subject->getLabel() ?? 'Unknown Question';
34+
$question_type = $subject->getType() ?? 'Unknown Type';
35+
$summit = $subject->getSummit();
36+
$summit_name = $summit ? ($summit->getName() ?? 'Unknown Summit') : 'Unknown Summit';
37+
38+
switch ($this->event_type) {
39+
case IAuditStrategy::EVENT_ENTITY_CREATION:
40+
return sprintf("Order Extra Question '%s' (%s) of type '%s' created in Summit '%s' by user %s", $label, $id, $question_type, $summit_name, $this->getUserInfo());
41+
case IAuditStrategy::EVENT_ENTITY_UPDATE:
42+
$details = $this->buildChangeDetails($change_set);
43+
return sprintf("Order Extra Question '%s' (%s) in Summit '%s' updated: %s by user %s", $label, $id, $summit_name, $details, $this->getUserInfo());
44+
case IAuditStrategy::EVENT_ENTITY_DELETION:
45+
return sprintf("Order Extra Question '%s' (%s) in Summit '%s' deleted by user %s", $label, $id, $summit_name, $this->getUserInfo());
46+
}
47+
} catch (\Exception $ex) {
48+
Log::warning("SummitOrderExtraQuestionTypeAuditLogFormatter error: " . $ex->getMessage());
49+
}
50+
51+
return null;
52+
}
53+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
3+
namespace Tests\OpenTelemetry\Formatters;
4+
5+
use App\Audit\ConcreteFormatters\ExtraQuestionTypeValueAuditLogFormatter;
6+
use App\Audit\Interfaces\IAuditStrategy;
7+
use Tests\OpenTelemetry\Formatters\Support\AuditContextBuilder;
8+
use Mockery;
9+
use Tests\TestCase;
10+
11+
class ExtraQuestionTypeValueAuditLogFormatterTest extends TestCase
12+
{
13+
private const VALUE_ID = 333;
14+
private const VALUE_LABEL = 'Option A';
15+
private const VALUE_VALUE = 'OPTION_A';
16+
private const QUESTION_LABEL = 'Choose an Option';
17+
private const CHANGED_LABEL = 'Option B';
18+
19+
private mixed $mockSubject;
20+
21+
protected function setUp(): void
22+
{
23+
parent::setUp();
24+
$this->mockSubject = $this->createMockSubject();
25+
}
26+
27+
protected function tearDown(): void
28+
{
29+
Mockery::close();
30+
parent::tearDown();
31+
}
32+
33+
private function createMockSubject(): mixed
34+
{
35+
$mockQuestion = Mockery::mock('App\Models\Foundation\ExtraQuestions\ExtraQuestionType');
36+
$mockQuestion->shouldReceive('getLabel')->andReturn(self::QUESTION_LABEL);
37+
38+
$mock = Mockery::mock('App\Models\Foundation\ExtraQuestions\ExtraQuestionTypeValue');
39+
$mock->shouldReceive('getId')->andReturn(self::VALUE_ID);
40+
$mock->shouldReceive('getLabel')->andReturn(self::VALUE_LABEL);
41+
$mock->shouldReceive('getValue')->andReturn(self::VALUE_VALUE);
42+
$mock->shouldReceive('getQuestion')->andReturn($mockQuestion);
43+
44+
return $mock;
45+
}
46+
47+
public function testSubjectCreationAuditMessage(): void
48+
{
49+
$formatter = new ExtraQuestionTypeValueAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_CREATION);
50+
$formatter->setContext(AuditContextBuilder::default()->build());
51+
$result = $formatter->format($this->mockSubject, []);
52+
53+
$this->assertNotNull($result);
54+
$this->assertStringContainsString('created', $result);
55+
$this->assertStringContainsString(self::VALUE_LABEL, $result);
56+
$this->assertStringContainsString((string)self::VALUE_ID, $result);
57+
}
58+
59+
public function testSubjectUpdateAuditMessage(): void
60+
{
61+
$formatter = new ExtraQuestionTypeValueAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_UPDATE);
62+
$formatter->setContext(AuditContextBuilder::default()->build());
63+
$changeSet = ['label' => [self::VALUE_LABEL, self::CHANGED_LABEL]];
64+
65+
$result = $formatter->format($this->mockSubject, $changeSet);
66+
67+
$this->assertNotNull($result);
68+
$this->assertStringContainsString('updated', $result);
69+
}
70+
71+
public function testSubjectDeletionAuditMessage(): void
72+
{
73+
$formatter = new ExtraQuestionTypeValueAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_DELETION);
74+
$formatter->setContext(AuditContextBuilder::default()->build());
75+
$result = $formatter->format($this->mockSubject, []);
76+
77+
$this->assertNotNull($result);
78+
$this->assertStringContainsString('deleted', $result);
79+
}
80+
81+
public function testFormatterReturnsNullForInvalidSubject(): void
82+
{
83+
$formatter = new ExtraQuestionTypeValueAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_CREATION);
84+
$formatter->setContext(AuditContextBuilder::default()->build());
85+
$result = $formatter->format(new \stdClass(), []);
86+
87+
$this->assertNull($result);
88+
}
89+
90+
public function testFormatterHandlesEmptyChangeSet(): void
91+
{
92+
$formatter = new ExtraQuestionTypeValueAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_UPDATE);
93+
$formatter->setContext(AuditContextBuilder::default()->build());
94+
$result = $formatter->format($this->mockSubject, []);
95+
96+
$this->assertNotNull($result);
97+
$this->assertStringContainsString('updated', $result);
98+
}
99+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
3+
namespace Tests\OpenTelemetry\Formatters;
4+
5+
use App\Audit\ConcreteFormatters\SummitAttendeeBadgeAuditLogFormatter;
6+
use App\Audit\Interfaces\IAuditStrategy;
7+
use Tests\OpenTelemetry\Formatters\Support\AuditContextBuilder;
8+
use Mockery;
9+
use Tests\TestCase;
10+
11+
class SummitAttendeeBadgeAuditLogFormatterTest extends TestCase
12+
{
13+
private const BADGE_ID = 111;
14+
private const TICKET_ID = 789;
15+
private const TICKET_NUMBER = 'TICKET-2024-001';
16+
private const SUMMIT_NAME = 'OpenStack Summit 2024';
17+
18+
private mixed $mockSubject;
19+
20+
protected function setUp(): void
21+
{
22+
parent::setUp();
23+
$this->mockSubject = $this->createMockSubject();
24+
}
25+
26+
protected function tearDown(): void
27+
{
28+
Mockery::close();
29+
parent::tearDown();
30+
}
31+
32+
private function createMockSubject(): mixed
33+
{
34+
$mockSummit = Mockery::mock('models\summit\Summit');
35+
$mockSummit->shouldReceive('getName')->andReturn(self::SUMMIT_NAME);
36+
37+
$mockOrder = Mockery::mock('models\summit\SummitOrder');
38+
$mockOrder->shouldReceive('getSummit')->andReturn($mockSummit);
39+
40+
$mockTicket = Mockery::mock('models\summit\SummitAttendeeTicket');
41+
$mockTicket->shouldReceive('getId')->andReturn(self::TICKET_ID);
42+
$mockTicket->shouldReceive('getNumber')->andReturn(self::TICKET_NUMBER);
43+
$mockTicket->shouldReceive('getOrder')->andReturn($mockOrder);
44+
45+
$mock = Mockery::mock('models\summit\SummitAttendeeBadge');
46+
$mock->shouldReceive('getId')->andReturn(self::BADGE_ID);
47+
$mock->shouldReceive('isVoid')->andReturn(false);
48+
$mock->shouldReceive('getTicket')->andReturn($mockTicket);
49+
50+
return $mock;
51+
}
52+
53+
public function testSubjectCreationAuditMessage(): void
54+
{
55+
$formatter = new SummitAttendeeBadgeAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_CREATION);
56+
$formatter->setContext(AuditContextBuilder::default()->build());
57+
$result = $formatter->format($this->mockSubject, []);
58+
59+
$this->assertNotNull($result);
60+
$this->assertStringContainsString('created', $result);
61+
$this->assertStringContainsString(self::TICKET_NUMBER, $result);
62+
$this->assertStringContainsString((string)self::TICKET_ID, $result);
63+
}
64+
65+
public function testSubjectUpdateAuditMessage(): void
66+
{
67+
$formatter = new SummitAttendeeBadgeAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_UPDATE);
68+
$formatter->setContext(AuditContextBuilder::default()->build());
69+
$changeSet = ['is_void' => [false, true]];
70+
71+
$result = $formatter->format($this->mockSubject, $changeSet);
72+
73+
$this->assertNotNull($result);
74+
$this->assertStringContainsString('updated', $result);
75+
}
76+
77+
public function testSubjectDeletionAuditMessage(): void
78+
{
79+
$formatter = new SummitAttendeeBadgeAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_DELETION);
80+
$formatter->setContext(AuditContextBuilder::default()->build());
81+
$result = $formatter->format($this->mockSubject, []);
82+
83+
$this->assertNotNull($result);
84+
$this->assertStringContainsString('deleted', $result);
85+
}
86+
87+
public function testFormatterReturnsNullForInvalidSubject(): void
88+
{
89+
$formatter = new SummitAttendeeBadgeAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_CREATION);
90+
$formatter->setContext(AuditContextBuilder::default()->build());
91+
$result = $formatter->format(new \stdClass(), []);
92+
93+
$this->assertNull($result);
94+
}
95+
96+
public function testFormatterHandlesEmptyChangeSet(): void
97+
{
98+
$formatter = new SummitAttendeeBadgeAuditLogFormatter(IAuditStrategy::EVENT_ENTITY_UPDATE);
99+
$formatter->setContext(AuditContextBuilder::default()->build());
100+
$result = $formatter->format($this->mockSubject, []);
101+
102+
$this->assertNotNull($result);
103+
$this->assertStringContainsString('updated', $result);
104+
}
105+
}

0 commit comments

Comments
 (0)