Skip to content

Commit 0066cab

Browse files
author
Jeroen de Graaf
committed
Setup demo CLI command, to auto dispatch a set of
commands at random.
1 parent 2c2e31f commit 0066cab

File tree

5 files changed

+277
-2
lines changed

5 files changed

+277
-2
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,9 @@ bin/console doctrine:migrations:migrate
6565
You're all set, see what commands you can run:
6666
```
6767
bin/console gember
68+
```
69+
70+
Or run the demo command to run random sets of commands automatically:
71+
```
72+
bin/console gember:demo
6873
```

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"doctrine/doctrine-bundle": "^2.13",
2727
"doctrine/doctrine-migrations-bundle": "^3.3",
2828
"doctrine/orm": "^3.2",
29+
"fakerphp/faker": "^1.23",
2930
"gember/event-sourcing": "dev-main",
3031
"gember/event-sourcing-symfony-bundle": "dev-main",
3132
"symfony/console": "7.1.*",

composer.lock

Lines changed: 65 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

phpstan-baseline.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,18 @@
127127
'count' => 1,
128128
'path' => __DIR__ . '/src/Infrastructure/Api/Cli/Command/Course/RenameCourseCliCommand.php',
129129
];
130+
$ignoreErrors[] = [
131+
// identifier: cast.int
132+
'message' => '#^Cannot cast mixed to int\\.$#',
133+
'count' => 1,
134+
'path' => __DIR__ . '/src/Infrastructure/Api/Cli/Command/DemoRunCommand.php',
135+
];
136+
$ignoreErrors[] = [
137+
// identifier: argument.type
138+
'message' => '#^Parameter \\#1 \\$min \\(0\\) of function random_int expects lower number than parameter \\#2 \\$max \\(int\\<\\-1, max\\>\\)\\.$#',
139+
'count' => 1,
140+
'path' => __DIR__ . '/src/Infrastructure/Api/Cli/Command/DemoRunCommand.php',
141+
];
130142
$ignoreErrors[] = [
131143
// identifier: argument.type
132144
'message' => '#^Parameter \\#1 \\$studentId of class Gember\\\\ExampleEventSourcingDcb\\\\Application\\\\Command\\\\StudentToCourseSubscription\\\\SubscribeStudentToCourseCommand constructor expects string, mixed given\\.$#',
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Gember\ExampleEventSourcingDcb\Infrastructure\Api\Cli\Command;
6+
7+
use Faker\Factory;
8+
use Gember\EventSourcing\Util\Generator\Identity\IdentityGenerator;
9+
use Gember\ExampleEventSourcingDcb\Application\Command\Course\ChangeCourseCapacityCommand;
10+
use Gember\ExampleEventSourcingDcb\Application\Command\Course\CreateCourseCommand;
11+
use Gember\ExampleEventSourcingDcb\Application\Command\Course\RenameCourseCommand;
12+
use Gember\ExampleEventSourcingDcb\Application\Command\Student\CreateStudentCommand;
13+
use Gember\ExampleEventSourcingDcb\Application\Command\StudentToCourseSubscription\SubscribeStudentToCourseCommand;
14+
use Gember\ExampleEventSourcingDcb\Application\Command\StudentToCourseSubscription\UnsubscribeStudentFromCourseCommand;
15+
use Symfony\Component\Console\Attribute\AsCommand;
16+
use Symfony\Component\Console\Command\Command;
17+
use Symfony\Component\Console\Input\InputInterface;
18+
use Symfony\Component\Console\Input\InputOption;
19+
use Symfony\Component\Console\Output\OutputInterface;
20+
use Symfony\Component\Messenger\MessageBusInterface;
21+
use Throwable;
22+
use Override;
23+
24+
#[AsCommand(
25+
name: 'gember:demo',
26+
description: 'Demo run of POC; creating courses, students, subscribe, etc.',
27+
)]
28+
final class DemoRunCommand extends Command
29+
{
30+
private const array ACTIONS_BASE = ['createCourse', 'createStudent'];
31+
private const array ACTIONS_COURSES = ['renameCourse', 'changeCourseCapacity'];
32+
private const array ACTIONS_SUBSCRIPTIONS = ['subscribeStudentToCourse', 'unsubscribeStudentFromCourse'];
33+
34+
/**
35+
* @var list<string>
36+
*/
37+
private array $courses = [];
38+
39+
/**
40+
* @var list<string>
41+
*/
42+
private array $students = [];
43+
44+
private OutputInterface $output;
45+
46+
public function __construct(
47+
private readonly IdentityGenerator $identityGenerator,
48+
private readonly MessageBusInterface $commandBus,
49+
) {
50+
parent::__construct();
51+
}
52+
53+
#[Override]
54+
protected function initialize(InputInterface $input, OutputInterface $output): void
55+
{
56+
$this->output = $output;
57+
}
58+
59+
#[Override]
60+
protected function configure(): void
61+
{
62+
$this->addOption('iterations', 'i', InputOption::VALUE_REQUIRED, 'Number of iterations', 20);
63+
$this->addOption('sleep', 's', InputOption::VALUE_REQUIRED, 'Slow down demo iterations in seconds', 1);
64+
}
65+
66+
#[Override]
67+
protected function execute(InputInterface $input, OutputInterface $output): int
68+
{
69+
for ($i = 1; $i <= $input->getOption('iterations'); ++$i) {
70+
$action = $this->pickAction();
71+
72+
$output->writeln(sprintf('%d. %s', $i, $action));
73+
74+
try {
75+
$this->{$action}();
76+
} catch (Throwable $exception) {
77+
$output->writeln(sprintf('<error>%s</error>', $exception->getPrevious()?->getMessage()));
78+
}
79+
80+
// Slow down demo
81+
sleep((int) $input->getOption('sleep'));
82+
}
83+
84+
return self::SUCCESS;
85+
}
86+
87+
private function pickAction(): string
88+
{
89+
/** @var list<string> $actions */
90+
$actions = [...self::ACTIONS_BASE, ...self::ACTIONS_COURSES, ...self::ACTIONS_SUBSCRIPTIONS];
91+
92+
return $actions[random_int(0, count($actions) - 1)];
93+
}
94+
95+
private function createCourse(): void
96+
{
97+
$courseId = $this->identityGenerator->generate();
98+
$capacity = random_int(1, 10);
99+
/** @var string $name */
100+
$name = Factory::create()->words(2, true);
101+
$name = ucfirst($name);
102+
103+
$this->output->writeln(sprintf(' <info>Create course %s with name "%s" and capacity %d</info>', $courseId, $name, $capacity));
104+
105+
$this->commandBus->dispatch(new CreateCourseCommand($courseId, $name, $capacity));
106+
107+
$this->courses[] = $courseId;
108+
}
109+
110+
private function createStudent(): void
111+
{
112+
$studentId = $this->identityGenerator->generate();
113+
$this->commandBus->dispatch(new CreateStudentCommand($studentId));
114+
115+
$this->output->writeln(sprintf(' <info>Create student %s</info>', $studentId));
116+
117+
$this->students[] = $studentId;
118+
}
119+
120+
private function renameCourse(): void
121+
{
122+
if ($this->courses === []) {
123+
$this->output->writeln(' <comment>Nothing to trigger yet</comment>');
124+
125+
return;
126+
}
127+
128+
$courseId = $this->courses[random_int(0, count($this->courses) - 1)];
129+
/** @var string $name */
130+
$name = Factory::create()->words(2, true);
131+
$name = ucfirst($name);
132+
133+
$this->output->writeln(sprintf(' <info>Rename course %s to "%s"</info>', $courseId, $name));
134+
135+
$this->commandBus->dispatch(new RenameCourseCommand(
136+
$courseId,
137+
$name,
138+
));
139+
}
140+
141+
private function changeCourseCapacity(): void
142+
{
143+
if ($this->courses === []) {
144+
$this->output->writeln(' <comment>Nothing to trigger yet</comment>');
145+
146+
return;
147+
}
148+
149+
$courseId = $this->courses[random_int(0, count($this->courses) - 1)];
150+
$capacity = random_int(1, 10);
151+
152+
$this->output->writeln(sprintf(' <info>Change course %s capacity to %d</info>', $courseId, $capacity));
153+
154+
$this->commandBus->dispatch(new ChangeCourseCapacityCommand($courseId, $capacity));
155+
}
156+
157+
private function subscribeStudentToCourse(): void
158+
{
159+
if ($this->courses === [] || $this->students === []) {
160+
$this->output->writeln(' <comment>Nothing to trigger yet</comment>');
161+
162+
return;
163+
}
164+
165+
$courseId = $this->courses[random_int(0, count($this->courses) - 1)];
166+
$studentId = $this->students[random_int(0, count($this->students) - 1)];
167+
168+
$this->output->writeln(sprintf(' <info>Subscribe student %s to course %s</info>', $studentId, $courseId));
169+
170+
$this->commandBus->dispatch(new SubscribeStudentToCourseCommand(
171+
$studentId,
172+
$courseId,
173+
));
174+
}
175+
176+
private function unsubscribeStudentFromCourse(): void
177+
{
178+
if ($this->courses === [] || $this->students === []) {
179+
$this->output->writeln(' <comment>Nothing to trigger yet</comment>');
180+
181+
return;
182+
}
183+
184+
$courseId = $this->courses[random_int(0, count($this->courses) - 1)];
185+
$studentId = $this->students[random_int(0, count($this->students) - 1)];
186+
187+
$this->output->writeln(sprintf(' <info>Unsubscribe student %s from course %s</info>', $studentId, $courseId));
188+
189+
$this->commandBus->dispatch(new UnsubscribeStudentFromCourseCommand(
190+
$studentId,
191+
$courseId,
192+
));
193+
}
194+
}

0 commit comments

Comments
 (0)