-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
37 changed files
with
1,319 additions
and
92 deletions.
There are no files selected for viewing
23 changes: 23 additions & 0 deletions
23
config/validator/Application/User/CreateInvitationCommand.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd"> | ||
<class name="App\Application\User\Command\CreateInvitationCommand"> | ||
<property name="fullName"> | ||
<constraint name="NotBlank"/> | ||
<constraint name="Length"> | ||
<option name="max">255</option> | ||
</constraint> | ||
</property> | ||
<property name="email"> | ||
<constraint name="NotBlank"/> | ||
<constraint name="Email"/> | ||
<constraint name="Length"> | ||
<option name="max">255</option> | ||
</constraint> | ||
</property> | ||
<property name="role"> | ||
<constraint name="NotBlank"/> | ||
</property> | ||
</class> | ||
</constraint-mapping> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Application\User\Command; | ||
|
||
use App\Application\CommandInterface; | ||
use App\Domain\User\Organization; | ||
use App\Domain\User\User; | ||
|
||
final class CreateInvitationCommand implements CommandInterface | ||
{ | ||
public function __construct( | ||
public Organization $organization, | ||
public User $owner, | ||
) { | ||
} | ||
|
||
public ?string $fullName = null; | ||
public ?string $email = null; | ||
public ?string $role = null; | ||
} |
51 changes: 51 additions & 0 deletions
51
src/Application/User/Command/CreateInvitationCommandHandler.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Application\User\Command; | ||
|
||
use App\Application\DateUtilsInterface; | ||
use App\Application\IdFactoryInterface; | ||
use App\Application\StringUtilsInterface; | ||
use App\Domain\User\Exception\InvitationAlreadyExistsException; | ||
use App\Domain\User\Exception\OrganizationUserAlreadyExistException; | ||
use App\Domain\User\Invitation; | ||
use App\Domain\User\Repository\InvitationRepositoryInterface; | ||
use App\Infrastructure\Persistence\Doctrine\Repository\User\OrganizationUserRepository; | ||
|
||
final readonly class CreateInvitationCommandHandler | ||
{ | ||
public function __construct( | ||
private InvitationRepositoryInterface $invitationRepository, | ||
private OrganizationUserRepository $organizationUserRepository, | ||
private IdFactoryInterface $idFactory, | ||
private DateUtilsInterface $dateUtils, | ||
private StringUtilsInterface $stringUtils, | ||
) { | ||
} | ||
|
||
public function __invoke(CreateInvitationCommand $command): Invitation | ||
{ | ||
$email = $this->stringUtils->normalizeEmail($command->email); | ||
|
||
if ($this->invitationRepository->findOneByEmailAndOrganization($email, $command->organization)) { | ||
throw new InvitationAlreadyExistsException(); | ||
} | ||
|
||
if ($this->organizationUserRepository->findByEmailAndOrganization($email, $command->organization->getUuid())) { | ||
throw new OrganizationUserAlreadyExistException(); | ||
} | ||
|
||
return $this->invitationRepository->add( | ||
new Invitation( | ||
uuid: $this->idFactory->make(), | ||
email: $email, | ||
role: $command->role, | ||
fullName: $command->fullName, | ||
createdAt: $this->dateUtils->getNow(), | ||
owner: $command->owner, | ||
organization: $command->organization, | ||
), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Application\User\Command; | ||
|
||
use App\Application\CommandInterface; | ||
use App\Domain\User\User; | ||
|
||
final readonly class JoinOrganizationCommand implements CommandInterface | ||
{ | ||
public function __construct( | ||
public string $invitationUuid, | ||
public User $user, | ||
) { | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
src/Application/User/Command/JoinOrganizationCommandHandler.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Application\User\Command; | ||
|
||
use App\Application\IdFactoryInterface; | ||
use App\Domain\User\Exception\InvitationNotFoundException; | ||
use App\Domain\User\Exception\OrganizationUserAlreadyExistException; | ||
use App\Domain\User\Invitation; | ||
use App\Domain\User\OrganizationUser; | ||
use App\Domain\User\Repository\InvitationRepositoryInterface; | ||
use App\Domain\User\Repository\OrganizationUserRepositoryInterface; | ||
|
||
final readonly class JoinOrganizationCommandHandler | ||
{ | ||
public function __construct( | ||
private InvitationRepositoryInterface $invitationRepository, | ||
private OrganizationUserRepositoryInterface $organizationUserRepository, | ||
private IdFactoryInterface $idFactory, | ||
) { | ||
} | ||
|
||
public function __invoke(JoinOrganizationCommand $command): void | ||
{ | ||
$invitation = $this->invitationRepository->findOneByUuid($command->invitationUuid); | ||
if (!$invitation instanceof Invitation) { | ||
throw new InvitationNotFoundException(); | ||
} | ||
|
||
$user = $command->user; | ||
$organization = $invitation->getOrganization(); | ||
|
||
$userOrganization = $this->organizationUserRepository | ||
->findOrganizationUser($organization->getUuid(), $user->getUuid()); | ||
|
||
if ($userOrganization instanceof OrganizationUser) { | ||
throw new OrganizationUserAlreadyExistException(); | ||
} | ||
|
||
$this->organizationUserRepository->add( | ||
(new OrganizationUser($this->idFactory->make())) | ||
->setUser($user) | ||
->setOrganization($organization) | ||
->setRoles($invitation->getRole()), | ||
); | ||
|
||
$this->invitationRepository->delete($invitation); | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
src/Application/User/Command/Mail/SendInvitationMailCommand.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Application\User\Command\Mail; | ||
|
||
use App\Application\AsyncCommandInterface; | ||
use App\Domain\User\Invitation; | ||
|
||
final class SendInvitationMailCommand implements AsyncCommandInterface | ||
{ | ||
public function __construct( | ||
public readonly Invitation $invitation, | ||
) { | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
src/Application/User/Command/Mail/SendInvitationMailCommandHandler.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Application\User\Command\Mail; | ||
|
||
use App\Application\MailerInterface; | ||
use App\Domain\Mail; | ||
|
||
final readonly class SendInvitationMailCommandHandler | ||
{ | ||
public function __construct( | ||
private MailerInterface $mailer, | ||
) { | ||
} | ||
|
||
public function __invoke(SendInvitationMailCommand $command): void | ||
{ | ||
$invitation = $command->invitation; | ||
|
||
$this->mailer->send( | ||
new Mail( | ||
address: $invitation->getEmail(), | ||
subject: 'organization_invitation.subject', | ||
template: 'email/user/organization_invitation.html.twig', | ||
payload: [ | ||
'fullName' => $invitation->getFullName(), | ||
'invitedBy' => $invitation->getOwner()->getFullName(), | ||
'organizationName' => $invitation->getOrganization()->getName(), | ||
'invitationUuid' => $invitation->getUuid(), | ||
], | ||
), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Application\User\Query; | ||
|
||
use App\Application\QueryInterface; | ||
|
||
final class GetInvitationsQuery implements QueryInterface | ||
{ | ||
public function __construct( | ||
public readonly string $organizationUuid, | ||
) { | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Application\User\Query; | ||
|
||
use App\Domain\User\Repository\InvitationRepositoryInterface; | ||
|
||
final class GetInvitationsQueryHandler | ||
{ | ||
public function __construct( | ||
private InvitationRepositoryInterface $invitationRepository, | ||
) { | ||
} | ||
|
||
public function __invoke(GetInvitationsQuery $query): array | ||
{ | ||
return $this->invitationRepository->findByOrganizationUuid($query->organizationUuid); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Application\User\View; | ||
|
||
final readonly class InvitationView | ||
{ | ||
public function __construct( | ||
public string $uuid, | ||
public string $fullName, | ||
public string $email, | ||
public string $role, | ||
) { | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
src/Domain/User/Exception/InvitationAlreadyExistsException.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Domain\User\Exception; | ||
|
||
final class InvitationAlreadyExistsException extends \Exception | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Domain\User\Exception; | ||
|
||
final class InvitationNotFoundException extends \Exception | ||
{ | ||
} |
9 changes: 9 additions & 0 deletions
9
src/Domain/User/Exception/OrganizationUserAlreadyExistException.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Domain\User\Exception; | ||
|
||
final class OrganizationUserAlreadyExistException extends \Exception | ||
{ | ||
} |
21 changes: 21 additions & 0 deletions
21
src/Domain/User/Repository/InvitationRepositoryInterface.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Domain\User\Repository; | ||
|
||
use App\Domain\User\Invitation; | ||
use App\Domain\User\Organization; | ||
|
||
interface InvitationRepositoryInterface | ||
{ | ||
public function add(Invitation $invitation): Invitation; | ||
|
||
public function delete(Invitation $invitation): void; | ||
|
||
public function findOneByEmailAndOrganization(string $email, Organization $organization): ?Invitation; | ||
|
||
public function findByOrganizationUuid(string $organizationUuid): array; | ||
|
||
public function findOneByUuid(string $uuid): ?Invitation; | ||
} |
63 changes: 63 additions & 0 deletions
63
src/Infrastructure/Controller/MyArea/Organization/User/AcceptInvitationController.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Infrastructure\Controller\MyArea\Organization\User; | ||
|
||
use App\Application\CommandBusInterface; | ||
use App\Application\User\Command\JoinOrganizationCommand; | ||
use App\Domain\User\Exception\InvitationNotFoundException; | ||
use App\Domain\User\Exception\OrganizationUserAlreadyExistException; | ||
use App\Infrastructure\Security\AuthenticatedUser; | ||
use Symfony\Component\HttpFoundation\RedirectResponse; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Symfony\Component\HttpFoundation\Session\FlashBagAwareSessionInterface; | ||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | ||
use Symfony\Component\Routing\Annotation\Route; | ||
use Symfony\Component\Routing\Requirement\Requirement; | ||
use Symfony\Component\Routing\RouterInterface; | ||
use Symfony\Contracts\Translation\TranslatorInterface; | ||
|
||
final class AcceptInvitationController | ||
{ | ||
public function __construct( | ||
private CommandBusInterface $commandBus, | ||
private RouterInterface $router, | ||
private AuthenticatedUser $authenticatedUser, | ||
private TranslatorInterface $translator, | ||
) { | ||
} | ||
|
||
#[Route( | ||
'/invitations/{uuid}/accept', | ||
name: 'app_invitation_accept', | ||
requirements: ['uuid' => Requirement::UUID], | ||
methods: ['GET'], | ||
)] | ||
public function __invoke(Request $request, string $uuid): Response | ||
{ | ||
/** @var FlashBagAwareSessionInterface */ | ||
$session = $request->getSession(); | ||
$user = $this->authenticatedUser->getUser(); | ||
|
||
try { | ||
$this->commandBus->handle(new JoinOrganizationCommand($uuid, $user)); | ||
$session->getFlashBag()->add('success', $this->translator->trans('invitation.accept.success')); | ||
|
||
return new RedirectResponse( | ||
url: $this->router->generate('app_my_area'), | ||
status: Response::HTTP_SEE_OTHER, | ||
); | ||
} catch (InvitationNotFoundException) { | ||
throw new NotFoundHttpException(); | ||
} catch (OrganizationUserAlreadyExistException) { | ||
$session->getFlashBag()->add('error', $this->translator->trans('invitation.accept.error')); | ||
|
||
return new RedirectResponse( | ||
url: $this->router->generate('app_my_area'), | ||
status: Response::HTTP_SEE_OTHER, | ||
); | ||
} | ||
} | ||
} |
Oops, something went wrong.