Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Database\Migrations;

use Cycle\Migrations\Migration;

class OrmDefaultAddIsPinnedToEvents extends Migration
{
protected const DATABASE = 'default';

public function up(): void
{
$this->table('events')
->addColumn('is_pinned', 'boolean', ['nullable' => false, 'defaultValue' => false])
->update();
}

public function down(): void
{
$this->table('events')
->dropColumn('is_pinned')
->update();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public function toBroadcast(object $event): BroadcastEvent
type: $event->event->getType(),
payload: $event->event->getPayload(),
),
'is_pinned' => $event->event->isPinned(),
],
);
}
Expand Down
18 changes: 18 additions & 0 deletions app/modules/Events/Domain/Event.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class Event
public const PAYLOAD = 'payload';
public const TIMESTAMP = 'timestamp';
public const PROJECT = 'project';
public const IS_PINNED = 'is_pinned';

/** @internal */
public function __construct(
Expand All @@ -42,6 +43,8 @@ public function __construct(
private Timestamp $timestamp,
#[Column(type: 'string', name: self::PROJECT, nullable: true, typecast: Key::class)]
private ?Key $project = null,
#[Column(type: 'boolean', name: self::IS_PINNED, default: false)]
private bool $isPinned = false,
) {}

public function getUuid(): Uuid
Expand Down Expand Up @@ -73,4 +76,19 @@ public function getProject(): ?Key
{
return $this->project;
}

public function isPinned(): bool
{
return $this->isPinned;
}

public function pin(): void
{
$this->isPinned = true;
}

public function unpin(): void
{
$this->isPinned = false;
}
}
4 changes: 4 additions & 0 deletions app/modules/Events/Domain/EventRepositoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@ public function store(Event $event): bool;
public function deleteAll(array $scope = []): void;

public function deleteByPK(string $uuid): bool;

public function pin(string $uuid): bool;

public function unpin(string $uuid): bool;
}
26 changes: 23 additions & 3 deletions app/modules/Events/Integration/CycleOrm/EventRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public function store(Event $event): bool
Event::PAYLOAD => $payload,
Event::TIMESTAMP => (string) $event->getTimestamp(),
Event::PROJECT => $event->getProject() !== null ? (string) $event->getProject() : null,
Event::IS_PINNED => $event->isPinned(),
])->run();
} catch (\Throwable) {
$this->db->update(Event::TABLE_NAME)
Expand All @@ -47,16 +48,35 @@ public function store(Event $event): bool

public function deleteAll(array $scope = []): void
{
$this->db
$query = $this->db
->delete(Event::TABLE_NAME)
->where($this->buildScope($scope))
->run();
->where($this->buildScope($scope));

$query->where(Event::IS_PINNED, false);
$query->run();
}

public function deleteByPK(string $uuid): bool
{
return $this->db->delete(Event::TABLE_NAME)
->where(Event::UUID, $uuid)
->where(Event::IS_PINNED, false)
->run() > 0;
}

public function pin(string $uuid): bool
{
return $this->db->update(Event::TABLE_NAME)
->where(Event::UUID, $uuid)
->values([Event::IS_PINNED => true])
->run() > 0;
}

public function unpin(string $uuid): bool
{
return $this->db->update(Event::TABLE_NAME)
->where(Event::UUID, $uuid)
->values([Event::IS_PINNED => false])
->run() > 0;
}

Expand Down
20 changes: 15 additions & 5 deletions app/modules/Events/Interfaces/Commands/StoreEventHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Modules\Events\Interfaces\Commands;

use App\Application\Commands\CreateProject;
use App\Application\Commands\FindProjectByKey;
use App\Application\Commands\HandleReceivedEvent;
use App\Application\Domain\ValueObjects\Json;
Expand All @@ -24,16 +25,26 @@ public function __construct(
private EventDispatcherInterface $dispatcher,
private EventRepositoryInterface $events,
private QueryBusInterface $queryBus,
private \Spiral\Cqrs\CommandBusInterface $commandBus,
private EventMetrics $metrics,
) {}

#[CommandHandler]
public function handle(HandleReceivedEvent $command): void
{
$project = null;
// If the project is not null, we will find the project by key
if ($command->project !== null) {
$project = $this->queryBus->ask(new FindProjectByKey($command->project));
$projectKey = $command->project ?? Project::DEFAULT_KEY;

$project = $this->queryBus->ask(new FindProjectByKey($projectKey));

if ($project === null) {
try {
$project = $this->commandBus->dispatch(
new CreateProject(key: $projectKey, name: $projectKey),
);
} catch (\Throwable) {
// Race condition: project was created between check and create
$project = $this->queryBus->ask(new FindProjectByKey($projectKey));
}
}

$this->events->store(
Expand All @@ -42,7 +53,6 @@ public function handle(HandleReceivedEvent $command): void
type: $command->type,
payload: new Json($command->payload),
timestamp: Timestamp::create(),
// todo: use better option for default project
project: $project?->getKey() ?? Key::create(Project::DEFAULT_KEY),
),
);
Expand Down
37 changes: 37 additions & 0 deletions app/modules/Events/Interfaces/Http/Controllers/PinEventAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

namespace Modules\Events\Interfaces\Http\Controllers;

use App\Application\Domain\ValueObjects\Uuid;
use Modules\Events\Domain\EventRepositoryInterface;
use Spiral\Http\Exception\ClientException\NotFoundException;
use Spiral\Router\Annotation\Route;

final readonly class PinEventAction
{
#[Route(route: 'event/<uuid>/pin', name: 'event.pin', methods: ['POST'], group: 'api')]
public function pin(
EventRepositoryInterface $events,
Uuid $uuid,
): array {
if (!$events->pin((string) $uuid)) {
throw new NotFoundException('Event not found');
}

return ['status' => 'pinned'];
}

#[Route(route: 'event/<uuid>/pin', name: 'event.unpin', methods: ['DELETE'], group: 'api')]
public function unpin(
EventRepositoryInterface $events,
Uuid $uuid,
): array {
if (!$events->unpin((string) $uuid)) {
throw new NotFoundException('Event not found');
}

return ['status' => 'unpinned'];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ protected function mapData(): array|\JsonSerializable
type: $this->data->getType(),
payload: $this->data->getPayload(),
) ?? '',
'is_pinned' => $this->data->isPinned(),
];
}
}
17 changes: 17 additions & 0 deletions app/modules/Profiler/Application/Query/CompareProfiles.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Modules\Profiler\Application\Query;

