Skip to content

Commit 151a0af

Browse files
committed
feat: add workspace detail endpoint and support for slug-based retrieval
- Introduced `WorkspaceHandler` for handling workspace detail requests - Added `findBySlug` method to workspace repository and table implementations - Defined DTO for `Workspace` with `fromArray` method - Updated config provider to register new `/api/workspace/{slug}` route
1 parent 5ae19e3 commit 151a0af

8 files changed

Lines changed: 99 additions & 8 deletions

File tree

src/App/Shared/Infrastructure/Persistence/Repository/WorkspaceRepositoryInterface.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ public function findByAccountId(int $accountId, Pagination $pagination): Workspa
1919

2020
public function findByName(string $name): ?WorkspaceInterface;
2121

22+
public function findBySlug(string $slug): ?WorkspaceInterface;
23+
2224
public function findAll(): WorkspaceCollectionInterface;
2325

2426
public function countByAccount(int $accountId): int;

src/App/Shared/Infrastructure/Persistence/Table/WorkspaceStoreInterface.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public function findByAccountId(int $accountId, Pagination $pagination): Workspa
2525

2626
public function findByName(string $name): ?WorkspaceInterface;
2727

28+
public function findBySlug(string $slug): ?WorkspaceInterface;
29+
2830
public function findAll(): WorkspaceCollectionInterface;
2931

3032
public function countByAccount(int $accountId): int;

src/App/Workspace/ConfigProvider.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Exdrals\Core\Shared\Infrastructure\Service\SlugService;
77
use Exdrals\Core\Shared\Middleware\FluentTransactionMiddleware;
88
use Exdrals\Core\Shared\Utils\UuidFactoryInterface;
9+
use Exdrals\Identity\Infrastructure\Persistence\Repository\AccountRepositoryInterface;
910
use Exdrals\Identity\Middleware\RequireLoginMiddleware;
1011
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
1112
use Laminas\ServiceManager\Factory\InvokableFactory;
@@ -18,6 +19,7 @@
1819
use ownHackathon\Shared\Middleware\PaginationMiddleware;
1920
use ownHackathon\Workspace\Handler\ListOwnWorkspacesHandler;
2021
use ownHackathon\Workspace\Handler\WorkspaceCreateHandler;
22+
use ownHackathon\Workspace\Handler\WorkspaceHandler;
2123
use ownHackathon\Workspace\Infrastructure\Hydrator\WorkspaceHydrator;
2224
use ownHackathon\Workspace\Infrastructure\Persistence\Repository\WorkspaceRepository;
2325
use ownHackathon\Workspace\Infrastructure\Persistence\Table\WorkspaceTable;
@@ -64,10 +66,11 @@ public function getRoutes(): array
6466
'name' => 'api_workspace_list_own_workspaces',
6567
],
6668
[
67-
'path' => '/api/workspace/{slug}',
69+
'path' => '/api/workspace/{slug:[a-zA-Z0-9\-]+}[/]',
6870
'allowed_methods' => ['GET'],
6971
'middleware' => [
7072
RequireLoginMiddleware::class,
73+
WorkspaceHandler::class,
7174
],
7275
'name' => 'api_workspace_detail',
7376
],
@@ -97,6 +100,7 @@ public function getDependencies(): array
97100
WorkspaceCreateHandler::class => ConfigAbstractFactory::class,
98101
ListOwnWorkspacesHandler::class => ConfigAbstractFactory::class,
99102
PaginationService::class => ConfigAbstractFactory::class,
103+
WorkspaceHandler::class => ConfigAbstractFactory::class,
100104
],
101105
];
102106
}
@@ -138,6 +142,10 @@ public function getAbstractFactoryConfig(): array
138142
WorkspaceRepositoryInterface::class,
139143
PaginationTotalPages::class,
140144
],
145+
WorkspaceHandler::class => [
146+
WorkspaceRepositoryInterface::class,
147+
AccountRepositoryInterface::class,
148+
]
141149
];
142150
}
143151
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace ownHackathon\Workspace\DTO;
4+
5+
readonly class Workspace
6+
{
7+
public function __construct(
8+
public string $name,
9+
public string $description,
10+
public string $owner,
11+
public string $ownerUuid,
12+
) {
13+
}
14+
15+
public static function fromArray(array $data): self
16+
{
17+
return new self(
18+
$data['name'],
19+
$data['description'],
20+
$data['owner'],
21+
$data['ownerUuid']
22+
);
23+
}
24+
}

src/App/Workspace/Handler/WorkspaceCreateHandler.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44

