From 9e3d9103d8804432b027096e1ef43210b5bfa3a2 Mon Sep 17 00:00:00 2001 From: Dani Garcia Date: Tue, 6 Feb 2024 18:02:54 +0100 Subject: [PATCH 1/4] schema: added Services entity --- app/config/packages/orm_target_entities.yml | 4 +- app/migrations/Version20240206172427.php | 44 +++++++++++++++++++ .../Doctrine/Mapping/Service.Service.orm.xml | 11 +++++ .../Mapping/Service.ServiceAbstract.orm.xml | 13 ++++++ 4 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 app/migrations/Version20240206172427.php create mode 100644 app/src/Demo/Infrastructure/Persistence/Doctrine/Mapping/Service.Service.orm.xml create mode 100644 app/src/Demo/Infrastructure/Persistence/Doctrine/Mapping/Service.ServiceAbstract.orm.xml diff --git a/app/config/packages/orm_target_entities.yml b/app/config/packages/orm_target_entities.yml index edc57a5..2aec2a5 100755 --- a/app/config/packages/orm_target_entities.yml +++ b/app/config/packages/orm_target_entities.yml @@ -8,4 +8,6 @@ doctrine: Ivoz\Core\Domain\Model\Changelog\ChangelogInterface: Ivoz\Core\Domain\Model\Changelog\Changelog Demo\Domain\Model\Timezone\TimezoneInterface: - Demo\Domain\Model\Timezone\Timezone \ No newline at end of file + Demo\Domain\Model\Timezone\Timezone + Demo\Domain\Model\Service\ServiceInterface: + Demo\Domain\Model\Service\Service \ No newline at end of file diff --git a/app/migrations/Version20240206172427.php b/app/migrations/Version20240206172427.php new file mode 100644 index 0000000..6eba7f2 --- /dev/null +++ b/app/migrations/Version20240206172427.php @@ -0,0 +1,44 @@ +addSql('CREATE TABLE Services (id INT UNSIGNED AUTO_INCREMENT NOT NULL, iden VARCHAR(50) NOT NULL, UNIQUE INDEX services_iden (iden), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->populateFactoryServices(); + } + + private function populateFactoryServices() + { + $factoryServices = [ + 'Recording', 'Voicemail', 'Queues' + ]; + + foreach ($factoryServices as $srv) { + $this->addSql('INSERT INTO Services(iden) VALUES(:srv)', ['srv' => $srv]); + } + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE Services'); + } +} diff --git a/app/src/Demo/Infrastructure/Persistence/Doctrine/Mapping/Service.Service.orm.xml b/app/src/Demo/Infrastructure/Persistence/Doctrine/Mapping/Service.Service.orm.xml new file mode 100644 index 0000000..f339f85 --- /dev/null +++ b/app/src/Demo/Infrastructure/Persistence/Doctrine/Mapping/Service.Service.orm.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/app/src/Demo/Infrastructure/Persistence/Doctrine/Mapping/Service.ServiceAbstract.orm.xml b/app/src/Demo/Infrastructure/Persistence/Doctrine/Mapping/Service.ServiceAbstract.orm.xml new file mode 100644 index 0000000..8fe8e73 --- /dev/null +++ b/app/src/Demo/Infrastructure/Persistence/Doctrine/Mapping/Service.ServiceAbstract.orm.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + From 5d4fe371617bbe095c88603b9238ab113a2b2dcf Mon Sep 17 00:00:00 2001 From: Dani Garcia Date: Tue, 6 Feb 2024 18:09:11 +0100 Subject: [PATCH 2/4] core: regenerated entities --- app/src/Demo/Domain/Model/Service/Service.php | 29 ++++ .../Domain/Model/Service/ServiceAbstract.php | 161 ++++++++++++++++++ .../Demo/Domain/Model/Service/ServiceDto.php | 7 + .../Model/Service/ServiceDtoAbstract.php | 96 +++++++++++ .../Domain/Model/Service/ServiceInterface.php | 51 ++++++ .../Model/Service/ServiceRepository.php | 12 ++ .../Domain/Model/Service/ServiceTrait.php | 85 +++++++++ .../Doctrine/ServiceDoctrineRepository.php | 33 ++++ 8 files changed, 474 insertions(+) create mode 100644 app/src/Demo/Domain/Model/Service/Service.php create mode 100644 app/src/Demo/Domain/Model/Service/ServiceAbstract.php create mode 100644 app/src/Demo/Domain/Model/Service/ServiceDto.php create mode 100644 app/src/Demo/Domain/Model/Service/ServiceDtoAbstract.php create mode 100644 app/src/Demo/Domain/Model/Service/ServiceInterface.php create mode 100644 app/src/Demo/Domain/Model/Service/ServiceRepository.php create mode 100644 app/src/Demo/Domain/Model/Service/ServiceTrait.php create mode 100644 app/src/Demo/Infrastructure/Persistence/Doctrine/ServiceDoctrineRepository.php diff --git a/app/src/Demo/Domain/Model/Service/Service.php b/app/src/Demo/Domain/Model/Service/Service.php new file mode 100644 index 0000000..413d797 --- /dev/null +++ b/app/src/Demo/Domain/Model/Service/Service.php @@ -0,0 +1,29 @@ + + */ + public function getChangeSet(): array + { + return parent::getChangeSet(); + } + + /** + * Get id + * @codeCoverageIgnore + */ + public function getId(): int|string + { + return $this->id; + } +} diff --git a/app/src/Demo/Domain/Model/Service/ServiceAbstract.php b/app/src/Demo/Domain/Model/Service/ServiceAbstract.php new file mode 100644 index 0000000..31661dc --- /dev/null +++ b/app/src/Demo/Domain/Model/Service/ServiceAbstract.php @@ -0,0 +1,161 @@ +setIden($iden); + } + + abstract public function getId(): null|string|int; + + public function __toString(): string + { + return sprintf( + "%s#%s", + "Service", + (string) $this->getId() + ); + } + + /** + * @throws \Exception + */ + protected function sanitizeValues(): void + { + } + + /** + * @param int | null $id + */ + public static function createDto($id = null): ServiceDto + { + return new ServiceDto($id); + } + + /** + * @internal use EntityTools instead + * @param null|ServiceInterface $entity + */ + public static function entityToDto(?EntityInterface $entity, int $depth = 0): ?ServiceDto + { + if (!$entity) { + return null; + } + + Assertion::isInstanceOf($entity, ServiceInterface::class); + + if ($depth < 1) { + return static::createDto($entity->getId()); + } + + if ($entity instanceof \Doctrine\ORM\Proxy\Proxy && !$entity->__isInitialized()) { + return static::createDto($entity->getId()); + } + + $dto = $entity->toDto($depth - 1); + + return $dto; + } + + /** + * Factory method + * @internal use EntityTools instead + * @param ServiceDto $dto + */ + public static function fromDto( + DataTransferObjectInterface $dto, + ForeignKeyTransformerInterface $fkTransformer + ): static { + Assertion::isInstanceOf($dto, ServiceDto::class); + $iden = $dto->getIden(); + Assertion::notNull($iden, 'getIden value is null, but non null value was expected.'); + + $self = new static( + $iden + ); + + ; + + $self->initChangelog(); + + return $self; + } + + /** + * @internal use EntityTools instead + * @param ServiceDto $dto + */ + public function updateFromDto( + DataTransferObjectInterface $dto, + ForeignKeyTransformerInterface $fkTransformer + ): static { + Assertion::isInstanceOf($dto, ServiceDto::class); + + $iden = $dto->getIden(); + Assertion::notNull($iden, 'getIden value is null, but non null value was expected.'); + + $this + ->setIden($iden); + + return $this; + } + + /** + * @internal use EntityTools instead + */ + public function toDto(int $depth = 0): ServiceDto + { + return self::createDto() + ->setIden(self::getIden()); + } + + /** + * @return array + */ + protected function __toArray(): array + { + return [ + 'iden' => self::getIden() + ]; + } + + protected function setIden(string $iden): static + { + Assertion::maxLength($iden, 50, 'iden value "%s" is too long, it should have no more than %d characters, but has %d characters.'); + + $this->iden = $iden; + + return $this; + } + + public function getIden(): string + { + return $this->iden; + } +} diff --git a/app/src/Demo/Domain/Model/Service/ServiceDto.php b/app/src/Demo/Domain/Model/Service/ServiceDto.php new file mode 100644 index 0000000..4c40734 --- /dev/null +++ b/app/src/Demo/Domain/Model/Service/ServiceDto.php @@ -0,0 +1,7 @@ +setId($id); + } + + /** + * @inheritdoc + */ + public static function getPropertyMap(string $context = '', string $role = null): array + { + if ($context === self::CONTEXT_COLLECTION) { + return ['id' => 'id']; + } + + return [ + 'iden' => 'iden', + 'id' => 'id' + ]; + } + + /** + * @return array + */ + public function toArray(bool $hideSensitiveData = false): array + { + $response = [ + 'iden' => $this->getIden(), + 'id' => $this->getId() + ]; + + if (!$hideSensitiveData) { + return $response; + } + + foreach ($this->sensitiveFields as $sensitiveField) { + if (!array_key_exists($sensitiveField, $response)) { + throw new \Exception($sensitiveField . ' field was not found'); + } + $response[$sensitiveField] = '*****'; + } + + return $response; + } + + public function setIden(string $iden): static + { + $this->iden = $iden; + + return $this; + } + + public function getIden(): ?string + { + return $this->iden; + } + + /** + * @param int|null $id + */ + public function setId($id): static + { + $this->id = $id; + + return $this; + } + + public function getId(): ?int + { + return $this->id; + } +} diff --git a/app/src/Demo/Domain/Model/Service/ServiceInterface.php b/app/src/Demo/Domain/Model/Service/ServiceInterface.php new file mode 100644 index 0000000..80ab8c6 --- /dev/null +++ b/app/src/Demo/Domain/Model/Service/ServiceInterface.php @@ -0,0 +1,51 @@ + + */ + public function getChangeSet(): array; + + /** + * Get id + * @codeCoverageIgnore + */ + public function getId(): ?int; + + /** + * @param int | null $id + */ + public static function createDto($id = null): ServiceDto; + + /** + * @internal use EntityTools instead + * @param null|ServiceInterface $entity + */ + public static function entityToDto(?EntityInterface $entity, int $depth = 0): ?ServiceDto; + + /** + * Factory method + * @internal use EntityTools instead + * @param ServiceDto $dto + */ + public static function fromDto(DataTransferObjectInterface $dto, ForeignKeyTransformerInterface $fkTransformer): static; + + /** + * @internal use EntityTools instead + */ + public function toDto(int $depth = 0): ServiceDto; + + public function getIden(): string; +} diff --git a/app/src/Demo/Domain/Model/Service/ServiceRepository.php b/app/src/Demo/Domain/Model/Service/ServiceRepository.php new file mode 100644 index 0000000..e1f071d --- /dev/null +++ b/app/src/Demo/Domain/Model/Service/ServiceRepository.php @@ -0,0 +1,12 @@ + + */ +interface ServiceRepository extends RepositoryInterface +{ +} diff --git a/app/src/Demo/Domain/Model/Service/ServiceTrait.php b/app/src/Demo/Domain/Model/Service/ServiceTrait.php new file mode 100644 index 0000000..390cc47 --- /dev/null +++ b/app/src/Demo/Domain/Model/Service/ServiceTrait.php @@ -0,0 +1,85 @@ +sanitizeValues(); + if ($dto->getId()) { + $self->id = $dto->getId(); + $self->initChangelog(); + } + + return $self; + } + + /** + * @internal use EntityTools instead + * @param ServiceDto $dto + */ + public function updateFromDto( + DataTransferObjectInterface $dto, + ForeignKeyTransformerInterface $fkTransformer + ): static { + parent::updateFromDto($dto, $fkTransformer); + + $this->sanitizeValues(); + + return $this; + } + + /** + * @internal use EntityTools instead + */ + public function toDto(int $depth = 0): ServiceDto + { + $dto = parent::toDto($depth); + return $dto + ->setId($this->getId()); + } + + /** + * @return array + */ + protected function __toArray(): array + { + return parent::__toArray() + [ + 'id' => self::getId() + ]; + } +} diff --git a/app/src/Demo/Infrastructure/Persistence/Doctrine/ServiceDoctrineRepository.php b/app/src/Demo/Infrastructure/Persistence/Doctrine/ServiceDoctrineRepository.php new file mode 100644 index 0000000..495719e --- /dev/null +++ b/app/src/Demo/Infrastructure/Persistence/Doctrine/ServiceDoctrineRepository.php @@ -0,0 +1,33 @@ + + */ +class ServiceDoctrineRepository extends DoctrineRepository implements ServiceRepository +{ + public function __construct( + ManagerRegistry $registry, + EntityPersisterInterface $entityPersisterInterface, + ) { + parent::__construct( + $registry, + Service::class, + $entityPersisterInterface + ); + } +} \ No newline at end of file From 6aeb6efb2432d59f8492b77c6a760a52350bd037 Mon Sep 17 00:00:00 2001 From: Dani Garcia Date: Tue, 6 Feb 2024 18:32:19 +0100 Subject: [PATCH 3/4] core: exposed Service entity in api --- app/config/api/raw/demo.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/config/api/raw/demo.yml b/app/config/api/raw/demo.yml index 7a47c94..27e6260 100755 --- a/app/config/api/raw/demo.yml +++ b/app/config/api/raw/demo.yml @@ -37,3 +37,7 @@ Demo\Domain\Model\Administrator\Administrator: ROLE_ADMIN: id: neq: 1 + +Demo\Domain\Model\Service\Service: ~ + + From 1b3a5f9f374d568e08afe88e7ee46f1b837f63e9 Mon Sep 17 00:00:00 2001 From: Dani Garcia Date: Tue, 6 Feb 2024 18:58:54 +0100 Subject: [PATCH 4/4] core: avoided edit/deletion of pre-configured services --- app/src/Demo/Domain/Model/Service/Service.php | 4 +- .../Service/Service/AvoidUpdateDelete.php | 40 +++++++++++++++++++ .../ServiceLifecycleEventHandlerInterface.php | 11 +++++ .../ServiceLifecycleServiceCollection.php | 32 +++++++++++++++ 4 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 app/src/Demo/Domain/Service/Service/AvoidUpdateDelete.php create mode 100644 app/src/Demo/Domain/Service/Service/ServiceLifecycleEventHandlerInterface.php create mode 100644 app/src/Demo/Domain/Service/Service/ServiceLifecycleServiceCollection.php diff --git a/app/src/Demo/Domain/Model/Service/Service.php b/app/src/Demo/Domain/Model/Service/Service.php index 413d797..60dc9f2 100644 --- a/app/src/Demo/Domain/Model/Service/Service.php +++ b/app/src/Demo/Domain/Model/Service/Service.php @@ -9,6 +9,8 @@ class Service extends ServiceAbstract implements ServiceInterface { use ServiceTrait; + const BUILTIN_SERVICES = ['Recording', 'Voicemail', 'Queues']; + /** * @codeCoverageIgnore * @return array @@ -22,7 +24,7 @@ public function getChangeSet(): array * Get id * @codeCoverageIgnore */ - public function getId(): int|string + public function getId(): null|int { return $this->id; } diff --git a/app/src/Demo/Domain/Service/Service/AvoidUpdateDelete.php b/app/src/Demo/Domain/Service/Service/AvoidUpdateDelete.php new file mode 100644 index 0000000..6dd6270 --- /dev/null +++ b/app/src/Demo/Domain/Service/Service/AvoidUpdateDelete.php @@ -0,0 +1,40 @@ + + */ + public static function getSubscribedEvents(): array + { + return [ + self::EVENT_PRE_PERSIST => self::PRE_PERSIST_PRIORITY, + self::EVENT_PRE_REMOVE => self::PRE_REMOVE_PRIORITY + ]; + } + + public function execute(ServiceInterface $service): void + { + if ($service->isNew()) { + return; + } + + $iden = $service->getInitialValue('iden'); + + if (in_array($iden, Service::BUILTIN_SERVICES)) { + $msg = $service->hasBeenDeleted() + ? 'Service ' . $iden . 'can’t be deleted' + : 'Service ' . $iden . 'can’t be edited'; + + throw new \DomainException($msg); + } + } +} \ No newline at end of file diff --git a/app/src/Demo/Domain/Service/Service/ServiceLifecycleEventHandlerInterface.php b/app/src/Demo/Domain/Service/Service/ServiceLifecycleEventHandlerInterface.php new file mode 100644 index 0000000..c3c20c7 --- /dev/null +++ b/app/src/Demo/Domain/Service/Service/ServiceLifecycleEventHandlerInterface.php @@ -0,0 +1,11 @@ + $bindedBaseServices */ + public static $bindedBaseServices = [ + "pre_persist" => + [ + \Demo\Domain\Service\Service\AvoidUpdateDelete::class => 100, + ], + "pre_remove" => + [ + \Demo\Domain\Service\Service\AvoidUpdateDelete::class => 100, + ], + ]; + + protected function addService(string $event, LifecycleEventHandlerInterface|DomainEventSubscriberInterface $service): void + { + Assertion::isInstanceOf($service, ServiceLifecycleEventHandlerInterface::class); + $this->services[$event][] = $service; + } +} \ No newline at end of file