use App\Application\Domain\ValueObjects\Uuid;
use Spiral\Cqrs\QueryInterface;

final class CompareProfiles implements QueryInterface
{
public function __construct(
public Uuid $baseProfileUuid,
public Uuid $compareProfileUuid,
public int $limit = 50,
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Modules\Profiler\Application\Query;

use App\Application\Domain\ValueObjects\Uuid;
use Spiral\Cqrs\QueryInterface;

final class FindProfileSummaryByUuid implements QueryInterface
{
public function __construct(
public Uuid $profileUuid,
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Modules\Profiler\Interfaces\Http\Controllers;

use App\Application\Domain\ValueObjects\Uuid;
use App\Application\Exception\EntityNotFoundException;
use Modules\Profiler\Application\Query\CompareProfiles;
use Modules\Profiler\Interfaces\Http\Request\CompareProfilesRequest;
use Spiral\Cqrs\QueryBusInterface;
use Spiral\Http\Exception\ClientException\NotFoundException;
use Spiral\Router\Annotation\Route;

final readonly class CompareProfilesAction
{
#[Route(route: 'profiler/compare', name: 'profiler.compare', methods: ['GET'], group: 'api')]
public function __invoke(
CompareProfilesRequest $request,
QueryBusInterface $bus,
): array {
try {
return $bus->ask(
new CompareProfiles(
baseProfileUuid: new Uuid($request->base),
compareProfileUuid: new Uuid($request->compare),
),
);
} catch (EntityNotFoundException $e) {
throw new NotFoundException($e->getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Modules\Profiler\Interfaces\Http\Controllers;

use App\Application\Domain\ValueObjects\Uuid;
use App\Application\Exception\EntityNotFoundException;
use Modules\Profiler\Application\Query\FindProfileSummaryByUuid;
use Spiral\Cqrs\QueryBusInterface;
use Spiral\Http\Exception\ClientException\NotFoundException;
use Spiral\Router\Annotation\Route;

final readonly class ShowSummaryAction
{
#[Route(route: 'profiler/<uuid>/summary', name: 'profiler.show.summary', methods: ['GET'], group: 'api')]
public function __invoke(
QueryBusInterface $bus,
Uuid $uuid,
): array {
try {
return $bus->ask(new FindProfileSummaryByUuid(profileUuid: $uuid));
} catch (EntityNotFoundException $e) {
throw new NotFoundException($e->getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace Modules\Profiler\Interfaces\Http\Request;

use Spiral\Filters\Attribute\Input\Query;
use Spiral\Filters\Model\Filter;
use Spiral\Filters\Model\FilterDefinitionInterface;
use Spiral\Filters\Model\HasFilterDefinition;
use Spiral\Validator\FilterDefinition;

final class CompareProfilesRequest extends Filter implements HasFilterDefinition
{
#[Query]
public string $base = '';

#[Query]
public string $compare = '';

public function filterDefinition(): FilterDefinitionInterface
{
return new FilterDefinition([
'base' => [
['notEmpty'],
['string::length', 36, 36],
],
'compare' => [
['notEmpty'],
['string::length', 36, 36],
],
]);
}
}
Loading
Loading