55
use Exdrals\Core\Shared\Domain\Exception\HttpDuplicateEntryException;
66
use Exdrals\Core\Shared\Infrastructure\DTO\HttpResponseMessage;
7+
use Exdrals\Identity\Domain\AccountInterface;
78
use Fig\Http\Message\StatusCodeInterface as HTTP;
89
use Laminas\Diactoros\Response\JsonResponse;
910
use Mezzio\Helper\UrlHelper;
1011
use OpenApi\Attributes as OA;
11-
use Exdrals\Identity\Domain\AccountInterface;
1212
use ownHackathon\Shared\Infrastructure\Service\WorkspaceCreatorInterface;
1313
use ownHackathon\Workspace\Domain\Exception\WorkspaceNameAlreadyExistsException;
1414
use ownHackathon\Workspace\Domain\Message\WorkspaceLogMessage;
@@ -78,6 +78,7 @@ public function handle(ServerRequestInterface $request): ResponseInterface
7878
}
7979

8080
$location = $this->urlHelper->generate('api_workspace_detail', ['slug' => $workspace->slug]);
81+
$location = $location !== '/' ? rtrim($location, '/') : $location;
8182

8283
$response = WorkspaceResponse::fromEntity($workspace, $account);
8384

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace ownHackathon\Workspace\Handler;
4+
5+
use Exdrals\Identity\Infrastructure\Persistence\Repository\AccountRepositoryInterface;
6+
use Fig\Http\Message\StatusCodeInterface as Http;
7+
use Laminas\Diactoros\Response\JsonResponse;
8+
use ownHackathon\Shared\Infrastructure\Persistence\Repository\WorkspaceRepositoryInterface;
9+
use ownHackathon\Workspace\DTO\Workspace;
10+
use Psr\Http\Message\ResponseInterface;
11+
use Psr\Http\Message\ServerRequestInterface;
12+
use Psr\Http\Server\RequestHandlerInterface;
13+
14+
readonly class WorkspaceHandler implements RequestHandlerInterface
15+
{
16+
public function __construct(
17+
private WorkspaceRepositoryInterface $workspaceRepository,
18+
private AccountRepositoryInterface $accountRepository,
19+
) {
20+
}
21+
22+
public function handle(ServerRequestInterface $request): ResponseInterface
23+
{
24+
$slug = $request->getAttribute('slug');
25+
26+
$workspace = $this->workspaceRepository->findBySlug($slug);
27+
$account = $this->accountRepository->findById($workspace->accountId);
28+
29+
$response = Workspace::fromArray(
30+
[
31+
'name' => $workspace->name,
32+
'description' => $workspace->description,
33+
'owner' => $account->name,
34+
'ownerUuid' => $account->uuid->toString(),
35+
]
36+
);
37+
38+
return new JsonResponse($response, Http::STATUS_OK);
39+
}
40+
}

src/App/Workspace/Infrastructure/Persistence/Repository/WorkspaceRepository.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ public function findByName(string $name): ?WorkspaceInterface
5151
return $this->store->findByName($name);
5252
}
5353

54+
public function findBySlug(string $slug): ?WorkspaceInterface
55+
{
56+
return $this->store->findBySlug($slug);
57+
}
58+
5459
public function findAll(): WorkspaceCollectionInterface
5560
{
5661
return $this->store->findAll();

src/App/Workspace/Infrastructure/Persistence/Table/WorkspaceTable.php

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,17 @@ public function findById(int $id): ?WorkspaceInterface
7272
return is_array($result) ? $this->hydrator->hydrate($result) : null;
7373
}
7474

75+
public function findByAccountId(int $accountId, Pagination $pagination): WorkspaceCollectionInterface
76+
{
77+
$result = $this->query->from($this->table)
78+
->where('accountId', $accountId)
79+
->limit($pagination->limit)
80+
->offset($pagination->offset)
81+
->fetchAll();
82+
83+
return is_array($result) ? $this->hydrator->hydrateCollection($result) : $this->hydrator->hydrateCollection([]);
84+
}
85+
7586
public function findByName(string $name): ?WorkspaceInterface
7687
{
7788
$result = $this->query->from($this->table)
@@ -81,15 +92,13 @@ public function findByName(string $name): ?WorkspaceInterface
8192
return is_array($result) ? $this->hydrator->hydrate($result) : null;
8293
}
8394

84-
public function findByAccountId(int $accountId, Pagination $pagination): WorkspaceCollectionInterface
95+
public function findBySlug(string $slug): ?WorkspaceInterface
8596
{
8697
$result = $this->query->from($this->table)
87-
->where('accountId', $accountId)
88-
->limit($pagination->limit)
89-
->offset($pagination->offset)
90-
->fetchAll();
98+
->where('slug', $slug)
99+
->fetch();
91100

92-
return is_array($result) ? $this->hydrator->hydrateCollection($result) : $this->hydrator->hydrateCollection([]);
101+
return is_array($result) ? $this->hydrator->hydrate($result) : null;
93102
}
94103

95104
public function findAll(): WorkspaceCollectionInterface

0 commit comments

Comments
 (0)