From 27af7d6835761f399d3d4685ed6766740b699efb Mon Sep 17 00:00:00 2001 From: Bastek Bielawski Date: Tue, 3 Sep 2019 10:49:30 +0200 Subject: [PATCH 01/27] Fix multimedia ID generation --- .../src/Application/Controller/Api/MultimediaController.php | 3 +-- .../multimedia/src/Domain/Command/UploadMultimediaCommand.php | 2 +- module/multimedia/src/Domain/Entity/MultimediaId.php | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/module/multimedia/src/Application/Controller/Api/MultimediaController.php b/module/multimedia/src/Application/Controller/Api/MultimediaController.php index 75d6c5a4a..0caf8a282 100644 --- a/module/multimedia/src/Application/Controller/Api/MultimediaController.php +++ b/module/multimedia/src/Application/Controller/Api/MultimediaController.php @@ -16,7 +16,6 @@ use Ergonode\Multimedia\Domain\Command\UploadMultimediaCommand; use Ergonode\Multimedia\Domain\Entity\Multimedia; use Ergonode\Multimedia\Infrastructure\Provider\MultimediaFileProviderInterface; -use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Swagger\Annotations as SWG; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -82,7 +81,7 @@ public function uploadMultimedia(Request $request): Response $form = $this->createForm(MultimediaUploadForm::class, $uploadModel); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { - $command = new UploadMultimediaCommand('TestName', $uploadModel->upload); + $command = new UploadMultimediaCommand('Default', $uploadModel->upload); $this->messageBus->dispatch($command); $response = new CreatedResponse($command->getId()); diff --git a/module/multimedia/src/Domain/Command/UploadMultimediaCommand.php b/module/multimedia/src/Domain/Command/UploadMultimediaCommand.php index f097a71ef..6c8d4b77a 100644 --- a/module/multimedia/src/Domain/Command/UploadMultimediaCommand.php +++ b/module/multimedia/src/Domain/Command/UploadMultimediaCommand.php @@ -39,7 +39,7 @@ class UploadMultimediaCommand */ public function __construct(string $name, UploadedFile $file) { - $this->id = MultimediaId::generate(); + $this->id = MultimediaId::createFromFile($file); $this->name = $name; $this->file = $file; } diff --git a/module/multimedia/src/Domain/Entity/MultimediaId.php b/module/multimedia/src/Domain/Entity/MultimediaId.php index 47d1e92a9..d2d97af67 100644 --- a/module/multimedia/src/Domain/Entity/MultimediaId.php +++ b/module/multimedia/src/Domain/Entity/MultimediaId.php @@ -25,6 +25,6 @@ class MultimediaId extends AbstractId */ public static function createFromFile(\SplFileInfo $file): self { - return new self(Uuid::uuid5(self::NAMESPACE, file_get_contents($file->getRealPath()))->toString()); + return new self(Uuid::uuid5(self::NAMESPACE, sha1_file($file->getRealPath()))->toString()); } } From f6df4402db9fc6f751c62ec34d4a2b12471bef74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Prz=C4=99dzik?= Date: Tue, 3 Sep 2019 15:07:36 +0200 Subject: [PATCH 02/27] add segment entity and releted objects --- config/bundles.php | 1 + config/packages/nelmio_api_doc.yaml | 19 +- .../Infrastructure/Faker/SegmentCodeFaker.php | 34 ++ .../Infrastructure/Faker/SegmentIdFaker.php | 35 ++ .../Infrastructure/Process/FixtureProcess.php | 17 +- .../Resources/fixtures/develop/fixture.yaml | 1 + .../Resources/fixtures/develop/segment.yaml | 21 ++ module/segment/LICENSE.txt | 26 ++ module/segment/README.md | 19 ++ module/segment/composer.json | 31 ++ .../migrations/Version20190130104000.php | 37 +++ .../Controller/Api/SegmentController.php | 300 ++++++++++++++++++ .../SegmentGeneratorCompilerPass.php | 49 +++ .../ErgonodeSegmentExtension.php | 42 +++ .../Application/Form/CreateSegmentForm.php | 63 ++++ .../Form/Model/CreateSegmentFormModel.php | 44 +++ .../Form/Model/UpdateSegmentFormModel.php | 36 +++ .../Application/Form/UpdateSegmentForm.php | 59 ++++ .../ParamConverter/SegmentParamConverter.php | 69 ++++ .../Domain/Command/CreateSegmentCommand.php | 93 ++++++ .../Domain/Command/GenerateSegmentCommand.php | 76 +++++ .../Domain/Command/UpdateSegmentCommand.php | 76 +++++ module/segment/src/Domain/Entity/Segment.php | 227 +++++++++++++ .../segment/src/Domain/Entity/SegmentId.php | 32 ++ .../src/Domain/Event/SegmentCreatedEvent.php | 95 ++++++ .../Event/SegmentDescriptionChangedEvent.php | 18 ++ .../Domain/Event/SegmentNameChangedEvent.php | 18 ++ .../Event/SegmentSpecificationAddedEvent.php | 42 +++ .../Event/SegmentStatusChangedEvent.php | 59 ++++ .../Domain/Query/SegmentQueryInterface.php | 25 ++ .../Repository/SegmentRepositoryInterface.php | 38 +++ .../SegmentSpecificationInterface.php | 24 ++ .../src/Domain/ValueObject/SegmentCode.php | 58 ++++ .../src/Domain/ValueObject/SegmentStatus.php | 112 +++++++ module/segment/src/ErgonodeSegmentBundle.php | 30 ++ .../Exception/SegmentException.php | 16 + .../SegmentGeneratorProviderException.php | 16 + .../Generator/SegmentGeneratorInterface.php | 31 ++ .../src/Infrastructure/Grid/SegmentGrid.php | 64 ++++ .../Handler/CreateSegmentCommandHandler.php | 49 +++ .../Handler/GenerateSegmentCommandHandler.php | 54 ++++ .../Handler/UpdateSegmentCommandHandler.php | 49 +++ .../Serializer/Handler/SegmentCodeHandler.php | 75 +++++ .../Serializer/Handler/SegmentIdHandler.php | 75 +++++ .../Handler/SegmentStatusHandler.php | 75 +++++ .../Provider/SegmentGeneratorProvider.php | 49 +++ .../Provider/SegmentProvider.php | 61 ++++ .../Specification/AbstractSpecification.php | 25 ++ .../AttributeExistsSpecification.php | 45 +++ .../AttributeValueSpecification.php | 62 ++++ .../SegmentCreatedEventProjector.php | 90 ++++++ .../Dbal/Query/DbalSegmentQuery.php | 71 +++++ .../Dbal/Repository/DbalSegmentRepository.php | 93 ++++++ .../segment/src/Resources/config/routes.yml | 4 + .../segment/src/Resources/config/services.yml | 27 ++ .../Command/CreateSegmentCommandTest.php | 39 +++ .../Command/UpdateSegmentCommandTest.php | 38 +++ .../tests/Domain/Entity/SegmentTest.php | 98 ++++++ .../Domain/Event/SegmentCreatedEventTest.php | 42 +++ .../SegmentDescriptionChangedEventTest.php | 26 ++ .../Event/SegmentNameChangedEventTest.php | 26 ++ .../Event/SegmentStatusChangedEventTest.php | 25 ++ .../Domain/ValueObject/SegmentStatusTest.php | 90 ++++++ .../Handler/SegmentCodeHandlerTest.php | 77 +++++ .../Handler/SegmentIdHandlerTest.php | 78 +++++ .../Handler/SegmentStatusHandlerTest.php | 77 +++++ .../Workflow/CreateWorkflowCommand.php | 1 - 67 files changed, 3568 insertions(+), 6 deletions(-) create mode 100644 module/fixture/src/Infrastructure/Faker/SegmentCodeFaker.php create mode 100644 module/fixture/src/Infrastructure/Faker/SegmentIdFaker.php create mode 100644 module/fixture/src/Resources/fixtures/develop/segment.yaml create mode 100644 module/segment/LICENSE.txt create mode 100644 module/segment/README.md create mode 100644 module/segment/composer.json create mode 100644 module/segment/migrations/Version20190130104000.php create mode 100644 module/segment/src/Application/Controller/Api/SegmentController.php create mode 100644 module/segment/src/Application/DependencyInjection/CompilerPass/SegmentGeneratorCompilerPass.php create mode 100644 module/segment/src/Application/DependencyInjection/ErgonodeSegmentExtension.php create mode 100644 module/segment/src/Application/Form/CreateSegmentForm.php create mode 100644 module/segment/src/Application/Form/Model/CreateSegmentFormModel.php create mode 100644 module/segment/src/Application/Form/Model/UpdateSegmentFormModel.php create mode 100644 module/segment/src/Application/Form/UpdateSegmentForm.php create mode 100644 module/segment/src/Application/Request/ParamConverter/SegmentParamConverter.php create mode 100644 module/segment/src/Domain/Command/CreateSegmentCommand.php create mode 100644 module/segment/src/Domain/Command/GenerateSegmentCommand.php create mode 100644 module/segment/src/Domain/Command/UpdateSegmentCommand.php create mode 100644 module/segment/src/Domain/Entity/Segment.php create mode 100644 module/segment/src/Domain/Entity/SegmentId.php create mode 100644 module/segment/src/Domain/Event/SegmentCreatedEvent.php create mode 100644 module/segment/src/Domain/Event/SegmentDescriptionChangedEvent.php create mode 100644 module/segment/src/Domain/Event/SegmentNameChangedEvent.php create mode 100644 module/segment/src/Domain/Event/SegmentSpecificationAddedEvent.php create mode 100644 module/segment/src/Domain/Event/SegmentStatusChangedEvent.php create mode 100644 module/segment/src/Domain/Query/SegmentQueryInterface.php create mode 100644 module/segment/src/Domain/Repository/SegmentRepositoryInterface.php create mode 100644 module/segment/src/Domain/Specification/SegmentSpecificationInterface.php create mode 100644 module/segment/src/Domain/ValueObject/SegmentCode.php create mode 100644 module/segment/src/Domain/ValueObject/SegmentStatus.php create mode 100644 module/segment/src/ErgonodeSegmentBundle.php create mode 100644 module/segment/src/Infrastructure/Exception/SegmentException.php create mode 100644 module/segment/src/Infrastructure/Exception/SegmentGeneratorProviderException.php create mode 100644 module/segment/src/Infrastructure/Generator/SegmentGeneratorInterface.php create mode 100644 module/segment/src/Infrastructure/Grid/SegmentGrid.php create mode 100644 module/segment/src/Infrastructure/Handler/CreateSegmentCommandHandler.php create mode 100644 module/segment/src/Infrastructure/Handler/GenerateSegmentCommandHandler.php create mode 100644 module/segment/src/Infrastructure/Handler/UpdateSegmentCommandHandler.php create mode 100644 module/segment/src/Infrastructure/JMS/Serializer/Handler/SegmentCodeHandler.php create mode 100644 module/segment/src/Infrastructure/JMS/Serializer/Handler/SegmentIdHandler.php create mode 100644 module/segment/src/Infrastructure/JMS/Serializer/Handler/SegmentStatusHandler.php create mode 100644 module/segment/src/Infrastructure/Provider/SegmentGeneratorProvider.php create mode 100644 module/segment/src/Infrastructure/Provider/SegmentProvider.php create mode 100644 module/segment/src/Infrastructure/Specification/AbstractSpecification.php create mode 100644 module/segment/src/Infrastructure/Specification/AttributeExistsSpecification.php create mode 100644 module/segment/src/Infrastructure/Specification/AttributeValueSpecification.php create mode 100644 module/segment/src/Persistence/Dbal/Projector/SegmentCreatedEventProjector.php create mode 100644 module/segment/src/Persistence/Dbal/Query/DbalSegmentQuery.php create mode 100644 module/segment/src/Persistence/Dbal/Repository/DbalSegmentRepository.php create mode 100644 module/segment/src/Resources/config/routes.yml create mode 100644 module/segment/src/Resources/config/services.yml create mode 100644 module/segment/tests/Domain/Command/CreateSegmentCommandTest.php create mode 100644 module/segment/tests/Domain/Command/UpdateSegmentCommandTest.php create mode 100644 module/segment/tests/Domain/Entity/SegmentTest.php create mode 100644 module/segment/tests/Domain/Event/SegmentCreatedEventTest.php create mode 100644 module/segment/tests/Domain/Event/SegmentDescriptionChangedEventTest.php create mode 100644 module/segment/tests/Domain/Event/SegmentNameChangedEventTest.php create mode 100644 module/segment/tests/Domain/Event/SegmentStatusChangedEventTest.php create mode 100644 module/segment/tests/Domain/ValueObject/SegmentStatusTest.php create mode 100644 module/segment/tests/Infrastructure/JMS/Serializer/Handler/SegmentCodeHandlerTest.php create mode 100644 module/segment/tests/Infrastructure/JMS/Serializer/Handler/SegmentIdHandlerTest.php create mode 100644 module/segment/tests/Infrastructure/JMS/Serializer/Handler/SegmentStatusHandlerTest.php diff --git a/config/bundles.php b/config/bundles.php index b877aa6b7..c4e3784b8 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -25,6 +25,7 @@ Ergonode\Importer\ErgonodeImporterBundle::class => ['all' => true], Ergonode\Reader\ErgonodeReaderBundle::class => ['all' => true], Ergonode\Transformer\ErgonodeTransformerBundle::class => ['all' => true], + Ergonode\Segment\ErgonodeSegmentBundle::class => ['all' => true], Ergonode\Category\ErgonodeCategoryBundle::class => ['all' => true], Ergonode\CategoryTree\ErgonodeCategoryTreeBundle::class => ['all' => true], Ergonode\Product\ErgonodeProductBundle::class => ['all' => true], diff --git a/config/packages/nelmio_api_doc.yaml b/config/packages/nelmio_api_doc.yaml index 2480c8182..82de77722 100644 --- a/config/packages/nelmio_api_doc.yaml +++ b/config/packages/nelmio_api_doc.yaml @@ -274,7 +274,7 @@ nelmio_api_doc: $ref: "#/definitions/translation" example: key: key_1 - translation: + value: PL: Option PL 1 EN: Option EN 1 attribute_parameters: @@ -288,11 +288,22 @@ nelmio_api_doc: segment: type: object properties: - name: + code: type: string required: true - description: segment name - example: segment name + description: Segment unique code value + name: + $ref: "#/definitions/translation" + description: + $ref: "#/definitions/translation" + example: + code: code_1 + name: + PL: Name PL 1 + EN: Name EN 1 + description: + PL: Description PL 1 + EN: Description EN 1 languages_req: type: object properties: diff --git a/module/fixture/src/Infrastructure/Faker/SegmentCodeFaker.php b/module/fixture/src/Infrastructure/Faker/SegmentCodeFaker.php new file mode 100644 index 000000000..a0f163c52 --- /dev/null +++ b/module/fixture/src/Infrastructure/Faker/SegmentCodeFaker.php @@ -0,0 +1,34 @@ +loader = $loader; $this->generator = $generator; $this->manager = $manager; + $this->connection = $connection; } /** * @param string|null $group * * @throws FixtureException + * @throws ConnectionException */ public function process(?string $group = null): void { try { + $this->connection->beginTransaction(); $files = $this->loader->load($group); $loader = new NativeLoader($this->generator); @@ -65,7 +78,9 @@ public function process(?string $group = null): void $this->manager->persist($object); } } + $this->connection->commit(); } catch (\Throwable $exception) { + $this->connection->rollBack(); throw new FixtureException('Cant process fixtures', 0, $exception); } } diff --git a/module/fixture/src/Resources/fixtures/develop/fixture.yaml b/module/fixture/src/Resources/fixtures/develop/fixture.yaml index dc09520ef..a3099b59c 100644 --- a/module/fixture/src/Resources/fixtures/develop/fixture.yaml +++ b/module/fixture/src/Resources/fixtures/develop/fixture.yaml @@ -8,3 +8,4 @@ include: - 'templates.yaml' - 'categories.yaml' - 'products.yaml' + - 'segment.yaml' diff --git a/module/fixture/src/Resources/fixtures/develop/segment.yaml b/module/fixture/src/Resources/fixtures/develop/segment.yaml new file mode 100644 index 000000000..1096aa877 --- /dev/null +++ b/module/fixture/src/Resources/fixtures/develop/segment.yaml @@ -0,0 +1,21 @@ +Ergonode\Core\Domain\ValueObject\TranslatableString: + segment_name: + __construct: + - + 'PL': 'Polish_segment_name' + 'EN': 'English_segment_name' + 'ZH': 'Chinese_segment_name' + segment_description: + __construct: + - + 'PL': 'Polish_segment_name' + 'EN': 'English_segment_name' + 'ZH': 'Chinese_segment_name' + +Ergonode\Segment\Domain\Entity\Segment: + test_segment: + __construct: + - '' + - '' + - '@segment_name' + - '@segment_description' diff --git a/module/segment/LICENSE.txt b/module/segment/LICENSE.txt new file mode 100644 index 000000000..996dcc641 --- /dev/null +++ b/module/segment/LICENSE.txt @@ -0,0 +1,26 @@ +Open Software License ("OSL") v. 3.0 + +This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: +Licensed under the Open Software License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + a) to reproduce the Original Work in copies, either alone or as part of a collective work; + b) to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + c) to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; + d) to perform the Original Work publicly; and + e) to display the Original Work publicly. + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/module/segment/README.md b/module/segment/README.md new file mode 100644 index 000000000..b44db384c --- /dev/null +++ b/module/segment/README.md @@ -0,0 +1,19 @@ +# Ergonode - Segment + +## Documentation + +* Follow link to [**Ergonode Documentation**](https://docs.ergonode.com), + +## Community + +* Get Ergonode support on **Stack Overflow**, [**Slack**](https://ergonode.slack.com) and [**email**](team@ergonode.com). +* Follow us on [**GitHub**](https://github.com/ergonode), [**Twitter**](https://twitter.com/ergonode) and [**Facebook**](https://www.facebook.com/ergonode), + +## Contributing + +Ergonode is a Open Source. Join us as a [**contributor**](https://ergonode.com/contribution). + +## About Us + +Ergonode development is sponsored by Bold Brand Commerce Sp. z o.o., lead by **Eronode Core Team** and supported by Ergonode contributors. + diff --git a/module/segment/composer.json b/module/segment/composer.json new file mode 100644 index 000000000..3e4cc5a62 --- /dev/null +++ b/module/segment/composer.json @@ -0,0 +1,31 @@ +{ + "name": "ergonode/segment", + "type": "ergonode-module", + "description": "Ergonode - Segment", + "homepage": "https://ergonode.com", + "license": "OSL-3.0", + "require": { + "php": "^7.2", + "doctrine/dbal": "^2.9", + "ergonode/attribute": "^0.3.0", + "ergonode/product": "^0.3.0", + "ergonode/core": "^0.3.0", + "ergonode/es": "^0.3.0", + "ergonode/grid": "^0.3.0", + "ergonode/value": "^0.3.0", + "friendsofsymfony/rest-bundle": "^2.5", + "jms/serializer": "^3.1", + "nelmio/api-doc-bundle": "^3.4", + "ramsey/uuid": "^3.8", + "sensio/framework-extra-bundle": "^5.4", + "symfony/form": "^4.3", + "symfony/messenger": "^4.3", + "symfony/translation": "^4.3", + "symfony/validator": "^4.3" + }, + "autoload": { + "psr-4": { + "Ergonode\\Segment\\": "src/" + } + } +} diff --git a/module/segment/migrations/Version20190130104000.php b/module/segment/migrations/Version20190130104000.php new file mode 100644 index 000000000..2259ed105 --- /dev/null +++ b/module/segment/migrations/Version20190130104000.php @@ -0,0 +1,37 @@ +addSql( + 'CREATE TABLE segment ( + id UUID NOT NULL, + code VARCHAR(100) NOT NULL, + name JSON NOT NULL, + description JSON NOT NULL, + status VARCHAR(32) NOT NULL, + PRIMARY KEY(id) + )' + ); + } +} diff --git a/module/segment/src/Application/Controller/Api/SegmentController.php b/module/segment/src/Application/Controller/Api/SegmentController.php new file mode 100644 index 000000000..b4133b797 --- /dev/null +++ b/module/segment/src/Application/Controller/Api/SegmentController.php @@ -0,0 +1,300 @@ +grid = $grid; + $this->query = $query; + $this->messageBus = $messageBus; + } + + /** + * @Route("segments", methods={"GET"}) + * + * @SWG\Tag(name="Segment") + * @SWG\Parameter( + * name="language", + * in="path", + * type="string", + * required=true, + * default="EN", + * description="Language Code", + * ) + * @SWG\Parameter( + * name="limit", + * in="query", + * type="integer", + * required=true, + * default="50", + * description="Number of returned lines", + * ) + * @SWG\Parameter( + * name="offset", + * in="query", + * type="integer", + * required=true, + * default="0", + * description="Number of start line", + * ) + * @SWG\Parameter( + * name="field", + * in="query", + * required=false, + * type="string", + * description="Order field", + * ) + * @SWG\Parameter( + * name="order", + * in="query", + * required=false, + * type="string", + * enum={"ASC","DESC"}, + * description="Order", + * ) + * @SWG\Parameter( + * name="filter", + * in="query", + * required=false, + * type="string", + * description="Filter" + * ) + * @SWG\Parameter( + * name="show", + * in="query", + * required=false, + * type="string", + * enum={"COLUMN","DATA"}, + * description="Specify what response should containts" + * ) + * @SWG\Response( + * response=200, + * description="Returns imported data collection", + * ) + * + * @ParamConverter(class="Ergonode\Grid\RequestGridConfiguration") + * + * @param Language $language + * @param RequestGridConfiguration $configuration + * + * @return Response + */ + public function getSegments(Language $language, RequestGridConfiguration $configuration): Response + { + return new GridResponse($this->grid, $configuration, $this->query->getDataSet($language), $language); + } + + /** + * @Route("/segments/{segment}", methods={"GET"}, requirements={"segment" = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"}) + * + * @SWG\Tag(name="Segment") + * @SWG\Parameter( + * name="language", + * in="path", + * type="string", + * description="Language code", + * default="EN" + * ) + * @SWG\Parameter( + * name="segment", + * in="path", + * type="string", + * description="Segment id", + * ) + * @SWG\Response( + * response=200, + * description="Returns segment", + * ) + * @SWG\Response( + * response=404, + * description="Not found", + * ) + * + * @ParamConverter(class="Ergonode\Segment\Domain\Entity\Segment") + * + * @param Segment $segment + * + * @return Response + */ + public function getSegment(Segment $segment): Response + { + return new SuccessResponse($segment); + } + + /** + * @Route("/segments", methods={"POST"}) + * + * @SWG\Tag(name="Segment") + * @SWG\Parameter( + * name="language", + * in="path", + * type="string", + * description="Language code", + * default="EN" + * ) + * @SWG\Parameter( + * name="body", + * in="body", + * description="Add segment", + * required=true, + * @SWG\Schema(ref="#/definitions/segment") + * ) + * @SWG\Response( + * response=200, + * description="Returns segment", + * ) + * @SWG\Response( + * response=404, + * description="Not found", + * ) + * + * @param Request $request + * + * @return Response + * + * @throws \Exception + */ + public function createSegment(Request $request): Response + { + $form = $this->createForm(CreateSegmentForm::class); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + /** @var CreateSegmentFormModel $data */ + $data = $form->getData(); + + $command = new CreateSegmentCommand( + new SegmentCode($data->code), + new TranslatableString($data->name), + new TranslatableString($data->description) + ); + $this->messageBus->dispatch($command); + + return new CreatedResponse($command->getId()); + } + + throw new FormValidationHttpException($form); + } + + /** + * @Route("/segments/{segment}", methods={"PUT"}) + * + * @SWG\Tag(name="Segment") + * @SWG\Parameter( + * name="segment", + * in="path", + * type="string", + * description="Segment id", + * ) + * @SWG\Parameter( + * name="language", + * in="path", + * type="string", + * description="Language code", + * default="EN" + * ) + * @SWG\Parameter( + * name="body", + * in="body", + * description="Add segment", + * required=true, + * @SWG\Schema(ref="#/definitions/segment") + * ) + * @SWG\Response( + * response=200, + * description="Returns segment", + * ) + * @SWG\Response( + * response=404, + * description="Not found", + * ) + * + * @ParamConverter(class="Ergonode\Segment\Domain\Entity\Segment") + * + * @param Segment $segment + * @param Request $request + * + * @return Response + */ + public function updateSegment(Segment $segment, Request $request): Response + { + $model = new UpdateSegmentFormModel(); + $form = $this->createForm(UpdateSegmentForm::class, $model, ['method' => Request::METHOD_PUT]); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + /** @var CreateSegmentFormModel $data */ + $data = $form->getData(); + + $command = new UpdateSegmentCommand( + $segment->getId(), + new TranslatableString($data->name), + new TranslatableString($data->description) + ); + $this->messageBus->dispatch($command); + + return new CreatedResponse($command->getId()); + } + + throw new FormValidationHttpException($form); + } +} diff --git a/module/segment/src/Application/DependencyInjection/CompilerPass/SegmentGeneratorCompilerPass.php b/module/segment/src/Application/DependencyInjection/CompilerPass/SegmentGeneratorCompilerPass.php new file mode 100644 index 000000000..be3c830ce --- /dev/null +++ b/module/segment/src/Application/DependencyInjection/CompilerPass/SegmentGeneratorCompilerPass.php @@ -0,0 +1,49 @@ +has(SegmentGeneratorProvider::class)) { + $this->processTransformers($container); + } + } + + /** + * @param ContainerBuilder $container + */ + private function processTransformers(ContainerBuilder $container): void + { + $arguments = []; + $definition = $container->findDefinition(SegmentGeneratorProvider::class); + $strategies = $container->findTaggedServiceIds(self::TAG); + + foreach ($strategies as $id => $strategy) { + $arguments[] = new Reference($id); + } + + $definition->setArguments($arguments); + } +} diff --git a/module/segment/src/Application/DependencyInjection/ErgonodeSegmentExtension.php b/module/segment/src/Application/DependencyInjection/ErgonodeSegmentExtension.php new file mode 100644 index 000000000..2ce2769f5 --- /dev/null +++ b/module/segment/src/Application/DependencyInjection/ErgonodeSegmentExtension.php @@ -0,0 +1,42 @@ +registerForAutoconfiguration(SegmentGeneratorInterface::class) + ->addTag(SegmentGeneratorCompilerPass::TAG); + + $loader->load('services.yml'); + } +} diff --git a/module/segment/src/Application/Form/CreateSegmentForm.php b/module/segment/src/Application/Form/CreateSegmentForm.php new file mode 100644 index 000000000..9746f0a3e --- /dev/null +++ b/module/segment/src/Application/Form/CreateSegmentForm.php @@ -0,0 +1,63 @@ +add( + 'code', + TextType::class + ) + ->add( + 'name', + TranslationType::class + ) + ->add( + 'description', + TranslationType::class + ); + } + + /** + * @param OptionsResolver $resolver + */ + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => CreateSegmentFormModel::class, + 'translation_domain' => 'segment', + 'empty_data' => new CreateSegmentFormModel(), + ]); + } + + /** + * @return null|string + */ + public function getBlockPrefix(): ?string + { + return null; + } +} diff --git a/module/segment/src/Application/Form/Model/CreateSegmentFormModel.php b/module/segment/src/Application/Form/Model/CreateSegmentFormModel.php new file mode 100644 index 000000000..94071e819 --- /dev/null +++ b/module/segment/src/Application/Form/Model/CreateSegmentFormModel.php @@ -0,0 +1,44 @@ +name = []; + $this->description = []; + } +} diff --git a/module/segment/src/Application/Form/Model/UpdateSegmentFormModel.php b/module/segment/src/Application/Form/Model/UpdateSegmentFormModel.php new file mode 100644 index 000000000..afc05c60a --- /dev/null +++ b/module/segment/src/Application/Form/Model/UpdateSegmentFormModel.php @@ -0,0 +1,36 @@ +name = []; + $this->description = []; + } +} diff --git a/module/segment/src/Application/Form/UpdateSegmentForm.php b/module/segment/src/Application/Form/UpdateSegmentForm.php new file mode 100644 index 000000000..a8647d3b2 --- /dev/null +++ b/module/segment/src/Application/Form/UpdateSegmentForm.php @@ -0,0 +1,59 @@ +add( + 'name', + TranslationType::class + ) + ->add( + 'description', + TranslationType::class + ); + } + + /** + * @param OptionsResolver $resolver + */ + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => UpdateSegmentFormModel::class, + 'translation_domain' => 'segment', + ]); + } + + /** + * @return null|string + */ + public function getBlockPrefix(): ?string + { + return null; + } +} diff --git a/module/segment/src/Application/Request/ParamConverter/SegmentParamConverter.php b/module/segment/src/Application/Request/ParamConverter/SegmentParamConverter.php new file mode 100644 index 000000000..62ddf14e1 --- /dev/null +++ b/module/segment/src/Application/Request/ParamConverter/SegmentParamConverter.php @@ -0,0 +1,69 @@ +repository = $repository; + } + + /** + * {@inheritDoc} + */ + public function apply(Request $request, ParamConverter $configuration): void + { + $parameter = $request->get('segment'); + + if (null === $parameter) { + throw new BadRequestHttpException('Route parameter "segment" is missing'); + } + + if (!SegmentId::isValid($parameter)) { + throw new BadRequestHttpException('Invalid segment ID format'); + } + + $entity = $this->repository->load(new SegmentId($parameter)); + + if (null === $entity) { + throw new NotFoundHttpException(sprintf('Segment by ID "%s" not found', $parameter)); + } + + $request->attributes->set($configuration->getName(), $entity); + } + + /** + * {@inheritDoc} + */ + public function supports(ParamConverter $configuration): bool + { + return Segment::class === $configuration->getClass(); + } +} diff --git a/module/segment/src/Domain/Command/CreateSegmentCommand.php b/module/segment/src/Domain/Command/CreateSegmentCommand.php new file mode 100644 index 000000000..bbf4a5167 --- /dev/null +++ b/module/segment/src/Domain/Command/CreateSegmentCommand.php @@ -0,0 +1,93 @@ +id = SegmentId::fromCode($code); + $this->code = $code; + $this->name = $name; + $this->description = $description; + } + + /** + * @return SegmentId + */ + public function getId(): SegmentId + { + return $this->id; + } + + /** + * @return SegmentCode + */ + public function getCode(): SegmentCode + { + return $this->code; + } + + /** + * @return TranslatableString + */ + public function getName(): TranslatableString + { + return $this->name; + } + + /** + * @return TranslatableString + */ + public function getDescription(): TranslatableString + { + return $this->description; + } +} diff --git a/module/segment/src/Domain/Command/GenerateSegmentCommand.php b/module/segment/src/Domain/Command/GenerateSegmentCommand.php new file mode 100644 index 000000000..9c95eea6a --- /dev/null +++ b/module/segment/src/Domain/Command/GenerateSegmentCommand.php @@ -0,0 +1,76 @@ +id = SegmentId::generate(); + $this->code = $code; + $this->type = $type; + } + + /** + * @return SegmentId + */ + public function getId(): SegmentId + { + return $this->id; + } + + /** + * @return string + */ + public function getCode(): string + { + return $this->code; + } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } +} diff --git a/module/segment/src/Domain/Command/UpdateSegmentCommand.php b/module/segment/src/Domain/Command/UpdateSegmentCommand.php new file mode 100644 index 000000000..9e8816f62 --- /dev/null +++ b/module/segment/src/Domain/Command/UpdateSegmentCommand.php @@ -0,0 +1,76 @@ +id = $id; + $this->name = $name; + $this->description = $description; + } + + /** + * @return SegmentId + */ + public function getId(): SegmentId + { + return $this->id; + } + + /** + * @return TranslatableString + */ + public function getName(): TranslatableString + { + return $this->name; + } + + /** + * @return TranslatableString + */ + public function getDescription(): TranslatableString + { + return $this->description; + } +} diff --git a/module/segment/src/Domain/Entity/Segment.php b/module/segment/src/Domain/Entity/Segment.php new file mode 100644 index 000000000..d59b68821 --- /dev/null +++ b/module/segment/src/Domain/Entity/Segment.php @@ -0,0 +1,227 @@ +apply(new SegmentCreatedEvent($id, $code, $name, $description)); + } + + /** + * @return SegmentId|AbstractId + */ + public function getId(): AbstractId + { + return $this->id; + } + + /** + * @return SegmentCode + */ + public function getCode(): SegmentCode + { + return $this->code; + } + + /** + * @return TranslatableString + */ + public function getName(): TranslatableString + { + return $this->name; + } + + /** + * @return TranslatableString + */ + public function getDescription(): TranslatableString + { + return $this->description; + } + + /** + * @return SegmentStatus + */ + public function getStatus(): SegmentStatus + { + return $this->status; + } + + /** + * @param SegmentStatus $status + * + * @throws \Exception + */ + public function changeStatus(SegmentStatus $status): void + { + if (!$status->isEqual($this->status)) { + $this->apply(new SegmentStatusChangedEvent($this->status, $status)); + } + } + + /** + * @param TranslatableString $name + * + * @throws \Exception + */ + public function changeName(TranslatableString $name): void + { + if (!$name->isEqual($this->name)) { + $this->apply(new SegmentNameChangedEvent($this->name, $name)); + } + } + + /** + * @param TranslatableString $description + * + * @throws \Exception + */ + public function changeDescription(TranslatableString $description): void + { + if (!$description->isEqual($this->description)) { + $this->apply(new SegmentDescriptionChangedEvent($this->description, $description)); + } + } + + /** + * @param SegmentSpecificationInterface $specification + * + * @throws \Exception + */ + public function addSpecification(SegmentSpecificationInterface $specification): void + { + $this->apply(new SegmentSpecificationAddedEvent($specification)); + } + + /** + * @return SegmentSpecificationInterface[] + */ + public function getSpecifications(): array + { + return $this->specifications; + } + + /** + * @param SegmentCreatedEvent $event + */ + protected function applySegmentCreatedEvent(SegmentCreatedEvent $event): void + { + $this->id = $event->getId(); + $this->code = $event->getCode(); + $this->name = $event->getName(); + $this->description = $event->getDescription(); + $this->status = new SegmentStatus(); + $this->specifications = []; + } + + /** + * @param SegmentSpecificationAddedEvent $event + */ + protected function applySegmentSpecificationAddedEvent(SegmentSpecificationAddedEvent $event): void + { + $this->specifications[] = $event->getSpecification(); + } + + /** + * @param SegmentStatusChangedEvent $event + */ + protected function applySegmentStatusChangedEvent(SegmentStatusChangedEvent $event): void + { + $this->status = $event->getTo(); + } + + /** + * @param SegmentNameChangedEvent $event + */ + protected function applySegmentNameChangedEvent(SegmentNameChangedEvent $event): void + { + $this->name = $event->getTo(); + } + + /** + * @param SegmentDescriptionChangedEvent $event + */ + protected function applySegmentDescriptionChangedEvent(SegmentDescriptionChangedEvent $event): void + { + $this->description = $event->getTo(); + } +} diff --git a/module/segment/src/Domain/Entity/SegmentId.php b/module/segment/src/Domain/Entity/SegmentId.php new file mode 100644 index 000000000..6275b9147 --- /dev/null +++ b/module/segment/src/Domain/Entity/SegmentId.php @@ -0,0 +1,32 @@ +getValue())->toString()); + } +} diff --git a/module/segment/src/Domain/Event/SegmentCreatedEvent.php b/module/segment/src/Domain/Event/SegmentCreatedEvent.php new file mode 100644 index 000000000..4c5407c72 --- /dev/null +++ b/module/segment/src/Domain/Event/SegmentCreatedEvent.php @@ -0,0 +1,95 @@ +id = $id; + $this->code = $code; + $this->name = $name; + $this->description = $description; + } + + /** + * @return SegmentId + */ + public function getId(): SegmentId + { + return $this->id; + } + + /** + * @return SegmentCode + */ + public function getCode(): SegmentCode + { + return $this->code; + } + + /** + * @return TranslatableString + */ + public function getName(): TranslatableString + { + return $this->name; + } + + /** + * @return TranslatableString + */ + public function getDescription(): TranslatableString + { + return $this->description; + } +} diff --git a/module/segment/src/Domain/Event/SegmentDescriptionChangedEvent.php b/module/segment/src/Domain/Event/SegmentDescriptionChangedEvent.php new file mode 100644 index 000000000..87ced9a33 --- /dev/null +++ b/module/segment/src/Domain/Event/SegmentDescriptionChangedEvent.php @@ -0,0 +1,18 @@ +specification = $specification; + } + + /** + * @return SegmentSpecificationInterface + */ + public function getSpecification(): SegmentSpecificationInterface + { + return $this->specification; + } +} diff --git a/module/segment/src/Domain/Event/SegmentStatusChangedEvent.php b/module/segment/src/Domain/Event/SegmentStatusChangedEvent.php new file mode 100644 index 000000000..7efbbfd1e --- /dev/null +++ b/module/segment/src/Domain/Event/SegmentStatusChangedEvent.php @@ -0,0 +1,59 @@ +from = $from; + $this->to = $to; + } + + /** + * @return SegmentStatus + */ + public function getFrom(): SegmentStatus + { + return $this->from; + } + + /** + * @return SegmentStatus + */ + public function getTo(): SegmentStatus + { + return $this->to; + } +} diff --git a/module/segment/src/Domain/Query/SegmentQueryInterface.php b/module/segment/src/Domain/Query/SegmentQueryInterface.php new file mode 100644 index 000000000..f6232dfd2 --- /dev/null +++ b/module/segment/src/Domain/Query/SegmentQueryInterface.php @@ -0,0 +1,25 @@ +value = $value; + } + + /** + * @return string + */ + public function getValue(): string + { + return $this->value; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->value; + } + + /** + * @param string $value + * + * @return bool + */ + public static function isValid(string $value): bool + { + return strlen($value) <= 100; + } +} diff --git a/module/segment/src/Domain/ValueObject/SegmentStatus.php b/module/segment/src/Domain/ValueObject/SegmentStatus.php new file mode 100644 index 000000000..6fe887a81 --- /dev/null +++ b/module/segment/src/Domain/ValueObject/SegmentStatus.php @@ -0,0 +1,112 @@ +value = $value; + } + + /** + * @param string $value + * + * @return bool + */ + public static function isValid(string $value): bool + { + return in_array(strtoupper($value), self::AVAILABLE, true); + } + + /** + * @return bool + */ + public function isNew(): bool + { + return self::NEW === $this->value; + } + + /** + * @return bool + */ + public function isProcessed(): bool + { + return self::PROCESSED === $this->value; + } + + /** + * @return bool + */ + public function isCalculated(): bool + { + return self::CALCULATED === $this->value; + } + + /** + * @return bool + */ + public function isOutdated(): bool + { + return self::OUTDATED === $this->value; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->value; + } + + /** + * @param SegmentStatus $status + * + * @return bool + */ + public function isEqual(self $status): bool + { + return (string) $status === $this->value; + } +} diff --git a/module/segment/src/ErgonodeSegmentBundle.php b/module/segment/src/ErgonodeSegmentBundle.php new file mode 100644 index 000000000..060a43676 --- /dev/null +++ b/module/segment/src/ErgonodeSegmentBundle.php @@ -0,0 +1,30 @@ +addCompilerPass(new SegmentGeneratorCompilerPass()); + } +} diff --git a/module/segment/src/Infrastructure/Exception/SegmentException.php b/module/segment/src/Infrastructure/Exception/SegmentException.php new file mode 100644 index 000000000..4e2866179 --- /dev/null +++ b/module/segment/src/Infrastructure/Exception/SegmentException.php @@ -0,0 +1,16 @@ +translator = $translator; + } + + /** + * @param GridConfigurationInterface $configuration + * @param Language $language + */ + public function init(GridConfigurationInterface $configuration, Language $language): void + { + $filters = $configuration->getFilters(); + + $id = new TextColumn('id', $this->trans('Id'), new TextFilter()); + $id->setVisible(false); + $this->addColumn('id', $id); + $this->addColumn('code', new TextColumn('name', $this->trans('Code'), new TextFilter($filters->getString('code')))); + $this->addColumn('name', new TextColumn('name', $this->trans('Name'), new TextFilter($filters->getString('name')))); + $this->addColumn('status', new TextColumn('status', $this->trans('Status'), new TextFilter($filters->getString('status')))); + $this->addColumn('description', new TextColumn('description', $this->trans('Description'), new TextFilter($filters->getString('description')))); + $this->orderBy('id', 'DESC'); + } + + /** + * @param string $id + * @param array $parameters + * + * @return string + */ + private function trans(string $id, array $parameters = []): string + { + return $this->translator->trans($id, $parameters, 'grid'); + } +} diff --git a/module/segment/src/Infrastructure/Handler/CreateSegmentCommandHandler.php b/module/segment/src/Infrastructure/Handler/CreateSegmentCommandHandler.php new file mode 100644 index 000000000..7ec17682e --- /dev/null +++ b/module/segment/src/Infrastructure/Handler/CreateSegmentCommandHandler.php @@ -0,0 +1,49 @@ +repository = $repository; + } + + /** + * @param CreateSegmentCommand $command + * + * @throws \Exception + */ + public function __invoke(CreateSegmentCommand $command) + { + $segment = new Segment( + $command->getId(), + $command->getCode(), + $command->getName(), + $command->getDescription() + ); + + $this->repository->save($segment); + } +} diff --git a/module/segment/src/Infrastructure/Handler/GenerateSegmentCommandHandler.php b/module/segment/src/Infrastructure/Handler/GenerateSegmentCommandHandler.php new file mode 100644 index 000000000..9c4be6f77 --- /dev/null +++ b/module/segment/src/Infrastructure/Handler/GenerateSegmentCommandHandler.php @@ -0,0 +1,54 @@ +repository = $repository; + $this->generator = $generator; + } + + /** + * @param GenerateSegmentCommand $command + * + * @throws SegmentGeneratorProviderException + */ + public function __invoke(GenerateSegmentCommand $command) + { + $generator = $this->generator->provide($command->getType()); + + $segment = $generator->generate($command->getId(), $command->getCode()); + + $this->repository->save($segment); + } +} diff --git a/module/segment/src/Infrastructure/Handler/UpdateSegmentCommandHandler.php b/module/segment/src/Infrastructure/Handler/UpdateSegmentCommandHandler.php new file mode 100644 index 000000000..e09844aba --- /dev/null +++ b/module/segment/src/Infrastructure/Handler/UpdateSegmentCommandHandler.php @@ -0,0 +1,49 @@ +repository = $repository; + } + + /** + * @param UpdateSegmentCommand $command + * + * @throws \Exception + */ + public function __invoke(UpdateSegmentCommand $command) + { + $segment = $this->repository->load($command->getId()); + + Assert::notNull($segment); + + $segment->changeName($command->getName()); + $segment->changeDescription($command->getDescription()); + + $this->repository->save($segment); + } +} diff --git a/module/segment/src/Infrastructure/JMS/Serializer/Handler/SegmentCodeHandler.php b/module/segment/src/Infrastructure/JMS/Serializer/Handler/SegmentCodeHandler.php new file mode 100644 index 000000000..666519b78 --- /dev/null +++ b/module/segment/src/Infrastructure/JMS/Serializer/Handler/SegmentCodeHandler.php @@ -0,0 +1,75 @@ + GraphNavigatorInterface::DIRECTION_SERIALIZATION, + 'type' => SegmentCode::class, + 'format' => $format, + 'method' => 'serialize', + ]; + + $methods[] = [ + 'direction' => GraphNavigatorInterface::DIRECTION_DESERIALIZATION, + 'type' => SegmentCode::class, + 'format' => $format, + 'method' => 'deserialize', + ]; + } + + return $methods; + } + + /** + * @param SerializationVisitorInterface $visitor + * @param SegmentCode $status + * @param array $type + * @param Context $context + * + * @return string + */ + public function serialize(SerializationVisitorInterface $visitor, SegmentCode $status, array $type, Context $context): string + { + return (string) $status; + } + + /** + * @param DeserializationVisitorInterface $visitor + * @param mixed $data + * @param array $type + * @param Context $context + * + * @return SegmentCode + */ + public function deserialize(DeserializationVisitorInterface $visitor, $data, array $type, Context $context): SegmentCode + { + return new SegmentCode($data); + } +} diff --git a/module/segment/src/Infrastructure/JMS/Serializer/Handler/SegmentIdHandler.php b/module/segment/src/Infrastructure/JMS/Serializer/Handler/SegmentIdHandler.php new file mode 100644 index 000000000..683161a9e --- /dev/null +++ b/module/segment/src/Infrastructure/JMS/Serializer/Handler/SegmentIdHandler.php @@ -0,0 +1,75 @@ + GraphNavigatorInterface::DIRECTION_SERIALIZATION, + 'type' => SegmentId::class, + 'format' => $format, + 'method' => 'serialize', + ]; + + $methods[] = [ + 'direction' => GraphNavigatorInterface::DIRECTION_DESERIALIZATION, + 'type' => SegmentId::class, + 'format' => $format, + 'method' => 'deserialize', + ]; + } + + return $methods; + } + + /** + * @param SerializationVisitorInterface $visitor + * @param SegmentId $id + * @param array $type + * @param Context $context + * + * @return string + */ + public function serialize(SerializationVisitorInterface $visitor, SegmentId $id, array $type, Context $context): string + { + return $id->getValue(); + } + + /** + * @param DeserializationVisitorInterface $visitor + * @param mixed $data + * @param array $type + * @param Context $context + * + * @return SegmentId + */ + public function deserialize(DeserializationVisitorInterface $visitor, $data, array $type, Context $context): SegmentId + { + return new SegmentId($data); + } +} diff --git a/module/segment/src/Infrastructure/JMS/Serializer/Handler/SegmentStatusHandler.php b/module/segment/src/Infrastructure/JMS/Serializer/Handler/SegmentStatusHandler.php new file mode 100644 index 000000000..06991371f --- /dev/null +++ b/module/segment/src/Infrastructure/JMS/Serializer/Handler/SegmentStatusHandler.php @@ -0,0 +1,75 @@ + GraphNavigatorInterface::DIRECTION_SERIALIZATION, + 'type' => SegmentStatus::class, + 'format' => $format, + 'method' => 'serialize', + ]; + + $methods[] = [ + 'direction' => GraphNavigatorInterface::DIRECTION_DESERIALIZATION, + 'type' => SegmentStatus::class, + 'format' => $format, + 'method' => 'deserialize', + ]; + } + + return $methods; + } + + /** + * @param SerializationVisitorInterface $visitor + * @param SegmentStatus $status + * @param array $type + * @param Context $context + * + * @return string + */ + public function serialize(SerializationVisitorInterface $visitor, SegmentStatus $status, array $type, Context $context): string + { + return (string) $status; + } + + /** + * @param DeserializationVisitorInterface $visitor + * @param mixed $data + * @param array $type + * @param Context $context + * + * @return SegmentStatus + */ + public function deserialize(DeserializationVisitorInterface $visitor, $data, array $type, Context $context): SegmentStatus + { + return new SegmentStatus($data); + } +} diff --git a/module/segment/src/Infrastructure/Provider/SegmentGeneratorProvider.php b/module/segment/src/Infrastructure/Provider/SegmentGeneratorProvider.php new file mode 100644 index 000000000..efa71de1c --- /dev/null +++ b/module/segment/src/Infrastructure/Provider/SegmentGeneratorProvider.php @@ -0,0 +1,49 @@ +generators = $generators; + } + + /** + * @param string $type + * + * @return SegmentGeneratorInterface + * @throws SegmentGeneratorProviderException + */ + public function provide(string $type): SegmentGeneratorInterface + { + foreach ($this->generators as $generator) { + if (strtoupper($type) === $generator->getType()) { + return $generator; + } + } + + throw new SegmentGeneratorProviderException(sprintf('Can\'t find segment %s generator ', $type)); + } +} diff --git a/module/segment/src/Infrastructure/Provider/SegmentProvider.php b/module/segment/src/Infrastructure/Provider/SegmentProvider.php new file mode 100644 index 000000000..3c9b4995f --- /dev/null +++ b/module/segment/src/Infrastructure/Provider/SegmentProvider.php @@ -0,0 +1,61 @@ +repository = $repository; + $this->provider = $provider; + } + + /** + * @param SegmentCode $code + * + * @return Segment + * + * @throws SegmentGeneratorProviderException + */ + public function provide(SegmentCode $code): Segment + { + $segmentId = SegmentId::fromCode($code); + $segment = $this->repository->load($segmentId); + if (null === $segment) { + $generator = $this->provider->provide($code->getValue()); + $segment = $generator->generate($segmentId, $code->getValue()); + $this->repository->save($segment); + } + + return $segment; + } +} diff --git a/module/segment/src/Infrastructure/Specification/AbstractSpecification.php b/module/segment/src/Infrastructure/Specification/AbstractSpecification.php new file mode 100644 index 000000000..24f2be3ab --- /dev/null +++ b/module/segment/src/Infrastructure/Specification/AbstractSpecification.php @@ -0,0 +1,25 @@ +code = $code; + } + + /** + * @param AbstractProduct $product + * + * @return bool + */ + public function isSatisfiedBy(AbstractProduct $product): bool + { + return $product->hasAttribute($this->code); + } +} diff --git a/module/segment/src/Infrastructure/Specification/AttributeValueSpecification.php b/module/segment/src/Infrastructure/Specification/AttributeValueSpecification.php new file mode 100644 index 000000000..79b3b0aa5 --- /dev/null +++ b/module/segment/src/Infrastructure/Specification/AttributeValueSpecification.php @@ -0,0 +1,62 @@ +code = $code; + $this->value = $value; + } + + /** + * @param AbstractProduct $product + * + * @return bool + */ + public function isSatisfiedBy(AbstractProduct $product): bool + { + if ($product->hasAttribute($this->code)) { + $value = $product->getAttribute($this->code); + if ($value->getValue() === $this->value->getValue()) { + return true; + } + } + + return false; + } +} diff --git a/module/segment/src/Persistence/Dbal/Projector/SegmentCreatedEventProjector.php b/module/segment/src/Persistence/Dbal/Projector/SegmentCreatedEventProjector.php new file mode 100644 index 000000000..dbe473c03 --- /dev/null +++ b/module/segment/src/Persistence/Dbal/Projector/SegmentCreatedEventProjector.php @@ -0,0 +1,90 @@ +connection = $connection; + $this->serializer = $serializer; + } + + /** + * @param DomainEventInterface $event + * + * @return bool + */ + public function support(DomainEventInterface $event): bool + { + return $event instanceof SegmentCreatedEvent; + } + + /** + * @param AbstractId $aggregateId + * @param DomainEventInterface $event + * + * @throws ProjectorException + * @throws UnsupportedEventException + * @throws \Doctrine\DBAL\ConnectionException + */ + public function projection(AbstractId $aggregateId, DomainEventInterface $event): void + { + if (!$event instanceof SegmentCreatedEvent) { + throw new UnsupportedEventException($event, SegmentCreatedEvent::class); + } + + try { + $this->connection->beginTransaction(); + $this->connection->insert( + self::TABLE, + [ + 'id' => $event->getId()->getValue(), + 'code' => $event->getCode(), + 'name' => $this->serializer->serialize($event->getName(), 'json'), + 'description' => $this->serializer->serialize($event->getDescription(), 'json'), + 'status' => SegmentStatus::NEW, + ] + ); + $this->connection->commit(); + } catch (\Throwable $exception) { + $this->connection->rollBack(); + throw new ProjectorException($event, $exception); + } + } +} diff --git a/module/segment/src/Persistence/Dbal/Query/DbalSegmentQuery.php b/module/segment/src/Persistence/Dbal/Query/DbalSegmentQuery.php new file mode 100644 index 000000000..33050437b --- /dev/null +++ b/module/segment/src/Persistence/Dbal/Query/DbalSegmentQuery.php @@ -0,0 +1,71 @@ +connection = $connection; + } + + /** + * @param Language $language + * + * @return DbalDataSet + */ + public function getDataSet(Language $language): DbalDataSet + { + $query = $this->getQuery(); + $query->addSelect('id'); + $query->addSelect('code'); + $query->addSelect(sprintf('(name->>\'%s\') AS name', $language->getCode())); + $query->addSelect(sprintf('(description->>\'%s\') AS description', $language->getCode())); + + $result = $this->connection->createQueryBuilder(); + $result->select('*'); + $result->from(sprintf('(%s)', $query->getSQL()), 't'); + + return new DbalDataSet($result); + } + + /** + * @return QueryBuilder + */ + private function getQuery(): QueryBuilder + { + return $this->connection->createQueryBuilder() + ->select(self::FIELDS) + ->from(self::TABLE, 't'); + } +} diff --git a/module/segment/src/Persistence/Dbal/Repository/DbalSegmentRepository.php b/module/segment/src/Persistence/Dbal/Repository/DbalSegmentRepository.php new file mode 100644 index 000000000..3e1a03f19 --- /dev/null +++ b/module/segment/src/Persistence/Dbal/Repository/DbalSegmentRepository.php @@ -0,0 +1,93 @@ +eventStore = $eventStore; + $this->eventDispatcher = $eventDispatcher; + } + + /** + * @param SegmentId $id + * + * @return Segment|null + * @throws \ReflectionException + */ + public function load(SegmentId $id): ?AbstractAggregateRoot + { + $eventStream = $this->eventStore->load($id); + + if (\count($eventStream) > 0) { + $class = new \ReflectionClass(Segment::class); + /** @var AbstractAggregateRoot $aggregate */ + $aggregate = $class->newInstanceWithoutConstructor(); + if (!$aggregate instanceof AbstractAggregateRoot) { + throw new \LogicException(sprintf('Impossible to initialize "%s"', $class)); + } + + $aggregate->initialize($eventStream); + + return $aggregate; + } + + return null; + } + + /** + * @param AbstractAggregateRoot $aggregateRoot + */ + public function save(AbstractAggregateRoot $aggregateRoot): void + { + $events = $aggregateRoot->popEvents(); + + $this->eventStore->append($aggregateRoot->getId(), $events); + foreach ($events as $envelope) { + $this->eventDispatcher->dispatch($envelope); + } + } + + /** + * @param SegmentId $id + * + * @return bool + */ + public function exists(SegmentId $id): bool + { + $eventStream = $this->eventStore->load($id); + + return \count($eventStream) > 0; + } +} diff --git a/module/segment/src/Resources/config/routes.yml b/module/segment/src/Resources/config/routes.yml new file mode 100644 index 000000000..dac9b2caa --- /dev/null +++ b/module/segment/src/Resources/config/routes.yml @@ -0,0 +1,4 @@ +ergonode_channel_api: + resource: '../../Application/Controller/Api/Segment*' + type: annotation + prefix: /api/v1/{language} diff --git a/module/segment/src/Resources/config/services.yml b/module/segment/src/Resources/config/services.yml new file mode 100644 index 000000000..629c25d8e --- /dev/null +++ b/module/segment/src/Resources/config/services.yml @@ -0,0 +1,27 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + + Ergonode\Segment\Application\: + resource: '../../Application/*' + + Ergonode\Segment\Persistence\: + resource: '../../Persistence/*' + + Ergonode\Segment\Infrastructure\: + resource: '../../Infrastructure/*' + exclude: '../../Infrastructure/{Specification}' + + Ergonode\Segment\Infrastructure\Handler\: + resource: '../../Infrastructure/Handler/*' + exclude: '../../Infrastructure/Handler/{Strategy}' + tags: ['messenger.message_handler'] + + Ergonode\Segment\Infrastructure\JMS\Serializer\Handler\: + resource: '../../Infrastructure/JMS/Serializer/Handler/*' + tags: ['jms_serializer.subscribing_handler'] + + Ergonode\Segment\Domain\Repository\SegmentRepositoryInterface: '@Ergonode\Segment\Persistence\Dbal\Repository\DbalSegmentRepository' + Ergonode\Segment\Domain\Query\SegmentQueryInterface: '@Ergonode\Segment\Persistence\Dbal\Query\DbalSegmentQuery' diff --git a/module/segment/tests/Domain/Command/CreateSegmentCommandTest.php b/module/segment/tests/Domain/Command/CreateSegmentCommandTest.php new file mode 100644 index 000000000..626c888f2 --- /dev/null +++ b/module/segment/tests/Domain/Command/CreateSegmentCommandTest.php @@ -0,0 +1,39 @@ +createMock(SegmentCode::class); + /** @var TranslatableString $name */ + $name = $this->createMock(TranslatableString::class); + /** @var TranslatableString $description */ + $description = $this->createMock(TranslatableString::class); + + $command = new CreateSegmentCommand($code, $name, $description); + $this->assertEquals($code, $command->getCode()); + $this->assertEquals($name, $command->getName()); + $this->assertEquals($description, $command->getDescription()); + } +} diff --git a/module/segment/tests/Domain/Command/UpdateSegmentCommandTest.php b/module/segment/tests/Domain/Command/UpdateSegmentCommandTest.php new file mode 100644 index 000000000..71967be7a --- /dev/null +++ b/module/segment/tests/Domain/Command/UpdateSegmentCommandTest.php @@ -0,0 +1,38 @@ +createMock(SegmentId::class); + /** @var TranslatableString $name */ + $name = $this->createMock(TranslatableString::class); + /** @var TranslatableString $description */ + $description = $this->createMock(TranslatableString::class); + + $command = new UpdateSegmentCommand($id, $name, $description); + $this->assertEquals($id, $command->getId()); + $this->assertEquals($name, $command->getName()); + $this->assertEquals($description, $command->getDescription()); + } +} diff --git a/module/segment/tests/Domain/Entity/SegmentTest.php b/module/segment/tests/Domain/Entity/SegmentTest.php new file mode 100644 index 000000000..204d16261 --- /dev/null +++ b/module/segment/tests/Domain/Entity/SegmentTest.php @@ -0,0 +1,98 @@ +id = $this->createMock(SegmentId::class); + $this->code = $this->createMock(SegmentCode::class); + $this->name = $this->createMock(TranslatableString::class); + $this->description = $this->createMock(TranslatableString::class); + } + + /** + * @throws \Exception + */ + public function testSegmentCreation():void + { + /** @var TranslatableString $name */ + $name = $this->createMock(TranslatableString::class); + /** @var TranslatableString $description */ + $description = $this->createMock(TranslatableString::class); + /** @var SegmentStatus $name */ + $status = $this->createMock(SegmentStatus::class); + + $segment = new Segment($this->id, $this->code, $this->name, $this->description); + $this->assertEquals($this->id, $segment->getId()); + $this->assertEquals($this->code, $segment->getCode()); + $this->assertEquals($this->name, $segment->getName()); + $this->assertEquals($this->description, $segment->getDescription()); + $this->assertEquals(new SegmentStatus(SegmentStatus::NEW), $segment->getStatus()); + } + + /** + * @throws \Exception + */ + public function testSegmentManipulation():void + { + /** @var TranslatableString|MockObject $name */ + $name = $this->createMock(TranslatableString::class); + $name->method('isEqual')->willReturn(false); + /** @var TranslatableString|MockObject $description */ + $description = $this->createMock(TranslatableString::class); + $description->method('isEqual')->willReturn(false); + /** @var SegmentStatus|MockObject $status */ + $status = $this->createMock(SegmentStatus::class); + $status->method('isEqual')->willReturn(false); + + $segment = new Segment($this->id, $this->code, $this->name, $this->description); + $segment->changeStatus($status); + $segment->changeName($name); + $segment->changeDescription($description); + + $this->assertSame($name, $segment->getName()); + $this->assertSame($description, $segment->getDescription()); + $this->assertSame($status, $segment->getStatus()); + } +} diff --git a/module/segment/tests/Domain/Event/SegmentCreatedEventTest.php b/module/segment/tests/Domain/Event/SegmentCreatedEventTest.php new file mode 100644 index 000000000..aebb10ba0 --- /dev/null +++ b/module/segment/tests/Domain/Event/SegmentCreatedEventTest.php @@ -0,0 +1,42 @@ +createMock(SegmentId::class); + /** @var TranslatableString $name */ + $name = $this->createMock(TranslatableString::class); + /** @var TranslatableString $description */ + $description = $this->createMock(TranslatableString::class); + /** @var SegmentCode|MockObject $code */ + $code = $this->createMock(SegmentCode::class); + + $event = new SegmentCreatedEvent($id, $code, $name, $description); + $this->assertSame($id, $event->getId()); + $this->assertSame($code, $event->getCode()); + $this->assertSame($name, $event->getName()); + $this->assertSame($description, $event->getDescription()); + } +} diff --git a/module/segment/tests/Domain/Event/SegmentDescriptionChangedEventTest.php b/module/segment/tests/Domain/Event/SegmentDescriptionChangedEventTest.php new file mode 100644 index 000000000..291a38bce --- /dev/null +++ b/module/segment/tests/Domain/Event/SegmentDescriptionChangedEventTest.php @@ -0,0 +1,26 @@ +createMock(TranslatableString::class); + /** @var TranslatableString $to */ + $to = $this->createMock(TranslatableString::class); + + $event = new SegmentDescriptionChangedEvent($from, $to); + $this->assertSame($from, $event->getFrom()); + $this->assertSame($to, $event->getTo()); + } +} diff --git a/module/segment/tests/Domain/Event/SegmentNameChangedEventTest.php b/module/segment/tests/Domain/Event/SegmentNameChangedEventTest.php new file mode 100644 index 000000000..1c23bc813 --- /dev/null +++ b/module/segment/tests/Domain/Event/SegmentNameChangedEventTest.php @@ -0,0 +1,26 @@ +createMock(TranslatableString::class); + /** @var TranslatableString $to */ + $to = $this->createMock(TranslatableString::class); + + $event = new SegmentNameChangedEvent($from, $to); + $this->assertSame($from, $event->getFrom()); + $this->assertSame($to, $event->getTo()); + } +} diff --git a/module/segment/tests/Domain/Event/SegmentStatusChangedEventTest.php b/module/segment/tests/Domain/Event/SegmentStatusChangedEventTest.php new file mode 100644 index 000000000..7820f259f --- /dev/null +++ b/module/segment/tests/Domain/Event/SegmentStatusChangedEventTest.php @@ -0,0 +1,25 @@ +createMock(SegmentStatus::class); + /** @var SegmentStatus $to */ + $to = $this->createMock(SegmentStatus::class); + + $event = new SegmentStatusChangedEvent($from, $to); + $this->assertSame($from, $event->getFrom()); + $this->assertSame($to, $event->getTo()); + } +} diff --git a/module/segment/tests/Domain/ValueObject/SegmentStatusTest.php b/module/segment/tests/Domain/ValueObject/SegmentStatusTest.php new file mode 100644 index 000000000..81b80b761 --- /dev/null +++ b/module/segment/tests/Domain/ValueObject/SegmentStatusTest.php @@ -0,0 +1,90 @@ +assertEquals(strtoupper($status), (string) $status); + $this->assertTrue(SegmentStatus::isValid($status)); + $this->assertEquals($new, $status->isNew()); + $this->assertEquals($processed, $status->isProcessed()); + $this->assertEquals($calculated, $status->isCalculated()); + $this->assertEquals($outdated, $status->isOutdated()); + } + + /** + * @param string $status + * + * @dataProvider validDataProvider + */ + public function testPositiveValidation(string $status): void + { + $this->assertTrue(SegmentStatus::isValid($status)); + } + + /** + * @param string $status + * + * @dataProvider inValidDataProvider + */ + public function testNegativeValidation(string $status): void + { + $this->assertFalse(SegmentStatus::isValid($status)); + } + + /** + * @param string $status + * + * @dataProvider inValidDataProvider + * + * @expectedException \InvalidArgumentException + */ + public function testInvalidData(string $status): void + { + new SegmentStatus($status); + } + + /** + * @return array + */ + public function validDataProvider(): array + { + return [ + [SegmentStatus::NEW, true, false, false, false], + [SegmentStatus::PROCESSED, false, true, false, false], + [SegmentStatus::CALCULATED, false,false, true, false], + [SegmentStatus::OUTDATED, false, false, false , true], + ]; + } + + /** + * @return array + */ + public function inValidDataProvider(): array + { + return [ + [''], + ['not exists status'], + [123], + ['!@#)(*&(^^*^('], + ]; + } +} diff --git a/module/segment/tests/Infrastructure/JMS/Serializer/Handler/SegmentCodeHandlerTest.php b/module/segment/tests/Infrastructure/JMS/Serializer/Handler/SegmentCodeHandlerTest.php new file mode 100644 index 000000000..6db400015 --- /dev/null +++ b/module/segment/tests/Infrastructure/JMS/Serializer/Handler/SegmentCodeHandlerTest.php @@ -0,0 +1,77 @@ +handler = new SegmentCodeHandler(); + $this->serializerVisitor = $this->createMock(SerializationVisitorInterface::class); + $this->deserializerVisitor = $this->createMock(DeserializationVisitorInterface::class); + $this->context = $this->createMock(Context::class); + } + + /** + */ + public function testConfiguration(): void + { + $configurations = SegmentCodeHandler::getSubscribingMethods(); + foreach ($configurations as $configuration) { + $this->assertArrayHasKey('direction', $configuration); + $this->assertArrayHasKey('type', $configuration); + $this->assertArrayHasKey('format', $configuration); + $this->assertArrayHasKey('method', $configuration); + } + } + + /** + */ + public function testSerialize(): void + { + $testValue = 'code'; + $code = new SegmentCode($testValue); + $result = $this->handler->serialize($this->serializerVisitor, $code, [], $this->context); + + $this->assertEquals($testValue, $result); + } + + /** + */ + public function testDeserialize(): void + { + $testValue = 'code'; + $result = $this->handler->deserialize($this->deserializerVisitor, $testValue, [], $this->context); + + $this->assertEquals($testValue, (string) $result); + } +} diff --git a/module/segment/tests/Infrastructure/JMS/Serializer/Handler/SegmentIdHandlerTest.php b/module/segment/tests/Infrastructure/JMS/Serializer/Handler/SegmentIdHandlerTest.php new file mode 100644 index 000000000..f835eb0b2 --- /dev/null +++ b/module/segment/tests/Infrastructure/JMS/Serializer/Handler/SegmentIdHandlerTest.php @@ -0,0 +1,78 @@ +handler = new SegmentIdHandler(); + $this->serializerVisitor = $this->createMock(SerializationVisitorInterface::class); + $this->deserializerVisitor = $this->createMock(DeserializationVisitorInterface::class); + $this->context = $this->createMock(Context::class); + } + + /** + */ + public function testConfiguration(): void + { + $configurations = SegmentIdHandler::getSubscribingMethods(); + foreach ($configurations as $configuration) { + $this->assertArrayHasKey('direction', $configuration); + $this->assertArrayHasKey('type', $configuration); + $this->assertArrayHasKey('format', $configuration); + $this->assertArrayHasKey('method', $configuration); + } + } + + /** + */ + public function testSerialize(): void + { + $testValue = Uuid::uuid4()->toString(); + $code = new SegmentId($testValue); + $result = $this->handler->serialize($this->serializerVisitor, $code, [], $this->context); + + $this->assertEquals($testValue, $result); + } + + /** + */ + public function testDeserialize(): void + { + $testValue = Uuid::uuid4()->toString();; + $result = $this->handler->deserialize($this->deserializerVisitor, $testValue, [], $this->context); + + $this->assertEquals($testValue, (string) $result); + } +} diff --git a/module/segment/tests/Infrastructure/JMS/Serializer/Handler/SegmentStatusHandlerTest.php b/module/segment/tests/Infrastructure/JMS/Serializer/Handler/SegmentStatusHandlerTest.php new file mode 100644 index 000000000..2aceb1a92 --- /dev/null +++ b/module/segment/tests/Infrastructure/JMS/Serializer/Handler/SegmentStatusHandlerTest.php @@ -0,0 +1,77 @@ +handler = new SegmentStatusHandler(); + $this->serializerVisitor = $this->createMock(SerializationVisitorInterface::class); + $this->deserializerVisitor = $this->createMock(DeserializationVisitorInterface::class); + $this->context = $this->createMock(Context::class); + } + + /** + */ + public function testConfiguration(): void + { + $configurations = SegmentStatusHandler::getSubscribingMethods(); + foreach ($configurations as $configuration) { + $this->assertArrayHasKey('direction', $configuration); + $this->assertArrayHasKey('type', $configuration); + $this->assertArrayHasKey('format', $configuration); + $this->assertArrayHasKey('method', $configuration); + } + } + + /** + */ + public function testSerialize(): void + { + $testValue = SegmentStatus::OUTDATED; + $code = new SegmentStatus($testValue); + $result = $this->handler->serialize($this->serializerVisitor, $code, [], $this->context); + + $this->assertEquals($testValue, $result); + } + + /** + */ + public function testDeserialize(): void + { + $testValue = SegmentStatus::OUTDATED; + $result = $this->handler->deserialize($this->deserializerVisitor, $testValue, [], $this->context); + + $this->assertEquals($testValue, (string) $result); + } +} diff --git a/module/workflow/src/Domain/Command/Workflow/CreateWorkflowCommand.php b/module/workflow/src/Domain/Command/Workflow/CreateWorkflowCommand.php index bac66d768..676ca47b8 100644 --- a/module/workflow/src/Domain/Command/Workflow/CreateWorkflowCommand.php +++ b/module/workflow/src/Domain/Command/Workflow/CreateWorkflowCommand.php @@ -11,7 +11,6 @@ use Ergonode\Workflow\Domain\Entity\StatusId; use Ergonode\Workflow\Domain\Entity\WorkflowId; -use Ergonode\Workflow\Domain\ValueObject\Status; use Ergonode\Workflow\Domain\ValueObject\Transition; use JMS\Serializer\Annotation as JMS; use Webmozart\Assert\Assert; From ef796932ce12222e60d5a274a0d71512cc85d3ea Mon Sep 17 00:00:00 2001 From: Sebastian Bielawski Date: Tue, 3 Sep 2019 15:57:06 +0200 Subject: [PATCH 03/27] Event store history (#119) * #118 Event Store history --- features/category-tree.feature | 19 +++-- module/account/src/Domain/Entity/Role.php | 35 -------- ...eRemovedEvent.php => RoleDeletedEvent.php} | 4 +- .../Repository/RoleRepositoryInterface.php | 5 ++ .../Handler/Role/DeleteRoleCommandHandler.php | 3 +- .../Role/RoleCreatedEventProjector.php | 42 +++++----- ...ctor.php => RoleDeletedEventProjector.php} | 39 +++------ .../RoleDescriptionChangedEventProjector.php | 35 +++----- .../Role/RoleNameChangedEventProjector.php | 35 +++----- .../RolePrivilegesChangedEventProjector.php | 42 +++++----- .../User/UserActivatedEventProjector.php | 32 +++----- .../User/UserAvatarChangedEventProjector.php | 35 +++----- .../User/UserCreatedEventProjector.php | 44 +++++------ .../User/UserDeactivatedEventProjector.php | 32 +++----- .../UserFirstNameChangedEventProjector.php | 35 +++----- .../UserLanguageChangedEventProjector.php | 35 +++----- .../UserLastNameChangedEventProjector.php | 35 +++----- .../UserPasswordChangedEventProjector.php | 35 +++----- .../User/UserRoleChangedEventProjector.php | 48 +++-------- .../Dbal/Repository/DbalRoleRepository.php | 25 ++++-- .../src/Resources/translations/log.en.yaml | 2 +- .../src/Resources/translations/log.pl.yaml | 2 +- .../account/tests/Domain/Entity/RoleTest.php | 9 --- .../Domain/Command/DeleteAttributeCommand.php | 1 - .../src/Domain/Entity/AbstractAttribute.php | 20 ++++- ...ent.php => AttributeGroupDeletedEvent.php} | 2 +- ...buteArrayParameterChangeEventProjector.php | 53 ++++++------- .../AttributeCreatedEventProjector.php | 35 ++++---- .../AttributeHintChangedEventProjector.php | 21 ++--- .../AttributeLabelChangedEventProjector.php | 21 ++--- ...AttributeParameterChangeEventProjector.php | 53 ++++++------- ...ributePlaceholderChangedEventProjector.php | 22 +----- .../AttributeGroupAddedEventProjector.php | 33 +++----- .../AttributeGroupCreatedEventProjector.php | 31 +++----- .../AttributeGroupRemovedEventProjector.php | 41 +++------- .../AttributeOptionAddedEventProjector.php | 21 ++--- .../AttributeOptionChangedEventProjector.php | 13 +-- .../AttributeOptionRemovedEventProjector.php | 11 +-- .../src/Resources/translations/log.en.yaml | 2 +- .../src/Resources/translations/log.pl.yaml | 2 +- ...php => AttributeGroupDeletedEventTest.php} | 6 +- .../Controller/Api/CategoryTreeController.php | 1 - .../CategoryTreeCreatedEventProjector.php | 47 +++++------ .../CategoryTreeNameChangedEventProjector.php | 49 +++++------- .../CategoryCreatedEventProjector.php | 23 ++---- .../CategoryNameChangedEventProjector.php | 49 +++++------- .../core/migrations/Version20180610093112.php | 14 ---- .../Controller/Api/TemplateController.php | 5 +- .../TemplateGroupCreatedEventProjector.php | 35 +++----- .../TemplateCreatedEventProjector.php | 37 +++------ .../TemplateElementAddedEventProjector.php | 43 ++++------ .../TemplateElementChangedEventProjector.php | 47 ++++------- .../TemplateElementRemovedEventProjector.php | 37 +++------ .../TemplateGroupChangedEventProjector.php | 39 +++------ .../TemplateImageAddedEventProjector.php | 37 +++------ .../TemplateImageChangedEventProjector.php | 37 +++------ .../TemplateImageRemovedEventProjector.php | 37 +++------ .../TemplateNameChangedEventProjector.php | 37 +++------ .../TemplateRemovedEventProjector.php | 19 ++--- .../ProductCreatedEventProjector.php | 33 +++----- .../ProductDraftAppliedEventProjector.php | 43 ++++------ .../ProductDraftCreatedEventProjector.php | 35 +++----- .../ProductDraftValueAddedEventProjector.php | 22 ++---- ...ProductDraftValueChangedEventProjector.php | 21 ++--- ...ProductDraftValueRemovedEventProjector.php | 25 ++---- .../migrations/Version20190903112016.php | 52 ++++++++++++ .../src/Domain/AbstractAggregateRoot.php | 17 ++-- .../Infrastructure/AbstractDeleteEvent.php | 17 ++++ .../DomainEventStoreInterface.php | 6 ++ .../DomainEventEnvelopeSubscriber.php | 7 +- .../Store/DbalDomainEventStore.php | 68 +++++++++++----- .../migrations/Version20180618134343.php | 16 +++- .../ProductAddedToCategoryEventProjector.php | 32 +++----- .../Projector/ProductCreateEventProjector.php | 23 ++---- ...oductRemovedFromCategoryEventProjector.php | 34 +++----- .../ProductStatusChangedEventProjector.php | 79 ------------------- .../ProductValueAddedEventProjector.php | 21 +---- .../ProductValueChangedEventProjector.php | 22 +----- .../ProductValueRemovedEventProjector.php | 20 +---- .../ProductVersionIncreasedEventProjector.php | 46 ++++------- .../Projector/ReaderCreatedEventProjector.php | 36 +++------ .../ProcessorCreatedEventProjector.php | 2 + .../ProcessorStatusChangedEventProjector.php | 2 + .../TransformerCreatedEventProjector.php | 2 + module/workflow/src/Domain/Entity/Status.php | 36 --------- .../Event/Status/StatusDeletedEvent.php | 18 +++++ .../Event/Status/StatusRemovedEvent.php | 42 ---------- .../Repository/StatusRepositoryInterface.php | 13 ++- .../Status/DeleteStatusCommandHandler.php | 6 +- .../StatusColorChangedEventProjector.php | 39 +++------ .../Projector/StatusCreatedEventProjector.php | 50 ++++++------ ...or.php => StatusDeletedEventProjector.php} | 40 +++------- ...StatusDescriptionChangedEventProjector.php | 48 +++++------ .../StatusNameChangedEventProjector.php | 48 +++++------ .../Dbal/Repository/DbalStatusRepository.php | 34 +++++--- 95 files changed, 975 insertions(+), 1689 deletions(-) rename module/account/src/Domain/Event/Role/{RoleRemovedEvent.php => RoleDeletedEvent.php} (64%) rename module/account/src/Persistence/Dbal/Projector/Role/{RoleRemovedEventProjector.php => RoleDeletedEventProjector.php} (51%) rename module/attribute/src/Domain/Event/{AttributeGroupRemovedEvent.php => AttributeGroupDeletedEvent.php} (92%) rename module/attribute/tests/Domain/Event/{AttributeGroupRemovedEventTest.php => AttributeGroupDeletedEventTest.php} (75%) create mode 100644 module/event-sourcing/migrations/Version20190903112016.php create mode 100644 module/event-sourcing/src/Infrastructure/AbstractDeleteEvent.php delete mode 100644 module/product/src/Persistence/Dbal/Projector/ProductStatusChangedEventProjector.php create mode 100644 module/workflow/src/Domain/Event/Status/StatusDeletedEvent.php delete mode 100644 module/workflow/src/Domain/Event/Status/StatusRemovedEvent.php rename module/workflow/src/Persistence/Dbal/Projector/{StatusRemovedEventProjector.php => StatusDeletedEventProjector.php} (50%) diff --git a/features/category-tree.feature b/features/category-tree.feature index 9f9090431..bdcc29b3b 100644 --- a/features/category-tree.feature +++ b/features/category-tree.feature @@ -2,10 +2,16 @@ Feature: Category tree module Scenario: Create category tree Given current authentication token - Given the following form parameters are set: - | name | value | - | name | Test | - | code | TREE_@@random_code@@ | + Given the request body is: + """ + { + "code": "TREE_@@random_code@@", + "name": { + "DE": "Test DE", + "EN": "Test EN" + } + } + """ When I request "/api/v1/EN/trees" using HTTP POST Then created response is received And remember response param "id" as "category_tree" @@ -35,7 +41,10 @@ Feature: Category tree module Given the request body is: """ { - "name": "Test (changed)", + "name": { + "DE": "Test DE (changed)", + "EN": "Test EN (changed)" + }, "categories": [ { "category_id": "@tree_category@", diff --git a/module/account/src/Domain/Entity/Role.php b/module/account/src/Domain/Entity/Role.php index 06b2c246c..7f416cf5f 100644 --- a/module/account/src/Domain/Entity/Role.php +++ b/module/account/src/Domain/Entity/Role.php @@ -15,10 +15,8 @@ use Ergonode\Account\Domain\Event\Role\RoleDescriptionChangedEvent; use Ergonode\Account\Domain\Event\Role\RoleNameChangedEvent; use Ergonode\Account\Domain\Event\Role\RolePrivilegesChangedEvent; -use Ergonode\Account\Domain\Event\Role\RoleRemovedEvent; use Ergonode\Account\Domain\ValueObject\Privilege; use Ergonode\Core\Domain\Entity\AbstractId; -use Ergonode\Core\Domain\ValueObject\State; use Ergonode\EventSourcing\Domain\AbstractAggregateRoot; use JMS\Serializer\Annotation as JMS; use Webmozart\Assert\Assert; @@ -55,13 +53,6 @@ class Role extends AbstractAggregateRoot */ private $privileges; - /** - * @var State - * - * @JMS\Exclude() - */ - private $state; - /** * @param RoleId $id * @param string $name @@ -77,15 +68,6 @@ public function __construct(RoleId $id, string $name, string $description, array $this->apply(new RoleCreatedEvent($id, $name, $description, $privileges)); } - /** - */ - public function remove(): void - { - if ($this->state->getValue() !== State::STATE_DELETED) { - $this->apply(new RoleRemovedEvent()); - } - } - /** * @return RoleId|AbstractId */ @@ -188,14 +170,6 @@ public function removePrivilege(Privilege $privilege): void $this->apply(new RemovePrivilegeFromRoleEvent($privilege)); } - /** - * @return bool - */ - public function isDeleted(): bool - { - return $this->state->getValue() === State::STATE_DELETED; - } - /** * @param RoleCreatedEvent $event */ @@ -205,15 +179,6 @@ protected function applyRoleCreatedEvent(RoleCreatedEvent $event): void $this->name = $event->getName(); $this->description = $event->getDescription(); $this->privileges = $event->getPrivileges(); - $this->state = new State(); - } - - /** - * @param RoleRemovedEvent $event - */ - protected function applyRoleRemovedEvent(RoleRemovedEvent $event): void - { - $this->state = new State(State::STATE_DELETED); } /** diff --git a/module/account/src/Domain/Event/Role/RoleRemovedEvent.php b/module/account/src/Domain/Event/Role/RoleDeletedEvent.php similarity index 64% rename from module/account/src/Domain/Event/Role/RoleRemovedEvent.php rename to module/account/src/Domain/Event/Role/RoleDeletedEvent.php index dbc60514c..a2cbd0a24 100644 --- a/module/account/src/Domain/Event/Role/RoleRemovedEvent.php +++ b/module/account/src/Domain/Event/Role/RoleDeletedEvent.php @@ -9,10 +9,10 @@ namespace Ergonode\Account\Domain\Event\Role; -use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; +use Ergonode\EventSourcing\Infrastructure\AbstractDeleteEvent; /** */ -class RoleRemovedEvent implements DomainEventInterface +class RoleDeletedEvent extends AbstractDeleteEvent { } diff --git a/module/account/src/Domain/Repository/RoleRepositoryInterface.php b/module/account/src/Domain/Repository/RoleRepositoryInterface.php index 68e3790f6..da4627120 100644 --- a/module/account/src/Domain/Repository/RoleRepositoryInterface.php +++ b/module/account/src/Domain/Repository/RoleRepositoryInterface.php @@ -28,4 +28,9 @@ public function load(RoleId $id): ?AbstractAggregateRoot; * @param AbstractAggregateRoot $aggregateRoot */ public function save(AbstractAggregateRoot $aggregateRoot): void; + + /** + * @param AbstractAggregateRoot $aggregateRoot + */ + public function delete(AbstractAggregateRoot $aggregateRoot): void; } diff --git a/module/account/src/Infrastructure/Handler/Role/DeleteRoleCommandHandler.php b/module/account/src/Infrastructure/Handler/Role/DeleteRoleCommandHandler.php index 776df8a4f..4baf41f2d 100644 --- a/module/account/src/Infrastructure/Handler/Role/DeleteRoleCommandHandler.php +++ b/module/account/src/Infrastructure/Handler/Role/DeleteRoleCommandHandler.php @@ -40,8 +40,7 @@ public function __invoke(DeleteRoleCommand $command) { $role = $this->repository->load($command->getId()); Assert::isInstanceOf($role, Role::class, sprintf('Can\'t find Role with id %s', $command->getId())); - $role->remove(); - $this->repository->save($role); + $this->repository->delete($role); } } diff --git a/module/account/src/Persistence/Dbal/Projector/Role/RoleCreatedEventProjector.php b/module/account/src/Persistence/Dbal/Projector/Role/RoleCreatedEventProjector.php index d187b84b5..11bd0b231 100644 --- a/module/account/src/Persistence/Dbal/Projector/Role/RoleCreatedEventProjector.php +++ b/module/account/src/Persistence/Dbal/Projector/Role/RoleCreatedEventProjector.php @@ -15,6 +15,7 @@ use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; +use JMS\Serializer\SerializerInterface; /** */ @@ -28,17 +29,22 @@ class RoleCreatedEventProjector implements DomainEventProjectorInterface private $connection; /** - * @param Connection $connection + * @var SerializerInterface */ - public function __construct(Connection $connection) + private $serializer; + + /** + * @param Connection $connection + * @param SerializerInterface $serializer + */ + public function __construct(Connection $connection, SerializerInterface $serializer) { $this->connection = $connection; + $this->serializer = $serializer; } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -46,11 +52,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws UnsupportedEventException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -58,16 +60,14 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, RoleCreatedEvent::class); } - $this->connection->transactional(function () use ($event) { - $this->connection->insert( - self::TABLE, - [ - 'id' => $event->getId()->getValue(), - 'name' => $event->getName(), - 'description' => $event->getDescription(), - 'privileges' => json_encode($event->getPrivileges()), - ] - ); - }); + $this->connection->insert( + self::TABLE, + [ + 'id' => $event->getId()->getValue(), + 'name' => $event->getName(), + 'description' => $event->getDescription(), + 'privileges' => $this->serializer->serialize($event->getPrivileges(), 'json'), + ] + ); } } diff --git a/module/account/src/Persistence/Dbal/Projector/Role/RoleRemovedEventProjector.php b/module/account/src/Persistence/Dbal/Projector/Role/RoleDeletedEventProjector.php similarity index 51% rename from module/account/src/Persistence/Dbal/Projector/Role/RoleRemovedEventProjector.php rename to module/account/src/Persistence/Dbal/Projector/Role/RoleDeletedEventProjector.php index ea1ec04c1..713c4b6a1 100644 --- a/module/account/src/Persistence/Dbal/Projector/Role/RoleRemovedEventProjector.php +++ b/module/account/src/Persistence/Dbal/Projector/Role/RoleDeletedEventProjector.php @@ -10,7 +10,7 @@ namespace Ergonode\Account\Persistence\Dbal\Projector\Role; use Doctrine\DBAL\Connection; -use Ergonode\Account\Domain\Event\Role\RoleRemovedEvent; +use Ergonode\Account\Domain\Event\Role\RoleDeletedEvent; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; @@ -18,7 +18,7 @@ /** */ -class RoleRemovedEventProjector implements DomainEventProjectorInterface +class RoleDeletedEventProjector implements DomainEventProjectorInterface { private const TABLE = 'roles'; @@ -36,40 +36,27 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { - return $event instanceof RoleRemovedEvent; + return $event instanceof RoleDeletedEvent; } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { - if (!$event instanceof RoleRemovedEvent) { - throw new UnsupportedEventException($event, RoleRemovedEvent::class); + if (!$event instanceof RoleDeletedEvent) { + throw new UnsupportedEventException($event, RoleDeletedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->delete( - self::TABLE, - [ - 'id' => $aggregateId->getValue(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->connection->delete( + self::TABLE, + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/account/src/Persistence/Dbal/Projector/Role/RoleDescriptionChangedEventProjector.php b/module/account/src/Persistence/Dbal/Projector/Role/RoleDescriptionChangedEventProjector.php index 0641556d5..b82ddb348 100644 --- a/module/account/src/Persistence/Dbal/Projector/Role/RoleDescriptionChangedEventProjector.php +++ b/module/account/src/Persistence/Dbal/Projector/Role/RoleDescriptionChangedEventProjector.php @@ -36,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -46,11 +44,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -58,21 +52,14 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, RoleDescriptionChangedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->update( - self::TABLE, - [ - 'description' => $event->getTo(), - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->connection->update( + self::TABLE, + [ + 'description' => $event->getTo(), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/account/src/Persistence/Dbal/Projector/Role/RoleNameChangedEventProjector.php b/module/account/src/Persistence/Dbal/Projector/Role/RoleNameChangedEventProjector.php index 86456a7aa..b154ca213 100644 --- a/module/account/src/Persistence/Dbal/Projector/Role/RoleNameChangedEventProjector.php +++ b/module/account/src/Persistence/Dbal/Projector/Role/RoleNameChangedEventProjector.php @@ -36,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -46,11 +44,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -58,21 +52,14 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, RoleNameChangedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->update( - self::TABLE, - [ - 'name' => $event->getTo(), - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->connection->update( + self::TABLE, + [ + 'name' => $event->getTo(), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/account/src/Persistence/Dbal/Projector/Role/RolePrivilegesChangedEventProjector.php b/module/account/src/Persistence/Dbal/Projector/Role/RolePrivilegesChangedEventProjector.php index 847ebd878..0579f3fe5 100644 --- a/module/account/src/Persistence/Dbal/Projector/Role/RolePrivilegesChangedEventProjector.php +++ b/module/account/src/Persistence/Dbal/Projector/Role/RolePrivilegesChangedEventProjector.php @@ -15,6 +15,7 @@ use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; +use JMS\Serializer\SerializerInterface; /** */ @@ -28,17 +29,22 @@ class RolePrivilegesChangedEventProjector implements DomainEventProjectorInterfa private $connection; /** - * @param Connection $connection + * @var SerializerInterface */ - public function __construct(Connection $connection) + private $serializer; + + /** + * @param Connection $connection + * @param SerializerInterface $serializer + */ + public function __construct(Connection $connection, SerializerInterface $serializer) { $this->connection = $connection; + $this->serializer = $serializer; } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -46,11 +52,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws UnsupportedEventException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -58,16 +60,14 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, RolePrivilegesChangedEvent::class); } - $this->connection->transactional(function () use ($event, $aggregateId) { - $this->connection->update( - self::TABLE, - [ - 'privileges' => json_encode($event->getTo()), - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - }); + $this->connection->update( + self::TABLE, + [ + 'privileges' => $this->serializer->serialize($event->getTo(), 'json'), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/account/src/Persistence/Dbal/Projector/User/UserActivatedEventProjector.php b/module/account/src/Persistence/Dbal/Projector/User/UserActivatedEventProjector.php index 8fcdff9f9..06038cf08 100644 --- a/module/account/src/Persistence/Dbal/Projector/User/UserActivatedEventProjector.php +++ b/module/account/src/Persistence/Dbal/Projector/User/UserActivatedEventProjector.php @@ -44,11 +44,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws UnsupportedEventException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -56,19 +52,17 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, UserActivatedEvent::class); } - $this->connection->transactional(function () use ($aggregateId, $event) { - $this->connection->update( - self::TABLE, - [ - 'is_active' => $event->isActive(), - ], - [ - 'id' => $aggregateId->getValue(), - ], - [ - 'is_active' => \PDO::PARAM_BOOL, - ] - ); - }); + $this->connection->update( + self::TABLE, + [ + 'is_active' => $event->isActive(), + ], + [ + 'id' => $aggregateId->getValue(), + ], + [ + 'is_active' => \PDO::PARAM_BOOL, + ] + ); } } diff --git a/module/account/src/Persistence/Dbal/Projector/User/UserAvatarChangedEventProjector.php b/module/account/src/Persistence/Dbal/Projector/User/UserAvatarChangedEventProjector.php index 0941fd124..d8f0428bc 100644 --- a/module/account/src/Persistence/Dbal/Projector/User/UserAvatarChangedEventProjector.php +++ b/module/account/src/Persistence/Dbal/Projector/User/UserAvatarChangedEventProjector.php @@ -36,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -46,11 +44,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -58,21 +52,14 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, UserAvatarChangedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->update( - self::TABLE, - [ - 'avatar_id' => $event->getAvatarId() ? $event->getAvatarId()->getValue() : null, - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->connection->update( + self::TABLE, + [ + 'avatar_id' => $event->getAvatarId() ? $event->getAvatarId()->getValue() : null, + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/account/src/Persistence/Dbal/Projector/User/UserCreatedEventProjector.php b/module/account/src/Persistence/Dbal/Projector/User/UserCreatedEventProjector.php index 220721c9a..48302e30f 100644 --- a/module/account/src/Persistence/Dbal/Projector/User/UserCreatedEventProjector.php +++ b/module/account/src/Persistence/Dbal/Projector/User/UserCreatedEventProjector.php @@ -36,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -46,11 +44,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws UnsupportedEventException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -58,23 +52,21 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, UserCreatedEvent::class); } - $this->connection->transactional(function () use ($event) { - $this->connection->insert( - self::TABLE, - [ - 'id' => $event->getId()->getValue(), - 'first_name' => $event->getFirstName(), - 'last_name' => $event->getLastName(), - 'username' => $event->getEmail(), - 'role_id' => $event->getRoleId()->getValue(), - 'language' => $event->getLanguage()->getCode(), - 'password' => $event->getPassword()->getValue(), - 'is_active' => $event->isActive(), - ], - [ - 'is_active' => \PDO::PARAM_BOOL, - ] - ); - }); + $this->connection->insert( + self::TABLE, + [ + 'id' => $event->getId()->getValue(), + 'first_name' => $event->getFirstName(), + 'last_name' => $event->getLastName(), + 'username' => $event->getEmail(), + 'role_id' => $event->getRoleId()->getValue(), + 'language' => $event->getLanguage()->getCode(), + 'password' => $event->getPassword()->getValue(), + 'is_active' => $event->isActive(), + ], + [ + 'is_active' => \PDO::PARAM_BOOL, + ] + ); } } diff --git a/module/account/src/Persistence/Dbal/Projector/User/UserDeactivatedEventProjector.php b/module/account/src/Persistence/Dbal/Projector/User/UserDeactivatedEventProjector.php index a859b2b16..d08140f13 100644 --- a/module/account/src/Persistence/Dbal/Projector/User/UserDeactivatedEventProjector.php +++ b/module/account/src/Persistence/Dbal/Projector/User/UserDeactivatedEventProjector.php @@ -44,11 +44,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws UnsupportedEventException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -56,19 +52,17 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, UserDeactivatedEvent::class); } - $this->connection->transactional(function () use ($aggregateId, $event) { - $this->connection->update( - self::TABLE, - [ - 'is_active' => $event->isActive(), - ], - [ - 'id' => $aggregateId->getValue(), - ], - [ - 'is_active' => \PDO::PARAM_BOOL, - ] - ); - }); + $this->connection->update( + self::TABLE, + [ + 'is_active' => $event->isActive(), + ], + [ + 'id' => $aggregateId->getValue(), + ], + [ + 'is_active' => \PDO::PARAM_BOOL, + ] + ); } } diff --git a/module/account/src/Persistence/Dbal/Projector/User/UserFirstNameChangedEventProjector.php b/module/account/src/Persistence/Dbal/Projector/User/UserFirstNameChangedEventProjector.php index f24cb1ac7..c65e0a596 100644 --- a/module/account/src/Persistence/Dbal/Projector/User/UserFirstNameChangedEventProjector.php +++ b/module/account/src/Persistence/Dbal/Projector/User/UserFirstNameChangedEventProjector.php @@ -36,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -46,11 +44,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -58,21 +52,14 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, UserFirstNameChangedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->update( - self::TABLE, - [ - 'first_name' => $event->getTo(), - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->connection->update( + self::TABLE, + [ + 'first_name' => $event->getTo(), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/account/src/Persistence/Dbal/Projector/User/UserLanguageChangedEventProjector.php b/module/account/src/Persistence/Dbal/Projector/User/UserLanguageChangedEventProjector.php index c27bbcf48..5d341e630 100644 --- a/module/account/src/Persistence/Dbal/Projector/User/UserLanguageChangedEventProjector.php +++ b/module/account/src/Persistence/Dbal/Projector/User/UserLanguageChangedEventProjector.php @@ -36,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -46,11 +44,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -58,21 +52,14 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, UserLanguageChangedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->update( - self::TABLE, - [ - 'language' => $event->getTo()->getCode(), - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->connection->update( + self::TABLE, + [ + 'language' => $event->getTo()->getCode(), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/account/src/Persistence/Dbal/Projector/User/UserLastNameChangedEventProjector.php b/module/account/src/Persistence/Dbal/Projector/User/UserLastNameChangedEventProjector.php index 10c3ef72c..31f180be4 100644 --- a/module/account/src/Persistence/Dbal/Projector/User/UserLastNameChangedEventProjector.php +++ b/module/account/src/Persistence/Dbal/Projector/User/UserLastNameChangedEventProjector.php @@ -36,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -46,11 +44,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -58,21 +52,14 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, UserLastNameChangedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->update( - self::TABLE, - [ - 'last_name' => $event->getTo(), - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->connection->update( + self::TABLE, + [ + 'last_name' => $event->getTo(), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/account/src/Persistence/Dbal/Projector/User/UserPasswordChangedEventProjector.php b/module/account/src/Persistence/Dbal/Projector/User/UserPasswordChangedEventProjector.php index db60ef6c4..c5e158b4a 100644 --- a/module/account/src/Persistence/Dbal/Projector/User/UserPasswordChangedEventProjector.php +++ b/module/account/src/Persistence/Dbal/Projector/User/UserPasswordChangedEventProjector.php @@ -36,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -46,11 +44,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -58,21 +52,14 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, UserPasswordChangedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->update( - self::TABLE, - [ - 'password' => $event->getPassword(), - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->connection->update( + self::TABLE, + [ + 'password' => $event->getPassword(), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/account/src/Persistence/Dbal/Projector/User/UserRoleChangedEventProjector.php b/module/account/src/Persistence/Dbal/Projector/User/UserRoleChangedEventProjector.php index a6e5fcfe3..7444d8ff7 100644 --- a/module/account/src/Persistence/Dbal/Projector/User/UserRoleChangedEventProjector.php +++ b/module/account/src/Persistence/Dbal/Projector/User/UserRoleChangedEventProjector.php @@ -11,12 +11,10 @@ use Doctrine\DBAL\Connection; use Ergonode\Account\Domain\Event\User\UserRoleChangedEvent; -use Ergonode\Account\Domain\Repository\RoleRepositoryInterface; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; -use Webmozart\Assert\Assert; /** */ @@ -30,24 +28,15 @@ class UserRoleChangedEventProjector implements DomainEventProjectorInterface private $connection; /** - * @var RoleRepositoryInterface + * @param Connection $connection */ - private $repository; - - /** - * @param Connection $connection - * @param RoleRepositoryInterface $repository - */ - public function __construct(Connection $connection, RoleRepositoryInterface $repository) + public function __construct(Connection $connection) { $this->connection = $connection; - $this->repository = $repository; } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -55,12 +44,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -68,20 +52,14 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, UserRoleChangedEvent::class); } - $role = $this->repository->load($event->getTo()); - - Assert::notNull($role); - - $this->connection->transactional(function () use ($event, $aggregateId) { - $this->connection->update( - self::TABLE, - [ - 'role_id' => $event->getTo()->getValue(), - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - }); + $this->connection->update( + self::TABLE, + [ + 'role_id' => $event->getTo()->getValue(), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/account/src/Persistence/Dbal/Repository/DbalRoleRepository.php b/module/account/src/Persistence/Dbal/Repository/DbalRoleRepository.php index f9e5dc60a..72af2929a 100644 --- a/module/account/src/Persistence/Dbal/Repository/DbalRoleRepository.php +++ b/module/account/src/Persistence/Dbal/Repository/DbalRoleRepository.php @@ -9,6 +9,7 @@ use Ergonode\Account\Domain\Entity\Role; use Ergonode\Account\Domain\Entity\RoleId; +use Ergonode\Account\Domain\Event\Role\RoleDeletedEvent; use Ergonode\Account\Domain\Repository\RoleRepositoryInterface; use Ergonode\EventSourcing\Domain\AbstractAggregateRoot; use Ergonode\EventSourcing\Infrastructure\DomainEventDispatcherInterface; @@ -32,8 +33,10 @@ class DbalRoleRepository implements RoleRepositoryInterface * @param DomainEventStoreInterface $eventStore * @param DomainEventDispatcherInterface $eventDispatcher */ - public function __construct(DomainEventStoreInterface $eventStore, DomainEventDispatcherInterface $eventDispatcher) - { + public function __construct( + DomainEventStoreInterface $eventStore, + DomainEventDispatcherInterface $eventDispatcher + ) { $this->eventStore = $eventStore; $this->eventDispatcher = $eventDispatcher; } @@ -59,9 +62,7 @@ public function load(RoleId $id): ?AbstractAggregateRoot $aggregate->initialize($eventStream); - if (!$aggregate->isDeleted()) { - return $aggregate; - } + return $aggregate; } return null; @@ -79,4 +80,18 @@ public function save(AbstractAggregateRoot $aggregateRoot): void $this->eventDispatcher->dispatch($envelope); } } + + /** + * {@inheritDoc} + * + * @throws \Exception + */ + public function delete(AbstractAggregateRoot $aggregateRoot): void + { + $aggregateRoot->apply(new RoleDeletedEvent()); + + $this->save($aggregateRoot); + + $this->eventStore->delete($aggregateRoot->getId()); + } } diff --git a/module/account/src/Resources/translations/log.en.yaml b/module/account/src/Resources/translations/log.en.yaml index 17a742bad..2639e471e 100644 --- a/module/account/src/Resources/translations/log.en.yaml +++ b/module/account/src/Resources/translations/log.en.yaml @@ -13,7 +13,7 @@ "Ergonode\\Account\\Domain\\Event\\Role\\RoleNameChangedEvent": Role name changed from "%from%" to "%to%" "Ergonode\\Account\\Domain\\Event\\Role\\RoleDescriptionChangedEvent": Role description changed from "%from%" to "%to%" "Ergonode\\Account\\Domain\\Event\\Role\\RolePrivilegesChangedEvent": List of pirivileges changed -"Ergonode\\Account\\Domain\\Event\\Role\\RoleRemovedEvent": Role removed +"Ergonode\\Account\\Domain\\Event\\Role\\RoleDeletedEvent": Role deleted diff --git a/module/account/src/Resources/translations/log.pl.yaml b/module/account/src/Resources/translations/log.pl.yaml index 93a70ea15..15d48df3f 100644 --- a/module/account/src/Resources/translations/log.pl.yaml +++ b/module/account/src/Resources/translations/log.pl.yaml @@ -13,4 +13,4 @@ "Ergonode\\Account\\Domain\\Event\\Role\\RoleDescriptionChangedEvent": Opis roli została zmieniona z "%from%" na "%to%" "Ergonode\\Account\\Domain\\Event\\Role\\RoleNameChangedEvent": Nazwa roli została zmieniona z "%from%" na "%to%" "Ergonode\\Account\\Domain\\Event\\Role\\RolePrivilegesChangedEvent": List uprawnień została zmieniona -"Ergonode\\Account\\Domain\\Event\\Role\\RoleRemovedEvent": Rola została usunięta +"Ergonode\\Account\\Domain\\Event\\Role\\RoleDeletedEvent": Rola została usunięta diff --git a/module/account/tests/Domain/Entity/RoleTest.php b/module/account/tests/Domain/Entity/RoleTest.php index 0cceb94fb..b51c9387e 100644 --- a/module/account/tests/Domain/Entity/RoleTest.php +++ b/module/account/tests/Domain/Entity/RoleTest.php @@ -130,13 +130,4 @@ public function testChangePrivilegesWithIncorrectType(): void $role = new Role($this->roleId, $this->name, $this->description); $role->changesPrivileges([$privileges]); } - - /** - */ - public function testDelete(): void - { - $role = new Role($this->roleId, $this->name, $this->description); - $role->remove(); - $this->assertTrue($role->isDeleted()); - } } diff --git a/module/attribute/src/Domain/Command/DeleteAttributeCommand.php b/module/attribute/src/Domain/Command/DeleteAttributeCommand.php index b5eb61d75..976b0621e 100644 --- a/module/attribute/src/Domain/Command/DeleteAttributeCommand.php +++ b/module/attribute/src/Domain/Command/DeleteAttributeCommand.php @@ -12,7 +12,6 @@ use Ergonode\Attribute\Domain\Entity\AttributeId; /** - * Class DeleteAttributeCommand */ class DeleteAttributeCommand { diff --git a/module/attribute/src/Domain/Entity/AbstractAttribute.php b/module/attribute/src/Domain/Entity/AbstractAttribute.php index 01ac6145d..b7aa2b5a3 100644 --- a/module/attribute/src/Domain/Entity/AbstractAttribute.php +++ b/module/attribute/src/Domain/Entity/AbstractAttribute.php @@ -17,7 +17,7 @@ use Ergonode\Attribute\Domain\Event\Attribute\AttributeParameterChangeEvent; use Ergonode\Attribute\Domain\Event\Attribute\AttributePlaceholderChangedEvent; use Ergonode\Attribute\Domain\Event\AttributeGroupAddedEvent; -use Ergonode\Attribute\Domain\Event\AttributeGroupRemovedEvent; +use Ergonode\Attribute\Domain\Event\AttributeGroupDeletedEvent; use Ergonode\Attribute\Domain\ValueObject\AttributeCode; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\Core\Domain\ValueObject\TranslatableString; @@ -76,6 +76,8 @@ abstract class AbstractAttribute extends AbstractAggregateRoot * @param TranslatableString $placeholder * @param bool $multilingual * @param array $parameters + * + * @throws \Exception */ public function __construct( AttributeId $id, @@ -137,6 +139,8 @@ public function isMultilingual(): bool /** * @param TranslatableString $label + * + * @throws \Exception */ public function changeLabel(TranslatableString $label): void { @@ -147,6 +151,8 @@ public function changeLabel(TranslatableString $label): void /** * @param TranslatableString $hint + * + * @throws \Exception */ public function changeHint(TranslatableString $hint): void { @@ -157,6 +163,8 @@ public function changeHint(TranslatableString $hint): void /** * @param TranslatableString $placeholder + * + * @throws \Exception */ public function changePlaceholder(TranslatableString $placeholder): void { @@ -209,6 +217,8 @@ public function inGroup(AttributeGroupId $groupId): bool /** * @param AttributeGroupId $groupId + * + * @throws \Exception */ public function addGroup(AttributeGroupId $groupId): void { @@ -219,11 +229,13 @@ public function addGroup(AttributeGroupId $groupId): void /** * @param AttributeGroupId $groupId + * + * @throws \Exception */ public function removeGroup(AttributeGroupId $groupId): void { if ($this->inGroup($groupId)) { - $this->apply(new AttributeGroupRemovedEvent($groupId)); + $this->apply(new AttributeGroupDeletedEvent($groupId)); } } @@ -288,9 +300,9 @@ protected function applyAttributeGroupAddedEvent(AttributeGroupAddedEvent $event } /** - * @param AttributeGroupRemovedEvent $event + * @param AttributeGroupDeletedEvent $event */ - protected function applyAttributeGroupRemovedEvent(AttributeGroupRemovedEvent $event): void + protected function applyAttributeGroupDeletedEvent(AttributeGroupDeletedEvent $event): void { unset($this->groups[$event->getGroupId()->getValue()]); } diff --git a/module/attribute/src/Domain/Event/AttributeGroupRemovedEvent.php b/module/attribute/src/Domain/Event/AttributeGroupDeletedEvent.php similarity index 92% rename from module/attribute/src/Domain/Event/AttributeGroupRemovedEvent.php rename to module/attribute/src/Domain/Event/AttributeGroupDeletedEvent.php index 7096d875f..46de60f91 100644 --- a/module/attribute/src/Domain/Event/AttributeGroupRemovedEvent.php +++ b/module/attribute/src/Domain/Event/AttributeGroupDeletedEvent.php @@ -15,7 +15,7 @@ /** */ -class AttributeGroupRemovedEvent implements DomainEventInterface +class AttributeGroupDeletedEvent implements DomainEventInterface { /** * @var AttributeGroupId diff --git a/module/attribute/src/Persistence/Dbal/Projector/AttributeArrayParameterChangeEventProjector.php b/module/attribute/src/Persistence/Dbal/Projector/AttributeArrayParameterChangeEventProjector.php index bafc4d86c..f31c0dc4c 100644 --- a/module/attribute/src/Persistence/Dbal/Projector/AttributeArrayParameterChangeEventProjector.php +++ b/module/attribute/src/Persistence/Dbal/Projector/AttributeArrayParameterChangeEventProjector.php @@ -13,9 +13,9 @@ use Ergonode\Attribute\Domain\Event\Attribute\AttributeArrayParameterChangeEvent; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; +use JMS\Serializer\SerializerInterface; /** */ @@ -29,17 +29,22 @@ class AttributeArrayParameterChangeEventProjector implements DomainEventProjecto private $connection; /** - * @param Connection $connection + * @var SerializerInterface */ - public function __construct(Connection $connection) + private $serializer; + + /** + * @param Connection $connection + * @param SerializerInterface $serializer + */ + public function __construct(Connection $connection, SerializerInterface $serializer) { $this->connection = $connection; + $this->serializer = $serializer; } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -47,12 +52,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -60,24 +60,17 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, AttributeArrayParameterChangeEvent::class); } - try { - $this->connection->beginTransaction(); - if (!empty($event->getTo())) { - $this->connection->update( - self::TABLE_PARAMETER, - [ - 'value' => \json_encode($event->getTo()), - ], - [ - 'attribute_id' => $aggregateId->getValue(), - 'type' => $event->getName(), - ] - ); - } - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); + if (!empty($event->getTo())) { + $this->connection->update( + self::TABLE_PARAMETER, + [ + 'value' => $this->serializer->serialize($event->getTo(), 'json'), + ], + [ + 'attribute_id' => $aggregateId->getValue(), + 'type' => $event->getName(), + ] + ); } } } diff --git a/module/attribute/src/Persistence/Dbal/Projector/AttributeCreatedEventProjector.php b/module/attribute/src/Persistence/Dbal/Projector/AttributeCreatedEventProjector.php index c8e4668a9..073aa4d9c 100644 --- a/module/attribute/src/Persistence/Dbal/Projector/AttributeCreatedEventProjector.php +++ b/module/attribute/src/Persistence/Dbal/Projector/AttributeCreatedEventProjector.php @@ -13,9 +13,9 @@ use Ergonode\Attribute\Domain\Event\Attribute\AttributeCreatedEvent; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; +use JMS\Serializer\SerializerInterface; use Ramsey\Uuid\Uuid; /** @@ -33,17 +33,22 @@ class AttributeCreatedEventProjector implements DomainEventProjectorInterface private $connection; /** - * @param Connection $connection + * @var SerializerInterface */ - public function __construct(Connection $connection) + private $serializer; + + /** + * @param Connection $connection + * @param SerializerInterface $serializer + */ + public function __construct(Connection $connection, SerializerInterface $serializer) { $this->connection = $connection; + $this->serializer = $serializer; } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -51,12 +56,9 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event + * {@inheritDoc} * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * @throws \Throwable */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -64,8 +66,7 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, AttributeCreatedEvent::class); } - try { - $this->connection->beginTransaction(); + $this->connection->transactional(function () use ($aggregateId, $event) { $labelUuid = Uuid::uuid4(); $placeholderUuid = Uuid::uuid4(); $hintUuid = Uuid::uuid4(); @@ -150,15 +151,11 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) [ 'attribute_id' => $aggregateId->getValue(), 'type' => $name, - 'value' => \json_encode($value), + 'value' => $this->serializer->serialize($value, 'json'), ] ); } } - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + }); } } diff --git a/module/attribute/src/Persistence/Dbal/Projector/AttributeHintChangedEventProjector.php b/module/attribute/src/Persistence/Dbal/Projector/AttributeHintChangedEventProjector.php index 1ab535c81..792983352 100644 --- a/module/attribute/src/Persistence/Dbal/Projector/AttributeHintChangedEventProjector.php +++ b/module/attribute/src/Persistence/Dbal/Projector/AttributeHintChangedEventProjector.php @@ -13,7 +13,6 @@ use Ergonode\Attribute\Domain\Event\Attribute\AttributeHintChangedEvent; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; use Ramsey\Uuid\Uuid; @@ -38,9 +37,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -48,12 +45,9 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event + * {@inheritDoc} * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * @throws \Throwable */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -61,8 +55,7 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, AttributeHintChangedEvent::class); } - $this->connection->beginTransaction(); - try { + $this->connection->transactional(function () use ($aggregateId, $event) { $from = $event->getFrom()->getTranslations(); $to = $event->getTo()->getTranslations(); @@ -102,11 +95,7 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) ); } } - $this->connection->commit(); - } catch (\Exception $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + }); } /** diff --git a/module/attribute/src/Persistence/Dbal/Projector/AttributeLabelChangedEventProjector.php b/module/attribute/src/Persistence/Dbal/Projector/AttributeLabelChangedEventProjector.php index 5bf051356..a4f6c4a5b 100644 --- a/module/attribute/src/Persistence/Dbal/Projector/AttributeLabelChangedEventProjector.php +++ b/module/attribute/src/Persistence/Dbal/Projector/AttributeLabelChangedEventProjector.php @@ -13,7 +13,6 @@ use Ergonode\Attribute\Domain\Event\Attribute\AttributeLabelChangedEvent; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; use Ramsey\Uuid\Uuid; @@ -38,9 +37,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -48,12 +45,9 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event + * {@inheritDoc} * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * @throws \Throwable */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -64,8 +58,7 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) $from = $event->getFrom()->getTranslations(); $to = $event->getTo()->getTranslations(); - try { - $this->connection->beginTransaction(); + $this->connection->transactional(function () use ($aggregateId, $to, $from) { foreach ($to as $language => $value) { $result = $this->connection->update( self::TABLE, @@ -102,11 +95,7 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) ); } } - $this->connection->commit(); - } catch (\Exception $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + }); } /** diff --git a/module/attribute/src/Persistence/Dbal/Projector/AttributeParameterChangeEventProjector.php b/module/attribute/src/Persistence/Dbal/Projector/AttributeParameterChangeEventProjector.php index 296124ecb..7f2eaaec4 100644 --- a/module/attribute/src/Persistence/Dbal/Projector/AttributeParameterChangeEventProjector.php +++ b/module/attribute/src/Persistence/Dbal/Projector/AttributeParameterChangeEventProjector.php @@ -13,9 +13,9 @@ use Ergonode\Attribute\Domain\Event\Attribute\AttributeParameterChangeEvent; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; +use JMS\Serializer\SerializerInterface; /** */ @@ -29,17 +29,22 @@ class AttributeParameterChangeEventProjector implements DomainEventProjectorInte private $connection; /** - * @param Connection $connection + * @var SerializerInterface */ - public function __construct(Connection $connection) + private $serializer; + + /** + * @param Connection $connection + * @param SerializerInterface $serializer + */ + public function __construct(Connection $connection, SerializerInterface $serializer) { $this->connection = $connection; + $this->serializer = $serializer; } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -47,12 +52,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -60,24 +60,17 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, AttributeParameterChangeEvent::class); } - try { - $this->connection->beginTransaction(); - if (!empty($event->getTo())) { - $this->connection->update( - self::TABLE_PARAMETER, - [ - 'value' => \json_encode($event->getTo()), - ], - [ - 'attribute_id' => $aggregateId->getValue(), - 'type' => $event->getName(), - ] - ); - } - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); + if (!empty($event->getTo())) { + $this->connection->update( + self::TABLE_PARAMETER, + [ + 'value' => $this->serializer->serialize($event->getTo(), 'json'), + ], + [ + 'attribute_id' => $aggregateId->getValue(), + 'type' => $event->getName(), + ] + ); } } } diff --git a/module/attribute/src/Persistence/Dbal/Projector/AttributePlaceholderChangedEventProjector.php b/module/attribute/src/Persistence/Dbal/Projector/AttributePlaceholderChangedEventProjector.php index fce823842..5a1408c9b 100644 --- a/module/attribute/src/Persistence/Dbal/Projector/AttributePlaceholderChangedEventProjector.php +++ b/module/attribute/src/Persistence/Dbal/Projector/AttributePlaceholderChangedEventProjector.php @@ -13,7 +13,6 @@ use Ergonode\Attribute\Domain\Event\Attribute\AttributePlaceholderChangedEvent; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; use Ramsey\Uuid\Uuid; @@ -38,9 +37,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -48,12 +45,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -61,12 +53,10 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, AttributePlaceholderChangedEvent::class); } - $from = $event->getFrom()->getTranslations(); $to = $event->getTo()->getTranslations(); - try { - $this->connection->beginTransaction(); + $this->connection->transactional(function () use ($aggregateId, $from, $to) { foreach ($to as $language => $value) { $result = $this->connection->update( self::TABLE, @@ -103,11 +93,7 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) ); } } - $this->connection->commit(); - } catch (\Exception $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + }); } /** diff --git a/module/attribute/src/Persistence/Dbal/Projector/Group/AttributeGroupAddedEventProjector.php b/module/attribute/src/Persistence/Dbal/Projector/Group/AttributeGroupAddedEventProjector.php index a9efd5c43..87f015525 100644 --- a/module/attribute/src/Persistence/Dbal/Projector/Group/AttributeGroupAddedEventProjector.php +++ b/module/attribute/src/Persistence/Dbal/Projector/Group/AttributeGroupAddedEventProjector.php @@ -13,7 +13,6 @@ use Ergonode\Attribute\Domain\Event\AttributeGroupAddedEvent; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; @@ -37,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -47,12 +44,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -60,19 +52,12 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, AttributeGroupAddedEvent::class); } - try { - $this->connection->beginTransaction(); - $this->connection->insert( - self::TABLE, - [ - 'attribute_id' => $aggregateId->getValue(), - 'attribute_group_id' => $event->getGroupId()->getValue(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + $this->connection->insert( + self::TABLE, + [ + 'attribute_id' => $aggregateId->getValue(), + 'attribute_group_id' => $event->getGroupId()->getValue(), + ] + ); } } diff --git a/module/attribute/src/Persistence/Dbal/Projector/Group/AttributeGroupCreatedEventProjector.php b/module/attribute/src/Persistence/Dbal/Projector/Group/AttributeGroupCreatedEventProjector.php index 9c32f6234..f7d16eef8 100644 --- a/module/attribute/src/Persistence/Dbal/Projector/Group/AttributeGroupCreatedEventProjector.php +++ b/module/attribute/src/Persistence/Dbal/Projector/Group/AttributeGroupCreatedEventProjector.php @@ -36,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -46,10 +44,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws \Exception + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -57,21 +52,13 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, AttributeGroupCreatedEvent::class); } - try { - $this->connection->beginTransaction(); - $this->connection->insert( - self::TABLE, - [ - 'id' => $aggregateId->getValue(), - 'label' => $event->getLabel(), + $this->connection->insert( + self::TABLE, + [ + 'id' => $aggregateId->getValue(), + 'label' => $event->getLabel(), - ] - ); - $this->connection->commit(); - } catch (\Exception $exception) { - $this->connection->rollBack(); - - throw $exception; - } + ] + ); } } diff --git a/module/attribute/src/Persistence/Dbal/Projector/Group/AttributeGroupRemovedEventProjector.php b/module/attribute/src/Persistence/Dbal/Projector/Group/AttributeGroupRemovedEventProjector.php index b102ae270..c271488c5 100644 --- a/module/attribute/src/Persistence/Dbal/Projector/Group/AttributeGroupRemovedEventProjector.php +++ b/module/attribute/src/Persistence/Dbal/Projector/Group/AttributeGroupRemovedEventProjector.php @@ -10,10 +10,9 @@ namespace Ergonode\Attribute\Persistence\Dbal\Projector\Group; use Doctrine\DBAL\Connection; -use Ergonode\Attribute\Domain\Event\AttributeGroupRemovedEvent; +use Ergonode\Attribute\Domain\Event\AttributeGroupDeletedEvent; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; @@ -37,42 +36,28 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { - return $event instanceof AttributeGroupRemovedEvent; + return $event instanceof AttributeGroupDeletedEvent; } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { - if (!$event instanceof AttributeGroupRemovedEvent) { - throw new UnsupportedEventException($event, AttributeGroupRemovedEvent::class); + if (!$event instanceof AttributeGroupDeletedEvent) { + throw new UnsupportedEventException($event, AttributeGroupDeletedEvent::class); } - try { - $this->connection->beginTransaction(); - $this->connection->delete( - self::TABLE, - [ - 'attribute_id' => $aggregateId->getValue(), - 'attribute_group_id' => $event->getGroupId()->getValue(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + $this->connection->delete( + self::TABLE, + [ + 'attribute_id' => $aggregateId->getValue(), + 'attribute_group_id' => $event->getGroupId()->getValue(), + ] + ); } } diff --git a/module/attribute/src/Persistence/Dbal/Projector/Option/AttributeOptionAddedEventProjector.php b/module/attribute/src/Persistence/Dbal/Projector/Option/AttributeOptionAddedEventProjector.php index 43b48b0ba..31388780e 100644 --- a/module/attribute/src/Persistence/Dbal/Projector/Option/AttributeOptionAddedEventProjector.php +++ b/module/attribute/src/Persistence/Dbal/Projector/Option/AttributeOptionAddedEventProjector.php @@ -17,7 +17,6 @@ use Ergonode\Attribute\Domain\ValueObject\OptionValue\StringOption; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; use Ramsey\Uuid\Uuid; @@ -43,9 +42,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -53,12 +50,9 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event + * {@inheritDoc} * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * @throws \Throwable */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -66,8 +60,7 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, AttributeOptionAddedEvent::class); } - try { - $this->connection->beginTransaction(); + $this->connection->transactional(function () use ($aggregateId, $event) { $valueId = Uuid::uuid4()->toString(); $this->connection->insert( @@ -80,11 +73,7 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) ); $this->insertOption($valueId, $event->getOption()); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + }); } /** diff --git a/module/attribute/src/Persistence/Dbal/Projector/Option/AttributeOptionChangedEventProjector.php b/module/attribute/src/Persistence/Dbal/Projector/Option/AttributeOptionChangedEventProjector.php index e1ceaee4e..73c2a66c5 100644 --- a/module/attribute/src/Persistence/Dbal/Projector/Option/AttributeOptionChangedEventProjector.php +++ b/module/attribute/src/Persistence/Dbal/Projector/Option/AttributeOptionChangedEventProjector.php @@ -17,7 +17,6 @@ use Ergonode\Attribute\Domain\ValueObject\OptionValue\StringOption; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; use Ramsey\Uuid\Uuid; @@ -52,6 +51,8 @@ public function support(DomainEventInterface $event): bool /** * {@inheritDoc} + * + * @throws \Throwable */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -59,8 +60,7 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, AttributeOptionChangedEvent::class); } - $this->connection->beginTransaction(); - try { + $this->connection->transactional(function () use ($aggregateId, $event) { $valueId = Uuid::uuid4()->toString(); $attributeId = $aggregateId->getValue(); @@ -76,12 +76,7 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) ); $this->insertOption($valueId, $event->getTo()); - - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + }); } /** diff --git a/module/attribute/src/Persistence/Dbal/Projector/Option/AttributeOptionRemovedEventProjector.php b/module/attribute/src/Persistence/Dbal/Projector/Option/AttributeOptionRemovedEventProjector.php index 088541bb4..520ccf27e 100644 --- a/module/attribute/src/Persistence/Dbal/Projector/Option/AttributeOptionRemovedEventProjector.php +++ b/module/attribute/src/Persistence/Dbal/Projector/Option/AttributeOptionRemovedEventProjector.php @@ -37,9 +37,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -47,12 +45,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws DBALException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\Exception\InvalidArgumentException + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { diff --git a/module/attribute/src/Resources/translations/log.en.yaml b/module/attribute/src/Resources/translations/log.en.yaml index b4a911951..6e771d5ec 100644 --- a/module/attribute/src/Resources/translations/log.en.yaml +++ b/module/attribute/src/Resources/translations/log.en.yaml @@ -5,7 +5,7 @@ "Ergonode\\Attribute\\Domain\\Event\\Attribute\\AttributeArrayParameterChangeEvent": Attribute parameters changed "Ergonode\\Attribute\\Domain\\Event\\Attribute\\AttributeParameterChangeEvent": Attribute parameter changed "Ergonode\\Attribute\\Domain\\Event\\AttributeGroupAddedEvent": Attribute added to group -"Ergonode\\Attribute\\Domain\\Event\\AttributeGroupRemovedEvent": Attribute removed from group +"Ergonode\\Attribute\\Domain\\Event\\AttributeGroupDeletedEvent": Attribute removed from group "Ergonode\\Attribute\\Domain\\Event\\AttributeOptionAddedEvent": Attribute option "%key%" added "Ergonode\\Attribute\\Domain\\Event\\AttributeOptionRemovedEvent": Attribute option "%key%" removed "Ergonode\\Attribute\\Domain\\Event\\AttributeOptionChangedEvent": Attribute option "%key%" changed diff --git a/module/attribute/src/Resources/translations/log.pl.yaml b/module/attribute/src/Resources/translations/log.pl.yaml index 1327bca5c..cb1f7530a 100644 --- a/module/attribute/src/Resources/translations/log.pl.yaml +++ b/module/attribute/src/Resources/translations/log.pl.yaml @@ -5,7 +5,7 @@ "Ergonode\\Attribute\\Domain\\Event\\Attribute\\AttributeArrayParameterChangeEvent": Ustawienia atrybutu zostały zmienione "Ergonode\\Attribute\\Domain\\Event\\Attribute\\AttributeParameterChangeEvent": Ustawienia atrybutu zostały zmieniona "Ergonode\\Attribute\\Domain\\Event\\AttributeGroupAddedEvent": Atrybut dodany do grupy -"Ergonode\\Attribute\\Domain\\Event\\AttributeGroupRemovedEvent": Atrybut usunięty z grupy +"Ergonode\\Attribute\\Domain\\Event\\AttributeGroupDeletedEvent": Atrybut usunięty z grupy "Ergonode\\Attribute\\Domain\\Event\\AttributeOptionAddedEvent": Opcja atrybutu "%key%" została dodana "Ergonode\\Attribute\\Domain\\Event\\AttributeOptionRemovedEvent": Opcja atrybutu "%key%" została zmieniona "Ergonode\\Attribute\\Domain\\Event\\AttributeOptionChangedEvent": Opcja atrybutu "%key%" została usunięta diff --git a/module/attribute/tests/Domain/Event/AttributeGroupRemovedEventTest.php b/module/attribute/tests/Domain/Event/AttributeGroupDeletedEventTest.php similarity index 75% rename from module/attribute/tests/Domain/Event/AttributeGroupRemovedEventTest.php rename to module/attribute/tests/Domain/Event/AttributeGroupDeletedEventTest.php index 6fe8915a7..3b224c2ef 100644 --- a/module/attribute/tests/Domain/Event/AttributeGroupRemovedEventTest.php +++ b/module/attribute/tests/Domain/Event/AttributeGroupDeletedEventTest.php @@ -10,12 +10,12 @@ namespace Ergonode\Attribute\Tests\Domain\Event; use Ergonode\Attribute\Domain\Entity\AttributeGroupId; -use Ergonode\Attribute\Domain\Event\AttributeGroupRemovedEvent; +use Ergonode\Attribute\Domain\Event\AttributeGroupDeletedEvent; use PHPUnit\Framework\TestCase; /** */ -class AttributeGroupRemovedEventTest extends TestCase +class AttributeGroupDeletedEventTest extends TestCase { /** */ @@ -23,7 +23,7 @@ public function testEventCreation(): void { /** @var AttributeGroupId $groupId */ $groupId = $this->createMock(AttributeGroupId::class); - $event = new AttributeGroupRemovedEvent($groupId); + $event = new AttributeGroupDeletedEvent($groupId); $this->assertEquals($groupId, $event->getGroupId()); } } diff --git a/module/category-tree/src/Application/Controller/Api/CategoryTreeController.php b/module/category-tree/src/Application/Controller/Api/CategoryTreeController.php index ab80fc6cb..dabd90ddf 100644 --- a/module/category-tree/src/Application/Controller/Api/CategoryTreeController.php +++ b/module/category-tree/src/Application/Controller/Api/CategoryTreeController.php @@ -196,7 +196,6 @@ public function createTree(Request $request): Response { $model = new CategoryTreeCreateFormModel(); $form = $this->createForm(CategoryTreeCreateForm::class, $model); - $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { diff --git a/module/category-tree/src/Persistence/Dbal/Projector/CategoryTreeCreatedEventProjector.php b/module/category-tree/src/Persistence/Dbal/Projector/CategoryTreeCreatedEventProjector.php index 03c1de2f0..c390e584a 100644 --- a/module/category-tree/src/Persistence/Dbal/Projector/CategoryTreeCreatedEventProjector.php +++ b/module/category-tree/src/Persistence/Dbal/Projector/CategoryTreeCreatedEventProjector.php @@ -13,9 +13,9 @@ use Ergonode\CategoryTree\Domain\Event\CategoryTreeCreatedEvent; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; +use JMS\Serializer\SerializerInterface; /** */ @@ -29,17 +29,22 @@ class CategoryTreeCreatedEventProjector implements DomainEventProjectorInterface protected $connection; /** - * @param Connection $connection + * @var SerializerInterface */ - public function __construct(Connection $connection) + private $serializer; + + /** + * @param Connection $connection + * @param SerializerInterface $serializer + */ + public function __construct(Connection $connection, SerializerInterface $serializer) { $this->connection = $connection; + $this->serializer = $serializer; } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -47,12 +52,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -60,20 +60,13 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, CategoryTreeCreatedEvent::class); } - try { - $this->connection->beginTransaction(); - $this->connection->insert( - self::TABLE, - [ - 'id' => $event->getId(), - 'code' => $event->getCode(), - 'name' => json_encode($event->getName()->getTranslations()), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + $this->connection->insert( + self::TABLE, + [ + 'id' => $event->getId(), + 'code' => $event->getCode(), + 'name' => $this->serializer->serialize($event->getName()->getTranslations(), 'json'), + ] + ); } } diff --git a/module/category-tree/src/Persistence/Dbal/Projector/CategoryTreeNameChangedEventProjector.php b/module/category-tree/src/Persistence/Dbal/Projector/CategoryTreeNameChangedEventProjector.php index 7dfcdc3a8..373663744 100644 --- a/module/category-tree/src/Persistence/Dbal/Projector/CategoryTreeNameChangedEventProjector.php +++ b/module/category-tree/src/Persistence/Dbal/Projector/CategoryTreeNameChangedEventProjector.php @@ -13,9 +13,9 @@ use Ergonode\CategoryTree\Domain\Event\CategoryTreeNameChangedEvent; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; +use JMS\Serializer\SerializerInterface; /** */ @@ -29,17 +29,22 @@ class CategoryTreeNameChangedEventProjector implements DomainEventProjectorInter private $connection; /** - * @param Connection $connection + * @var SerializerInterface */ - public function __construct(Connection $connection) + private $serializer; + + /** + * @param Connection $connection + * @param SerializerInterface $serializer + */ + public function __construct(Connection $connection, SerializerInterface $serializer) { $this->connection = $connection; + $this->serializer = $serializer; } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -47,12 +52,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -60,21 +60,14 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, CategoryTreeNameChangedEvent::class); } - try { - $this->connection->beginTransaction(); - $this->connection->update( - self::TABLE, - [ - 'name' => json_encode($event->getTo()->getTranslations()), - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + $this->connection->update( + self::TABLE, + [ + 'name' => $this->serializer->serialize($event->getTo()->getTranslations(), 'json'), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/category/src/Persistence/Dbal/Projector/CategoryCreatedEventProjector.php b/module/category/src/Persistence/Dbal/Projector/CategoryCreatedEventProjector.php index d12ddcff3..878fd0008 100644 --- a/module/category/src/Persistence/Dbal/Projector/CategoryCreatedEventProjector.php +++ b/module/category/src/Persistence/Dbal/Projector/CategoryCreatedEventProjector.php @@ -15,7 +15,6 @@ use Ergonode\Category\Domain\Event\CategoryCreatedEvent; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; use JMS\Serializer\SerializerInterface; @@ -51,9 +50,7 @@ public function __construct(Connection $connection, SerializerInterface $seriali } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -61,12 +58,9 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event + * {@inheritDoc} * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * @throws \Throwable */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -74,13 +68,12 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, CategoryCreatedEvent::class); } - try { - $this->connection->beginTransaction(); + $this->connection->transactional(function () use ($aggregateId, $event) { $this->connection->insert( self::TABLE, [ 'id' => $aggregateId->getValue(), - 'name' => json_encode($event->getName()->getTranslations()), + 'name' => $this->serializer->serialize($event->getName()->getTranslations(), 'json'), 'code' => $event->getCode()->getValue(), ] ); @@ -116,10 +109,6 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) ] ); } - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + }); } } diff --git a/module/category/src/Persistence/Dbal/Projector/CategoryNameChangedEventProjector.php b/module/category/src/Persistence/Dbal/Projector/CategoryNameChangedEventProjector.php index 76067415f..b6e429b10 100644 --- a/module/category/src/Persistence/Dbal/Projector/CategoryNameChangedEventProjector.php +++ b/module/category/src/Persistence/Dbal/Projector/CategoryNameChangedEventProjector.php @@ -13,9 +13,9 @@ use Ergonode\Category\Domain\Event\CategoryNameChangedEvent; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; +use JMS\Serializer\SerializerInterface; /** */ @@ -29,17 +29,22 @@ class CategoryNameChangedEventProjector implements DomainEventProjectorInterface private $connection; /** - * @param Connection $connection + * @var SerializerInterface */ - public function __construct(Connection $connection) + private $serializer; + + /** + * @param Connection $connection + * @param SerializerInterface $serializer + */ + public function __construct(Connection $connection, SerializerInterface $serializer) { $this->connection = $connection; + $this->serializer = $serializer; } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -47,12 +52,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -60,21 +60,14 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, CategoryNameChangedEvent::class); } - try { - $this->connection->beginTransaction(); - $this->connection->update( - self::TABLE, - [ - 'name' => json_encode($event->getTo()->getTranslations()), - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + $this->connection->update( + self::TABLE, + [ + 'name' => $this->serializer->serialize($event->getTo()->getTranslations(), 'json'), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/core/migrations/Version20180610093112.php b/module/core/migrations/Version20180610093112.php index 5cdeda1d0..d814a9580 100644 --- a/module/core/migrations/Version20180610093112.php +++ b/module/core/migrations/Version20180610093112.php @@ -36,20 +36,6 @@ public function up(Schema $schema): void ); $this->addSql('CREATE TABLE translation (translation_id UUID NOT NULL, language VARCHAR(2) NOT NULL, phrase VARCHAR(255), PRIMARY KEY(translation_id, language))'); - $this->addSql( - 'CREATE TABLE event_store ( - id BIGSERIAL NOT NULL, - aggregate_id uuid NOT NULL, - sequence int, - event character varying(255) NOT NULL, - payload jsonb NOT NULL, - recorded_by uuid default NULL, - recorded_at timestamp without time zone NOT NULL, - CONSTRAINT event_store_pkey PRIMARY KEY (id) - )' - ); - $this->addSql('CREATE UNIQUE INDEX event_store_aggregate_id_sequence_key ON event_store USING btree (aggregate_id, sequence)'); - foreach ($this->getLanguages() as $iso => $name) { $this->addSql('INSERT INTO language (id, iso, name) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), $iso, $name]); } diff --git a/module/designer/src/Application/Controller/Api/TemplateController.php b/module/designer/src/Application/Controller/Api/TemplateController.php index 222ab6ed4..441eae8d8 100644 --- a/module/designer/src/Application/Controller/Api/TemplateController.php +++ b/module/designer/src/Application/Controller/Api/TemplateController.php @@ -354,11 +354,11 @@ public function getTemplate(Template $template): Response * description="Can't remove Template, it has relations to products" * ) * + * @ParamConverter(class="Ergonode\Designer\Domain\Entity\Template") + * * @param Template $template * * @return Response - * - * @ParamConverter(class="Ergonode\Designer\Domain\Entity\Template") */ public function deleteTemplate(Template $template): Response { @@ -366,6 +366,7 @@ public function deleteTemplate(Template $template): Response throw new ConflictHttpException('Can\'t remove Template, it has relations to products'); } + // @todo Handler not found $command = new DeleteTemplateCommand($template->getId()); $this->messageBus->dispatch($command); diff --git a/module/designer/src/Persistence/Dbal/Projector/Group/TemplateGroupCreatedEventProjector.php b/module/designer/src/Persistence/Dbal/Projector/Group/TemplateGroupCreatedEventProjector.php index ee01d896b..c6927d2eb 100644 --- a/module/designer/src/Persistence/Dbal/Projector/Group/TemplateGroupCreatedEventProjector.php +++ b/module/designer/src/Persistence/Dbal/Projector/Group/TemplateGroupCreatedEventProjector.php @@ -28,8 +28,6 @@ class TemplateGroupCreatedEventProjector implements DomainEventProjectorInterfac private $connection; /** - * TemplateCreateEventProjector constructor. - * * @param Connection $connection */ public function __construct(Connection $connection) @@ -38,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -48,33 +44,20 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { - if (!$event instanceof TemplateGroupCreatedEvent) { throw new UnsupportedEventException($event, TemplateGroupCreatedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->insert( - self::TABLE, - [ - 'id' => $aggregateId->getValue(), - 'name' => $event->getName(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->connection->insert( + self::TABLE, + [ + 'id' => $aggregateId->getValue(), + 'name' => $event->getName(), + ] + ); } } diff --git a/module/designer/src/Persistence/Dbal/Projector/TemplateCreatedEventProjector.php b/module/designer/src/Persistence/Dbal/Projector/TemplateCreatedEventProjector.php index 140b3ff0b..e0458012f 100644 --- a/module/designer/src/Persistence/Dbal/Projector/TemplateCreatedEventProjector.php +++ b/module/designer/src/Persistence/Dbal/Projector/TemplateCreatedEventProjector.php @@ -36,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -46,35 +44,22 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { - if (!$event instanceof TemplateCreatedEvent) { throw new UnsupportedEventException($event, TemplateCreatedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->insert( - self::TABLE, - [ - 'id' => $aggregateId->getValue(), - 'name' => $event->getName(), - 'image_id' => $event->getImageId() ? $event->getImageId()->getValue() : null, - 'template_group_id' => $event->getGroupId()->getValue(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->connection->insert( + self::TABLE, + [ + 'id' => $aggregateId->getValue(), + 'name' => $event->getName(), + 'image_id' => $event->getImageId() ? $event->getImageId()->getValue() : null, + 'template_group_id' => $event->getGroupId()->getValue(), + ] + ); } } diff --git a/module/designer/src/Persistence/Dbal/Projector/TemplateElementAddedEventProjector.php b/module/designer/src/Persistence/Dbal/Projector/TemplateElementAddedEventProjector.php index 9db238910..0c477fc37 100644 --- a/module/designer/src/Persistence/Dbal/Projector/TemplateElementAddedEventProjector.php +++ b/module/designer/src/Persistence/Dbal/Projector/TemplateElementAddedEventProjector.php @@ -44,9 +44,7 @@ public function __construct(Connection $connection, SerializerInterface $seriali } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -54,38 +52,25 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { - if (!$event instanceof TemplateElementAddedEvent) { throw new UnsupportedEventException($event, TemplateElementAddedEvent::class); } - $this->connection->beginTransaction(); - try { - $element = $event->getElement(); - $this->connection->insert( - self::ELEMENT_TABLE, - [ - 'template_id' => $aggregateId->getValue(), - 'x' => $element->getPosition()->getX(), - 'y' => $element->getPosition()->getY(), - 'width' => $element->getSize()->getWidth(), - 'height' => $element->getSize()->getHeight(), - 'properties' => $this->serializer->serialize($element->getProperties(), 'json'), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $element = $event->getElement(); + $this->connection->insert( + self::ELEMENT_TABLE, + [ + 'template_id' => $aggregateId->getValue(), + 'x' => $element->getPosition()->getX(), + 'y' => $element->getPosition()->getY(), + 'width' => $element->getSize()->getWidth(), + 'height' => $element->getSize()->getHeight(), + 'properties' => $this->serializer->serialize($element->getProperties(), 'json'), + ] + ); } } diff --git a/module/designer/src/Persistence/Dbal/Projector/TemplateElementChangedEventProjector.php b/module/designer/src/Persistence/Dbal/Projector/TemplateElementChangedEventProjector.php index d80d87ae6..94498fa64 100644 --- a/module/designer/src/Persistence/Dbal/Projector/TemplateElementChangedEventProjector.php +++ b/module/designer/src/Persistence/Dbal/Projector/TemplateElementChangedEventProjector.php @@ -44,9 +44,7 @@ public function __construct(Connection $connection, SerializerInterface $seriali } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -54,40 +52,27 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { - if (!$event instanceof TemplateElementChangedEvent) { throw new UnsupportedEventException($event, TemplateElementChangedEvent::class); } - $this->connection->beginTransaction(); - try { - $element = $event->getElement(); - $this->connection->update( - self::ELEMENT_TABLE, - [ - 'width' => $element->getSize()->getWidth(), - 'height' => $element->getSize()->getHeight(), - 'properties' => $this->serializer->serialize($element->getProperties(), 'json'), - ], - [ - 'template_id' => $aggregateId->getValue(), - 'x' => $element->getPosition()->getX(), - 'y' => $element->getPosition()->getY(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $element = $event->getElement(); + $this->connection->update( + self::ELEMENT_TABLE, + [ + 'width' => $element->getSize()->getWidth(), + 'height' => $element->getSize()->getHeight(), + 'properties' => $this->serializer->serialize($element->getProperties(), 'json'), + ], + [ + 'template_id' => $aggregateId->getValue(), + 'x' => $element->getPosition()->getX(), + 'y' => $element->getPosition()->getY(), + ] + ); } } diff --git a/module/designer/src/Persistence/Dbal/Projector/TemplateElementRemovedEventProjector.php b/module/designer/src/Persistence/Dbal/Projector/TemplateElementRemovedEventProjector.php index 6c60c16d7..056eb2c7c 100644 --- a/module/designer/src/Persistence/Dbal/Projector/TemplateElementRemovedEventProjector.php +++ b/module/designer/src/Persistence/Dbal/Projector/TemplateElementRemovedEventProjector.php @@ -28,8 +28,6 @@ class TemplateElementRemovedEventProjector implements DomainEventProjectorInterf private $connection; /** - * TemplateCreateEventProjector constructor. - * * @param Connection $connection */ public function __construct(Connection $connection) @@ -38,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -48,34 +44,21 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { - if (!$event instanceof TemplateElementRemovedEvent) { throw new UnsupportedEventException($event, TemplateElementRemovedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->delete( - self::ELEMENT_TABLE, - [ - 'template_id' => $aggregateId->getValue(), - 'x' => $event->getPosition()->getX(), - 'y' => $event->getPosition()->getY(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->connection->delete( + self::ELEMENT_TABLE, + [ + 'template_id' => $aggregateId->getValue(), + 'x' => $event->getPosition()->getX(), + 'y' => $event->getPosition()->getY(), + ] + ); } } diff --git a/module/designer/src/Persistence/Dbal/Projector/TemplateGroupChangedEventProjector.php b/module/designer/src/Persistence/Dbal/Projector/TemplateGroupChangedEventProjector.php index 12fb788b7..5281f4a2e 100644 --- a/module/designer/src/Persistence/Dbal/Projector/TemplateGroupChangedEventProjector.php +++ b/module/designer/src/Persistence/Dbal/Projector/TemplateGroupChangedEventProjector.php @@ -28,8 +28,6 @@ class TemplateGroupChangedEventProjector implements DomainEventProjectorInterfac private $connection; /** - * TemplateCreateEventProjector constructor. - * * @param Connection $connection */ public function __construct(Connection $connection) @@ -38,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -48,35 +44,22 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { - if (!$event instanceof TemplateGroupChangedEvent) { throw new UnsupportedEventException($event, TemplateGroupChangedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->update( - self::TABLE, - [ - 'template_group_id' => $event->getNew()->getValue(), - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->connection->update( + self::TABLE, + [ + 'template_group_id' => $event->getNew()->getValue(), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/designer/src/Persistence/Dbal/Projector/TemplateImageAddedEventProjector.php b/module/designer/src/Persistence/Dbal/Projector/TemplateImageAddedEventProjector.php index 6a32cad71..75267a177 100644 --- a/module/designer/src/Persistence/Dbal/Projector/TemplateImageAddedEventProjector.php +++ b/module/designer/src/Persistence/Dbal/Projector/TemplateImageAddedEventProjector.php @@ -36,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -46,35 +44,22 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { - if (!$event instanceof TemplateImageAddedEvent) { throw new UnsupportedEventException($event, TemplateImageAddedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->update( - self::TABLE, - [ - 'image_id' => $event->getImageId()->getValue(), - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->connection->update( + self::TABLE, + [ + 'image_id' => $event->getImageId()->getValue(), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/designer/src/Persistence/Dbal/Projector/TemplateImageChangedEventProjector.php b/module/designer/src/Persistence/Dbal/Projector/TemplateImageChangedEventProjector.php index 2b45d54b1..687f8262d 100644 --- a/module/designer/src/Persistence/Dbal/Projector/TemplateImageChangedEventProjector.php +++ b/module/designer/src/Persistence/Dbal/Projector/TemplateImageChangedEventProjector.php @@ -36,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -46,35 +44,22 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { - if (!$event instanceof TemplateImageChangedEvent) { throw new UnsupportedEventException($event, TemplateImageChangedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->update( - self::TABLE, - [ - 'image_id' => $event->getTo()->getValue(), - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->connection->update( + self::TABLE, + [ + 'image_id' => $event->getTo()->getValue(), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/designer/src/Persistence/Dbal/Projector/TemplateImageRemovedEventProjector.php b/module/designer/src/Persistence/Dbal/Projector/TemplateImageRemovedEventProjector.php index d99c4e2de..5761d49b0 100644 --- a/module/designer/src/Persistence/Dbal/Projector/TemplateImageRemovedEventProjector.php +++ b/module/designer/src/Persistence/Dbal/Projector/TemplateImageRemovedEventProjector.php @@ -36,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -46,35 +44,22 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { - if (!$event instanceof TemplateImageRemovedEvent) { throw new UnsupportedEventException($event, TemplateImageRemovedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->update( - self::TABLE, - [ - 'image_id' => null, - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->connection->update( + self::TABLE, + [ + 'image_id' => null, + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/designer/src/Persistence/Dbal/Projector/TemplateNameChangedEventProjector.php b/module/designer/src/Persistence/Dbal/Projector/TemplateNameChangedEventProjector.php index 56b90e775..9bf54e331 100644 --- a/module/designer/src/Persistence/Dbal/Projector/TemplateNameChangedEventProjector.php +++ b/module/designer/src/Persistence/Dbal/Projector/TemplateNameChangedEventProjector.php @@ -38,9 +38,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -48,35 +46,22 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { - if (!$event instanceof TemplateNameChangedEvent) { throw new UnsupportedEventException($event, TemplateNameChangedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->update( - self::TABLE, - [ - 'name' => $event->getTo(), - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->connection->update( + self::TABLE, + [ + 'name' => $event->getTo(), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/designer/src/Persistence/Dbal/Projector/TemplateRemovedEventProjector.php b/module/designer/src/Persistence/Dbal/Projector/TemplateRemovedEventProjector.php index bc77e329b..fc3507493 100644 --- a/module/designer/src/Persistence/Dbal/Projector/TemplateRemovedEventProjector.php +++ b/module/designer/src/Persistence/Dbal/Projector/TemplateRemovedEventProjector.php @@ -37,9 +37,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -47,11 +45,8 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event + * {@inheritDoc} * - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException * @throws \Throwable */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void @@ -60,24 +55,20 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, TemplateRemovedEvent::class); } - $this->connection->beginTransaction(); - try { + $this->connection->transactional(function () use ($aggregateId) { $this->connection->delete( self::ELEMENT_TABLE, [ 'template_id' => $aggregateId->getValue(), ] ); + $this->connection->delete( self::TABLE, [ 'id' => $aggregateId->getValue(), ] ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + }); } } diff --git a/module/editor/src/Persistence/Projector/ProductCreatedEventProjector.php b/module/editor/src/Persistence/Projector/ProductCreatedEventProjector.php index 2c0573e2b..4974b292a 100644 --- a/module/editor/src/Persistence/Projector/ProductCreatedEventProjector.php +++ b/module/editor/src/Persistence/Projector/ProductCreatedEventProjector.php @@ -12,7 +12,6 @@ use Doctrine\DBAL\Connection; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; use Ergonode\Product\Domain\Event\ProductCreated; @@ -37,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -47,12 +44,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -60,19 +52,12 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, ProductCreated::class); } - try { - $this->connection->beginTransaction(); - $this->connection->insert( - self::TABLE, - [ - 'product_id' => $aggregateId->getValue(), - 'template_id' => $event->getTemplateId()->getValue(), - ] - ); - $this->connection->commit(); - } catch (\Exception $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + $this->connection->insert( + self::TABLE, + [ + 'product_id' => $aggregateId->getValue(), + 'template_id' => $event->getTemplateId()->getValue(), + ] + ); } } diff --git a/module/editor/src/Persistence/Projector/ProductDraftAppliedEventProjector.php b/module/editor/src/Persistence/Projector/ProductDraftAppliedEventProjector.php index 7eb88e6f3..5e2f26a86 100644 --- a/module/editor/src/Persistence/Projector/ProductDraftAppliedEventProjector.php +++ b/module/editor/src/Persistence/Projector/ProductDraftAppliedEventProjector.php @@ -13,7 +13,6 @@ use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\Editor\Domain\Event\ProductDraftApplied; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; @@ -37,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -47,12 +44,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -60,24 +52,17 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, ProductDraftApplied::class); } - try { - $this->connection->beginTransaction(); - $this->connection->update( - self::DRAFT_TABLE, - [ - 'applied' => true, - ], - [ - 'id' => $aggregateId->getValue(), - ], - [ - 'applied' => \PDO::PARAM_BOOL, - ] - ); - $this->connection->commit(); - } catch (\Exception $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + $this->connection->update( + self::DRAFT_TABLE, + [ + 'applied' => true, + ], + [ + 'id' => $aggregateId->getValue(), + ], + [ + 'applied' => \PDO::PARAM_BOOL, + ] + ); } } diff --git a/module/editor/src/Persistence/Projector/ProductDraftCreatedEventProjector.php b/module/editor/src/Persistence/Projector/ProductDraftCreatedEventProjector.php index 583d3c5e7..8c9d76d9a 100644 --- a/module/editor/src/Persistence/Projector/ProductDraftCreatedEventProjector.php +++ b/module/editor/src/Persistence/Projector/ProductDraftCreatedEventProjector.php @@ -13,7 +13,6 @@ use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\Editor\Domain\Event\ProductDraftCreated; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; @@ -37,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -47,12 +44,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -60,20 +52,13 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, ProductDraftCreated::class); } - try { - $this->connection->beginTransaction(); - $this->connection->insert( - self::DRAFT_TABLE, - [ - 'id' => $aggregateId->getValue(), - 'product_id' => $event->getProductId() ? $event->getProductId()->getValue() : null, - 'type' => $event->getProductId() ? 'EDITED': 'NEW', - ] - ); - $this->connection->commit(); - } catch (\Exception $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + $this->connection->insert( + self::DRAFT_TABLE, + [ + 'id' => $aggregateId->getValue(), + 'product_id' => $event->getProductId() ? $event->getProductId()->getValue() : null, + 'type' => $event->getProductId() ? 'EDITED': 'NEW', + ] + ); } } diff --git a/module/editor/src/Persistence/Projector/ProductDraftValueAddedEventProjector.php b/module/editor/src/Persistence/Projector/ProductDraftValueAddedEventProjector.php index 0ea834301..aec1d9152 100644 --- a/module/editor/src/Persistence/Projector/ProductDraftValueAddedEventProjector.php +++ b/module/editor/src/Persistence/Projector/ProductDraftValueAddedEventProjector.php @@ -15,7 +15,6 @@ use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\Editor\Domain\Event\ProductDraftValueAdded; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; use Ergonode\Value\Domain\ValueObject\StringCollectionValue; @@ -45,9 +44,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -55,12 +52,9 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event + * {@inheritDoc} * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * @throws \Throwable */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -68,19 +62,13 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, ProductDraftValueAdded::class); } - try { - $this->connection->beginTransaction(); + $this->connection->transactional(function () use ($aggregateId, $event) { $draftId = $aggregateId->getValue(); $elementId = AttributeId::fromKey($event->getAttributeCode())->getValue(); - $value = $event->getTo(); $this->insertValue($draftId, $elementId, $value); - $this->connection->commit(); - } catch (\Exception $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + }); } /** diff --git a/module/editor/src/Persistence/Projector/ProductDraftValueChangedEventProjector.php b/module/editor/src/Persistence/Projector/ProductDraftValueChangedEventProjector.php index a570342e1..f99b2d9b5 100644 --- a/module/editor/src/Persistence/Projector/ProductDraftValueChangedEventProjector.php +++ b/module/editor/src/Persistence/Projector/ProductDraftValueChangedEventProjector.php @@ -15,7 +15,6 @@ use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\Editor\Domain\Event\ProductDraftValueChanged; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; use Ergonode\Value\Domain\ValueObject\StringCollectionValue; @@ -45,9 +44,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -55,12 +52,9 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event + * {@inheritDoc} * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * @throws \Throwable */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -68,8 +62,7 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, ProductDraftValueChanged::class); } - try { - $this->connection->beginTransaction(); + $this->connection->transactional(function () use ($aggregateId, $event) { $draftId = $aggregateId->getValue(); $elementId = AttributeId::fromKey($event->getAttributeCode())->getValue(); @@ -77,11 +70,7 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) $this->delete($draftId, $elementId); $this->insertValue($draftId, $elementId, $value); - $this->connection->commit(); - } catch (\Exception $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + }); } /** diff --git a/module/editor/src/Persistence/Projector/ProductDraftValueRemovedEventProjector.php b/module/editor/src/Persistence/Projector/ProductDraftValueRemovedEventProjector.php index 098781159..aea26699f 100644 --- a/module/editor/src/Persistence/Projector/ProductDraftValueRemovedEventProjector.php +++ b/module/editor/src/Persistence/Projector/ProductDraftValueRemovedEventProjector.php @@ -15,7 +15,6 @@ use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\Editor\Domain\Event\ProductDraftValueRemoved; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; @@ -39,9 +38,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -49,12 +46,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -62,17 +54,10 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, ProductDraftValueRemoved::class); } - try { - $this->connection->beginTransaction(); - $draftId = $aggregateId->getValue(); - $elementId = AttributeId::fromKey($event->getAttributeCode())->getValue(); + $draftId = $aggregateId->getValue(); + $elementId = AttributeId::fromKey($event->getAttributeCode())->getValue(); - $this->delete($draftId, $elementId); - $this->connection->commit(); - } catch (\Exception $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + $this->delete($draftId, $elementId); } /** diff --git a/module/event-sourcing/migrations/Version20190903112016.php b/module/event-sourcing/migrations/Version20190903112016.php new file mode 100644 index 000000000..be4621216 --- /dev/null +++ b/module/event-sourcing/migrations/Version20190903112016.php @@ -0,0 +1,52 @@ +addSql(' + CREATE TABLE event_store ( + id BIGSERIAL NOT NULL, + aggregate_id uuid NOT NULL, + sequence int NOT NULL, + event character varying(255) NOT NULL, + payload jsonb NOT NULL, + recorded_by uuid default NULL, + recorded_at timestamp without time zone NOT NULL, + CONSTRAINT event_store_pkey PRIMARY KEY (id) + ) + '); + $this->addSql('CREATE UNIQUE INDEX event_store_unique_key ON event_store USING btree (aggregate_id, sequence)'); + + $this->addSql(' + CREATE TABLE event_store_history ( + id BIGSERIAL NOT NULL, + aggregate_id uuid NOT NULL, + sequence int NOT NULL, + variant int NOT NULL DEFAULT 1, + event character varying(255) NOT NULL, + payload jsonb NOT NULL, + recorded_by uuid default NULL, + recorded_at timestamp without time zone NOT NULL, + CONSTRAINT event_store_history_pkey PRIMARY KEY (id) + ) + '); + $this->addSql('CREATE UNIQUE INDEX event_store_history_unique_key ON event_store_history USING btree (aggregate_id, sequence, variant)'); + } +} diff --git a/module/event-sourcing/src/Domain/AbstractAggregateRoot.php b/module/event-sourcing/src/Domain/AbstractAggregateRoot.php index 2b5921f63..0f3f776f5 100644 --- a/module/event-sourcing/src/Domain/AbstractAggregateRoot.php +++ b/module/event-sourcing/src/Domain/AbstractAggregateRoot.php @@ -10,6 +10,7 @@ namespace Ergonode\EventSourcing\Domain; use Ergonode\Core\Domain\Entity\AbstractId; +use Ergonode\EventSourcing\Infrastructure\AbstractDeleteEvent; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; use Ergonode\EventSourcing\Infrastructure\Envelope\DomainEventEnvelope; use Ergonode\EventSourcing\Infrastructure\Stream\DomainEventStream; @@ -98,14 +99,16 @@ public function getSequence(): int private function handle(DomainEventInterface $event, \DateTime $recordedAt): void { $this->editedAt = $recordedAt; - $classArray = explode('\\', get_class($event)); - $class = end($classArray); - $method = sprintf('apply%s', $class); - if (!method_exists($this, $method)) { - throw new \RuntimeException(sprintf('Can\'t find method %s for event in aggregate %s', $method, get_class($this))); - } + if (!$event instanceof AbstractDeleteEvent) { + $classArray = explode('\\', get_class($event)); + $class = end($classArray); + $method = sprintf('apply%s', $class); + if (!method_exists($this, $method)) { + throw new \RuntimeException(sprintf('Can\'t find method %s for event in aggregate %s', $method, get_class($this))); + } - $this->$method($event); + $this->$method($event); + } } } diff --git a/module/event-sourcing/src/Infrastructure/AbstractDeleteEvent.php b/module/event-sourcing/src/Infrastructure/AbstractDeleteEvent.php new file mode 100644 index 000000000..ca1840f40 --- /dev/null +++ b/module/event-sourcing/src/Infrastructure/AbstractDeleteEvent.php @@ -0,0 +1,17 @@ +projector->projection($envelope); + try { + $this->projector->projection($envelope); + } catch (\Throwable $exception) { + throw new ProjectorException($envelope->getEvent(), $exception); + } } } diff --git a/module/event-sourcing/src/Infrastructure/Store/DbalDomainEventStore.php b/module/event-sourcing/src/Infrastructure/Store/DbalDomainEventStore.php index 6845572c7..c0ae3bd61 100644 --- a/module/event-sourcing/src/Infrastructure/Store/DbalDomainEventStore.php +++ b/module/event-sourcing/src/Infrastructure/Store/DbalDomainEventStore.php @@ -72,11 +72,7 @@ public function __construct( } /** - * @param AbstractId $id - * - * @param string $table - * - * @return DomainEventStream + * {@inheritDoc} * * @throws \Psr\Cache\InvalidArgumentException */ @@ -119,19 +115,16 @@ public function load(AbstractId $id, ?string $table = null): DomainEventStream } /** - * @param AbstractId $id - * @param DomainEventStream $stream - * - * @param string $table + * {@inheritDoc} * - * @throws \Doctrine\DBAL\ConnectionException * @throws \Throwable */ public function append(AbstractId $id, DomainEventStream $stream, ?string $table = null): void { $table = $table ?: self::TABLE; - $this->connection->beginTransaction(); - try { + + $this->connection->transactional(function () use ($id, $stream, $table) { + $userId = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser()->getId()->getValue() : null; foreach ($stream as $envelope) { $payload = $this->serializer->serialize($envelope->getEvent(), 'json'); $this->connection->insert( @@ -142,14 +135,53 @@ public function append(AbstractId $id, DomainEventStream $stream, ?string $table 'event' => $envelope->getType(), 'payload' => $payload, 'recorded_at' => $envelope->getRecordedAt()->format('Y-m-d H:i:s'), - 'recorded_by' => $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser()->getId()->getValue() : null, + 'recorded_by' => $userId, ] ); } - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + }); + } + + /** + * {@inheritDoc} + * + * @throws \Throwable + * @throws \Psr\Cache\InvalidArgumentException + */ + public function delete(AbstractId $id, ?string $table = null): void + { + $dataTable = $table ?? self::TABLE; + $historyTable = sprintf('%s_history', $dataTable); + + $this->connection->transactional(function () use ($id, $dataTable, $historyTable) { + $queryBuilder = $this->connection->createQueryBuilder() + ->from($historyTable) + ->select('variant') + ->where('aggregate_id = :id') + ->orderBy('variant', 'DESC') + ->setMaxResults(1) + ->setParameter('id', $id->getValue()); + $version = $queryBuilder->execute()->fetchColumn(); + + if (empty($version)) { + $version = 1; + } + + $this->connection->executeQuery( + sprintf( + 'INSERT INTO %s (aggregate_id, sequence, event, payload, recorded_by, recorded_at, variant) + SELECT aggregate_id, sequence, event, payload, recorded_by, recorded_at, %d FROM %s WHERE aggregate_id = ?', + $historyTable, + $version, + $dataTable + ), + [$id->getValue()] + ); + + $this->connection->delete($dataTable, ['aggregate_id' => $id->getValue()]); + }); + + $key = sprintf(self::KEY, $id->getValue()); + $this->cache->deleteItem($key); } } diff --git a/module/importer/migrations/Version20180618134343.php b/module/importer/migrations/Version20180618134343.php index cd07b086a..f2e42a8bc 100644 --- a/module/importer/migrations/Version20180618134343.php +++ b/module/importer/migrations/Version20180618134343.php @@ -5,7 +5,6 @@ namespace Ergonode\Migration; use Doctrine\DBAL\Schema\Schema; -use Ergonode\Migration\AbstractErgonodeMigration; use Ramsey\Uuid\Uuid; /** @@ -59,6 +58,21 @@ public function up(Schema $schema): void )' ); + $this->addSql(' + CREATE TABLE importer.event_store_history ( + id BIGSERIAL NOT NULL, + aggregate_id uuid NOT NULL, + sequence int NOT NULL, + variant int NOT NULL DEFAULT 1, + event character varying(255) NOT NULL, + payload jsonb NOT NULL, + recorded_by uuid default NULL, + recorded_at timestamp without time zone NOT NULL, + CONSTRAINT event_store_history_pkey PRIMARY KEY (id) + ) + '); + $this->addSql('CREATE UNIQUE INDEX importer_event_store_history_unique_key ON importer.event_store_history USING btree (aggregate_id, sequence, variant)'); + $this->addSql('CREATE INDEX import_line_import_id_idx ON importer.import_line USING btree (import_id)'); $this->addSql('ALTER TABLE importer.import_line ADD CONSTRAINT import_line_import_id_fk FOREIGN KEY (import_id) REFERENCES importer.import (id) ON DELETE CASCADE'); diff --git a/module/product/src/Persistence/Dbal/Projector/ProductAddedToCategoryEventProjector.php b/module/product/src/Persistence/Dbal/Projector/ProductAddedToCategoryEventProjector.php index 7eebc6923..e38cf346a 100644 --- a/module/product/src/Persistence/Dbal/Projector/ProductAddedToCategoryEventProjector.php +++ b/module/product/src/Persistence/Dbal/Projector/ProductAddedToCategoryEventProjector.php @@ -39,9 +39,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -49,11 +47,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -61,20 +55,12 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, ProductAddedToCategory::class); } - $this->connection->beginTransaction(); - - try { - $this->connection->insert( - self::TABLE_PRODUCT_CATEGORY, - [ - 'product_id' => $aggregateId->getValue(), - 'category_id' => CategoryId::fromCode($event->getCategoryCode()), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->connection->insert( + self::TABLE_PRODUCT_CATEGORY, + [ + 'product_id' => $aggregateId->getValue(), + 'category_id' => CategoryId::fromCode($event->getCategoryCode()), + ] + ); } } diff --git a/module/product/src/Persistence/Dbal/Projector/ProductCreateEventProjector.php b/module/product/src/Persistence/Dbal/Projector/ProductCreateEventProjector.php index e83b934a4..f77ee11d9 100644 --- a/module/product/src/Persistence/Dbal/Projector/ProductCreateEventProjector.php +++ b/module/product/src/Persistence/Dbal/Projector/ProductCreateEventProjector.php @@ -46,8 +46,6 @@ class ProductCreateEventProjector implements DomainEventProjectorInterface private $cache = []; /** - * ProductCreateEventProjector constructor. - * * @param Connection $connection */ public function __construct(Connection $connection) @@ -56,9 +54,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -66,11 +62,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -78,9 +70,9 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, ProductCreated::class); } - try { + $this->connection->transactional(function () use ($aggregateId, $event) { $productId = $aggregateId->getValue(); - $this->connection->beginTransaction(); + $this->connection->insert( self::TABLE_PRODUCT, [ @@ -105,12 +97,7 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) $attributeId = AttributeId::fromKey(new AttributeCode($code))->getValue(); $this->insertValue($productId, $attributeId, $value); } - - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + }); } /** diff --git a/module/product/src/Persistence/Dbal/Projector/ProductRemovedFromCategoryEventProjector.php b/module/product/src/Persistence/Dbal/Projector/ProductRemovedFromCategoryEventProjector.php index 9525eef4e..3d7511cfe 100644 --- a/module/product/src/Persistence/Dbal/Projector/ProductRemovedFromCategoryEventProjector.php +++ b/module/product/src/Persistence/Dbal/Projector/ProductRemovedFromCategoryEventProjector.php @@ -29,8 +29,6 @@ class ProductRemovedFromCategoryEventProjector implements DomainEventProjectorIn private $connection; /** - * ProductCreateEventProjector constructor. - * * @param Connection $connection */ public function __construct(Connection $connection) @@ -39,9 +37,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -49,11 +45,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -61,20 +53,12 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, ProductRemovedFromCategory::class); } - $this->connection->beginTransaction(); - - try { - $this->connection->delete( - self::TABLE_PRODUCT_CATEGORY, - [ - 'product_id' => $aggregateId->getValue(), - 'category_id' => CategoryId::fromCode($event->getCategoryCode()), - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->connection->delete( + self::TABLE_PRODUCT_CATEGORY, + [ + 'product_id' => $aggregateId->getValue(), + 'category_id' => CategoryId::fromCode($event->getCategoryCode()), + ] + ); } } diff --git a/module/product/src/Persistence/Dbal/Projector/ProductStatusChangedEventProjector.php b/module/product/src/Persistence/Dbal/Projector/ProductStatusChangedEventProjector.php deleted file mode 100644 index 438b71d26..000000000 --- a/module/product/src/Persistence/Dbal/Projector/ProductStatusChangedEventProjector.php +++ /dev/null @@ -1,79 +0,0 @@ -connection = $connection; - } - - /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable - */ - public function projection(AbstractId $aggregateId, DomainEventInterface $event): void - { - if (!$event instanceof ProductStatusChanged) { - throw new UnsupportedEventException($event, ProductStatusChanged::class); - } - - $this->connection->beginTransaction(); - try { - $this->connection->update( - self::TABLE_PRODUCT, - [ - 'status' => $event->getTo()->getValue(), - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } - } -} diff --git a/module/product/src/Persistence/Dbal/Projector/ProductValueAddedEventProjector.php b/module/product/src/Persistence/Dbal/Projector/ProductValueAddedEventProjector.php index ff2b0ba3e..8f51745e7 100644 --- a/module/product/src/Persistence/Dbal/Projector/ProductValueAddedEventProjector.php +++ b/module/product/src/Persistence/Dbal/Projector/ProductValueAddedEventProjector.php @@ -37,8 +37,6 @@ class ProductValueAddedEventProjector implements DomainEventProjectorInterface private $connection; /** - * ProductCreateEventProjector constructor. - * * @param Connection $connection */ public function __construct(Connection $connection) @@ -47,9 +45,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -57,10 +53,8 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event + * {@inheritDoc} * - * @throws \Doctrine\DBAL\ConnectionException * @throws \Throwable */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void @@ -69,19 +63,12 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, ProductValueAdded::class); } - $this->connection->beginTransaction(); - - try { + $this->connection->transactional(function () use ($aggregateId, $event) { $productId = $aggregateId->getValue(); $attributeId = AttributeId::fromKey($event->getAttributeCode())->getValue(); $this->insertValue($productId, $attributeId, $event->getValue()); - - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + }); } /** diff --git a/module/product/src/Persistence/Dbal/Projector/ProductValueChangedEventProjector.php b/module/product/src/Persistence/Dbal/Projector/ProductValueChangedEventProjector.php index 7f937e1c3..acfa0938d 100644 --- a/module/product/src/Persistence/Dbal/Projector/ProductValueChangedEventProjector.php +++ b/module/product/src/Persistence/Dbal/Projector/ProductValueChangedEventProjector.php @@ -37,8 +37,6 @@ class ProductValueChangedEventProjector implements DomainEventProjectorInterface private $connection; /** - * ProductCreateEventProjector constructor. - * * @param Connection $connection */ public function __construct(Connection $connection) @@ -47,9 +45,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -57,11 +53,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -69,19 +61,13 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, ProductValueChanged::class); } - $this->connection->beginTransaction(); - try { + $this->connection->transactional(function () use ($aggregateId, $event) { $productId = $aggregateId->getValue(); $attributeId = AttributeId::fromKey($event->getAttributeCode())->getValue(); $this->delete($productId, $attributeId); $this->insertValue($productId, $attributeId, $event->getTo()); - - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + }); } /** diff --git a/module/product/src/Persistence/Dbal/Projector/ProductValueRemovedEventProjector.php b/module/product/src/Persistence/Dbal/Projector/ProductValueRemovedEventProjector.php index 632070b61..08a853778 100644 --- a/module/product/src/Persistence/Dbal/Projector/ProductValueRemovedEventProjector.php +++ b/module/product/src/Persistence/Dbal/Projector/ProductValueRemovedEventProjector.php @@ -37,9 +37,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -47,11 +45,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -59,15 +53,7 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, ProductValueRemoved::class); } - $this->connection->beginTransaction(); - try { - $this->delete($aggregateId->getValue(), AttributeId::fromKey($event->getAttributeCode())->getValue()); - - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->delete($aggregateId->getValue(), AttributeId::fromKey($event->getAttributeCode())->getValue()); } /** diff --git a/module/product/src/Persistence/Dbal/Projector/ProductVersionIncreasedEventProjector.php b/module/product/src/Persistence/Dbal/Projector/ProductVersionIncreasedEventProjector.php index 3a313c1a3..59acfaf84 100644 --- a/module/product/src/Persistence/Dbal/Projector/ProductVersionIncreasedEventProjector.php +++ b/module/product/src/Persistence/Dbal/Projector/ProductVersionIncreasedEventProjector.php @@ -28,29 +28,23 @@ class ProductVersionIncreasedEventProjector implements DomainEventProjectorInter private $connection; /** - * @param DomainEventInterface $event - * - * @return bool + * @param Connection $connection */ - public function support(DomainEventInterface $event): bool + public function __construct(Connection $connection) { - return $event instanceof ProductVersionIncreased; + $this->connection = $connection; } /** - * @param Connection $connection + * {@inheritDoc} */ - public function __construct(Connection $connection) + public function support(DomainEventInterface $event): bool { - $this->connection = $connection; + return $event instanceof ProductVersionIncreased; } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Throwable + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -58,22 +52,14 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, ProductVersionIncreased::class); } - $this->connection->beginTransaction(); - try { - $this->connection->update( - self::TABLE_PRODUCT, - [ - 'version' => $event->getTo(), - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw $exception; - } + $this->connection->update( + self::TABLE_PRODUCT, + [ + 'version' => $event->getTo(), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/reader/src/Persistence/Dbal/Projector/ReaderCreatedEventProjector.php b/module/reader/src/Persistence/Dbal/Projector/ReaderCreatedEventProjector.php index da348aac4..98cd1ea3d 100644 --- a/module/reader/src/Persistence/Dbal/Projector/ReaderCreatedEventProjector.php +++ b/module/reader/src/Persistence/Dbal/Projector/ReaderCreatedEventProjector.php @@ -12,7 +12,6 @@ use Doctrine\DBAL\Connection; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; use Ergonode\Reader\Domain\Event\ReaderCreatedEvent; @@ -37,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -47,12 +44,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -60,21 +52,13 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, ReaderCreatedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->insert( - self::TABLE, - [ - 'id' => $aggregateId->getValue(), - 'name' => $event->getName(), - 'type' => $event->getType(), - ] - ); - - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + $this->connection->insert( + self::TABLE, + [ + 'id' => $aggregateId->getValue(), + 'name' => $event->getName(), + 'type' => $event->getType(), + ] + ); } } diff --git a/module/transformer/src/Persistence/Dbal/Projector/ProcessorCreatedEventProjector.php b/module/transformer/src/Persistence/Dbal/Projector/ProcessorCreatedEventProjector.php index d2faf6f31..4aa1e5d34 100644 --- a/module/transformer/src/Persistence/Dbal/Projector/ProcessorCreatedEventProjector.php +++ b/module/transformer/src/Persistence/Dbal/Projector/ProcessorCreatedEventProjector.php @@ -44,6 +44,8 @@ public function support(DomainEventInterface $event): bool /** * {@inheritDoc} + * + * @throws \Throwable */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { diff --git a/module/transformer/src/Persistence/Dbal/Projector/ProcessorStatusChangedEventProjector.php b/module/transformer/src/Persistence/Dbal/Projector/ProcessorStatusChangedEventProjector.php index 3e8fe2fd5..b3a278128 100644 --- a/module/transformer/src/Persistence/Dbal/Projector/ProcessorStatusChangedEventProjector.php +++ b/module/transformer/src/Persistence/Dbal/Projector/ProcessorStatusChangedEventProjector.php @@ -44,6 +44,8 @@ public function support(DomainEventInterface $event): bool /** * {@inheritDoc} + * + * @throws \Throwable */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { diff --git a/module/transformer/src/Persistence/Dbal/Projector/TransformerCreatedEventProjector.php b/module/transformer/src/Persistence/Dbal/Projector/TransformerCreatedEventProjector.php index 590220336..ac2963e8a 100644 --- a/module/transformer/src/Persistence/Dbal/Projector/TransformerCreatedEventProjector.php +++ b/module/transformer/src/Persistence/Dbal/Projector/TransformerCreatedEventProjector.php @@ -43,6 +43,8 @@ public function support(DomainEventInterface $event): bool /** * {@inheritDoc} + * + * @throws \Throwable */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { diff --git a/module/workflow/src/Domain/Entity/Status.php b/module/workflow/src/Domain/Entity/Status.php index 79fab4b9b..c8d655012 100644 --- a/module/workflow/src/Domain/Entity/Status.php +++ b/module/workflow/src/Domain/Entity/Status.php @@ -9,17 +9,14 @@ namespace Ergonode\Workflow\Domain\Entity; -use Ergonode\Account\Domain\Event\Role\RoleRemovedEvent; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\Core\Domain\ValueObject\Color; -use Ergonode\Core\Domain\ValueObject\State; use Ergonode\Core\Domain\ValueObject\TranslatableString; use Ergonode\EventSourcing\Domain\AbstractAggregateRoot; use Ergonode\Workflow\Domain\Event\Status\StatusColorChangedEvent; use Ergonode\Workflow\Domain\Event\Status\StatusCreatedEvent; use Ergonode\Workflow\Domain\Event\Status\StatusDescriptionChangedEvent; use Ergonode\Workflow\Domain\Event\Status\StatusNameChangedEvent; -use Ergonode\Workflow\Domain\Event\Status\StatusRemovedEvent; use JMS\Serializer\Annotation as JMS; /** @@ -61,13 +58,6 @@ class Status extends AbstractAggregateRoot */ private $description; - /** - * @var State - * - * @JMS\Exclude() - */ - private $state; - /** * @param StatusId $id * @param string $code @@ -90,23 +80,6 @@ public function getId(): AbstractId return $this->id; } - /** - */ - public function remove(): void - { - if ($this->state->getValue() !== State::STATE_DELETED) { - $this->apply(new StatusRemovedEvent($this->id)); - } - } - - /** - * @return bool - */ - public function isDeleted(): bool - { - return $this->state->getValue() === State::STATE_DELETED; - } - /** * @return string */ @@ -185,7 +158,6 @@ protected function applyStatusCreatedEvent(StatusCreatedEvent $event): void $this->color = $event->getColor(); $this->name = $event->getName(); $this->description = $event->getDescription(); - $this->state = new State(); } /** @@ -211,12 +183,4 @@ protected function applyStatusColorChangedEvent(StatusColorChangedEvent $event): { $this->color = $event->getTo(); } - - /** - * @param StatusRemovedEvent $event - */ - protected function applyStatusRemovedEvent(StatusRemovedEvent $event): void - { - $this->state = new State(State::STATE_DELETED); - } } diff --git a/module/workflow/src/Domain/Event/Status/StatusDeletedEvent.php b/module/workflow/src/Domain/Event/Status/StatusDeletedEvent.php new file mode 100644 index 000000000..c0877941c --- /dev/null +++ b/module/workflow/src/Domain/Event/Status/StatusDeletedEvent.php @@ -0,0 +1,18 @@ +id = $id; - } - - /** - * @return StatusId - */ - public function getId(): StatusId - { - return $this->id; - } -} diff --git a/module/workflow/src/Domain/Repository/StatusRepositoryInterface.php b/module/workflow/src/Domain/Repository/StatusRepositoryInterface.php index 6d89713bd..f196b957a 100644 --- a/module/workflow/src/Domain/Repository/StatusRepositoryInterface.php +++ b/module/workflow/src/Domain/Repository/StatusRepositoryInterface.php @@ -26,15 +26,20 @@ interface StatusRepositoryInterface */ public function load(StatusId $id): ?AbstractAggregateRoot; + /** + * @param StatusId $id + * + * @return bool + */ + public function exists(StatusId $id): bool; + /** * @param AbstractAggregateRoot $aggregateRoot */ public function save(AbstractAggregateRoot $aggregateRoot): void; /** - * @param StatusId $id - * - * @return bool + * @param AbstractAggregateRoot $aggregateRoot */ - public function exists(StatusId $id): bool; + public function delete(AbstractAggregateRoot $aggregateRoot): void; } diff --git a/module/workflow/src/Infrastructure/Handler/Status/DeleteStatusCommandHandler.php b/module/workflow/src/Infrastructure/Handler/Status/DeleteStatusCommandHandler.php index ed70d91bd..1ecb49301 100644 --- a/module/workflow/src/Infrastructure/Handler/Status/DeleteStatusCommandHandler.php +++ b/module/workflow/src/Infrastructure/Handler/Status/DeleteStatusCommandHandler.php @@ -9,13 +9,11 @@ namespace Ergonode\Workflow\Infrastructure\Handler\Status; -use Ergonode\Core\Application\Exception\NotImplementedException; use Ergonode\Workflow\Domain\Command\Status\DeleteStatusCommand; use Ergonode\Workflow\Domain\Repository\StatusRepositoryInterface; use Webmozart\Assert\Assert; /** - * @todo Implement workflow status delete */ class DeleteStatusCommandHandler { @@ -41,7 +39,7 @@ public function __invoke(DeleteStatusCommand $command) { $status = $this->repository->load($command->getId()); Assert::notNull($status); - $status->remove(); - $this->repository->save($status); + + $this->repository->delete($status); } } diff --git a/module/workflow/src/Persistence/Dbal/Projector/StatusColorChangedEventProjector.php b/module/workflow/src/Persistence/Dbal/Projector/StatusColorChangedEventProjector.php index 67026aee7..37965d02d 100644 --- a/module/workflow/src/Persistence/Dbal/Projector/StatusColorChangedEventProjector.php +++ b/module/workflow/src/Persistence/Dbal/Projector/StatusColorChangedEventProjector.php @@ -10,10 +10,8 @@ namespace Ergonode\Workflow\Persistence\Dbal\Projector; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\ConnectionException; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; use Ergonode\Workflow\Domain\Event\Status\StatusColorChangedEvent; @@ -38,9 +36,7 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -48,12 +44,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws ProjectorException - * @throws UnsupportedEventException - * @throws ConnectionException + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -61,22 +52,14 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, StatusColorChangedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->update( - self::TABLE, - [ - 'color' => $event->getTo()->getValue(), - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + $this->connection->update( + self::TABLE, + [ + 'color' => $event->getTo()->getValue(), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/workflow/src/Persistence/Dbal/Projector/StatusCreatedEventProjector.php b/module/workflow/src/Persistence/Dbal/Projector/StatusCreatedEventProjector.php index addba2a95..eff17738c 100644 --- a/module/workflow/src/Persistence/Dbal/Projector/StatusCreatedEventProjector.php +++ b/module/workflow/src/Persistence/Dbal/Projector/StatusCreatedEventProjector.php @@ -12,10 +12,10 @@ use Doctrine\DBAL\Connection; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; use Ergonode\Workflow\Domain\Event\Status\StatusCreatedEvent; +use JMS\Serializer\SerializerInterface; /** */ @@ -29,17 +29,22 @@ class StatusCreatedEventProjector implements DomainEventProjectorInterface private $connection; /** - * @param Connection $connection + * @var SerializerInterface */ - public function __construct(Connection $connection) + private $serializer; + + /** + * @param Connection $connection + * @param SerializerInterface $serializer + */ + public function __construct(Connection $connection, SerializerInterface $serializer) { $this->connection = $connection; + $this->serializer = $serializer; } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -47,11 +52,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws ProjectorException - * @throws UnsupportedEventException + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -59,22 +60,15 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, StatusCreatedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->insert( - self::TABLE, - [ - 'id' => $aggregateId->getValue(), - 'code' => $event->getCode(), - 'name' => json_encode($event->getName()->getTranslations()), - 'description' => json_encode($event->getDescription()->getTranslations()), - 'color' => $event->getColor()->getValue(), - ] - ); - - $this->connection->commit(); - } catch (\Throwable $exception) { - throw new ProjectorException($event, $exception); - } + $this->connection->insert( + self::TABLE, + [ + 'id' => $aggregateId->getValue(), + 'code' => $event->getCode(), + 'name' => $this->serializer->serialize($event->getName()->getTranslations(), 'json'), + 'description' => $this->serializer->serialize($event->getDescription()->getTranslations(), 'json'), + 'color' => $event->getColor()->getValue(), + ] + ); } } diff --git a/module/workflow/src/Persistence/Dbal/Projector/StatusRemovedEventProjector.php b/module/workflow/src/Persistence/Dbal/Projector/StatusDeletedEventProjector.php similarity index 50% rename from module/workflow/src/Persistence/Dbal/Projector/StatusRemovedEventProjector.php rename to module/workflow/src/Persistence/Dbal/Projector/StatusDeletedEventProjector.php index 36ef8def5..94b899576 100644 --- a/module/workflow/src/Persistence/Dbal/Projector/StatusRemovedEventProjector.php +++ b/module/workflow/src/Persistence/Dbal/Projector/StatusDeletedEventProjector.php @@ -12,14 +12,13 @@ use Doctrine\DBAL\Connection; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; -use Ergonode\Workflow\Domain\Event\Status\StatusRemovedEvent; +use Ergonode\Workflow\Domain\Event\Status\StatusDeletedEvent; /** */ -class StatusRemovedEventProjector implements DomainEventProjectorInterface +class StatusDeletedEventProjector implements DomainEventProjectorInterface { private const TABLE = 'status'; @@ -37,40 +36,27 @@ public function __construct(Connection $connection) } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { - return $event instanceof StatusRemovedEvent; + return $event instanceof StatusDeletedEvent; } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws ProjectorException - * @throws UnsupportedEventException + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { - if (!$event instanceof StatusRemovedEvent) { - throw new UnsupportedEventException($event, StatusRemovedEvent::class); + if (!$event instanceof StatusDeletedEvent) { + throw new UnsupportedEventException($event, StatusDeletedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->delete( - self::TABLE, - [ - 'id' => $aggregateId->getValue(), - ] - ); - - $this->connection->commit(); - } catch (\Throwable $exception) { - throw new ProjectorException($event, $exception); - } + $this->connection->delete( + self::TABLE, + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/workflow/src/Persistence/Dbal/Projector/StatusDescriptionChangedEventProjector.php b/module/workflow/src/Persistence/Dbal/Projector/StatusDescriptionChangedEventProjector.php index be099324d..49a39d9f8 100644 --- a/module/workflow/src/Persistence/Dbal/Projector/StatusDescriptionChangedEventProjector.php +++ b/module/workflow/src/Persistence/Dbal/Projector/StatusDescriptionChangedEventProjector.php @@ -12,10 +12,10 @@ use Doctrine\DBAL\Connection; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; use Ergonode\Workflow\Domain\Event\Status\StatusDescriptionChangedEvent; +use JMS\Serializer\SerializerInterface; /** */ @@ -29,17 +29,22 @@ class StatusDescriptionChangedEventProjector implements DomainEventProjectorInte private $connection; /** - * @param Connection $connection + * @var SerializerInterface */ - public function __construct(Connection $connection) + private $serializer; + + /** + * @param Connection $connection + * @param SerializerInterface $serializer + */ + public function __construct(Connection $connection, SerializerInterface $serializer) { $this->connection = $connection; + $this->serializer = $serializer; } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -47,11 +52,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws ProjectorException - * @throws UnsupportedEventException + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -59,21 +60,14 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, StatusDescriptionChangedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->update( - self::TABLE, - [ - 'description' => json_encode($event->getTo()->getTranslations()), - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - - $this->connection->commit(); - } catch (\Throwable $exception) { - throw new ProjectorException($event, $exception); - } + $this->connection->update( + self::TABLE, + [ + 'description' => $this->serializer->serialize($event->getTo()->getTranslations(), 'json'), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/workflow/src/Persistence/Dbal/Projector/StatusNameChangedEventProjector.php b/module/workflow/src/Persistence/Dbal/Projector/StatusNameChangedEventProjector.php index c325130cc..5e47e7c2e 100644 --- a/module/workflow/src/Persistence/Dbal/Projector/StatusNameChangedEventProjector.php +++ b/module/workflow/src/Persistence/Dbal/Projector/StatusNameChangedEventProjector.php @@ -12,10 +12,10 @@ use Doctrine\DBAL\Connection; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; use Ergonode\Workflow\Domain\Event\Status\StatusNameChangedEvent; +use JMS\Serializer\SerializerInterface; /** */ @@ -29,17 +29,22 @@ class StatusNameChangedEventProjector implements DomainEventProjectorInterface private $connection; /** - * @param Connection $connection + * @var SerializerInterface */ - public function __construct(Connection $connection) + private $serializer; + + /** + * @param Connection $connection + * @param SerializerInterface $serializer + */ + public function __construct(Connection $connection, SerializerInterface $serializer) { $this->connection = $connection; + $this->serializer = $serializer; } /** - * @param DomainEventInterface $event - * - * @return bool + * {@inheritDoc} */ public function support(DomainEventInterface $event): bool { @@ -47,11 +52,7 @@ public function support(DomainEventInterface $event): bool } /** - * @param AbstractId $aggregateId - * @param DomainEventInterface $event - * - * @throws ProjectorException - * @throws UnsupportedEventException + * {@inheritDoc} */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -59,21 +60,14 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, StatusNameChangedEvent::class); } - $this->connection->beginTransaction(); - try { - $this->connection->update( - self::TABLE, - [ - 'name' => json_encode($event->getTo()->getTranslations()), - ], - [ - 'id' => $aggregateId->getValue(), - ] - ); - - $this->connection->commit(); - } catch (\Throwable $exception) { - throw new ProjectorException($event, $exception); - } + $this->connection->update( + self::TABLE, + [ + 'name' => $this->serializer->serialize($event->getTo()->getTranslations(), 'json'), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); } } diff --git a/module/workflow/src/Persistence/Dbal/Repository/DbalStatusRepository.php b/module/workflow/src/Persistence/Dbal/Repository/DbalStatusRepository.php index fe26d9e36..8c1ae2b12 100644 --- a/module/workflow/src/Persistence/Dbal/Repository/DbalStatusRepository.php +++ b/module/workflow/src/Persistence/Dbal/Repository/DbalStatusRepository.php @@ -14,6 +14,7 @@ use Ergonode\EventSourcing\Infrastructure\DomainEventStoreInterface; use Ergonode\Workflow\Domain\Entity\Status; use Ergonode\Workflow\Domain\Entity\StatusId; +use Ergonode\Workflow\Domain\Event\Status\StatusDeletedEvent; use Ergonode\Workflow\Domain\Repository\StatusRepositoryInterface; /** @@ -34,8 +35,10 @@ class DbalStatusRepository implements StatusRepositoryInterface * @param DomainEventStoreInterface $eventStore * @param DomainEventDispatcherInterface $eventDispatcher */ - public function __construct(DomainEventStoreInterface $eventStore, DomainEventDispatcherInterface $eventDispatcher) - { + public function __construct( + DomainEventStoreInterface $eventStore, + DomainEventDispatcherInterface $eventDispatcher + ) { $this->eventStore = $eventStore; $this->eventDispatcher = $eventDispatcher; } @@ -50,7 +53,7 @@ public function __construct(DomainEventStoreInterface $eventStore, DomainEventDi public function load(StatusId $id): ?AbstractAggregateRoot { $eventStream = $this->eventStore->load($id); - if (\count($eventStream) > 0) { + if (count($eventStream) > 0) { $class = new \ReflectionClass(Status::class); /** @var Status $aggregate */ $aggregate = $class->newInstanceWithoutConstructor(); @@ -60,14 +63,22 @@ public function load(StatusId $id): ?AbstractAggregateRoot $aggregate->initialize($eventStream); - if (!$aggregate->isDeleted()) { - return $aggregate; - } + return $aggregate; } return null; } + /** + * @param StatusId $id + * + * @return bool + */ + public function exists(StatusId $id) : bool + { + return $this->eventStore->load($id)->count() > 0; + } + /** * @param AbstractAggregateRoot $aggregateRoot */ @@ -82,12 +93,15 @@ public function save(AbstractAggregateRoot $aggregateRoot): void } /** - * @param StatusId $id + * {@inheritDoc} * - * @return bool + * @throws \Exception */ - public function exists(StatusId $id) : bool + public function delete(AbstractAggregateRoot $aggregateRoot): void { - return $this->eventStore->load($id)->count() > 0; + $aggregateRoot->apply(new StatusDeletedEvent()); + $this->save($aggregateRoot); + + $this->eventStore->delete($aggregateRoot->getId()); } } From 302366ea8691a377a0f84c6122f3d185c5095163 Mon Sep 17 00:00:00 2001 From: "mateusz.kolankowski" Date: Wed, 4 Sep 2019 09:30:47 +0200 Subject: [PATCH 04/27] behat-tests (core,designer) --- features/core.feature | 78 +++++++++++++++++++ features/designer.feature | 42 +++++++++- .../Type/LanguageConfigurationFormType.php | 3 +- 3 files changed, 120 insertions(+), 3 deletions(-) diff --git a/features/core.feature b/features/core.feature index c7d47736d..6808975e6 100644 --- a/features/core.feature +++ b/features/core.feature @@ -8,3 +8,81 @@ Feature: Core module Scenario: Get languages (not authorized) When I request "/api/v1/EN/dictionary/languages" using HTTP GET Then unauthorized response is received + + Scenario: Get translation language (not authorized) + When I request "/api/v1/EN/languages/EN" using HTTP GET + Then unauthorized response is received + + Scenario: Get translation language + Given current authentication token + When I request "/api/v1/EN/languages/EN" using HTTP GET + Then the response code is 200 + + Scenario: Get translation language (not found) + Given current authentication token + When I request "/api/v1/EN/languages/ZZ" using HTTP GET + Then not found response is received + + Scenario: Update language (not authorized) + When I request "/api/v1/{language}/languages" using HTTP PUT + Then unauthorized response is received + + Scenario: Update language + Given current authentication token + Given the request body is: + """ + { + "collection": [ + { + "code": "EN", + "active": true + } + ] +} + """ + When I request "/api/v1/{language}/languages" using HTTP PUT + Then the response code is 204 + + Scenario: Update language (wrong active - bad request) + Given current authentication token + Given the request body is: + """ + { + "collection": [ + { + "code": "EN", + "active": "tralalal" + } + ] +} + """ + When I request "/api/v1/{language}/languages" using HTTP PUT + Then validation error response is received + + Scenario: Update language (wrong code - bad request) + Given current authentication token + Given the request body is: + """ + { + "collection": [ + { + "code": "ZZ", + "active": true + } + ] +} + """ + When I request "/api/v1/{language}/languages" using HTTP PUT + Then validation error response is received + + Scenario: Update language (wrong structure - bad request) + Given current authentication token + Given the request body is: + """ + { + "code": "ZZ", + "active": true + } + """ + When I request "/api/v1/{language}/languages" using HTTP PUT + Then validation error response is received diff --git a/features/designer.feature b/features/designer.feature index 99f4e84af..14a5c7396 100644 --- a/features/designer.feature +++ b/features/designer.feature @@ -148,12 +148,52 @@ Feature: Designer module Scenario: Get templates Given current authentication token When I request "/api/v1/EN/templates" using HTTP GET - Then the response code is 200 + Then grid response is received Scenario: Get templates (not authorized) When I request "/api/v1/EN/templates" using HTTP GET Then unauthorized response is received + Scenario: Get templates (order by id) + Given current authentication token + When I request "/api/v1/EN/templates?field=id" using HTTP GET + Then grid response is received + + Scenario: Get templates (order by name) + Given current authentication token + When I request "/api/v1/EN/templates?field=name" using HTTP GET + Then grid response is received + + Scenario: Get templates (order by image_id) + Given current authentication token + When I request "/api/v1/EN/templates?field=image_id" using HTTP GET + Then grid response is received + + Scenario: Get templates (order by group_id) + Given current authentication token + When I request "/api/v1/EN/templates?field=group_id" using HTTP GET + Then grid response is received + + Scenario: Get templates (filter by id) + Given current authentication token + When I request "/api/v1/EN/templates?limit=25&offset=0&filter=id%3D1" using HTTP GET + Then grid response is received + + Scenario: Get templates (filter by name) + Given current authentication token + When I request "/api/v1/EN/templates?limit=25&offset=0&filter=name%3Dasd" using HTTP GET + Then grid response is received + + Scenario: Get templates (filter by image_id) + Given current authentication token + When I request "/api/v1/EN/templates?limit=25&offset=0&filter=image_id%3D4fbba5a0-61c7-5dc8-ba1b-3314f398bfa2" using HTTP GET + Then grid response is received + + Scenario: Get templates (filter by group_id) + Given current authentication token + When I request "/api/v1/EN/templates?limit=25&offset=0&filter=group_id%3D4fbba5a0-61c7-5dc8-ba1b-3314f398bfa2" using HTTP GET + Then grid response is received + # TODO Check template grid # TODO Check template group grid # TODO Check template type grid diff --git a/module/core/src/Application/Form/Type/LanguageConfigurationFormType.php b/module/core/src/Application/Form/Type/LanguageConfigurationFormType.php index abe9ea3b6..cfb7fa6ce 100644 --- a/module/core/src/Application/Form/Type/LanguageConfigurationFormType.php +++ b/module/core/src/Application/Form/Type/LanguageConfigurationFormType.php @@ -11,7 +11,6 @@ use Ergonode\Core\Application\Model\Type\LanguageConfigurationFormTypeModel; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -33,7 +32,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ) ->add( 'active', - CheckboxType::class + BooleanType::class ); } From a4d73a9f9d6c32505ce082552745bb2dad54f57c Mon Sep 17 00:00:00 2001 From: "mateusz.kolankowski" Date: Wed, 4 Sep 2019 12:46:24 +0200 Subject: [PATCH 05/27] behat-tests (templates) --- features/designer.feature | 419 +++++++++++++++++++++++++++++++++++--- 1 file changed, 395 insertions(+), 24 deletions(-) diff --git a/features/designer.feature b/features/designer.feature index 14a5c7396..88ef1fa27 100644 --- a/features/designer.feature +++ b/features/designer.feature @@ -66,6 +66,126 @@ Feature: Designer module When I request "/api/v1/EN/templates" using HTTP POST Then unauthorized response is received + Scenario: Create template (wrong image) + Given current authentication token + Given the request body is: + """ + { + "name": "@@random_md5@@", + "image": "test", + "elements": [ + { + "position": {"x": 0, "y": 0}, + "size": {"width": 2, "height": 1}, + "variant": "attribute", + "type": "text", + "properties": { + "attribute_id": "@template_text_attribute@", + "required": true + } + } + ] + } + """ + When I request "/api/v1/EN/templates" using HTTP POST + Then validation error response is received + + Scenario: Create template (wrong position) + Given current authentication token + Given the request body is: + """ + { + "name": "@@random_md5@@", + "image": "@template_image_attribute@", + "elements": [ + { + "position": {"x": "test", "y": 0}, + "size": {"width": 2, "height": 1}, + "variant": "attribute", + "type": "text", + "properties": { + "attribute_id": "@template_text_attribute@", + "required": true + } + } + ] + } + """ + When I request "/api/v1/EN/templates" using HTTP POST + Then validation error response is received + + Scenario: Create template (wrong size) + Given current authentication token + Given the request body is: + """ + { + "name": "@@random_md5@@", + "image": "@template_image_attribute@", + "elements": [ + { + "position": {"x": 0, "y": 0}, + "size": {"width": "test", "height": 1}, + "variant": "attribute", + "type": "text", + "properties": { + "attribute_id": "@template_text_attribute@", + "required": true + } + } + ] + } + """ + When I request "/api/v1/EN/templates" using HTTP POST + Then validation error response is received + + Scenario: Create template (wrong attribute_id) + Given current authentication token + Given the request body is: + """ + { + "name": "@@random_md5@@", + "image": "@template_image_attribute@", + "elements": [ + { + "position": {"x": 0, "y": 0}, + "size": {"width": "test", "height": 1}, + "variant": "attribute", + "type": "text", + "properties": { + "attribute_id": "test", + "required": true + } + } + ] + } + """ + When I request "/api/v1/EN/templates" using HTTP POST + Then validation error response is received + + Scenario: Create template (wrong required) + Given current authentication token + Given the request body is: + """ + { + "name": "@@random_md5@@", + "image": "@template_image_attribute@", + "elements": [ + { + "position": {"x": 0, "y": 0}, + "size": {"width": "test", "height": 1}, + "variant": "attribute", + "type": "text", + "properties": { + "attribute_id": "@template_text_attribute@", + "required": "test" + } + } + ] + } + """ + When I request "/api/v1/EN/templates" using HTTP POST + Then validation error response is received + Scenario: Update template Given current authentication token Given the request body is: @@ -99,6 +219,126 @@ Feature: Designer module When I request "/api/v1/EN/templates/@@static_uuid@@" using HTTP PUT Then not found response is received + Scenario: Update template (wrong image) + Given current authentication token + Given the request body is: + """ + { + "name": "@@random_md5@@", + "image": "test", + "elements": [ + { + "position": {"x": 10, "y": 10}, + "size": {"width": 2, "height": 2}, + "variant": "attribute", + "type": "text", + "properties": { + "attribute_id": "@template_text_attribute@", + "required": true + } + } + ] + } + """ + When I request "/api/v1/EN/templates/@template@" using HTTP PUT + Then validation error response is received + + Scenario: Update template (wrong position) + Given current authentication token + Given the request body is: + """ + { + "name": "@@random_md5@@", + "image": "@template_image_attribute@", + "elements": [ + { + "position": {"x": "test", "y": 10}, + "size": {"width": 2, "height": 2}, + "variant": "attribute", + "type": "text", + "properties": { + "attribute_id": "@template_text_attribute@", + "required": true + } + } + ] + } + """ + When I request "/api/v1/EN/templates/@template@" using HTTP PUT + Then validation error response is received + + Scenario: Update template (wrong size) + Given current authentication token + Given the request body is: + """ + { + "name": "@@random_md5@@", + "image": "@template_image_attribute@", + "elements": [ + { + "position": {"x": 10, "y": 10}, + "size": {"width": "test", "height": 2}, + "variant": "attribute", + "type": "text", + "properties": { + "attribute_id": "@template_text_attribute@", + "required": true + } + } + ] + } + """ + When I request "/api/v1/EN/templates/@template@" using HTTP PUT + Then validation error response is received + + Scenario: Update template (wrong attribute_id) + Given current authentication token + Given the request body is: + """ + { + "name": "@@random_md5@@", + "image": "@template_image_attribute@", + "elements": [ + { + "position": {"x": 10, "y": 10}, + "size": {"width": 2, "height": 2}, + "variant": "attribute", + "type": "text", + "properties": { + "attribute_id": "test", + "required": true + } + } + ] + } + """ + When I request "/api/v1/EN/templates/@template@" using HTTP PUT + Then validation error response is received + + Scenario: Update template (wrong required) + Given current authentication token + Given the request body is: + """ + { + "name": "@@random_md5@@", + "image": "@template_image_attribute@", + "elements": [ + { + "position": {"x": 10, "y": 10}, + "size": {"width": "test", "height": 2}, + "variant": "attribute", + "type": "text", + "properties": { + "attribute_id": "@template_text_attribute@", + "required": "test" + } + } + ] + } + """ + When I request "/api/v1/EN/templates/@template@" using HTTP PUT + Then validation error response is received + Scenario: Delete template Given current authentication token When I request "/api/v1/EN/templates/@template@" using HTTP DELETE @@ -127,24 +367,6 @@ Feature: Designer module When I request "/api/v1/EN/templates/@@static_uuid@@" using HTTP GET Then not found response is received - Scenario: Get template groups - Given current authentication token - When I request "/api/v1/EN/templates/groups" using HTTP GET - Then the response code is 200 - - Scenario: Get template groups (not authorized) - When I request "/api/v1/EN/templates/groups" using HTTP GET - Then unauthorized response is received - - Scenario: Get template types - Given current authentication token - When I request "/api/v1/EN/templates/types" using HTTP GET - Then the response code is 200 - - Scenario: Get template types (not authorized) - When I request "/api/v1/EN/templates/types" using HTTP GET - Then unauthorized response is received - Scenario: Get templates Given current authentication token When I request "/api/v1/EN/templates" using HTTP GET @@ -174,6 +396,16 @@ Feature: Designer module When I request "/api/v1/EN/templates?field=group_id" using HTTP GET Then grid response is received + Scenario: Get templates (order ASC) + Given current authentication token + When I request "/api/v1/EN/templates?field=name&order=ASC" using HTTP GET + Then grid response is received + + Scenario: Get templates (order DESC ) + Given current authentication token + When I request "/api/v1/EN/templates?field=name&order=DESC" using HTTP GET + Then grid response is received + Scenario: Get templates (filter by id) Given current authentication token When I request "/api/v1/EN/templates?limit=25&offset=0&filter=id%3D1" using HTTP GET @@ -186,7 +418,7 @@ Feature: Designer module Scenario: Get templates (filter by image_id) Given current authentication token - When I request "/api/v1/EN/templates?limit=25&offset=0&filter=image_id%3D4fbba5a0-61c7-5dc8-ba1b-3314f398bfa2" using HTTP GET + When I request "/api/v1/EN/templates?limit=25&offset=0&filter=image_id%3Dasd" using HTTP GET Then grid response is received Scenario: Get templates (filter by group_id) @@ -194,8 +426,147 @@ Feature: Designer module When I request "/api/v1/EN/templates?limit=25&offset=0&filter=group_id%3D4fbba5a0-61c7-5dc8-ba1b-3314f398bfa2" using HTTP GET Then grid response is received - # TODO Check template grid - # TODO Check template group grid - # TODO Check template type grid - # TODO Check create template action with all incorrect possibilities - # TODO Check update template action with all incorrect possibilities + Scenario: Get template groups + Given current authentication token + When I request "/api/v1/EN/templates/groups" using HTTP GET + Then the response code is 200 + + Scenario: Get template groups (not authorized) + When I request "/api/v1/EN/templates/groups" using HTTP GET + Then unauthorized response is received + + Scenario: Get templates groups(order by id) + Given current authentication token + When I request "/api/v1/EN/templates/groups?field=id" using HTTP GET + Then grid response is received + + Scenario: Get templates groups(order by name) + Given current authentication token + When I request "/api/v1/EN/templates/groups?field=name" using HTTP GET + Then grid response is received + + Scenario: Get templates groups(order by custom) + Given current authentication token + When I request "/api/v1/EN/templates/groups?field=custom" using HTTP GET + Then grid response is received + + Scenario: Get templates groups(order ASC) + Given current authentication token + When I request "/api/v1/EN/templates/groups?field=name&order=ASC" using HTTP GET + Then grid response is received + + Scenario: Get templates groups(order DESC ) + Given current authentication token + When I request "/api/v1/EN/templates/groups?field=name&order=DESC" using HTTP GET + Then grid response is received + + Scenario: Get templates groups(filter by id) + Given current authentication token + When I request "/api/v1/EN/templates/groups?limit=25&offset=0&filter=id%3D1" using HTTP GET + Then grid response is received + + Scenario: Get templates groups(filter by name) + Given current authentication token + When I request "/api/v1/EN/templates/groups?limit=25&offset=0&filter=name%3Dasd" using HTTP GET + Then grid response is received + + Scenario: Get templates groups(filter by custom) + Given current authentication token + When I request "/api/v1/EN/templates/groups?limit=25&offset=0&filter=custom%3Dasd" using HTTP GET + Then grid response is received + + Scenario: Get template types + Given current authentication token + When I request "/api/v1/EN/templates/types" using HTTP GET + Then the response code is 200 + + Scenario: Get template types (not authorized) + When I request "/api/v1/EN/templates/types" using HTTP GET + Then unauthorized response is received + + + Scenario: Get templates types (order by type) + Given current authentication token + When I request "/api/v1/EN/templates/types?field=type" using HTTP GET + Then grid response is received + + Scenario: Get templates types (order by variant) + Given current authentication token + When I request "/api/v1/EN/templates/types?field=variant" using HTTP GET + Then grid response is received + + Scenario: Get templates types (order by label) + Given current authentication token + When I request "/api/v1/EN/templates/types?field=label" using HTTP GET + Then grid response is received + + Scenario: Get templates types (order by min_width) + Given current authentication token + When I request "/api/v1/EN/templates/types?field=min_width" using HTTP GET + Then grid response is received + + Scenario: Get templates types (order by min_height) + Given current authentication token + When I request "/api/v1/EN/templates/types?field=min_height" using HTTP GET + Then grid response is received + + Scenario: Get templates types (order by max_width) + Given current authentication token + When I request "/api/v1/EN/templates/types?field=max_width" using HTTP GET + Then grid response is received + + Scenario: Get templates types (order by max_height) + Given current authentication token + When I request "/api/v1/EN/templates/types?field=max_height" using HTTP GET + Then grid response is received + + Scenario: Get templates types (order ASC) + Given current authentication token + When I request "/api/v1/EN/templates/types?field=type&order=ASC" using HTTP GET + Then grid response is received + + Scenario: Get templates types (order DESC ) + Given current authentication token + When I request "/api/v1/EN/templates/types?field=type&order=DESC" using HTTP GET + Then grid response is received + + Scenario: Get templates types (filter by id) + Given current authentication token + When I request "/api/v1/EN/templates/types?limit=25&offset=0&filter=id%3D1" using HTTP GET + Then grid response is received + + Scenario: Get templates types (filter by type) + Given current authentication token + When I request "/api/v1/EN/templates/types?limit=25&offset=0&filter=type%3Dasd" using HTTP GET + Then grid response is received + + Scenario: Get templates types (filter by variant) + Given current authentication token + When I request "/api/v1/EN/templates/types?limit=25&offset=0&filter=variant%3Dasd" using HTTP GET + Then grid response is received + + Scenario: Get templates types (filter by label) + Given current authentication token + When I request "/api/v1/EN/templates/types?limit=25&offset=0&filter=label%3D4fbba5a0-61c7-5dc8-ba1b-3314f398bfa2" using HTTP GET + Then grid response is received + + Scenario: Get templates types (filter by min_width) + Given current authentication token + When I request "/api/v1/EN/templates/types?limit=25&offset=0&filter=min_width%3D4fbba5a0-61c7-5dc8-ba1b-3314f398bfa2" using HTTP GET + Then grid response is received + + Scenario: Get templates types (filter by max_width) + Given current authentication token + When I request "/api/v1/EN/templates/types?limit=25&offset=0&filter=max_width%3D4fbba5a0-61c7-5dc8-ba1b-3314f398bfa2" using HTTP GET + Then grid response is received + + Scenario: Get templates types (filter by max_height) + Given current authentication token + When I request "/api/v1/EN/templates/types?limit=25&offset=0&filter=max_height%3D4fbba5a0-61c7-5dc8-ba1b-3314f398bfa2" using HTTP GET + Then grid response is received + + Scenario: Get templates types (filter by min_height) + Given current authentication token + When I request "/api/v1/EN/templates/types?limit=25&offset=0&filter=min_height%3D4fbba5a0-61c7-5dc8-ba1b-3314f398bfa2" using HTTP GET + Then grid response is received + From ae4f0a9ea5ffdb5f0c7e6e754f924bd1d8026ee7 Mon Sep 17 00:00:00 2001 From: Sebastian Bielawski Date: Wed, 4 Sep 2019 15:48:22 +0200 Subject: [PATCH 06/27] Event sourcing events (#123) #124 add register events to database --- .../migrations/Version20180610062601.php | 60 +++++++-- .../Persistence/Dbal/Query/DbalLogQuery.php | 9 +- .../src/Resources/translations/log.en.yaml | 35 +++--- .../src/Resources/translations/log.pl.yaml | 32 ++--- .../migrations/Version20180610062602.php | 41 +++++++ .../src/Resources/translations/log.en.yaml | 1 + .../src/Resources/translations/log.pl.yaml | 1 + .../migrations/Version20180625083834.php | 30 +++++ .../src/Resources/translations/log.en.yaml | 22 ++-- .../src/Resources/translations/log.pl.yaml | 22 ++-- .../migrations/Version20180619083700.php | 39 ++++-- .../src/Resources/translations/log.en.yaml | 9 +- .../src/Resources/translations/log.pl.yaml | 9 +- .../migrations/Version20180619083800.php | 39 ++++-- .../src/Resources/translations/log.en.yaml | 4 +- .../src/Resources/translations/log.pl.yaml | 4 +- .../migrations/Version20180719132703.php | 116 +++++++++++------- .../src/Resources/translations/log.en.yaml | 20 +-- .../src/Resources/translations/log.pl.yaml | 20 +-- .../migrations/Version20180731143300.php | 84 ++++++++----- .../src/Resources/translations/log.en.yaml | 10 +- .../src/Resources/translations/log.pl.yaml | 10 +- ...03112016.php => Version20180101000000.php} | 16 ++- .../Envelope/DomainEventEnvelope.php | 2 +- .../Factory/SimpleDomainEventFactory.php | 5 +- .../Provider/DomainEventProvider.php | 78 ++++++++++++ .../Provider/DomainEventProviderInterface.php | 22 ++++ .../Store/DbalDomainEventStore.php | 40 ++++-- .../src/Resources/config/services.yml | 1 + .../migrations/Version20180618134343.php | 83 +++++++------ .../migrations/Version20180619083830.php | 49 ++++++-- .../src/Resources/translations/log.en.yaml | 12 +- .../src/Resources/translations/log.pl.yaml | 12 +- .../migrations/Version20180619100000.php | 39 ++++-- .../src/Resources/translations/log.en.yaml | 1 + .../src/Resources/translations/log.pl.yaml | 1 + .../migrations/Version20190130104000.php | 46 +++++-- .../src/Resources/translations/log.en.yaml | 5 + .../src/Resources/translations/log.pl.yaml | 5 + .../migrations/Version20180619083829.php | 71 +++++++---- .../src/Resources/translations/log.en.yaml | 4 + .../src/Resources/translations/log.pl.yaml | 4 + .../migrations/Version20180619083831.php | 43 +++++++ .../src/Resources/translations/log.en.yaml | 3 + .../src/Resources/translations/log.pl.yaml | 3 + .../migrations/Version20190818160000.php | 49 ++++++-- .../src/Resources/translations/log.en.yaml | 10 ++ .../src/Resources/translations/log.pl.yaml | 10 ++ 48 files changed, 891 insertions(+), 340 deletions(-) create mode 100644 module/attribute-image/migrations/Version20180610062602.php create mode 100644 module/attribute-image/src/Resources/translations/log.en.yaml create mode 100644 module/attribute-image/src/Resources/translations/log.pl.yaml rename module/event-sourcing/migrations/{Version20190903112016.php => Version20180101000000.php} (72%) create mode 100644 module/event-sourcing/src/Infrastructure/Provider/DomainEventProvider.php create mode 100644 module/event-sourcing/src/Infrastructure/Provider/DomainEventProviderInterface.php create mode 100644 module/reader/src/Resources/translations/log.en.yaml create mode 100644 module/reader/src/Resources/translations/log.pl.yaml create mode 100644 module/segment/src/Resources/translations/log.en.yaml create mode 100644 module/segment/src/Resources/translations/log.pl.yaml create mode 100644 module/transformer/src/Resources/translations/log.en.yaml create mode 100644 module/transformer/src/Resources/translations/log.pl.yaml create mode 100644 module/value/migrations/Version20180619083831.php create mode 100644 module/value/src/Resources/translations/log.en.yaml create mode 100644 module/value/src/Resources/translations/log.pl.yaml create mode 100644 module/workflow/src/Resources/translations/log.en.yaml create mode 100644 module/workflow/src/Resources/translations/log.pl.yaml diff --git a/module/account/migrations/Version20180610062601.php b/module/account/migrations/Version20180610062601.php index a6d98a2a1..3d5ed87b3 100644 --- a/module/account/migrations/Version20180610062601.php +++ b/module/account/migrations/Version20180610062601.php @@ -8,7 +8,6 @@ use Ramsey\Uuid\Uuid; /** - * Auto-generated Ergonode Migration Class: */ final class Version20180610062601 extends AbstractErgonodeMigration { @@ -19,7 +18,8 @@ final class Version20180610062601 extends AbstractErgonodeMigration */ public function up(Schema $schema): void { - $this->addSql('CREATE TABLE users ( + $this->addSql(' + CREATE TABLE users ( id UUID NOT NULL, first_name VARCHAR(128) NOT NULL, last_name VARCHAR(128) NOT NULL, @@ -28,24 +28,29 @@ public function up(Schema $schema): void password VARCHAR(41) NOT NULL, role_id UUID NOT NULL, language VARCHAR(2) NOT NULL, - PRIMARY KEY(id))'); - + PRIMARY KEY(id) + ) + '); $this->addSql('CREATE UNIQUE INDEX users_username_key ON users (username)'); - $this->addSql('CREATE TABLE privileges ( + $this->addSql(' + CREATE TABLE privileges ( id UUID NOT NULL, code VARCHAR(128) NOT NULL, area VARCHAR(128) NOT NULL, - PRIMARY KEY(id))'); - + PRIMARY KEY(id) + ) + '); $this->addSql('CREATE UNIQUE INDEX privileges_name_key ON privileges (code)'); - $this->addSql('CREATE TABLE roles ( + $this->addSql(' + CREATE TABLE roles ( id UUID NOT NULL, name VARCHAR(100) NOT NULL, description VARCHAR(500) NOT NULL, - PRIMARY KEY(id))'); - + PRIMARY KEY(id) + ) + '); $this->addSql('CREATE UNIQUE INDEX role_name_key ON roles (name)'); $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'USER_ROLE_CREATE', 'Role']); @@ -61,5 +66,40 @@ public function up(Schema $schema): void $this->addSql('ALTER TABLE roles ADD privileges json DEFAULT NULL'); $this->addSql('ALTER TABLE users ADD is_active BOOLEAN DEFAULT TRUE NOT NULL'); + + $this->createEventStoreEvents([ + 'Ergonode\Account\Domain\Event\User\UserAvatarChangedEvent' => 'User avatar changed', + 'Ergonode\Account\Domain\Event\User\UserCreatedEvent' => 'User created', + 'Ergonode\Account\Domain\Event\User\UserFirstNameChangedEvent' => 'User first name changed', + 'Ergonode\Account\Domain\Event\User\UserLanguageChangedEvent' => 'User language changed', + 'Ergonode\Account\Domain\Event\User\UserLastNameChangedEvent' => 'User last name changed', + 'Ergonode\Account\Domain\Event\User\UserPasswordChangedEvent' => 'User password changed', + 'Ergonode\Account\Domain\Event\User\UserRoleChangedEvent' => 'User role changed', + 'Ergonode\Account\Domain\Event\User\UserActivatedEvent' => 'User activated', + 'Ergonode\Account\Domain\Event\User\UserDeactivatedEvent' => 'User disabled', + 'Ergonode\Account\Domain\Event\Role\AddPrivilegeToRoleEvent' => 'Privilege added', + 'Ergonode\Account\Domain\Event\Role\RemovePrivilegeFromRoleEvent' => 'Privilege removed', + 'Ergonode\Account\Domain\Event\Role\RoleCreatedEvent' => 'Role created', + 'Ergonode\Account\Domain\Event\Role\RoleNameChangedEvent' => 'Role name changed', + 'Ergonode\Account\Domain\Event\Role\RoleDescriptionChangedEvent' => 'Role description changed', + 'Ergonode\Account\Domain\Event\Role\RolePrivilegesChangedEvent' => 'List of privileges changed', + 'Ergonode\Account\Domain\Event\Role\RoleDeletedEvent' => 'Role deleted', + ]); + } + + /** + * @param array $collection + * + * @throws \Doctrine\DBAL\DBALException + */ + private function createEventStoreEvents(array $collection): void + { + foreach ($collection as $class => $translation) { + $this->connection->insert('event_store_event', [ + 'id' => Uuid::uuid4()->toString(), + 'event_class' => $class, + 'translation_key' => $translation, + ]); + } } } diff --git a/module/account/src/Persistence/Dbal/Query/DbalLogQuery.php b/module/account/src/Persistence/Dbal/Query/DbalLogQuery.php index 5a295435d..670ac2fa8 100644 --- a/module/account/src/Persistence/Dbal/Query/DbalLogQuery.php +++ b/module/account/src/Persistence/Dbal/Query/DbalLogQuery.php @@ -60,8 +60,11 @@ public function getDataSet(?UserId $id = null): DataSetInterface private function getQuery(): QueryBuilder { return $this->connection->createQueryBuilder() - ->select('es.id, event, payload, recorded_at, recorded_by AS author_id, coalesce(u.first_name || \' \' || u.last_name, \'System\') AS author') - ->leftJoin('es', 'users', 'u', 'u.id = es.recorded_by') - ->from('event_store', 'es'); + ->select('es.id, payload, recorded_at, recorded_by AS author_id') + ->addSelect('coalesce(u.first_name || \' \' || u.last_name, \'System\') AS author') + ->addSelect('ese.translation_key as event') + ->from('event_store', 'es') + ->join('es', 'event_store_event', 'ese', 'es.event_id = ese.id') + ->leftJoin('es', 'users', 'u', 'u.id = es.recorded_by'); } } diff --git a/module/account/src/Resources/translations/log.en.yaml b/module/account/src/Resources/translations/log.en.yaml index 2639e471e..421a10af0 100644 --- a/module/account/src/Resources/translations/log.en.yaml +++ b/module/account/src/Resources/translations/log.en.yaml @@ -1,19 +1,16 @@ -"Ergonode\\Account\\Domain\\Event\\User\\UserAvatarChangedEvent": User avatar changed -"Ergonode\\Account\\Domain\\Event\\User\\UserCreatedEvent": User "%first_name% %last_name%" created -"Ergonode\\Account\\Domain\\Event\\User\\UserFirstNameChangedEvent": User first name changed from "%from% to "%to%" -"Ergonode\\Account\\Domain\\Event\\User\\UserLanguageChangedEvent": User language changed from "%from% to %to%" -"Ergonode\\Account\\Domain\\Event\\User\\UserLastNameChangedEvent": User last name changed from "%from% to "%to%" -"Ergonode\\Account\\Domain\\Event\\User\\UserPasswordChangedEvent": User password changed -"Ergonode\\Account\\Domain\\Event\\User\\UserRoleChangedEvent": User role changed from "%from%" to "%to%" -"Ergonode\\Account\\Domain\\Event\\User\\UserActivatedEvent": User activated -"Ergonode\\Account\\Domain\\Event\\User\\UserDeactivatedEvent": User disabled -"Ergonode\\Account\\Domain\\Event\\Role\\AddPrivilegeToRoleEvent": Provilege "%privilege%" added -"Ergonode\\Account\\Domain\\Event\\Role\\RemovePrivilegeFromRoleEvent": privilege "%privilege%" removed -"Ergonode\\Account\\Domain\\Event\\Role\\RoleCreatedEvent": Role "%name%" created -"Ergonode\\Account\\Domain\\Event\\Role\\RoleNameChangedEvent": Role name changed from "%from%" to "%to%" -"Ergonode\\Account\\Domain\\Event\\Role\\RoleDescriptionChangedEvent": Role description changed from "%from%" to "%to%" -"Ergonode\\Account\\Domain\\Event\\Role\\RolePrivilegesChangedEvent": List of pirivileges changed -"Ergonode\\Account\\Domain\\Event\\Role\\RoleDeletedEvent": Role deleted - - - +'User avatar changed': 'User avatar changed' +'User created': 'User "%first_name% %last_name%" created' +'User first name changed': 'User first name changed from "%from% to "%to%"' +'User language changed': 'User language changed from "%from% to %to%"' +'User last name changed': 'User last name changed from "%from% to "%to%"' +'User password changed': 'User password changed' +'User role changed': 'User role changed' +'User activated': 'User activated' +'User disabled': 'User disabled' +'Privilege added': 'Provilege "%privilege%" added' +'Privilege removed': 'Privilege "%privilege%" removed' +'Role created': 'Role "%name%" created' +'Role name changed': 'Role name changed from "%from%" to "%to%"' +'Role description changed': 'Role description changed from "%from%" to "%to%"' +'List of privileges changed': 'List of privileges changed' +'Role deleted': 'Role deleted' diff --git a/module/account/src/Resources/translations/log.pl.yaml b/module/account/src/Resources/translations/log.pl.yaml index 15d48df3f..6360adb94 100644 --- a/module/account/src/Resources/translations/log.pl.yaml +++ b/module/account/src/Resources/translations/log.pl.yaml @@ -1,16 +1,16 @@ -"Ergonode\\Account\\Domain\\Event\\User\\UserAvatarChangedEvent": Awatar użytkownika został zmieniony -"Ergonode\\Account\\Domain\\Event\\User\\UserCreatedEvent": Użytkownik "%first_name% %last_name%" został utworzony -"Ergonode\\Account\\Domain\\Event\\User\\UserFirstNameChangedEvent": Imię użytkownika zostało zmienione z "%from% na "%to%" -"Ergonode\\Account\\Domain\\Event\\User\\UserLanguageChangedEvent": Język użytkownika został zmieniony z "%from% na %to%" -"Ergonode\\Account\\Domain\\Event\\User\\UserLastNameChangedEvent": Nazwisko użytkownika zostało zmienione z "%from% na "%to%" -"Ergonode\\Account\\Domain\\Event\\User\\UserPasswordChangedEvent": Hasło użytkownika zostało zmienione -"Ergonode\\Account\\Domain\\Event\\User\\UserRoleChangedEvent": Rola użytkonika została zmieniona z "%from%" na "%to%" -"Ergonode\\Account\\Domain\\Event\\User\\UserActivatedEvent": Użytkownik aktywowany -"Ergonode\\Account\\Domain\\Event\\User\\UserDeactivatedEvent": Użytkownik zablokowany -"Ergonode\\Account\\Domain\\Event\\Role\\AddPrivilegeToRoleEvent": Uprawnienie "%privilege%" zostało dodane -"Ergonode\\Account\\Domain\\Event\\Role\\RemovePrivilegeFromRoleEvent": Uprawnienie "%privilege%" zostało usunięte -"Ergonode\\Account\\Domain\\Event\\Role\\RoleCreatedEvent": Rola "%name%" została utworzona -"Ergonode\\Account\\Domain\\Event\\Role\\RoleDescriptionChangedEvent": Opis roli została zmieniona z "%from%" na "%to%" -"Ergonode\\Account\\Domain\\Event\\Role\\RoleNameChangedEvent": Nazwa roli została zmieniona z "%from%" na "%to%" -"Ergonode\\Account\\Domain\\Event\\Role\\RolePrivilegesChangedEvent": List uprawnień została zmieniona -"Ergonode\\Account\\Domain\\Event\\Role\\RoleDeletedEvent": Rola została usunięta +'User avatar changed': 'Awatar użytkownika został zmieniony' +'User created': 'Użytkownik "%first_name% %last_name%" został utworzony' +'User first name changed': 'Imię użytkownika zostało zmienione z "%from% na "%to%"' +'User language changed': 'Język użytkownika został zmieniony z "%from% na %to%"' +'User last name changed': 'Nazwisko użytkownika zostało zmienione z "%from% na "%to%"' +'User password changed': 'Hasło użytkownika zostało zmienione' +'User role changed': 'Rola użytkownika została zmieniona z "%from%" na "%to%"' +'User activated': 'Użytkownik aktywowany' +'User disabled': 'Użytkownik zablokowany' +'Privilege added': 'Uprawnienie "%privilege%" zostało dodane' +'Privilege removed': 'Uprawnienie "%privilege%" zostało usunięte' +'Role created': 'Rola "%name%" została utworzona' +'Role name changed': 'Opis roli został zmieniony z "%from%" na "%to%"' +'Role description changed': 'Nazwa roli została zmieniona z "%from%" na "%to%"' +'List of privileges changed': 'Lista uprawnień została zmieniona' +'Role deleted': 'Rola została usunięta' diff --git a/module/attribute-image/migrations/Version20180610062602.php b/module/attribute-image/migrations/Version20180610062602.php new file mode 100644 index 000000000..d21222044 --- /dev/null +++ b/module/attribute-image/migrations/Version20180610062602.php @@ -0,0 +1,41 @@ +createEventStoreEvents([ + 'Ergonode\AttributeImage\Domain\Event\AttributeImageFormatAddedEvent' => 'Added format to image attribute', + ]); + } + + /** + * @param array $collection + * + * @throws \Doctrine\DBAL\DBALException + */ + private function createEventStoreEvents(array $collection): void + { + foreach ($collection as $class => $translation) { + $this->connection->insert('event_store_event', [ + 'id' => Uuid::uuid4()->toString(), + 'event_class' => $class, + 'translation_key' => $translation, + ]); + } + } +} diff --git a/module/attribute-image/src/Resources/translations/log.en.yaml b/module/attribute-image/src/Resources/translations/log.en.yaml new file mode 100644 index 000000000..d3a1d6108 --- /dev/null +++ b/module/attribute-image/src/Resources/translations/log.en.yaml @@ -0,0 +1 @@ +'Added format to image attribute': 'Added format to image attribute' diff --git a/module/attribute-image/src/Resources/translations/log.pl.yaml b/module/attribute-image/src/Resources/translations/log.pl.yaml new file mode 100644 index 000000000..303411fcd --- /dev/null +++ b/module/attribute-image/src/Resources/translations/log.pl.yaml @@ -0,0 +1 @@ +'Added format to image attribute': 'Dodano nowy format do atrybutu obrazka' diff --git a/module/attribute/migrations/Version20180625083834.php b/module/attribute/migrations/Version20180625083834.php index 2202aa72e..f0b92262a 100644 --- a/module/attribute/migrations/Version20180625083834.php +++ b/module/attribute/migrations/Version20180625083834.php @@ -124,6 +124,20 @@ public function up(Schema $schema): void $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'ATTRIBUTE_GROUP_READ', 'Attribute group']); $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'ATTRIBUTE_GROUP_UPDATE', 'Attribute group']); $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'ATTRIBUTE_GROUP_DELETE', 'Attribute group']); + + $this->createEventStoreEvents([ + 'Ergonode\Attribute\Domain\Event\Attribute\AttributeCreatedEvent' => 'Attribute added', + 'Ergonode\Attribute\Domain\Event\Attribute\AttributeHintChangedEvent' => 'Attribute hint changed', + 'Ergonode\Attribute\Domain\Event\Attribute\AttributeLabelChangedEvent' => 'Attribute label changed', + 'Ergonode\Attribute\Domain\Event\Attribute\AttributePlaceholderChangedEvent' => 'Attribute placeholder changed', + 'Ergonode\Attribute\Domain\Event\Attribute\AttributeArrayParameterChangeEvent' => 'Attribute parameters changed', + 'Ergonode\Attribute\Domain\Event\Attribute\AttributeParameterChangeEvent' => 'Attribute parameter changed', + 'Ergonode\Attribute\Domain\Event\AttributeGroupAddedEvent' => 'Attribute added to group', + 'Ergonode\Attribute\Domain\Event\AttributeGroupDeletedEvent' => 'Attribute removed from group', + 'Ergonode\Attribute\Domain\Event\AttributeOptionAddedEvent' => 'Attribute option added', + 'Ergonode\Attribute\Domain\Event\AttributeOptionRemovedEvent' => 'Attribute option removed', + 'Ergonode\Attribute\Domain\Event\AttributeOptionChangedEvent' => 'Attribute option changed', + ]); } /** @@ -137,4 +151,20 @@ private function addGroup(string $label, bool $default = false): void $id = AttributeGroupId::generate(); $this->addSql('INSERT INTO attribute_group (id, label, "default") VALUES (?, ?, ?)', [$id, $label, (int) $default], ['default' => \PDO::PARAM_BOOL]); } + + /** + * @param array $collection + * + * @throws \Doctrine\DBAL\DBALException + */ + private function createEventStoreEvents(array $collection): void + { + foreach ($collection as $class => $translation) { + $this->connection->insert('event_store_event', [ + 'id' => Uuid::uuid4()->toString(), + 'event_class' => $class, + 'translation_key' => $translation, + ]); + } + } } diff --git a/module/attribute/src/Resources/translations/log.en.yaml b/module/attribute/src/Resources/translations/log.en.yaml index 6e771d5ec..e78982de6 100644 --- a/module/attribute/src/Resources/translations/log.en.yaml +++ b/module/attribute/src/Resources/translations/log.en.yaml @@ -1,12 +1,12 @@ -"Ergonode\\Attribute\\Domain\\Event\\Attribute\\AttributeCreatedEvent": Attribute "%code%" added -"Ergonode\\Attribute\\Domain\\Event\\Attribute\\AttributeHintChangedEvent": Attribute hint changed -"Ergonode\\Attribute\\Domain\\Event\\Attribute\\AttributeLabelChangedEvent": Attribute label changed -"Ergonode\\Attribute\\Domain\\Event\\Attribute\\AttributePlaceholderChangedEvent": Attribute placeholder changed -"Ergonode\\Attribute\\Domain\\Event\\Attribute\\AttributeArrayParameterChangeEvent": Attribute parameters changed -"Ergonode\\Attribute\\Domain\\Event\\Attribute\\AttributeParameterChangeEvent": Attribute parameter changed -"Ergonode\\Attribute\\Domain\\Event\\AttributeGroupAddedEvent": Attribute added to group -"Ergonode\\Attribute\\Domain\\Event\\AttributeGroupDeletedEvent": Attribute removed from group -"Ergonode\\Attribute\\Domain\\Event\\AttributeOptionAddedEvent": Attribute option "%key%" added -"Ergonode\\Attribute\\Domain\\Event\\AttributeOptionRemovedEvent": Attribute option "%key%" removed -"Ergonode\\Attribute\\Domain\\Event\\AttributeOptionChangedEvent": Attribute option "%key%" changed +'Attribute added': 'Attribute "%code%" added' +'Attribute hint changed': 'Attribute hint changed' +'Attribute label changed': 'Attribute label changed' +'Attribute placeholder changed': 'Attribute placeholder changed' +'Attribute parameters changed': 'Attribute parameters changed' +'Attribute parameter changed': 'Attribute parameter changed' +'Attribute added to group': 'Attribute added to group' +'Attribute removed from group': 'Attribute removed from group' +'Attribute option added': 'Attribute option "%key%" added' +'Attribute option removed': 'Attribute option "%key%" removed' +'Attribute option changed': 'Attribute option "%key%" changed' diff --git a/module/attribute/src/Resources/translations/log.pl.yaml b/module/attribute/src/Resources/translations/log.pl.yaml index cb1f7530a..a3549da52 100644 --- a/module/attribute/src/Resources/translations/log.pl.yaml +++ b/module/attribute/src/Resources/translations/log.pl.yaml @@ -1,12 +1,12 @@ -"Ergonode\\Attribute\\Domain\\Event\\Attribute\\AttributeCreatedEvent": Atrybut "%code%" został dodany -"Ergonode\\Attribute\\Domain\\Event\\Attribute\\AttributeHintChangedEvent": Wartość podpowiedzi dla atrybutu została zmieniona -"Ergonode\\Attribute\\Domain\\Event\\Attribute\\AttributeLabelChangedEvent": Nazwa atrybutu została zmieniona -"Ergonode\\Attribute\\Domain\\Event\\Attribute\\AttributePlaceholderChangedEvent": Wartość wskazówki dla atrybutu została zmieniona -"Ergonode\\Attribute\\Domain\\Event\\Attribute\\AttributeArrayParameterChangeEvent": Ustawienia atrybutu zostały zmienione -"Ergonode\\Attribute\\Domain\\Event\\Attribute\\AttributeParameterChangeEvent": Ustawienia atrybutu zostały zmieniona -"Ergonode\\Attribute\\Domain\\Event\\AttributeGroupAddedEvent": Atrybut dodany do grupy -"Ergonode\\Attribute\\Domain\\Event\\AttributeGroupDeletedEvent": Atrybut usunięty z grupy -"Ergonode\\Attribute\\Domain\\Event\\AttributeOptionAddedEvent": Opcja atrybutu "%key%" została dodana -"Ergonode\\Attribute\\Domain\\Event\\AttributeOptionRemovedEvent": Opcja atrybutu "%key%" została zmieniona -"Ergonode\\Attribute\\Domain\\Event\\AttributeOptionChangedEvent": Opcja atrybutu "%key%" została usunięta +'Attribute added': 'Atrybut "%code%" został dodany' +'Attribute hint changed': 'Wartość podpowiedzi dla atrybutu została zmieniona' +'Attribute label changed': 'Nazwa atrybutu została zmieniona' +'Attribute placeholder changed': 'Wartość wskazówki dla atrybutu została zmieniona' +'Attribute parameters changed': 'Ustawienia atrybutu zostały zmienione' +'Attribute parameter changed': 'Ustawienia atrybutu zostały zmieniona' +'Attribute added to group': 'Atrybut dodany do grupy' +'Attribute removed from group': 'Atrybut usunięty z grupy' +'Attribute option added': 'Opcja atrybutu "%key%" została dodana' +'Attribute option removed': 'Opcja atrybutu "%key%" została zmieniona' +'Attribute option changed': 'Opcja atrybutu "%key%" została usunięta' diff --git a/module/category-tree/migrations/Version20180619083700.php b/module/category-tree/migrations/Version20180619083700.php index bbe705848..9b7e8a806 100644 --- a/module/category-tree/migrations/Version20180619083700.php +++ b/module/category-tree/migrations/Version20180619083700.php @@ -18,17 +18,42 @@ final class Version20180619083700 extends AbstractErgonodeMigration */ public function up(Schema $schema): void { - $this->addSql( - 'CREATE TABLE IF NOT EXISTS tree ( - id UUID NOT NULL, - code VARCHAR(64) NOT NULL, - name JSONB NOT NULL, - PRIMARY KEY(id))' - ); + $this->addSql(' + CREATE TABLE IF NOT EXISTS tree ( + id UUID NOT NULL, + code VARCHAR(64) NOT NULL, + name JSONB NOT NULL, + PRIMARY KEY(id) + ) + '); $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'CATEGORY_TREE_CREATE', 'Category tree']); $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'CATEGORY_TREE_READ', 'Category tree']); $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'CATEGORY_TREE_UPDATE', 'Category tree']); $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'CATEGORY_TREE_DELETE', 'Category tree']); + + $this->createEventStoreEvents([ + 'Ergonode\CategoryTree\Domain\Event\CategoryTreeCategoriesChangedEvent' => 'Categories changed on category tree', + 'Ergonode\CategoryTree\Domain\Event\CategoryTreeCategoryAddedEvent' => 'Category added to category tree', + 'Ergonode\CategoryTree\Domain\Event\CategoryTreeCategoryRemovedEvent' => 'Category removed from category tree', + 'Ergonode\CategoryTree\Domain\Event\CategoryTreeCreatedEvent' => 'Category tree created', + 'Ergonode\CategoryTree\Domain\Event\CategoryTreeNameChangedEvent' => 'Category tree name changed', + ]); + } + + /** + * @param array $collection + * + * @throws \Doctrine\DBAL\DBALException + */ + private function createEventStoreEvents(array $collection): void + { + foreach ($collection as $class => $translation) { + $this->connection->insert('event_store_event', [ + 'id' => Uuid::uuid4()->toString(), + 'event_class' => $class, + 'translation_key' => $translation, + ]); + } } } diff --git a/module/category-tree/src/Resources/translations/log.en.yaml b/module/category-tree/src/Resources/translations/log.en.yaml index 86d4a3ef5..b143fb8dc 100644 --- a/module/category-tree/src/Resources/translations/log.en.yaml +++ b/module/category-tree/src/Resources/translations/log.en.yaml @@ -1,4 +1,5 @@ -"Ergonode\\CategoryTree\\Domain\\Event\\CategoryTreeCategoriesChangedEvent": Categories changed -"Ergonode\\CategoryTree\\Domain\\Event\\CategoryTreeCategoryAddedEvent": Category added -"Ergonode\\CategoryTree\\Domain\\Event\\CategoryTreeCategoryRemovedEvent": Category removed -"Ergonode\\CategoryTree\\Domain\\Event\\CategoryTreeCreatedEvent": Category Tree "%name%" created +'Categories changed on category tree': 'Categories changed on category tree' +'Category added to category tree': 'Category added to category tree' +'Category removed from category tree': 'Category removed from category tree' +'Category tree created': 'Category tree created' +'Category tree name changed': 'Category tree name changed' diff --git a/module/category-tree/src/Resources/translations/log.pl.yaml b/module/category-tree/src/Resources/translations/log.pl.yaml index 2824e0efe..502e50405 100644 --- a/module/category-tree/src/Resources/translations/log.pl.yaml +++ b/module/category-tree/src/Resources/translations/log.pl.yaml @@ -1,4 +1,5 @@ -"Ergonode\\CategoryTree\\Domain\\Event\\CategoryTreeCategoriesChangedEvent": Zamiana wielu kategorii -"Ergonode\\CategoryTree\\Domain\\Event\\CategoryTreeCategoryAddedEvent": Dodanie kategorii -"Ergonode\\CategoryTree\\Domain\\Event\\CategoryTreeCategoryRemovedEvent": Usunięcie kategorii -"Ergonode\\CategoryTree\\Domain\\Event\\CategoryTreeCreatedEvent": Stworzenie drzewa kategorii +'Categories changed on category tree': 'Zamiana wielu kategorii na drzewie kategorii' +'Category added to category tree': 'Dodanie kategorii do drzewa kategorii' +'Category removed from category tree': 'Usunięcie kategorii z drzewa kategorii' +'Category tree created': 'Stworzenie drzewa kategorii' +'Category tree name changed': 'Zmieniono nazwę drzewa kategorii' diff --git a/module/category/migrations/Version20180619083800.php b/module/category/migrations/Version20180619083800.php index 915035e59..1e59bc377 100644 --- a/module/category/migrations/Version20180619083800.php +++ b/module/category/migrations/Version20180619083800.php @@ -18,19 +18,40 @@ final class Version20180619083800 extends AbstractErgonodeMigration */ public function up(Schema $schema): void { - $this->addSql( - 'CREATE TABLE IF NOT EXISTS category ( - id UUID NOT NULL, - name JSONB NOT NULL, - code VARCHAR(255) DEFAULT NULL, - sequence SERIAL, - PRIMARY KEY(id) - )' - ); + $this->addSql(' + CREATE TABLE IF NOT EXISTS category ( + id UUID NOT NULL, + name JSONB NOT NULL, + code VARCHAR(255) DEFAULT NULL, + sequence SERIAL, + PRIMARY KEY(id) + ) + '); $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'CATEGORY_CREATE', 'Category']); $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'CATEGORY_READ', 'Category']); $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'CATEGORY_UPDATE', 'Category']); $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'CATEGORY_DELETE', 'Category']); + + $this->createEventStoreEvents([ + 'Ergonode\Category\Domain\Event\CategoryCreatedEvent' => 'Category created', + 'Ergonode\Category\Domain\Event\CategoryNameChangedEvent' => 'Category name changed', + ]); + } + + /** + * @param array $collection + * + * @throws \Doctrine\DBAL\DBALException + */ + private function createEventStoreEvents(array $collection): void + { + foreach ($collection as $class => $translation) { + $this->connection->insert('event_store_event', [ + 'id' => Uuid::uuid4()->toString(), + 'event_class' => $class, + 'translation_key' => $translation, + ]); + } } } diff --git a/module/category/src/Resources/translations/log.en.yaml b/module/category/src/Resources/translations/log.en.yaml index f60cec2e8..0cf9f891f 100644 --- a/module/category/src/Resources/translations/log.en.yaml +++ b/module/category/src/Resources/translations/log.en.yaml @@ -1,2 +1,2 @@ -"Ergonode\\Category\\Domain\\Event\\CategoryCreatedEvent": Category "%code%" created -"Ergonode\\Category\\Domain\\Event\\CategoryNameChangedEvent": Category name changed +'Category created': 'Category "%code%" created' +'Category name changed': 'Category name changed' diff --git a/module/category/src/Resources/translations/log.pl.yaml b/module/category/src/Resources/translations/log.pl.yaml index 8bb9dc04a..6961c8a64 100644 --- a/module/category/src/Resources/translations/log.pl.yaml +++ b/module/category/src/Resources/translations/log.pl.yaml @@ -1,2 +1,2 @@ -"Ergonode\\Category\\Domain\\Event\\CategoryCreatedEvent": Stworzono kategorię "%code%" -"Ergonode\\Category\\Domain\\Event\\CategoryNameChangedEvent": Nazwa kategori została zmieniona +'Category created': 'Stworzono kategorię "%code%"' +'Category name changed': 'Nazwa kategori została zmieniona' diff --git a/module/designer/migrations/Version20180719132703.php b/module/designer/migrations/Version20180719132703.php index bc0a17479..2aba1943c 100644 --- a/module/designer/migrations/Version20180719132703.php +++ b/module/designer/migrations/Version20180719132703.php @@ -9,7 +9,6 @@ use Ramsey\Uuid\Uuid; /** - * Auto-generated Ergonode Migration Class: */ final class Version20180719132703 extends AbstractErgonodeMigration { @@ -22,49 +21,49 @@ public function up(Schema $schema): void { $this->addSql('CREATE SCHEMA designer'); - $this->addSql( - 'CREATE TABLE designer.template ( - id UUID NOT NULL, - name VARCHAR(255) NOT NULL, - image_id UUID DEFAULT NULL, - template_group_id UUID NOT NULL, - PRIMARY KEY(id) - )' - ); - - $this->addSql( - 'CREATE TABLE designer.template_element ( - template_id UUID NOT NULL, - x INTEGER NOT NULL, - y INTEGER NOT NULL, - width INTEGER NOT NULL, - height INTEGER NOT NULL, - properties JSONB NOT NULL, - PRIMARY KEY(template_id, x, y) - )' - ); - - $this->addSql( - 'CREATE TABLE designer.template_group ( - id UUID NOT NULL, - name VARCHAR(32) NOT NULL, - custom boolean DEFAULT FALSE, - PRIMARY KEY(id) - )' - ); - - $this->addSql( - 'CREATE TABLE designer.element_type ( - type VARCHAR(32) NOT NULL, - variant VARCHAR(32) NOT NULL, - label VARCHAR(32) NOT NULL, - min_width INTEGER NOT NULL, - min_height INTEGER NOT NULL, - max_width INTEGER NOT NULL, - max_height INTEGER NOT NULL, - PRIMARY KEY(type) - )' - ); + $this->addSql(' + CREATE TABLE designer.template ( + id UUID NOT NULL, + name VARCHAR(255) NOT NULL, + image_id UUID DEFAULT NULL, + template_group_id UUID NOT NULL, + PRIMARY KEY(id) + ) + '); + + $this->addSql(' + CREATE TABLE designer.template_element ( + template_id UUID NOT NULL, + x INTEGER NOT NULL, + y INTEGER NOT NULL, + width INTEGER NOT NULL, + height INTEGER NOT NULL, + properties JSONB NOT NULL, + PRIMARY KEY(template_id, x, y) + ) + '); + + $this->addSql(' + CREATE TABLE designer.template_group ( + id UUID NOT NULL, + name VARCHAR(32) NOT NULL, + custom boolean DEFAULT FALSE, + PRIMARY KEY(id) + ) + '); + + $this->addSql(' + CREATE TABLE designer.element_type ( + type VARCHAR(32) NOT NULL, + variant VARCHAR(32) NOT NULL, + label VARCHAR(32) NOT NULL, + min_width INTEGER NOT NULL, + min_height INTEGER NOT NULL, + max_width INTEGER NOT NULL, + max_height INTEGER NOT NULL, + PRIMARY KEY(type) + ) + '); $this->addType('TEXT', 'attribute', 'Text'); $this->addType('NUMERIC', 'attribute', 'Numeric'); @@ -86,6 +85,19 @@ public function up(Schema $schema): void $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'TEMPLATE_DESIGNER_READ', 'Template designer']); $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'TEMPLATE_DESIGNER_UPDATE', 'Template designer']); $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'TEMPLATE_DESIGNER_DELETE', 'Template designer']); + + $this->createEventStoreEvents([ + 'Ergonode\Designer\Domain\Event\TemplateCreatedEvent' => 'Template created', + 'Ergonode\Designer\Domain\Event\TemplateElementAddedEvent' => 'Template element added to template', + 'Ergonode\Designer\Domain\Event\TemplateElementChangedEvent' => 'Template element changed', + 'Ergonode\Designer\Domain\Event\TemplateElementRemovedEvent' => 'Template element removed', + 'Ergonode\Designer\Domain\Event\TemplateGroupChangedEvent' => 'Template group removed', + 'Ergonode\Designer\Domain\Event\TemplateImageAddedEvent' => 'Template image added', + 'Ergonode\Designer\Domain\Event\TemplateImageChangedEvent' => 'Template image changed', + 'Ergonode\Designer\Domain\Event\TemplateImageRemovedEvent' => 'Template image removed', + 'Ergonode\Designer\Domain\Event\TemplateNameChangedEvent' => 'Template name changed', + 'Ergonode\Designer\Domain\Event\TemplateRemovedEvent' => 'Template removed', + ]); } /** @@ -123,4 +135,20 @@ private function addType( ): void { $this->addSql(\sprintf('INSERT INTO designer.element_type (type, variant, label, min_width, min_height, max_width, max_height) VALUES (\'%s\', \'%s\', \'%s\', %d, %d, %d, %d)', $code, $variant, $label, $minWidth, $minHeight, $maxWidth, $maxHeight)); } + + /** + * @param array $collection + * + * @throws \Doctrine\DBAL\DBALException + */ + private function createEventStoreEvents(array $collection): void + { + foreach ($collection as $class => $translation) { + $this->connection->insert('event_store_event', [ + 'id' => Uuid::uuid4()->toString(), + 'event_class' => $class, + 'translation_key' => $translation, + ]); + } + } } diff --git a/module/designer/src/Resources/translations/log.en.yaml b/module/designer/src/Resources/translations/log.en.yaml index 95552984e..428f8af7c 100644 --- a/module/designer/src/Resources/translations/log.en.yaml +++ b/module/designer/src/Resources/translations/log.en.yaml @@ -1,10 +1,10 @@ -"Ergonode\\Designer\\Domain\\Event\\TemplateCreatedEvent": Template "%name%" created -"Ergonode\\Designer\\Domain\\Event\\TemplateElementAddedEvent": Template element added to template -"Ergonode\\Designer\\Domain\\Event\\TemplateElementChangedEvent": Template element changed -"Ergonode\\Designer\\Domain\\Event\\TemplateElementRemovedEvent": Template element removed -"Ergonode\\Designer\\Domain\\Event\\TemplateGroupChangedEvent": Template group removed -"Ergonode\\Designer\\Domain\\Event\\TemplateImageAddedEvent": Template image added -"Ergonode\\Designer\\Domain\\Event\\TemplateImageChangedEvent": Template image changed -"Ergonode\\Designer\\Domain\\Event\\TemplateImageRemovedEvent": Template image removed -"Ergonode\\Designer\\Domain\\Event\\TemplateNameChangedEvent": Template name changed from "%from%" to "%to%" -"Ergonode\\Designer\\Domain\\Event\\TemplateRemovedEvent": Template removed +'Template created': 'Template "%name%" created' +'Template element added to template': 'Template element added to template' +'Template element changed': 'Template element changed' +'Template element removed': 'Template element removed' +'Template group removed': 'Template group removed' +'Template image added': 'Template image added' +'Template image changed': 'Template image changed' +'Template image removed': 'Template image removed' +'Template name changed': 'Template name changed from "%from%" to "%to%"' +'Template removed': 'Template removed' diff --git a/module/designer/src/Resources/translations/log.pl.yaml b/module/designer/src/Resources/translations/log.pl.yaml index 815df19ed..a94069253 100644 --- a/module/designer/src/Resources/translations/log.pl.yaml +++ b/module/designer/src/Resources/translations/log.pl.yaml @@ -1,10 +1,10 @@ -"Ergonode\\Designer\\Domain\\Event\\TemplateCreatedEvent": Szablon "%name%" dodany -"Ergonode\\Designer\\Domain\\Event\\TemplateElementAddedEvent": Element dodany do szablonu -"Ergonode\\Designer\\Domain\\Event\\TemplateElementChangedEvent": Element szablonu zmieniony -"Ergonode\\Designer\\Domain\\Event\\TemplateElementRemovedEvent": Element szablonu usunięty -"Ergonode\\Designer\\Domain\\Event\\TemplateGroupChangedEvent": Szablon usunięty z grupy -"Ergonode\\Designer\\Domain\\Event\\TemplateImageAddedEvent": Zdjęcie dodane do szablonu -"Ergonode\\Designer\\Domain\\Event\\TemplateImageChangedEvent": Zmieniono zdjęcie szablonu -"Ergonode\\Designer\\Domain\\Event\\TemplateImageRemovedEvent": Usunieto zdjęcie szablonu -"Ergonode\\Designer\\Domain\\Event\\TemplateNameChangedEvent": Nazwa szablonu zmieniona z "%from%" na "%to%" -"Ergonode\\Designer\\Domain\\Event\\TemplateRemovedEvent": Szablon usunieto +'Template created': 'Szablon "%name%" dodany' +'Template element added to template': 'Element dodany do szablonu' +'Template element changed': 'Element szablonu zmieniony' +'Template element removed': 'Element szablonu usunięty' +'Template group removed': 'Szablon usunięty z grupy' +'Template image added': 'Zdjęcie dodane do szablonu' +'Template image changed': 'Zmieniono zdjęcie szablonu' +'Template image removed': 'Usunieto zdjęcie szablonu' +'Template name changed': 'Nazwa szablonu zmieniona z "%from%" na "%to%"' +'Template removed': 'Szablon usunieto' diff --git a/module/editor/migrations/Version20180731143300.php b/module/editor/migrations/Version20180731143300.php index 8f07847f5..0b4755572 100644 --- a/module/editor/migrations/Version20180731143300.php +++ b/module/editor/migrations/Version20180731143300.php @@ -5,9 +5,9 @@ namespace Ergonode\Migration; use Doctrine\DBAL\Schema\Schema; +use Ramsey\Uuid\Uuid; /** - * Auto-generated Ergonode Migration Class: */ final class Version20180731143300 extends AbstractErgonodeMigration { @@ -18,36 +18,60 @@ final class Version20180731143300 extends AbstractErgonodeMigration */ public function up(Schema $schema): void { - $this->addSql( - 'CREATE TABLE designer.product ( - product_id UUID NOT NULL, - template_id UUID NOT NULL, - PRIMARY KEY(product_id, template_id) - )' - ); - - $this->addSql( - 'CREATE TABLE designer.draft ( - id UUID NOT NULL, - sku VARCHAR(255) DEFAULT NULL, - type VARCHAR(16) NOT NULL DEFAULT \'NEW\', - product_id UUID DEFAULT NULL, - applied boolean NOT NULL DEFAULT FALSE, - PRIMARY KEY(id) - )' - ); - - $this->addSql( - 'CREATE TABLE designer.draft_value ( - id UUID NOT NULL, - draft_id UUID DEFAULT NULL, - element_id UUID NOT NULL, - language VARCHAR(2) DEFAULT NULL, - value text NOT NULL, - PRIMARY KEY(id) - )' - ); + $this->addSql(' + CREATE TABLE designer.product ( + product_id UUID NOT NULL, + template_id UUID NOT NULL, + PRIMARY KEY(product_id, template_id) + ) + '); + + $this->addSql(' + CREATE TABLE designer.draft ( + id UUID NOT NULL, + sku VARCHAR(255) DEFAULT NULL, + type VARCHAR(16) NOT NULL DEFAULT \'NEW\', + product_id UUID DEFAULT NULL, + applied boolean NOT NULL DEFAULT FALSE, + PRIMARY KEY(id) + ) + '); + + $this->addSql(' + CREATE TABLE designer.draft_value ( + id UUID NOT NULL, + draft_id UUID DEFAULT NULL, + element_id UUID NOT NULL, + language VARCHAR(2) DEFAULT NULL, + value text NOT NULL, + PRIMARY KEY(id) + ) + '); $this->addSql('ALTER TABLE designer.product ADD CONSTRAINT product_template_id_fk FOREIGN KEY (template_id) REFERENCES designer.template (id) ON DELETE RESTRICT'); + + $this->createEventStoreEvents([ + 'Ergonode\Editor\Domain\Event\ProductDraftApplied' => 'Applied product draft', + 'Ergonode\Editor\Domain\Event\ProductDraftCreated' => 'Product draft created', + 'Ergonode\Editor\Domain\Event\ProductDraftValueAdded' => 'Value added to product draft', + 'Ergonode\Editor\Domain\Event\ProductDraftValueChanged' => 'Product draft value changed', + 'Ergonode\Editor\Domain\Event\ProductDraftValueRemoved' => 'Product draft value removed', + ]); + } + + /** + * @param array $collection + * + * @throws \Doctrine\DBAL\DBALException + */ + private function createEventStoreEvents(array $collection): void + { + foreach ($collection as $class => $translation) { + $this->connection->insert('event_store_event', [ + 'id' => Uuid::uuid4()->toString(), + 'event_class' => $class, + 'translation_key' => $translation, + ]); + } } } diff --git a/module/editor/src/Resources/translations/log.en.yaml b/module/editor/src/Resources/translations/log.en.yaml index b233410eb..e1e9a07b4 100644 --- a/module/editor/src/Resources/translations/log.en.yaml +++ b/module/editor/src/Resources/translations/log.en.yaml @@ -1,6 +1,6 @@ -"Ergonode\\Editor\\Domain\\Event\\ProductDraftApplied": Appliening product draft -"Ergonode\\Editor\\Domain\\Event\\ProductDraftCreated": Product draft created -"Ergonode\\Editor\\Domain\\Event\\ProductDraftValueAdded": Value added to draft -"Ergonode\\Editor\\Domain\\Event\\ProductDraftValueChanged": Draft value changed -"Ergonode\\Editor\\Domain\\Event\\ProductDraftValueRemoved": Draft value removed +'Applied product draft': 'Applied product draft' +'Product draft created': 'Product draft created' +'Value added to product draft': 'Value added to product draft' +'Product draft value changed': 'Product draft value changed' +'Product draft value removed': 'Product draft value removed' diff --git a/module/editor/src/Resources/translations/log.pl.yaml b/module/editor/src/Resources/translations/log.pl.yaml index b648bfe7d..6b53cb374 100644 --- a/module/editor/src/Resources/translations/log.pl.yaml +++ b/module/editor/src/Resources/translations/log.pl.yaml @@ -1,6 +1,6 @@ -"Ergonode\\Editor\\Domain\\Event\\ProductDraftApplied": Zastosowano roboczą wersję produktu -"Ergonode\\Editor\\Domain\\Event\\ProductDraftCreated": Utworzono roboczą wersję produktu -"Ergonode\\Editor\\Domain\\Event\\ProductDraftValueAdded": Dodano wartość do roboczej wersji produktu -"Ergonode\\Editor\\Domain\\Event\\ProductDraftValueChanged": Zmieniono wartość w roboczej wersji produktu -"Ergonode\\Editor\\Domain\\Event\\ProductDraftValueRemoved": Usunieto wartość z roboczej wersji produktu +'Applied product draft': 'Zastosowano roboczą wersję produktu' +'Product draft created': 'Utworzono roboczą wersję produktu' +'Value added to product draft': 'Dodano wartość do roboczej wersji produktu' +'Product draft value changed': 'Zmieniono wartość w roboczej wersji produktu' +'Product draft value removed': 'Usunieto wartość z roboczej wersji produktu' diff --git a/module/event-sourcing/migrations/Version20190903112016.php b/module/event-sourcing/migrations/Version20180101000000.php similarity index 72% rename from module/event-sourcing/migrations/Version20190903112016.php rename to module/event-sourcing/migrations/Version20180101000000.php index be4621216..96e29a570 100644 --- a/module/event-sourcing/migrations/Version20190903112016.php +++ b/module/event-sourcing/migrations/Version20180101000000.php @@ -13,7 +13,7 @@ /** */ -final class Version20190903112016 extends AbstractErgonodeMigration +final class Version20180101000000 extends AbstractErgonodeMigration { /** * @param Schema $schema @@ -25,7 +25,7 @@ public function up(Schema $schema): void id BIGSERIAL NOT NULL, aggregate_id uuid NOT NULL, sequence int NOT NULL, - event character varying(255) NOT NULL, + event_id UUID NOT NULL, payload jsonb NOT NULL, recorded_by uuid default NULL, recorded_at timestamp without time zone NOT NULL, @@ -40,7 +40,7 @@ public function up(Schema $schema): void aggregate_id uuid NOT NULL, sequence int NOT NULL, variant int NOT NULL DEFAULT 1, - event character varying(255) NOT NULL, + event_id UUID NOT NULL, payload jsonb NOT NULL, recorded_by uuid default NULL, recorded_at timestamp without time zone NOT NULL, @@ -48,5 +48,15 @@ public function up(Schema $schema): void ) '); $this->addSql('CREATE UNIQUE INDEX event_store_history_unique_key ON event_store_history USING btree (aggregate_id, sequence, variant)'); + + $this->addSql(' + CREATE TABLE event_store_event ( + id UUID NOT NULL, + event_class character varying(255) NOT NULL, + translation_key text NOT NULL, + CONSTRAINT event_store_event_pkey PRIMARY KEY (id) + ) + '); + $this->addSql('CREATE UNIQUE INDEX event_store_event_unique_key ON event_store_event USING btree (event_class)'); } } diff --git a/module/event-sourcing/src/Infrastructure/Envelope/DomainEventEnvelope.php b/module/event-sourcing/src/Infrastructure/Envelope/DomainEventEnvelope.php index 2a8304699..15d0c052c 100644 --- a/module/event-sourcing/src/Infrastructure/Envelope/DomainEventEnvelope.php +++ b/module/event-sourcing/src/Infrastructure/Envelope/DomainEventEnvelope.php @@ -96,7 +96,7 @@ public function setRecordedAt(\DateTime $recordedAt): void */ public function getType(): string { - return \get_class($this->event); + return get_class($this->event); } /** diff --git a/module/event-sourcing/src/Infrastructure/Factory/SimpleDomainEventFactory.php b/module/event-sourcing/src/Infrastructure/Factory/SimpleDomainEventFactory.php index ff797e1a6..5b9db07c5 100644 --- a/module/event-sourcing/src/Infrastructure/Factory/SimpleDomainEventFactory.php +++ b/module/event-sourcing/src/Infrastructure/Factory/SimpleDomainEventFactory.php @@ -33,10 +33,7 @@ public function __construct(SerializerInterface $serializer) } /** - * @param AbstractId $id - * @param array $records - * - * @return DomainEventEnvelope[] + * {@inheritDoc} */ public function create(AbstractId $id, array $records): array { diff --git a/module/event-sourcing/src/Infrastructure/Provider/DomainEventProvider.php b/module/event-sourcing/src/Infrastructure/Provider/DomainEventProvider.php new file mode 100644 index 000000000..ad48a1ebc --- /dev/null +++ b/module/event-sourcing/src/Infrastructure/Provider/DomainEventProvider.php @@ -0,0 +1,78 @@ +connection = $connection; + $this->cache = $cache; + } + + /** + * {@inheritDoc} + * + * @throws \Psr\Cache\InvalidArgumentException + * @throws \RuntimeException + */ + public function provideEventId(string $eventClass): string + { + $cacheItem = $this->cache->getItem(sha1($eventClass)); + $eventId = $cacheItem->isHit() ? $cacheItem->get() : $this->fetchFromDatabase($eventClass); + + return (string) $eventId; + } + + /** + * @param string $eventClass + * + * @return string + * + * @throws \RuntimeException + */ + private function fetchFromDatabase(string $eventClass): string + { + $queryBuilder = $this->connection->createQueryBuilder() + ->from('event_store_event') + ->select('id') + ->where('event_class = :class') + ->setParameter('class', $eventClass); + $eventId = $queryBuilder->execute()->fetchColumn(); + + if (empty($eventId)) { + throw new \RuntimeException(sprintf( + 'Event class "%s" not found. Check event definition in "event_store_event" table', + $eventClass + )); + } + + return (string) $eventId; + } +} diff --git a/module/event-sourcing/src/Infrastructure/Provider/DomainEventProviderInterface.php b/module/event-sourcing/src/Infrastructure/Provider/DomainEventProviderInterface.php new file mode 100644 index 000000000..4959f132c --- /dev/null +++ b/module/event-sourcing/src/Infrastructure/Provider/DomainEventProviderInterface.php @@ -0,0 +1,22 @@ +tokenStorage = $tokenStorage; $this->connection = $connection; $this->serializer = $serializer; $this->domainEventFactory = $domainEventFactory; $this->cache = $cache; + $this->domainEventProvider = $domainEventProvider; } /** @@ -84,7 +98,7 @@ public function load(AbstractId $id, ?string $table = null): DomainEventStream $item = $this->cache->getItem($key); if ($item->isHit()) { - $result = $item->get(); + $result = $item->get(); $sequence = count($result); } else { $result = []; @@ -94,8 +108,10 @@ public function load(AbstractId $id, ?string $table = null): DomainEventStream $qb = $this->connection->createQueryBuilder(); $records = $qb - ->select('*') - ->from($table) + ->select('es.id, es.aggregate_id, es.sequence, es.payload, es.recorded_by, es.recorded_at') + ->addSelect('ese.event_class as event') + ->from($table, 'es') + ->join('es', 'event_store_event', 'ese', 'es.event_id = ese.id') ->where($qb->expr()->eq('aggregate_id', ':aggregateId')) ->andWhere($qb->expr()->gt('sequence', ':sequence')) ->setParameter('aggregateId', $id->getValue()) @@ -132,7 +148,7 @@ public function append(AbstractId $id, DomainEventStream $stream, ?string $table [ 'aggregate_id' => $id->getValue(), 'sequence' => $envelope->getSequence(), - 'event' => $envelope->getType(), + 'event_id' => $this->domainEventProvider->provideEventId($envelope->getType()), 'payload' => $payload, 'recorded_at' => $envelope->getRecordedAt()->format('Y-m-d H:i:s'), 'recorded_by' => $userId, @@ -169,8 +185,8 @@ public function delete(AbstractId $id, ?string $table = null): void $this->connection->executeQuery( sprintf( - 'INSERT INTO %s (aggregate_id, sequence, event, payload, recorded_by, recorded_at, variant) - SELECT aggregate_id, sequence, event, payload, recorded_by, recorded_at, %d FROM %s WHERE aggregate_id = ?', + 'INSERT INTO %s (aggregate_id, sequence, event_id, payload, recorded_by, recorded_at, variant) + SELECT aggregate_id, sequence, event_id, payload, recorded_by, recorded_at, %d FROM %s WHERE aggregate_id = ?', $historyTable, $version, $dataTable diff --git a/module/event-sourcing/src/Resources/config/services.yml b/module/event-sourcing/src/Resources/config/services.yml index 1e7980cc7..fab7f6db1 100644 --- a/module/event-sourcing/src/Resources/config/services.yml +++ b/module/event-sourcing/src/Resources/config/services.yml @@ -9,3 +9,4 @@ services: Ergonode\EventSourcing\Infrastructure\DomainEventStoreInterface: '@Ergonode\EventSourcing\Infrastructure\Store\DbalDomainEventStore' Ergonode\EventSourcing\Infrastructure\DomainEventDispatcherInterface: '@Ergonode\EventSourcing\Infrastructure\Dispatcher\SymfonyDomainEventDispatcher' + Ergonode\EventSourcing\Infrastructure\Provider\DomainEventProviderInterface: '@Ergonode\EventSourcing\Infrastructure\Provider\DomainEventProvider' diff --git a/module/importer/migrations/Version20180618134343.php b/module/importer/migrations/Version20180618134343.php index f2e42a8bc..80e257c96 100644 --- a/module/importer/migrations/Version20180618134343.php +++ b/module/importer/migrations/Version20180618134343.php @@ -8,55 +8,58 @@ use Ramsey\Uuid\Uuid; /** - * Auto-generated Ergonode Migration Class */ final class Version20180618134343 extends AbstractErgonodeMigration { /** * @param Schema $schema + * + * @throws \Exception */ public function up(Schema $schema): void { $this->addSql('CREATE SCHEMA IF NOT EXISTS importer'); - $this->addSql( - 'CREATE TABLE importer.import ( - id UUID NOT NULL, - name VARCHAR(128) NOT NULL, - type VARCHAR(255) NOT NULL, - status VARCHAR(16) NOT NULL, - options JSON NOT NULL, - reason TEXT DEFAULT NULL, - created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - started_at TIMESTAMP WITHOUT TIME ZONE, - ended_at TIMESTAMP WITHOUT TIME ZONE, - PRIMARY KEY(id) - )' - ); - $this->addSql( - 'CREATE TABLE importer.import_line ( - id UUID NOT NULL, - lp BIGSERIAL, - import_id UUID NOT NULL, - line JSON NOT NULL, - created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - PRIMARY KEY(id) - )' - ); - $this->addSql( - 'CREATE TABLE importer.event_store ( - id BIGSERIAL NOT NULL, - aggregate_id uuid NOT NULL, - sequence int, - event character varying(255) NOT NULL, - payload jsonb NOT NULL, - recorded_by uuid default NULL, - recorded_at timestamp without time zone NOT NULL, - CONSTRAINT event_store_pkey PRIMARY KEY (id) - )' - ); + $this->addSql(' + CREATE TABLE importer.import ( + id UUID NOT NULL, + name VARCHAR(128) NOT NULL, + type VARCHAR(255) NOT NULL, + status VARCHAR(16) NOT NULL, + options JSON NOT NULL, + reason TEXT DEFAULT NULL, + created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + started_at TIMESTAMP WITHOUT TIME ZONE, + ended_at TIMESTAMP WITHOUT TIME ZONE, + PRIMARY KEY(id) + ) + '); + + $this->addSql(' + CREATE TABLE importer.import_line ( + id UUID NOT NULL, + lp BIGSERIAL, + import_id UUID NOT NULL, + line JSON NOT NULL, + created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + PRIMARY KEY(id) + ) + '); + + $this->addSql(' + CREATE TABLE importer.event_store ( + id BIGSERIAL NOT NULL, + aggregate_id uuid NOT NULL, + sequence int, + event_id UUID NOT NULL, + payload jsonb NOT NULL, + recorded_by uuid default NULL, + recorded_at timestamp without time zone NOT NULL, + CONSTRAINT event_store_pkey PRIMARY KEY (id) + ) + '); $this->addSql(' CREATE TABLE importer.event_store_history ( @@ -64,7 +67,7 @@ public function up(Schema $schema): void aggregate_id uuid NOT NULL, sequence int NOT NULL, variant int NOT NULL DEFAULT 1, - event character varying(255) NOT NULL, + event_id UUID NOT NULL, payload jsonb NOT NULL, recorded_by uuid default NULL, recorded_at timestamp without time zone NOT NULL, diff --git a/module/product/migrations/Version20180619083830.php b/module/product/migrations/Version20180619083830.php index 5bedc2ce8..43863b49e 100644 --- a/module/product/migrations/Version20180619083830.php +++ b/module/product/migrations/Version20180619083830.php @@ -18,18 +18,18 @@ final class Version20180619083830 extends AbstractErgonodeMigration */ public function up(Schema $schema): void { - $this->addSql( - 'CREATE TABLE IF NOT EXISTS product ( - id UUID NOT NULL, - index SERIAL, - template_id UUID NOT NULL, - sku VARCHAR(128) NOT NULL, - status VARCHAR(32) NOT NULL, - version INT NOT NULL DEFAULT 0, - attributes JSONB NOT NULL DEFAULT \'{}\'::JSONB, - PRIMARY KEY(id) - )' - ); + $this->addSql(' + CREATE TABLE IF NOT EXISTS product ( + id UUID NOT NULL, + index SERIAL, + template_id UUID NOT NULL, + sku VARCHAR(128) NOT NULL, + status VARCHAR(32) NOT NULL, + version INT NOT NULL DEFAULT 0, + attributes JSONB NOT NULL DEFAULT \'{}\'::JSONB, + PRIMARY KEY(id) + ) + '); $this->addSql('CREATE UNIQUE INDEX product_sku_key ON product USING btree(sku)'); $this->addSql('CREATE TABLE product_value (product_id UUID NOT NULL, attribute_id UUID NOT NULL, value_id UUID NOT NULL, PRIMARY KEY(product_id, attribute_id, value_id))'); @@ -45,5 +45,30 @@ public function up(Schema $schema): void $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'PRODUCT_READ', 'Product']); $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'PRODUCT_UPDATE', 'Product']); $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'PRODUCT_DELETE', 'Product']); + + $this->createEventStoreEvents([ + 'Ergonode\Product\Domain\Event\ProductAddedToCategory' => 'Product added to category', + 'Ergonode\Product\Domain\Event\ProductCreated' => 'Product created', + 'Ergonode\Product\Domain\Event\ProductRemovedFromCategory' => 'Product removed from category', + 'Ergonode\Product\Domain\Event\ProductValueAdded' => 'Product attribute value added', + 'Ergonode\Product\Domain\Event\ProductValueChanged' => 'Product attribute value changed', + 'Ergonode\Product\Domain\Event\ProductValueRemoved' => 'Product attribute value removed', + ]); + } + + /** + * @param array $collection + * + * @throws \Doctrine\DBAL\DBALException + */ + private function createEventStoreEvents(array $collection): void + { + foreach ($collection as $class => $translation) { + $this->connection->insert('event_store_event', [ + 'id' => Uuid::uuid4()->toString(), + 'event_class' => $class, + 'translation_key' => $translation, + ]); + } } } diff --git a/module/product/src/Resources/translations/log.en.yaml b/module/product/src/Resources/translations/log.en.yaml index 56bc9f7c3..1e3742283 100644 --- a/module/product/src/Resources/translations/log.en.yaml +++ b/module/product/src/Resources/translations/log.en.yaml @@ -1,6 +1,6 @@ -"Ergonode\\Product\\Domain\\Event\\ProductAddedToCategory": Product added to category "%categoryCode%" -"Ergonode\\Product\\Domain\\Event\\ProductCreated": Product "%sku%" created -"Ergonode\\Product\\Domain\\Event\\ProductRemovedFromCategory": Product removed from category "%categoryCode%" -"Ergonode\\Product\\Domain\\Event\\ProductValueAdded": Product value of attribute "%code%" added -"Ergonode\\Product\\Domain\\Event\\ProductValueChanged": Product value of attribute "%code%" changed -"Ergonode\\Product\\Domain\\Event\\ProductValueRemoved": Product value of attribute "%code%" removed +'Product added to category': 'Product added to category "%categoryCode%"' +'Product created': 'Product "%sku%" created' +'Product removed from category': 'Product removed from category "%categoryCode%"' +'Product attribute value added': 'Product value of attribute "%code%" added' +'Product attribute value changed': 'Product value of attribute "%code%" changed' +'Product attribute value removed': 'Product value of attribute "%code%" removed' diff --git a/module/product/src/Resources/translations/log.pl.yaml b/module/product/src/Resources/translations/log.pl.yaml index e2d4430a4..4196991db 100644 --- a/module/product/src/Resources/translations/log.pl.yaml +++ b/module/product/src/Resources/translations/log.pl.yaml @@ -1,6 +1,6 @@ -"Ergonode\\Product\\Domain\\Event\\ProductAddedToCategory": Produkt dodany do kategorii "%categoryCode%" -"Ergonode\\Product\\Domain\\Event\\ProductCreated": Produkt "%sku%" utworzony -"Ergonode\\Product\\Domain\\Event\\ProductRemovedFromCategory": Produkt usunięty z kategorii "%categoryCode%" -"Ergonode\\Product\\Domain\\Event\\ProductValueAdded": Dodano wartość atrybutu "%code%" -"Ergonode\\Product\\Domain\\Event\\ProductValueChanged": Zmieniono wartość atrybutu "%code%" -"Ergonode\\Product\\Domain\\Event\\ProductValueRemoved": Usunięto wartość atrybutu "%code%" +'Product added to category': 'Produkt dodany do kategorii "%categoryCode%"' +'Product created': 'Produkt "%sku%" utworzony' +'Product removed from category': 'Produkt usunięty z kategorii "%categoryCode%"' +'Product attribute value added': 'Dodano wartość atrybutu "%code%"' +'Product attribute value changed': 'Zmieniono wartość atrybutu "%code%"' +'Product attribute value removed': 'Usunięto wartość atrybutu "%code%"' diff --git a/module/reader/migrations/Version20180619100000.php b/module/reader/migrations/Version20180619100000.php index df5f4b19c..0bea82014 100644 --- a/module/reader/migrations/Version20180619100000.php +++ b/module/reader/migrations/Version20180619100000.php @@ -5,10 +5,9 @@ namespace Ergonode\Migration; use Doctrine\DBAL\Schema\Schema; -use Ergonode\Migration\AbstractErgonodeMigration; +use Ramsey\Uuid\Uuid; /** - * Auto-generated Ergonode Migration Class */ final class Version20180619100000 extends AbstractErgonodeMigration { @@ -19,13 +18,33 @@ public function up(Schema $schema): void { $this->addSql('CREATE SCHEMA IF NOT EXISTS importer'); - $this->addSql( - 'CREATE TABLE IF NOT EXISTS importer.reader ( - id UUID NOT NULL, - name VARCHAR(64) NOT NULL, - type VARCHAR(32) NOT NULL, - PRIMARY KEY(id) - )' - ); + $this->addSql(' + CREATE TABLE IF NOT EXISTS importer.reader ( + id UUID NOT NULL, + name VARCHAR(64) NOT NULL, + type VARCHAR(32) NOT NULL, + PRIMARY KEY(id) + ) + '); + + $this->createEventStoreEvents([ + 'Ergonode\Reader\Domain\Event\ReaderCreatedEvent' => 'Reader created', + ]); + } + + /** + * @param array $collection + * + * @throws \Doctrine\DBAL\DBALException + */ + private function createEventStoreEvents(array $collection): void + { + foreach ($collection as $class => $translation) { + $this->connection->insert('event_store_event', [ + 'id' => Uuid::uuid4()->toString(), + 'event_class' => $class, + 'translation_key' => $translation, + ]); + } } } diff --git a/module/reader/src/Resources/translations/log.en.yaml b/module/reader/src/Resources/translations/log.en.yaml new file mode 100644 index 000000000..d478bbaf4 --- /dev/null +++ b/module/reader/src/Resources/translations/log.en.yaml @@ -0,0 +1 @@ +'Reader created': 'Reader "%name%" created' diff --git a/module/reader/src/Resources/translations/log.pl.yaml b/module/reader/src/Resources/translations/log.pl.yaml new file mode 100644 index 000000000..1abfa5ed3 --- /dev/null +++ b/module/reader/src/Resources/translations/log.pl.yaml @@ -0,0 +1 @@ +'Reader created': 'Reader "%name%" utworzony' diff --git a/module/segment/migrations/Version20190130104000.php b/module/segment/migrations/Version20190130104000.php index 2259ed105..8ef2790c6 100644 --- a/module/segment/migrations/Version20190130104000.php +++ b/module/segment/migrations/Version20190130104000.php @@ -10,9 +10,9 @@ namespace Ergonode\Migration; use Doctrine\DBAL\Schema\Schema; +use Ramsey\Uuid\Uuid; /** - * Auto-generated Ergonode Migration Class: */ final class Version20190130104000 extends AbstractErgonodeMigration { @@ -23,15 +23,39 @@ final class Version20190130104000 extends AbstractErgonodeMigration */ public function up(Schema $schema): void { - $this->addSql( - 'CREATE TABLE segment ( - id UUID NOT NULL, - code VARCHAR(100) NOT NULL, - name JSON NOT NULL, - description JSON NOT NULL, - status VARCHAR(32) NOT NULL, - PRIMARY KEY(id) - )' - ); + $this->addSql(' + CREATE TABLE segment ( + id UUID NOT NULL, + code VARCHAR(100) NOT NULL, + name JSON NOT NULL, + description JSON NOT NULL, + status VARCHAR(32) NOT NULL, + PRIMARY KEY(id) + ) + '); + + $this->createEventStoreEvents([ + 'Ergonode\Segment\Domain\Event\SegmentCreatedEvent' => 'Segment created', + 'Ergonode\Segment\Domain\Event\SegmentDescriptionChangedEvent' => 'Segment description changed', + 'Ergonode\Segment\Domain\Event\SegmentNameChangedEvent' => 'Segment name changed', + 'Ergonode\Segment\Domain\Event\SegmentSpecificationAddedEvent' => 'Segment specification added', + 'Ergonode\Segment\Domain\Event\SegmentStatusChangedEvent' => 'Segment status changed', + ]); + } + + /** + * @param array $collection + * + * @throws \Doctrine\DBAL\DBALException + */ + private function createEventStoreEvents(array $collection): void + { + foreach ($collection as $class => $translation) { + $this->connection->insert('event_store_event', [ + 'id' => Uuid::uuid4()->toString(), + 'event_class' => $class, + 'translation_key' => $translation, + ]); + } } } diff --git a/module/segment/src/Resources/translations/log.en.yaml b/module/segment/src/Resources/translations/log.en.yaml new file mode 100644 index 000000000..1589c9bd6 --- /dev/null +++ b/module/segment/src/Resources/translations/log.en.yaml @@ -0,0 +1,5 @@ +'Segment created': 'Segment "%code%" created' +'Segment description changed': 'Segment description changed' +'Segment name changed': 'Segment name changed' +'Segment specification added': 'Segment specification added' +'Segment status changed': 'Segment status changed' diff --git a/module/segment/src/Resources/translations/log.pl.yaml b/module/segment/src/Resources/translations/log.pl.yaml new file mode 100644 index 000000000..3297e1439 --- /dev/null +++ b/module/segment/src/Resources/translations/log.pl.yaml @@ -0,0 +1,5 @@ +'Segment created': 'Segment "%code%" utworzono' +'Segment description changed': 'Zmieniono opis segmentu' +'Segment name changed': 'Zmieniono nazwę segmentu' +'Segment specification added': 'Dodano specyfikację do segmentu' +'Segment status changed': 'Zmieniono status segmentu' diff --git a/module/transformer/migrations/Version20180619083829.php b/module/transformer/migrations/Version20180619083829.php index 852e214b8..ddb1d4af7 100644 --- a/module/transformer/migrations/Version20180619083829.php +++ b/module/transformer/migrations/Version20180619083829.php @@ -10,9 +10,9 @@ namespace Ergonode\Migration; use Doctrine\DBAL\Schema\Schema; +use Ramsey\Uuid\Uuid; /** - * Auto-generated Ergonode Migration Class: */ final class Version20180619083829 extends AbstractErgonodeMigration { @@ -22,28 +22,53 @@ final class Version20180619083829 extends AbstractErgonodeMigration public function up(Schema $schema): void { $this->addSql('CREATE SCHEMA IF NOT EXISTS importer'); - $this->addSql( - 'CREATE TABLE importer.transformer ( - id UUID NOT NULL, - name VARCHAR(128) NOT NULL, - key VARCHAR(128) NOT NULL, - PRIMARY KEY(id) - )' - ); + + $this->addSql(' + CREATE TABLE importer.transformer ( + id UUID NOT NULL, + name VARCHAR(128) NOT NULL, + key VARCHAR(128) NOT NULL, + PRIMARY KEY(id) + ) + '); $this->addSql('CREATE TABLE importer.transformer_converter (id UUID NOT NULL, transformer_id UUID NOT NULL, field VARCHAR(64) NOT NULL, type VARCHAR(255) NOT NULL, options JSON NOT NULL, PRIMARY KEY(id))'); - $this->addSql( - 'CREATE TABLE importer.processor ( - id UUID NOT NULL, - import_id UUID NOT NULL, - transformer_Id UUID NOT NULL, - action VARCHAR(64) NOT NULL, - created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, - started_at TIMESTAMP WITHOUT TIME ZONE, - ended_at TIMESTAMP WITHOUT TIME ZONE, - status character varying(32) NOT NULL, - PRIMARY KEY(id) - )' - ); + + $this->addSql(' + CREATE TABLE importer.processor ( + id UUID NOT NULL, + import_id UUID NOT NULL, + transformer_Id UUID NOT NULL, + action VARCHAR(64) NOT NULL, + created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL, + started_at TIMESTAMP WITHOUT TIME ZONE, + ended_at TIMESTAMP WITHOUT TIME ZONE, + status character varying(32) NOT NULL, + PRIMARY KEY(id) + ) + '); + + $this->createEventStoreEvents([ + 'Ergonode\Transformer\Domain\Event\ProcessorCreatedEvent' => 'Transformer processor created', + 'Ergonode\Transformer\Domain\Event\ProcessorStatusChangedEvent' => 'Transformer processor status changed', + 'Ergonode\Transformer\Domain\Event\TransformerConverterAddedEvent' => 'Transformer converter added', + 'Ergonode\Transformer\Domain\Event\TransformerCreatedEvent' => 'Transformer created', + ]); + } + + /** + * @param array $collection + * + * @throws \Doctrine\DBAL\DBALException + */ + private function createEventStoreEvents(array $collection): void + { + foreach ($collection as $class => $translation) { + $this->connection->insert('event_store_event', [ + 'id' => Uuid::uuid4()->toString(), + 'event_class' => $class, + 'translation_key' => $translation, + ]); + } } } diff --git a/module/transformer/src/Resources/translations/log.en.yaml b/module/transformer/src/Resources/translations/log.en.yaml new file mode 100644 index 000000000..f794e83a1 --- /dev/null +++ b/module/transformer/src/Resources/translations/log.en.yaml @@ -0,0 +1,4 @@ +'Transformer created': 'Transformer "%key%" created' +'Transformer converter added': 'Transformer converter added' +'Transformer processor status changed': 'Transformer processor status changed' +'Transformer processor created': 'Transformer processor "%id%" created' diff --git a/module/transformer/src/Resources/translations/log.pl.yaml b/module/transformer/src/Resources/translations/log.pl.yaml new file mode 100644 index 000000000..b444f2e32 --- /dev/null +++ b/module/transformer/src/Resources/translations/log.pl.yaml @@ -0,0 +1,4 @@ +'Transformer created': 'Transformer "%key%" został utworzony' +'Transformer converter added': 'Dodano konwerter do transformera' +'Transformer processor status changed': 'Zmieniono status procesora transformera' +'Transformer processor created': 'Procesor transformera "%id%" został utworzony' diff --git a/module/value/migrations/Version20180619083831.php b/module/value/migrations/Version20180619083831.php new file mode 100644 index 000000000..4d2e3e26e --- /dev/null +++ b/module/value/migrations/Version20180619083831.php @@ -0,0 +1,43 @@ +createEventStoreEvents([ + 'Ergonode\Value\Domain\Event\ValueAddedEvent' => 'Value created', + 'Ergonode\Value\Domain\Event\ValueChangedEvent' => 'Value changed', + 'Ergonode\Value\Domain\Event\ValueRemovedEvent' => 'Value deleted', + ]); + } + + /** + * @param array $collection + * + * @throws \Doctrine\DBAL\DBALException + */ + private function createEventStoreEvents(array $collection): void + { + foreach ($collection as $class => $translation) { + $this->connection->insert('event_store_event', [ + 'id' => Uuid::uuid4()->toString(), + 'event_class' => $class, + 'translation_key' => $translation, + ]); + } + } +} diff --git a/module/value/src/Resources/translations/log.en.yaml b/module/value/src/Resources/translations/log.en.yaml new file mode 100644 index 000000000..d50562d0c --- /dev/null +++ b/module/value/src/Resources/translations/log.en.yaml @@ -0,0 +1,3 @@ +'Value created': 'Value "%code%" created' +'Value changed': 'Value changed from "%from%" to "%to%"' +'Value deleted': 'Value "%code%" deleted' diff --git a/module/value/src/Resources/translations/log.pl.yaml b/module/value/src/Resources/translations/log.pl.yaml new file mode 100644 index 000000000..f481daaaf --- /dev/null +++ b/module/value/src/Resources/translations/log.pl.yaml @@ -0,0 +1,3 @@ +'Value created': 'Wartość "%code%" została utworzona' +'Value changed': 'Zmieniono wartość z "%from%" na "%to%"' +'Value deleted': 'Wartość "%code%" została usunięta' diff --git a/module/workflow/migrations/Version20190818160000.php b/module/workflow/migrations/Version20190818160000.php index b73535554..2857748f1 100644 --- a/module/workflow/migrations/Version20190818160000.php +++ b/module/workflow/migrations/Version20190818160000.php @@ -18,20 +18,49 @@ final class Version20190818160000 extends AbstractErgonodeMigration */ public function up(Schema $schema): void { - $this->addSql( - 'CREATE TABLE IF NOT EXISTS status ( - id UUID NOT NULL, - code VARCHAR(128) NOT NULL, - color VARCHAR(7) NOT NULL, - name JSONB NOT NULL DEFAULT \'{}\', - description JSONB NOT NULL DEFAULT \'{}\', - PRIMARY KEY(id) - )' - ); + $this->addSql(' + CREATE TABLE IF NOT EXISTS status ( + id UUID NOT NULL, + code VARCHAR(128) NOT NULL, + color VARCHAR(7) NOT NULL, + name JSONB NOT NULL DEFAULT \'{}\', + description JSONB NOT NULL DEFAULT \'{}\', + PRIMARY KEY(id) + ) + '); $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'WORKFLOW_CREATE', 'Workflow']); $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'WORKFLOW_READ', 'Workflow']); $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'WORKFLOW_UPDATE', 'Workflow']); $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'WORKFLOW_DELETE', 'Workflow']); + + $this->createEventStoreEvents([ + 'Ergonode\Workflow\Domain\Event\Status\StatusColorChangedEvent' => 'Status color changed', + 'Ergonode\Workflow\Domain\Event\Status\StatusCreatedEvent' => 'Status created', + 'Ergonode\Workflow\Domain\Event\Status\StatusDeletedEvent' => 'Status deleted', + 'Ergonode\Workflow\Domain\Event\Status\StatusDescriptionChangedEvent' => 'Status description changed', + 'Ergonode\Workflow\Domain\Event\Status\StatusNameChangedEvent' => 'Status name changed', + 'Ergonode\Workflow\Domain\Event\Workflow\WorkflowCreatedEvent' => 'Workflow created', + 'Ergonode\Workflow\Domain\Event\Workflow\WorkflowStatusAddedEvent' => 'Added status to workflow', + 'Ergonode\Workflow\Domain\Event\Workflow\WorkflowStatusRemovedEvent' => 'Deleted status from workflow', + 'Ergonode\Workflow\Domain\Event\Workflow\WorkflowTransitionAddedEvent' => 'Added transition to workflow', + 'Ergonode\Workflow\Domain\Event\Workflow\WorkflowTransitionRemovedEvent' => 'Deleted transition from workflow', + ]); + } + + /** + * @param array $collection + * + * @throws \Doctrine\DBAL\DBALException + */ + private function createEventStoreEvents(array $collection): void + { + foreach ($collection as $class => $translation) { + $this->connection->insert('event_store_event', [ + 'id' => Uuid::uuid4()->toString(), + 'event_class' => $class, + 'translation_key' => $translation, + ]); + } } } diff --git a/module/workflow/src/Resources/translations/log.en.yaml b/module/workflow/src/Resources/translations/log.en.yaml new file mode 100644 index 000000000..bdc42c3b7 --- /dev/null +++ b/module/workflow/src/Resources/translations/log.en.yaml @@ -0,0 +1,10 @@ +'Status color changed': 'Status color changed from "%from%" to "%to%"' +'Status created': 'Status "%code%" created' +'Status deleted': 'Status deleted' +'Status description changed': 'Status description changed' +'Status name changed': 'Status name changed' +'Workflow created': 'Workflow "%code%" created' +'Added status to workflow': 'Added status to workflow' +'Deleted status from workflow': 'Deleted status from workflow' +'Added transition to workflow': 'Added transition to workflow' +'Deleted transition from workflow': 'Deleted transition from workflow' diff --git a/module/workflow/src/Resources/translations/log.pl.yaml b/module/workflow/src/Resources/translations/log.pl.yaml new file mode 100644 index 000000000..48154b7cc --- /dev/null +++ b/module/workflow/src/Resources/translations/log.pl.yaml @@ -0,0 +1,10 @@ +'Status color changed': 'Zmieniono kolor statusu z "%from%" na "%to%"' +'Status created': 'Status "%code%" został utworzony' +'Status deleted': 'Usunięto status' +'Status description changed': 'Zmieniono opis statusu' +'Status name changed': 'Zmieniono nazwę statusu' +'Workflow created': 'Przepływ pracy "%code%" utworzony' +'Added status to workflow': 'Dodano status do przepływu pracy' +'Deleted status from workflow': 'Usunięto status z przepływu pracy' +'Added transition to workflow': 'Dodano transformację do przepływu pracy' +'Deleted transition from workflow': 'Usunięto transformację z przepływu pracy' From 543086a4fce66a550cc4d20c996e3d174eadbba7 Mon Sep 17 00:00:00 2001 From: "mateusz.kolankowski" Date: Wed, 4 Sep 2019 17:54:27 +0200 Subject: [PATCH 07/27] behat-tests (templates, worflow) --- config/packages/nelmio_api_doc.yaml | 9 ++ features/product.feature | 126 ++++++++++++++++++ features/workflow.feature | 30 +++++ .../Controller/Api/ProductController.php | 2 +- 4 files changed, 166 insertions(+), 1 deletion(-) diff --git a/config/packages/nelmio_api_doc.yaml b/config/packages/nelmio_api_doc.yaml index 2480c8182..4924690e0 100644 --- a/config/packages/nelmio_api_doc.yaml +++ b/config/packages/nelmio_api_doc.yaml @@ -233,6 +233,15 @@ nelmio_api_doc: type: string required: false description: Lista id kategorii + product_upd: + type: object + properties: + categortyIds: + type: array + items: + type: string + required: false + description: Lista id kategorii template: type: object properties: diff --git a/features/product.feature b/features/product.feature index 5ddb61bdc..d95a22da2 100644 --- a/features/product.feature +++ b/features/product.feature @@ -94,6 +94,95 @@ Feature: Product module When I request "/api/v1/EN/products" using HTTP POST Then unauthorized response is received + Scenario: Create product (wrong product_template no UUID) + Given current authentication token + Given the request body is: + """ + { + "sku": "SKU_@@random_code@@", + "templateId": "test", + "categoryIds": ["@product_category@"] + } + """ + When I request "/api/v1/EN/products" using HTTP POST + Then validation error response is received + + Scenario: Create product (wrong product_template wrong UUID) + Given current authentication token + Given the request body is: + """ + { + "sku": "SKU_@@random_code@@", + "templateId": "@@random_uuid@@", + "categoryIds": ["@product_category@"] + } + """ + When I request "/api/v1/EN/products" using HTTP POST + Then validation error response is received + + Scenario: Create product (no templateId) + Given current authentication token + Given the request body is: + """ + { + "sku": "SKU_@@random_code@@", + "categoryIds": ["@product_category@"] + } + """ + When I request "/api/v1/EN/products" using HTTP POST + Then validation error response is received + + Scenario: Create product (empty categoryIds) + Given current authentication token + Given the request body is: + """ + { + "sku": "SKU_@@random_code@@", + "templateId": "@product_template@", + "categoryIds": [] + } + """ + When I request "/api/v1/EN/products" using HTTP POST + Then created response is received + + Scenario: Create product (no categoryIds) + Given current authentication token + Given the request body is: + """ + { + "sku": "SKU_@@random_code@@", + "templateId": "@product_template@" + } + """ + When I request "/api/v1/EN/products" using HTTP POST + Then created response is received + + + Scenario: Create product (categoryIds not UUID) + Given current authentication token + Given the request body is: + """ + { + "sku": "SKU_@@random_code@@", + "templateId": "@product_template@", + "categoryIds": ["test"] + } + """ + When I request "/api/v1/EN/products" using HTTP POST + Then validation error response is received + + Scenario: Create product (no categoryIds) + Given current authentication token + Given the request body is: + """ + { + "sku": "SKU_@@random_code@@", + "templateId": "@product_template@" + } + """ + When I request "/api/v1/EN/products" using HTTP POST + Then created response is received + Scenario: Update product Given current authentication token Given the request body is: @@ -105,6 +194,8 @@ Feature: Product module When I request "/api/v1/EN/products/@product@" using HTTP PUT Then empty response is received + + Scenario: Update product (not authorized) When I request "/api/v1/EN/products/@product@" using HTTP PUT Then unauthorized response is received @@ -114,6 +205,41 @@ Feature: Product module When I request "/api/v1/EN/products/@@static_uuid@@" using HTTP PUT Then not found response is received + Scenario: Update product (no categoryId) + Given current authentication token + Given the request body is: + """ + { + } + """ + When I request "/api/v1/EN/products/@product@" using HTTP PUT + Then validation error response is received + + + Scenario: Update product (categoryID not UUID) + Given current authentication token + Given the request body is: + """ + { + "categoryIds": ["@@random_md5@@"] + } + """ + When I request "/api/v1/EN/products/@product@" using HTTP PUT + Then validation error response is received + + Scenario: Update product (categoryID wrong UUID) + Given current authentication token + Given the request body is: + """ + { + "categoryIds": ["@@random_uuid@@"] + } + """ + When I request "/api/v1/EN/products/@product@" using HTTP PUT + Then validation error response is received + + + Scenario: Get product Given current authentication token When I request "/api/v1/EN/products/@product@" using HTTP GET diff --git a/features/workflow.feature b/features/workflow.feature index 740c12766..586be46cb 100644 --- a/features/workflow.feature +++ b/features/workflow.feature @@ -54,6 +54,7 @@ Feature: Workflow When I request "/api/v1/EN/status/@@static_uuid@@" using HTTP PUT Then not found response is received + Scenario: Get default status Given current authentication token When I request "/api/v1/EN/status/@workflow_status@" using HTTP GET @@ -81,6 +82,22 @@ Feature: Workflow When I request "/api/v1/EN/workflow/default" using HTTP PUT Then empty response is received + + Scenario: Update default workflow (wrong status) + Given current authentication token + Given the request body is: + """ + { + "code": "TEST_@@random_code@@", + "statuses": ["test"], + "transitions": [] + } + """ + When I request "/api/v1/EN/workflow/default" using HTTP PUT + Then validation error response is received + + + Scenario: Delete default status Given current authentication token When I request "/api/v1/EN/status/@workflow_status@" using HTTP DELETE @@ -118,6 +135,19 @@ Feature: Workflow Then created response is received And remember response param "id" as "workflow" + Scenario: Create workflow (wrong statuses) + Given current authentication token + Given the request body is: + """ + { + "code": "WRK_@@random_code@@", + "statuses": ["test"], + "transitions": [] + } + """ + When I request "/api/v1/EN/workflow" using HTTP POST + Then validation error response is received + Scenario: Create workflow (not authorized) When I request "/api/v1/EN/workflow" using HTTP POST Then unauthorized response is received diff --git a/module/product/src/Application/Controller/Api/ProductController.php b/module/product/src/Application/Controller/Api/ProductController.php index bb5f86dc1..06b287425 100644 --- a/module/product/src/Application/Controller/Api/ProductController.php +++ b/module/product/src/Application/Controller/Api/ProductController.php @@ -269,7 +269,7 @@ public function createProduct(Request $request): Response * in="body", * description="Add product", * required=true, - * @SWG\Schema(ref="#/definitions/product") + * @SWG\Schema(ref="#/definitions/product_upd") * ) * @SWG\Response( * response=204, From b551754dc1818f2ef72e455fe8e4fc52583c3c7f Mon Sep 17 00:00:00 2001 From: "mateusz.kolankowski" Date: Wed, 4 Sep 2019 17:56:21 +0200 Subject: [PATCH 08/27] small fixes --- features/product.feature | 6 ------ features/workflow.feature | 3 --- 2 files changed, 9 deletions(-) diff --git a/features/product.feature b/features/product.feature index d95a22da2..5a8d3c944 100644 --- a/features/product.feature +++ b/features/product.feature @@ -238,8 +238,6 @@ Feature: Product module When I request "/api/v1/EN/products/@product@" using HTTP PUT Then validation error response is received - - Scenario: Get product Given current authentication token When I request "/api/v1/EN/products/@product@" using HTTP GET @@ -253,7 +251,3 @@ Feature: Product module Given current authentication token When I request "/api/v1/EN/products/@@static_uuid@@" using HTTP GET Then not found response is received - - # TODO Check product grid - # TODO Check create product action with all incorrect possibilities - # TODO Check update product action with all incorrect possibilities diff --git a/features/workflow.feature b/features/workflow.feature index 586be46cb..74c82196a 100644 --- a/features/workflow.feature +++ b/features/workflow.feature @@ -164,6 +164,3 @@ Feature: Workflow Scenario: Get default workflow (not authorized) When I request "/api/v1/EN/workflow/default" using HTTP GET Then unauthorized response is received - - # TODO Check create workflow action with all incorrect possibilities - # TODO Check update workflow action with all incorrect possibilities From 9a88aa6f4fb1bc71279a6fbadce3c308d1f88377 Mon Sep 17 00:00:00 2001 From: "mateusz.kolankowski" Date: Wed, 4 Sep 2019 18:04:02 +0200 Subject: [PATCH 09/27] fixes after CR --- features/core.feature | 58 +++++++++++++++++++-------------------- features/designer.feature | 16 +++++------ 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/features/core.feature b/features/core.feature index 6808975e6..eebe2517f 100644 --- a/features/core.feature +++ b/features/core.feature @@ -24,55 +24,55 @@ Feature: Core module Then not found response is received Scenario: Update language (not authorized) - When I request "/api/v1/{language}/languages" using HTTP PUT + When I request "/api/v1/EN/languages" using HTTP PUT Then unauthorized response is received Scenario: Update language Given current authentication token Given the request body is: """ - { - "collection": [ - { - "code": "EN", - "active": true - } - ] -} + { + "collection":[ + { + "code":"EN", + "active":true + } + ] + } """ - When I request "/api/v1/{language}/languages" using HTTP PUT + When I request "/api/v1/EN/languages" using HTTP PUT Then the response code is 204 Scenario: Update language (wrong active - bad request) Given current authentication token Given the request body is: """ - { - "collection": [ - { - "code": "EN", - "active": "tralalal" - } - ] -} + { + "collection":[ + { + "code":"EN", + "active":"test" + } + ] + } """ - When I request "/api/v1/{language}/languages" using HTTP PUT + When I request "/api/v1/EN/languages" using HTTP PUT Then validation error response is received Scenario: Update language (wrong code - bad request) Given current authentication token Given the request body is: """ - { - "collection": [ - { - "code": "ZZ", - "active": true - } - ] -} + { + "collection":[ + { + "code":"ZZ", + "active":true + } + ] + } """ - When I request "/api/v1/{language}/languages" using HTTP PUT + When I request "/api/v1/EN/languages" using HTTP PUT Then validation error response is received Scenario: Update language (wrong structure - bad request) @@ -84,5 +84,5 @@ Feature: Core module "active": true } """ - When I request "/api/v1/{language}/languages" using HTTP PUT + When I request "/api/v1/EN/languages" using HTTP PUT Then validation error response is received diff --git a/features/designer.feature b/features/designer.feature index 88ef1fa27..18f3eca9e 100644 --- a/features/designer.feature +++ b/features/designer.feature @@ -435,42 +435,42 @@ Feature: Designer module When I request "/api/v1/EN/templates/groups" using HTTP GET Then unauthorized response is received - Scenario: Get templates groups(order by id) + Scenario: Get templates groups (order by id) Given current authentication token When I request "/api/v1/EN/templates/groups?field=id" using HTTP GET Then grid response is received - Scenario: Get templates groups(order by name) + Scenario: Get templates groups (order by name) Given current authentication token When I request "/api/v1/EN/templates/groups?field=name" using HTTP GET Then grid response is received - Scenario: Get templates groups(order by custom) + Scenario: Get templates groups (order by custom) Given current authentication token When I request "/api/v1/EN/templates/groups?field=custom" using HTTP GET Then grid response is received - Scenario: Get templates groups(order ASC) + Scenario: Get templates groups (order ASC) Given current authentication token When I request "/api/v1/EN/templates/groups?field=name&order=ASC" using HTTP GET Then grid response is received - Scenario: Get templates groups(order DESC ) + Scenario: Get templates groups (order DESC ) Given current authentication token When I request "/api/v1/EN/templates/groups?field=name&order=DESC" using HTTP GET Then grid response is received - Scenario: Get templates groups(filter by id) + Scenario: Get templates groups (filter by id) Given current authentication token When I request "/api/v1/EN/templates/groups?limit=25&offset=0&filter=id%3D1" using HTTP GET Then grid response is received - Scenario: Get templates groups(filter by name) + Scenario: Get templates groups (filter by name) Given current authentication token When I request "/api/v1/EN/templates/groups?limit=25&offset=0&filter=name%3Dasd" using HTTP GET Then grid response is received - Scenario: Get templates groups(filter by custom) + Scenario: Get templates groups (filter by custom) Given current authentication token When I request "/api/v1/EN/templates/groups?limit=25&offset=0&filter=custom%3Dasd" using HTTP GET Then grid response is received From 1f3fe7ad75dbbd216975bb0e41bab68415dbc477 Mon Sep 17 00:00:00 2001 From: "mateusz.kolankowski" Date: Thu, 5 Sep 2019 12:51:07 +0200 Subject: [PATCH 10/27] adding category-tree tests --- features/category-tree.feature | 93 +++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 7 deletions(-) diff --git a/features/category-tree.feature b/features/category-tree.feature index 9f9090431..0f0a4479e 100644 --- a/features/category-tree.feature +++ b/features/category-tree.feature @@ -2,10 +2,16 @@ Feature: Category tree module Scenario: Create category tree Given current authentication token - Given the following form parameters are set: - | name | value | - | name | Test | - | code | TREE_@@random_code@@ | + Given the request body is: + """ + { + "code": "TREE_CAT_@@random_code@@", + "name": { + "DE": "Test DE", + "EN": "Test EN" + } + } + """ When I request "/api/v1/EN/trees" using HTTP POST Then created response is received And remember response param "id" as "category_tree" @@ -28,17 +34,90 @@ Feature: Category tree module """ When I request "/api/v1/EN/categories" using HTTP POST Then created response is received - And remember response param "id" as "tree_category" + And remember response param "id" as "category" + + Scenario: Create category (no Name) + Given current authentication token + Given the request body is: + """ + { + "code": "TREE_CAT_@@random_code@@" + } + """ + When I request "/api/v1/EN/categories" using HTTP POST + Then created response is received + + Scenario: Create category (empty Name) + Given current authentication token + Given the request body is: + """ + { + "code": "TREE_CAT_@@random_code@@", + "name": { + } + } + """ + When I request "/api/v1/EN/categories" using HTTP POST + Then created response is received + + Scenario: Create category (name with language with empty string value) + Given current authentication token + Given the request body is: + """ + { + "code": "TREE_CAT_@@random_code@@", + "name": { + "DE": "", + "EN": "Test EN" + } + } + """ + When I request "/api/v1/EN/categories" using HTTP POST + Then validation error response is received + + Scenario: Create category (name with wrong language code ) + Given current authentication token + Given the request body is: + """ + { + "code": "TREE_CAT_@@random_code@@", + "name": { + "test": "Test DE", + "EN": "Test EN" + } + } + """ + When I request "/api/v1/EN/categories" using HTTP POST + Then validation error response is received + + Scenario: Create category (name with no existing language code ) + Given current authentication token + Given the request body is: + """ + { + "code": "TREE_CAT_@@random_code@@", + "name": { + "ZZ": "Test DE", + "EN": "Test EN" + } + } + """ + When I request "/api/v1/EN/categories" using HTTP POST + Then validation error response is received + Scenario: Update category tree Given current authentication token Given the request body is: """ { - "name": "Test (changed)", + "name": { + "DE": "Test DE", + "EN": "Test EN" + }, "categories": [ { - "category_id": "@tree_category@", + "category_id": "@category@", "childrens": [] } ] From a57b3ec9f531936872e2b5684b015c9c9fe3ef60 Mon Sep 17 00:00:00 2001 From: "mateusz.kolankowski" Date: Fri, 6 Sep 2019 11:21:11 +0200 Subject: [PATCH 11/27] behat tests for category, category tree, products --- config/packages/nelmio_api_doc.yaml | 43 + features/account.feature | 999 ++++++++++++++++-- features/category-tree.feature | 354 ++++++- features/category.feature | 148 ++- features/product.feature | 54 + .../Controller/Api/AccountController.php | 2 +- .../src/Domain/Command/UpdateTreeCommand.php | 7 - .../Controller/Api/CategoryController.php | 2 +- 8 files changed, 1492 insertions(+), 117 deletions(-) diff --git a/config/packages/nelmio_api_doc.yaml b/config/packages/nelmio_api_doc.yaml index 40196910d..033dcb08b 100644 --- a/config/packages/nelmio_api_doc.yaml +++ b/config/packages/nelmio_api_doc.yaml @@ -98,6 +98,40 @@ nelmio_api_doc: type: boolean required: true example: true + account_upd: + type: object + properties: + firstName: + type: string + required: true + description: User first name + example: Jon + lastName: + type: string + required: true + description: User last name + example: Dove + language: + type: string + required: true + description: User language + example: EN + password: + type: string + required: true + example: 12345678 + passwordRepeat: + type: string + equired: true + example: 12345678 + roleId: + type: string + required: true + example: 86800536-0f2a-4920-9291-f35fdcea3839 + isActive: + type: boolean + required: true + example: true status: type: object properties: @@ -210,6 +244,15 @@ nelmio_api_doc: example: DE: Name DE EN: Name EN + category_upd: + type: object + properties: + name: + type: object + description: category name + example: + DE: Name DE + EN: Name EN product: type: object properties: diff --git a/features/account.feature b/features/account.feature index 734937480..5eaf6bf1a 100644 --- a/features/account.feature +++ b/features/account.feature @@ -27,6 +27,133 @@ Feature: Account module Given I request "/api/v1/EN/roles" using HTTP POST Then unauthorized response is received + Scenario: Create role (without name) + Given current authentication token + Given the request body is: + """ + { + "description": "Test role", + "privileges": ["ATTRIBUTE_CREATE","ATTRIBUTE_UPDATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] + } + """ + When I request "/api/v1/EN/roles" using HTTP POST + Then validation error response is received + + Scenario: Create role (without description) + Given current authentication token + Given the request body is: + """ + { + "name": "Test role (@@random_uuid@@)", + "privileges": ["ATTRIBUTE_CREATE","ATTRIBUTE_UPDATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] + } + """ + When I request "/api/v1/EN/roles" using HTTP POST + Then validation error response is received + + Scenario: Create role (without privileges) + Given current authentication token + Given the request body is: + """ + { + "name": "Test role (@@random_uuid@@)", + "description": "Test role" + } + """ + When I request "/api/v1/EN/roles" using HTTP POST + Then validation error response is received + + Scenario: Create role (wrong parameter - name) + Given current authentication token + Given the request body is: + """ + { + "test": "Test role (@@random_uuid@@)", + "description": "Test role", + "privileges": ["ATTRIBUTE_CREATE","ATTRIBUTE_UPDATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] + } + """ + When I request "/api/v1/EN/roles" using HTTP POST + Then validation error response is received + + Scenario: Create role (wrong parameter - description) + Given current authentication token + Given the request body is: + """ + { + "name": "Test role (@@random_uuid@@)", + "test": "Test role", + "privileges": ["ATTRIBUTE_CREATE","ATTRIBUTE_UPDATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] + } + """ + When I request "/api/v1/EN/roles" using HTTP POST + Then validation error response is received + + Scenario: Create role (wrong parameter - privilages) + Given current authentication token + Given the request body is: + """ + { + "name": "Test role (@@random_uuid@@)", + "description": "Test role", + "test": ["ATTRIBUTE_CREATE","ATTRIBUTE_UPDATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] + } + """ + When I request "/api/v1/EN/roles" using HTTP POST + Then validation error response is received + + Scenario: Create role (empty name) + Given current authentication token + Given the request body is: + """ + { + "name": "", + "description": "Test role", + "privileges": ["ATTRIBUTE_CREATE","ATTRIBUTE_UPDATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] + } + """ + When I request "/api/v1/EN/roles" using HTTP POST + Then validation error response is received + + Scenario: Create role (empty description) + Given current authentication token + Given the request body is: + """ + { + "name": "Test role (@@random_uuid@@)", + "description": "", + "privileges": ["ATTRIBUTE_CREATE","ATTRIBUTE_UPDATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] + } + """ + When I request "/api/v1/EN/roles" using HTTP POST + Then validation error response is received + + Scenario: Create role (empty privilages) + Given current authentication token + Given the request body is: + """ + { + "name": "Test role (@@random_uuid@@)", + "description": "Test role", + "privileges": [] + } + """ + When I request "/api/v1/EN/roles" using HTTP POST + Then validation error response is received + + Scenario: Create role (no existing privilages) + Given current authentication token + Given the request body is: + """ + { + "name": "Test role (@@random_uuid@@)", + "description": "Test role", + "privileges": ["test", "test2"] + } + """ + When I request "/api/v1/EN/roles" using HTTP POST + Then validation error response is received + Scenario: Create role for delete Given current authentication token Given the request body is: @@ -56,134 +183,748 @@ Feature: Account module When I request "/api/v1/EN/roles/@@static_uuid@@" using HTTP DELETE Then not found response is received - Scenario: Update role + Scenario: Update role + Given current authentication token + Given the request body is: + """ + { + "name": "Test role 2 (@@random_uuid@@)", + "description": "Test role 2", + "privileges": ["ATTRIBUTE_CREATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] + } + """ + When I request "/api/v1/EN/roles/@role@" using HTTP PUT + Then empty response is received + + Scenario: Update role (not authorized) + When I request "/api/v1/EN/roles/@role@" using HTTP PUT + Then unauthorized response is received + + Scenario: Update role (not found) + Given current authentication token + When I request "/api/v1/EN/roles/@@static_uuid@@" using HTTP PUT + Then not found response is received + + Scenario: Update role (without name) + Given current authentication token + Given the request body is: + """ + { + "description": "Test role", + "privileges": ["ATTRIBUTE_CREATE","ATTRIBUTE_UPDATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] + } + """ + When I request "/api/v1/EN/roles/@role@" using HTTP PUT + Then validation error response is received + + Scenario: Update role (without description) + Given current authentication token + Given the request body is: + """ + { + "name": "Test role (@@random_uuid@@)", + "privileges": ["ATTRIBUTE_CREATE","ATTRIBUTE_UPDATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] + } + """ + When I request "/api/v1/EN/roles/@role@" using HTTP PUT + Then validation error response is received + + Scenario: Update role (without privileges) + Given current authentication token + Given the request body is: + """ + { + "name": "Test role (@@random_uuid@@)", + "description": "Test role" + } + """ + When I request "/api/v1/EN/roles/@role@" using HTTP PUT + Then validation error response is received + + Scenario: Update role (wrong parameter - name) + Given current authentication token + Given the request body is: + """ + { + "test": "Test role (@@random_uuid@@)", + "description": "Test role", + "privileges": ["ATTRIBUTE_CREATE","ATTRIBUTE_UPDATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] + } + """ + When I request "/api/v1/EN/roles/@role@" using HTTP PUT + Then validation error response is received + + Scenario: Update role (wrong parameter - description) + Given current authentication token + Given the request body is: + """ + { + "name": "Test role (@@random_uuid@@)", + "test": "Test role", + "privileges": ["ATTRIBUTE_CREATE","ATTRIBUTE_UPDATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] + } + """ + When I request "/api/v1/EN/roles/@role@" using HTTP PUT + Then validation error response is received + + Scenario: Update role (wrong parameter - privilages) + Given current authentication token + Given the request body is: + """ + { + "name": "Test role (@@random_uuid@@)", + "description": "Test role", + "test": ["ATTRIBUTE_CREATE","ATTRIBUTE_UPDATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] + } + """ + When I request "/api/v1/EN/roles/@role@" using HTTP PUT + Then validation error response is received + + Scenario: Update role (empty name) + Given current authentication token + Given the request body is: + """ + { + "name": "", + "description": "Test role", + "privileges": ["ATTRIBUTE_CREATE","ATTRIBUTE_UPDATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] + } + """ + When I request "/api/v1/EN/roles/@role@" using HTTP PUT + Then validation error response is received + + Scenario: Update role (empty description) + Given current authentication token + Given the request body is: + """ + { + "name": "Test role (@@random_uuid@@)", + "description": "", + "privileges": ["ATTRIBUTE_CREATE","ATTRIBUTE_UPDATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] + } + """ + When I request "/api/v1/EN/roles/@role@" using HTTP PUT + Then validation error response is received + + Scenario: Update role (empty privilages) + Given current authentication token + Given the request body is: + """ + { + "name": "Test role (@@random_uuid@@)", + "description": "Test role", + "privileges": [] + } + """ + When I request "/api/v1/EN/roles/@role@" using HTTP PUT + Then validation error response is received + + Scenario: Update role (no existing privilages) + Given current authentication token + Given the request body is: + """ + { + "name": "Test role (@@random_uuid@@)", + "description": "Test role", + "privileges": ["test", "test2"] + } + """ + When I request "/api/v1/EN/roles/@role@" using HTTP PUT + Then validation error response is received + + Scenario: Get role + Given current authentication token + When I request "/api/v1/EN/roles/@role@" using HTTP GET + Then the response code is 200 + And the JSON object contains keys "id" + + Scenario: Get role (not authorized) + When I request "/api/v1/EN/roles/@role@" using HTTP GET + Then unauthorized response is received + + Scenario: Get role (not found) + Given current authentication token + When I request "/api/v1/EN/roles/@@static_uuid@@" using HTTP GET + Then not found response is received + + Scenario: Get roles + Given current authentication token + When I request "/api/v1/EN/roles" using HTTP GET + Then grid response is received + + Scenario: Get roles (not authorized) + When I request "/api/v1/EN/roles" using HTTP GET + Then unauthorized response is received + + Scenario: Get roles (order by name) + Given current authentication token + When I request "/api/v1/EN/roles?field=name" using HTTP GET + Then grid response is received + + Scenario: Get roles (order by description) + Given current authentication token + When I request "/api/v1/EN/roles?field=description" using HTTP GET + Then grid response is received + + Scenario: Get roles (order by users_count) + Given current authentication token + When I request "/api/v1/EN/roles?field=users_count" using HTTP GET + Then grid response is received + + Scenario: Get roles (filter by name) + Given current authentication token + When I request "/api/v1/EN/roles?limit=25&offset=0&filter=name%3Dsuper" using HTTP GET + Then grid response is received + + Scenario: Get roles (filter by description) + Given current authentication token + When I request "/api/v1/EN/roles?limit=25&offset=0&filter=description%3DManage" using HTTP GET + Then grid response is received + + Scenario: Get roles (filter by user_count) + Given current authentication token + When I request "/api/v1/EN/roles?limit=25&offset=0&filter=users_count%3D1" using HTTP GET + Then grid response is received + + Scenario: Get roles (not authorized) + When I request "/api/v1/EN/roles" using HTTP GET + Then unauthorized response is received + + Scenario: Create user + Given current authentication token + Given the request body is: + """ + { + "email": "@@random_uuid@@@ergonode.com", + "firstName": "Test", + "lastName": "Test", + "language": "EN", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts" using HTTP POST + Then created response is received + And remember response param "id" as "user" + + Scenario: Create user (not authorized) + Given I request "/api/v1/EN/accounts" using HTTP POST + Then unauthorized response is received + + Scenario: Create user (no email) + Given current authentication token + Given the request body is: + """ + { + "firstName": "Test", + "lastName": "Test", + "language": "EN", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts" using HTTP POST + Then validation error response is received + + Scenario: Create user (empty email) + Given current authentication token + Given the request body is: + """ + { + "email": "", + "firstName": "Test", + "lastName": "Test", + "language": "EN", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts" using HTTP POST + Then validation error response is received + + Scenario: Create user (no firsName) + Given current authentication token + Given the request body is: + """ + { + "email": "@@random_uuid@@@ergonode.com", + "lastName": "Test", + "language": "EN", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts" using HTTP POST + Then validation error response is received + + Scenario: Create user (empty firsName + Given current authentication token + Given the request body is: + """ + { + "email": "@@random_uuid@@@ergonode.com", + "firstName": "", + "lastName": "Test", + "language": "EN", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts" using HTTP POST + Then validation error response is received + + Scenario: Create user (no lastName) + Given current authentication token + Given the request body is: + """ + { + "email": "@@random_uuid@@@ergonode.com", + "firstName": "Test", + "language": "EN", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts" using HTTP POST + Then validation error response is received + + Scenario: Create user (empty lastName) + Given current authentication token + Given the request body is: + """ + { + "email": "@@random_uuid@@@ergonode.com", + "firstName": "Test", + "lastName": "", + "language": "EN", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts" using HTTP POST + Then validation error response is received + + Scenario: Create user (no language) + Given current authentication token + Given the request body is: + """ + { + "email": "@@random_uuid@@@ergonode.com", + "firstName": "Test", + "lastName": "Test", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts" using HTTP POST + Then validation error response is received + + Scenario: Create user (empty language) + Given current authentication token + Given the request body is: + """ + { + "email": "@@random_uuid@@@ergonode.com", + "firstName": "Test", + "lastName": "Test", + "language": "", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts" using HTTP POST + Then validation error response is received + + Scenario: Create user (no password) + Given current authentication token + Given the request body is: + """ + { + "email": "@@random_uuid@@@ergonode.com", + "firstName": "Test", + "lastName": "Test", + "language": "EN", + "passwordRepeat": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts" using HTTP POST + Then validation error response is received + + Scenario: Create user (empty password) + Given current authentication token + Given the request body is: + """ + { + "email": "@@random_uuid@@@ergonode.com", + "firstName": "Test", + "lastName": "Test", + "language": "EN", + "password": "", + "passwordRepeat": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts" using HTTP POST + Then validation error response is received + + Scenario: Create user (no passwordRepeat) + Given current authentication token + Given the request body is: + """ + { + "email": "@@random_uuid@@@ergonode.com", + "firstName": "Test", + "lastName": "Test", + "language": "EN", + "password": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts" using HTTP POST + Then validation error response is received + + Scenario: Create user (empty passwordRepeat) + Given current authentication token + Given the request body is: + """ + { + "email": "@@random_uuid@@@ergonode.com", + "firstName": "Test", + "lastName": "Test", + "language": "EN", + "password": 12345678, + "passwordRepeat": "", + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts" using HTTP POST + Then validation error response is received + + Scenario: Create user (no roleId) + Given current authentication token + Given the request body is: + """ + { + "email": "@@random_uuid@@@ergonode.com", + "firstName": "Test", + "lastName": "Test", + "language": "EN", + "password": 12345678, + "passwordRepeat": 12345678 + } + """ + When I request "/api/v1/EN/accounts" using HTTP POST + Then validation error response is received + + Scenario: Create user (empty roleId) + Given current authentication token + Given the request body is: + """ + { + "email": "@@random_uuid@@@ergonode.com", + "firstName": "Test", + "lastName": "Test", + "language": "EN", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "" + } + """ + When I request "/api/v1/EN/accounts" using HTTP POST + Then validation error response is received + + Scenario: Create user (not UUID roleID) + Given current authentication token + Given the request body is: + """ + { + "email": "@@random_uuid@@@ergonode.com", + "firstName": "Test", + "lastName": "Test", + "language": "EN", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "test" + } + """ + When I request "/api/v1/EN/accounts" using HTTP POST + Then validation error response is received + + Scenario: Create user (random UUID roleID) + Given current authentication token + Given the request body is: + """ + { + "email": "@@random_uuid@@@ergonode.com", + "firstName": "Test", + "lastName": "Test", + "language": "EN", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "@@random_uuid@@" + } + """ + When I request "/api/v1/EN/accounts" using HTTP POST + Then validation error response is received + + Scenario: Delete role (with conflict) + Given current authentication token + When I request "/api/v1/EN/roles/@role@" using HTTP DELETE + Then the response code is 409 + And the JSON object contains keys "code,message" + + Scenario: Update user + Given current authentication token + Given the request body is: + """ + { + "firstName": "Test (changed)", + "lastName": "Test (changed)", + "language": "EN", + "password": 123456789, + "passwordRepeat": 123456789, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts/@user@" using HTTP PUT + Then empty response is received + + Scenario: Update user (not authorized) + When I request "/api/v1/EN/accounts/@user@" using HTTP PUT + Then unauthorized response is received + + Scenario: Update user (not found) + Given current authentication token + When I request "/api/v1/EN/accounts/@@static_uuid@@" using HTTP PUT + Then not found response is received + + Scenario: Update user (no firsName) + Given current authentication token + Given the request body is: + """ + { + "lastName": "Test", + "language": "EN", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts/@user@" using HTTP PUT + Then validation error response is received + + Scenario: Update user (empty firsName + Given current authentication token + Given the request body is: + """ + { + "firstName": "", + "lastName": "Test", + "language": "EN", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts/@user@" using HTTP PUT + Then validation error response is received + + Scenario: Update user (no lastName) + Given current authentication token + Given the request body is: + """ + { + "firstName": "Test", + "language": "EN", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts/@user@" using HTTP PUT + Then validation error response is received + + Scenario: Update user (empty lastName) Given current authentication token Given the request body is: """ { - "name": "Test role 2 (@@random_uuid@@)", - "description": "Test role 2", - "privileges": ["ATTRIBUTE_CREATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] + "firstName": "Test", + "lastName": "", + "language": "EN", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "@role@" } """ - When I request "/api/v1/EN/roles/@role@" using HTTP PUT - Then empty response is received - - Scenario: Update role (not authorized) - When I request "/api/v1/EN/roles/@role@" using HTTP PUT - Then unauthorized response is received - - Scenario: Update role (not found) - Given current authentication token - When I request "/api/v1/EN/roles/@@static_uuid@@" using HTTP PUT - Then not found response is received - - Scenario: Get role - Given current authentication token - When I request "/api/v1/EN/roles/@role@" using HTTP GET - Then the response code is 200 - And the JSON object contains keys "id" - - Scenario: Get role (not authorized) - When I request "/api/v1/EN/roles/@role@" using HTTP GET - Then unauthorized response is received - - Scenario: Get role (not found) - Given current authentication token - When I request "/api/v1/EN/roles/@@static_uuid@@" using HTTP GET - Then not found response is received - - Scenario: Get roles - Given current authentication token - When I request "/api/v1/EN/roles" using HTTP GET - Then grid response is received - - Scenario: Get roles (order by name) - Given current authentication token - When I request "/api/v1/EN/roles?field=name" using HTTP GET - Then grid response is received + When I request "/api/v1/EN/accounts/@user@" using HTTP PUT + Then validation error response is received - Scenario: Get roles (order by description) + Scenario: Update user (no language) Given current authentication token - When I request "/api/v1/EN/roles?field=description" using HTTP GET - Then grid response is received + Given the request body is: + """ + { + "firstName": "Test", + "lastName": "Test", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts/@user@" using HTTP PUT + Then validation error response is received - Scenario: Get roles (order by users_count) + Scenario: Update user (empty language) Given current authentication token - When I request "/api/v1/EN/roles?field=users_count" using HTTP GET - Then grid response is received + Given the request body is: + """ + { + "firstName": "Test", + "lastName": "Test", + "language": "", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts/@user@" using HTTP PUT + Then validation error response is received - Scenario: Get roles (filter by name) + Scenario: Update user (no password) Given current authentication token - When I request "/api/v1/EN/roles?limit=25&offset=0&filter=name%3Dsuper" using HTTP GET - Then grid response is received + Given the request body is: + """ + { + "firstName": "Test", + "lastName": "Test", + "language": "EN", + "passwordRepeat": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts/@user@" using HTTP PUT + Then validation error response is received - Scenario: Get roles (filter by description) + Scenario: Update user (empty password) Given current authentication token - When I request "/api/v1/EN/roles?limit=25&offset=0&filter=description%3DManage" using HTTP GET - Then grid response is received + Given the request body is: + """ + { + "firstName": "Test", + "lastName": "Test", + "language": "EN", + "password": "", + "passwordRepeat": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts/@user@" using HTTP PUT + Then validation error response is received - Scenario: Get roles (filter by user_count) + Scenario: Update user (no passwordRepeat) Given current authentication token - When I request "/api/v1/EN/roles?limit=25&offset=0&filter=users_count%3D1" using HTTP GET - Then grid response is received - - Scenario: Get roles (not authorized) - When I request "/api/v1/EN/roles" using HTTP GET - Then unauthorized response is received + Given the request body is: + """ + { + "firstName": "Test", + "lastName": "Test", + "language": "EN", + "password": 12345678, + "roleId": "@role@" + } + """ + When I request "/api/v1/EN/accounts/@user@" using HTTP PUT + Then validation error response is received - Scenario: Create user + Scenario: Update user (empty passwordRepeat) Given current authentication token Given the request body is: """ { - "email": "@@random_uuid@@@ergonode.com", "firstName": "Test", "lastName": "Test", "language": "EN", "password": 12345678, - "passwordRepeat": 12345678, + "passwordRepeat": "", "roleId": "@role@" } """ - When I request "/api/v1/EN/accounts" using HTTP POST - Then created response is received - And remember response param "id" as "user" - - Scenario: Create user (not authorized) - Given I request "/api/v1/EN/accounts" using HTTP POST - Then unauthorized response is received + When I request "/api/v1/EN/accounts/@user@" using HTTP PUT + Then validation error response is received - Scenario: Delete role (with conflict) + Scenario: Update user (no roleId) Given current authentication token - When I request "/api/v1/EN/roles/@role@" using HTTP DELETE - Then the response code is 409 - And the JSON object contains keys "code,message" + Given the request body is: + """ + { + "firstName": "Test", + "lastName": "Test", + "language": "EN", + "password": 12345678, + "passwordRepeat": 12345678 + } + """ + When I request "/api/v1/EN/accounts/@user@" using HTTP PUT + Then validation error response is received - Scenario: Update user + Scenario: Update user (empty roleId) Given current authentication token Given the request body is: """ { - "firstName": "Test (changed)", - "lastName": "Test (changed)", + "firstName": "Test", + "lastName": "Test", "language": "EN", - "password": 123456789, - "passwordRepeat": 123456789, - "roleId": "@role@" + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "" } """ When I request "/api/v1/EN/accounts/@user@" using HTTP PUT - Then empty response is received + Then validation error response is received - Scenario: Update user (not authorized) + Scenario: Update user (not UUID roleID) + Given current authentication token + Given the request body is: + """ + { + "firstName": "Test", + "lastName": "Test", + "language": "EN", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "test" + } + """ When I request "/api/v1/EN/accounts/@user@" using HTTP PUT - Then unauthorized response is received + Then validation error response is received - Scenario: Update user (not found) + Scenario: Update user (random UUID roleID) Given current authentication token - When I request "/api/v1/EN/accounts/@@static_uuid@@" using HTTP PUT - Then not found response is received + Given the request body is: + """ + { + "firstName": "Test", + "lastName": "Test", + "language": "EN", + "password": 12345678, + "passwordRepeat": 12345678, + "roleId": "@@random_uuid@@" + } + """ + When I request "/api/v1/EN/accounts/@user@" using HTTP PUT + Then validation error response is received Scenario: Get user Given current authentication token @@ -382,8 +1123,88 @@ Feature: Account module When I request "/api/v1/EN/accounts/log" using HTTP GET Then unauthorized response is received - # TODO Check role create action with all incorrect possibilities - # TODO Check role update action with all incorrect possibilities - # TODO Check user create action with all incorrect possibilities - # TODO Check user update action with all incorrect possibilities + Scenario: Get accounts (order by id) + Given current authentication token + When I request "/api/v1/EN/accounts?field=id" using HTTP GET + Then grid response is received + + Scenario: Get accounts (order by email) + Given current authentication token + When I request "/api/v1/EN/accounts?field=email" using HTTP GET + Then grid response is received + + Scenario: Get accounts (order by first_name) + Given current authentication token + When I request "/api/v1/EN/accounts?field=first_name" using HTTP GET + Then grid response is received + + Scenario: Get accounts (order by last_name) + Given current authentication token + When I request "/api/v1/EN/accounts?field=last_name" using HTTP GET + Then grid response is received + + Scenario: Get accounts (order by language) + Given current authentication token + When I request "/api/v1/EN/accounts?field=language" using HTTP GET + Then grid response is received + + Scenario: Get accounts (order by role_id) + Given current authentication token + When I request "/api/v1/EN/accounts?field=role_id" using HTTP GET + Then grid response is received + + Scenario: Get accounts (order by is_active) + Given current authentication token + When I request "/api/v1/EN/accounts?field=is_active" using HTTP GET + Then grid response is received + + Scenario: Get accounts (order ASC) + Given current authentication token + When I request "/api/v1/EN/accounts?field=email&order=ASC" using HTTP GET + Then grid response is received + + Scenario: Get accounts (order DESC) + Given current authentication token + When I request "/api/v1/EN/accounts?field=email&order=DESC" using HTTP GET + Then grid response is received + + Scenario: Get accounts (filter by id) + Given current authentication token + When I request "/api/v1/EN/accounts?limit=25&offset=0&filter=id%3D1" using HTTP GET + Then grid response is received + + Scenario: Get accounts (filter by email) + Given current authentication token + When I request "/api/v1/EN/accounts?limit=25&offset=0&filter=email%3Dasd" using HTTP GET + Then grid response is received + + Scenario: Get accounts (filter by first_name) + Given current authentication token + When I request "/api/v1/EN/accounts?limit=25&offset=0&filter=first_name%3DCAT" using HTTP GET + Then grid response is received + + Scenario: Get accounts (filter by last_name) + Given current authentication token + When I request "/api/v1/EN/accounts?limit=25&offset=0&filter=last_name%3D1" using HTTP GET + Then grid response is received + + Scenario: Get accounts (filter by language) + Given current authentication token + When I request "/api/v1/EN/accounts?limit=25&offset=0&filter=language%3D1" using HTTP GET + Then grid response is received + + Scenario: Get accounts (filter by role_id) + Given current authentication token + When I request "/api/v1/EN/accounts?limit=25&offset=0&filter=role_id%3Dasd1" using HTTP GET + Then grid response is received + + Scenario: Get accounts (filter by is_active) + Given current authentication token + When I request "/api/v1/EN/accounts?limit=25&offset=0&filter=last_name%3Dasd1" using HTTP GET + Then grid response is received + + Scenario: Get accounts (not authorized) + When I request "/api/v1/EN/accounts" using HTTP GET + Then unauthorized response is received + # TODO Check user avatar change action with correct and incorrect file diff --git a/features/category-tree.feature b/features/category-tree.feature index 22c6cf53e..ae27cbc16 100644 --- a/features/category-tree.feature +++ b/features/category-tree.feature @@ -20,7 +20,7 @@ Feature: Category tree module When I request "/api/v1/EN/trees" using HTTP POST Then unauthorized response is received - Scenario: Create category for update + Scenario: Create category for update 1 Given current authentication token Given the request body is: """ @@ -34,20 +34,36 @@ Feature: Category tree module """ When I request "/api/v1/EN/categories" using HTTP POST Then created response is received - And remember response param "id" as "category" + And remember response param "id" as "category_1" - Scenario: Create category (no Name) + Scenario: Create category for update 2 Given current authentication token Given the request body is: """ { - "code": "TREE_CAT_@@random_code@@" + "code": "TREE_CAT_@@random_code@@", + "name": { + "DE": "Test DE", + "EN": "Test EN" + } } """ When I request "/api/v1/EN/categories" using HTTP POST Then created response is received + And remember response param "id" as "category_2" + + Scenario: Create category tree (no Name) + Given current authentication token + Given the request body is: + """ + { + "code": "TREE_CAT_@@random_code@@" + } + """ + When I request "/api/v1/EN/trees" using HTTP POST + Then created response is received - Scenario: Create category (empty Name) + Scenario: Create category tree (empty Name) Given current authentication token Given the request body is: """ @@ -57,10 +73,10 @@ Feature: Category tree module } } """ - When I request "/api/v1/EN/categories" using HTTP POST + When I request "/api/v1/EN/trees" using HTTP POST Then created response is received - Scenario: Create category (name with language with empty string value) + Scenario: Create category tree (name with language with empty string value) Given current authentication token Given the request body is: """ @@ -72,10 +88,10 @@ Feature: Category tree module } } """ - When I request "/api/v1/EN/categories" using HTTP POST + When I request "/api/v1/EN/trees" using HTTP POST Then validation error response is received - Scenario: Create category (name with wrong language code ) + Scenario: Create category tree (name with wrong language code ) Given current authentication token Given the request body is: """ @@ -87,10 +103,10 @@ Feature: Category tree module } } """ - When I request "/api/v1/EN/categories" using HTTP POST + When I request "/api/v1/EN/trees" using HTTP POST Then validation error response is received - Scenario: Create category (name with no existing language code ) + Scenario: Create category tree (name with no existing language code ) Given current authentication token Given the request body is: """ @@ -102,7 +118,7 @@ Feature: Category tree module } } """ - When I request "/api/v1/EN/categories" using HTTP POST + When I request "/api/v1/EN/trees" using HTTP POST Then validation error response is received @@ -117,7 +133,7 @@ Feature: Category tree module }, "categories": [ { - "category_id": "@category@", + "category_id": "@category_1@", "childrens": [] } ] @@ -135,6 +151,271 @@ Feature: Category tree module When I request "/api/v1/EN/trees/@@static_uuid@@" using HTTP PUT Then not found response is received + Scenario: Update category tree (no name field) + Given current authentication token + Given the request body is: + """ + { + "categories": [ + { + "category_id": "@category_1@", + "childrens": [] + } + ] + } + """ + When I request "/api/v1/EN/trees/@category_tree@" using HTTP PUT + Then empty response is received + + Scenario: Update category tree (empty name) + Given current authentication token + Given the request body is: + """ + { + "name": { + }, + "categories": [ + { + "category_id": "@category_1@", + "childrens": [] + } + ] + } + """ + When I request "/api/v1/EN/trees/@category_tree@" using HTTP PUT + Then empty response is received + + Scenario: Update category tree (wrong parameter) + Given current authentication token + Given the request body is: + """ + { + "test": { + }, + "categories": [ + { + "category_id": "@category_1@", + "childrens": [] + } + ] + } + """ + When I request "/api/v1/EN/trees/@category_tree@" using HTTP PUT + Then validation error response is received + + Scenario: Update category tree (wrong language code) + Given current authentication token + Given the request body is: + """ + { + "name": { + "test": "Test DE (changed)", + "EN": "Test EN (changed)" + }, + "categories": [ + { + "category_id": "@category_1@", + "childrens": [] + } + ] + } + """ + When I request "/api/v1/EN/trees/@category_tree@" using HTTP PUT + Then validation error response is received + + Scenario: Update category tree (incorrect language code) + Given current authentication token + Given the request body is: + """ + { + "name": { + "ZZ": "Test DE (changed)", + "EN": "Test EN (changed)" + }, + "categories": [ + { + "category_id": "@category_1@", + "childrens": [] + } + ] + } + """ + When I request "/api/v1/EN/trees/@category_tree@" using HTTP PUT + Then validation error response is received + + Scenario: Update category tree (no categories) + Given current authentication token + Given the request body is: + """ + { + "name": { + "DE": "Test DE (changed)", + "EN": "Test EN (changed)" + } + } + """ + When I request "/api/v1/EN/trees/@category_tree@" using HTTP PUT + Then validation error response is received + + Scenario: Update category tree (incorrect category Id) + Given current authentication token + Given the request body is: + """ + { + "name": { + "DE": "Test DE (changed)", + "EN": "Test EN (changed)" + }, + "categories": [ + { + "category_id": "test", + "childrens": [] + } + ] + } + """ + When I request "/api/v1/EN/trees/@category_tree@" using HTTP PUT + Then validation error response is received + + Scenario: Update category tree (empty categotry id) + Given current authentication token + Given the request body is: + """ + { + "name": { + "DE": "Test DE (changed)", + "EN": "Test EN (changed)" + }, + "categories": [ + { + "category_id": "", + "childrens": [] + } + ] + } + """ + When I request "/api/v1/EN/trees/@category_tree@" using HTTP PUT + Then validation error response is received + + Scenario: Update category tree (wrong categories key) + Given current authentication token + Given the request body is: + """ + { + "name": { + "DE": "Test DE (changed)", + "EN": "Test EN (changed)" + }, + "categories": [ + { + "test": "@category_1@", + "childrens": [] + } + ] + } + """ + When I request "/api/v1/EN/trees/@category_tree@" using HTTP PUT + Then validation error response is received + + Scenario: Update category tree (with childrens) + Given current authentication token + Given the request body is: + """ + { + "name": { + "DE": "Test DE (changed)", + "EN": "Test EN (changed)" + }, + "categories": [ + { + "category_id": "@category_1@", + "childrens": [{"category_id":"@category_2@"}] + } + ] + } + """ + When I request "/api/v1/EN/trees/@category_tree@" using HTTP PUT + Then empty response is received + + Scenario: Update category tree (no childrens) + Given current authentication token + Given the request body is: + """ + { + "name": { + "DE": "Test DE (changed)", + "EN": "Test EN (changed)" + }, + "categories": [ + { + "category_id": "@category_1@" + } + ] + } + """ + When I request "/api/v1/EN/trees/@category_tree@" using HTTP PUT + Then empty response is received + + Scenario: Update category tree (with empty childrens category Id) + Given current authentication token + Given the request body is: + """ + { + "name": { + "DE": "Test DE (changed)", + "EN": "Test EN (changed)" + }, + "categories": [ + { + "category_id": "@category_1@", + "childrens": [{"category_id":""}] + } + ] + } + """ + When I request "/api/v1/EN/trees/@category_tree@" using HTTP PUT + Then validation error response is received + + Scenario: Update category tree (with empty childrens wrong key) + Given current authentication token + Given the request body is: + """ + { + "name": { + "DE": "Test DE (changed)", + "EN": "Test EN (changed)" + }, + "categories": [ + { + "category_id": "@category_1@", + "childrens": [{"test":"@category_2@"}] + } + ] + } + """ + When I request "/api/v1/EN/trees/@category_tree@" using HTTP PUT + Then validation error response is received + + Scenario: Update category tree (empty translation) + Given current authentication token + Given the request body is: + """ + { + "name": { + "DE": "", + "EN": "Test EN (changed)" + }, + "categories": [ + { + "category_id": "@category_1@", + "childrens": [] + } + ] + } + """ + When I request "/api/v1/EN/trees/@category_tree@" using HTTP PUT + Then validation error response is received + Scenario: Get category tree Given current authentication token When I request "/api/v1/EN/trees/@category_tree@" using HTTP GET @@ -158,6 +439,47 @@ Feature: Category tree module When I request "/api/v1/EN/trees" using HTTP GET Then unauthorized response is received - # TODO Check add category to category tree action - # TODO Check create category tree action with all incorrect possibilities - # TODO Check update category tree action with all incorrect possibilities + Scenario: Get category trees + Given current authentication token + When I request "/api/v1/EN/trees" using HTTP GET + Then grid response is received + + Scenario: Get category trees (order by id) + Given current authentication token + When I request "/api/v1/EN/trees?field=id" using HTTP GET + Then grid response is received + + Scenario: Get category trees (order by code) + Given current authentication token + When I request "/api/v1/EN/trees?field=code" using HTTP GET + Then grid response is received + + Scenario: Get category trees (order by name) + Given current authentication token + When I request "/api/v1/EN/trees?field=name" using HTTP GET + Then grid response is received + + Scenario: Get category trees (order ASC) + Given current authentication token + When I request "/api/v1/EN/trees?field=name&order=ASC" using HTTP GET + Then grid response is received + + Scenario: Get category trees (order DESC) + Given current authentication token + When I request "/api/v1/EN/trees?field=name&order=DESC" using HTTP GET + Then grid response is received + + Scenario: Get category trees (filter by name) + Given current authentication token + When I request "/api/v1/EN/trees?limit=25&offset=0&filter=name%3Dasd" using HTTP GET + Then grid response is received + + Scenario: Get category trees (filter by code) + Given current authentication token + When I request "/api/v1/EN/trees?limit=25&offset=0&filter=code%3DCAT" using HTTP GET + Then grid response is received + + Scenario: Get category trees (filter by name) + Given current authentication token + When I request "/api/v1/EN/trees?limit=25&offset=0&filter=name%3D1" using HTTP GET + Then grid response is received diff --git a/features/category.feature b/features/category.feature index 084b0065c..14a7ff650 100644 --- a/features/category.feature +++ b/features/category.feature @@ -20,6 +20,75 @@ Feature: Category module When I request "/api/v1/EN/categories" using HTTP POST Then unauthorized response is received + Scenario: Create category (no Name) + Given current authentication token + Given the request body is: + """ + { + "code": "TREE_CAT_@@random_code@@" + } + """ + When I request "/api/v1/EN/categories" using HTTP POST + Then created response is received + + Scenario: Create category (empty Name) + Given current authentication token + Given the request body is: + """ + { + "code": "TREE_CAT_@@random_code@@", + "name": { + } + } + """ + When I request "/api/v1/EN/categories" using HTTP POST + Then created response is received + + Scenario: Create category (name with language with empty string value) + Given current authentication token + Given the request body is: + """ + { + "code": "TREE_CAT_@@random_code@@", + "name": { + "DE": "", + "EN": "Test EN" + } + } + """ + When I request "/api/v1/EN/categories" using HTTP POST + Then validation error response is received + + Scenario: Create category (name with wrong language code ) + Given current authentication token + Given the request body is: + """ + { + "code": "TREE_CAT_@@random_code@@", + "name": { + "test": "Test DE", + "EN": "Test EN" + } + } + """ + When I request "/api/v1/EN/categories" using HTTP POST + Then validation error response is received + + Scenario: Create category (name with no existing language code ) + Given current authentication token + Given the request body is: + """ + { + "code": "TREE_CAT_@@random_code@@", + "name": { + "ZZ": "Test DE", + "EN": "Test EN" + } + } + """ + When I request "/api/v1/EN/categories" using HTTP POST + Then validation error response is received + Scenario: Update category Given current authentication token Given the request body is: @@ -43,6 +112,72 @@ Feature: Category module When I request "/api/v1/EN/categories/@@static_uuid@@" using HTTP PUT Then not found response is received + Scenario: Update category (empty name) + Given current authentication token + Given the request body is: + """ + { + "name": { + } + } + """ + When I request "/api/v1/EN/categories/@category@" using HTTP PUT + Then empty response is received + + Scenario: Update category (wrong parameter) + Given current authentication token + Given the request body is: + """ + { + "test": { + } + } + """ + When I request "/api/v1/EN/categories/@category@" using HTTP PUT + Then validation error response is received + + Scenario: Update category (wrong language code) + Given current authentication token + Given the request body is: + """ + { + "name": { + "test": "Test DE (changed)", + "EN": "Test EN (changed)" + } + } + """ + When I request "/api/v1/EN/categories/@category@" using HTTP PUT + Then validation error response is received + + Scenario: Update category (incorrect language code) + Given current authentication token + Given the request body is: + """ + { + "name": { + "ZZ": "Test DE (changed)", + "EN": "Test EN (changed)" + } + } + """ + When I request "/api/v1/EN/categories/@category@" using HTTP PUT + Then validation error response is received + + Scenario: Update category (empty translation) + Given current authentication token + Given the request body is: + """ + { + "name": { + "DE": "", + "EN": "Test EN (changed)" + } + } + """ + When I request "/api/v1/EN/categories/@category@" using HTTP PUT + Then validation error response is received + Scenario: Get category Given current authentication token When I request "/api/v1/EN/categories/@category@" using HTTP GET @@ -77,6 +212,16 @@ Feature: Category module When I request "/api/v1/EN/categories?field=sequence" using HTTP GET Then grid response is received + Scenario: Get categories (order ASC) + Given current authentication token + When I request "/api/v1/EN/categories?field=name&order=ASC" using HTTP GET + Then grid response is received + + Scenario: Get categories (order DESC) + Given current authentication token + When I request "/api/v1/EN/categories?field=name&order=DESC" using HTTP GET + Then grid response is received + Scenario: Get categories (filter by sequence) Given current authentication token When I request "/api/v1/EN/categories?limit=25&offset=0&filter=sequence%3D1" using HTTP GET @@ -100,6 +245,3 @@ Feature: Category module Scenario: Get categories (not authorized) When I request "/api/v1/EN/categories" using HTTP GET Then unauthorized response is received - - # TODO Check create category action with all incorrect possibilities - # TODO Check update category action with all incorrect possibilities diff --git a/features/product.feature b/features/product.feature index 5a8d3c944..e8bd5c886 100644 --- a/features/product.feature +++ b/features/product.feature @@ -251,3 +251,57 @@ Feature: Product module Given current authentication token When I request "/api/v1/EN/products/@@static_uuid@@" using HTTP GET Then not found response is received + + Scenario: Get products (order by id) + Given current authentication token + When I request "/api/v1/EN/products?field=id" using HTTP GET + Then grid response is received + + Scenario: Get products (order by index) + Given current authentication token + When I request "/api/v1/EN/products?field=index" using HTTP GET + Then grid response is received + + Scenario: Get products (order by sku) + Given current authentication token + When I request "/api/v1/EN/products?field=sku" using HTTP GET + Then grid response is received + + Scenario: Get products (order by template) + Given current authentication token + When I request "/api/v1/EN/products?field=template" using HTTP GET + Then grid response is received + + Scenario: Get products (order ASC) + Given current authentication token + When I request "/api/v1/EN/products?field=index&order=ASC" using HTTP GET + Then grid response is received + + Scenario: Get products (order DESC) + Given current authentication token + When I request "/api/v1/EN/products?field=index&order=DESC" using HTTP GET + Then grid response is received + + Scenario: Get products (filter by template) + Given current authentication token + When I request "/api/v1/EN/products?limit=25&offset=0&filter=template%3D1" using HTTP GET + Then grid response is received + + Scenario: Get products (filter by index) + Given current authentication token + When I request "/api/v1/EN/products?limit=25&offset=0&filter=index%3Dasd" using HTTP GET + Then grid response is received + + Scenario: Get products (filter by id) + Given current authentication token + When I request "/api/v1/EN/products?limit=25&offset=0&filter=id%3DCAT" using HTTP GET + Then grid response is received + + Scenario: Get products (filter by sku) + Given current authentication token + When I request "/api/v1/EN/products?limit=25&offset=0&filter=sku%3D1" using HTTP GET + Then grid response is received + + Scenario: Get products (not authorized) + When I request "/api/v1/EN/products" using HTTP GET + Then unauthorized response is received diff --git a/module/account/src/Application/Controller/Api/AccountController.php b/module/account/src/Application/Controller/Api/AccountController.php index 813dac628..e462e5568 100644 --- a/module/account/src/Application/Controller/Api/AccountController.php +++ b/module/account/src/Application/Controller/Api/AccountController.php @@ -305,7 +305,7 @@ public function createUser(Request $request): Response * in="body", * description="Add attribute", * required=true, - * @SWG\Schema(ref="#/definitions/account") + * @SWG\Schema(ref="#/definitions/account_upd") * ) * @SWG\Parameter( * name="language", diff --git a/module/category-tree/src/Domain/Command/UpdateTreeCommand.php b/module/category-tree/src/Domain/Command/UpdateTreeCommand.php index b02822643..02e12728b 100644 --- a/module/category-tree/src/Domain/Command/UpdateTreeCommand.php +++ b/module/category-tree/src/Domain/Command/UpdateTreeCommand.php @@ -27,13 +27,6 @@ class UpdateTreeCommand */ private $id; - /** - * @var string - * - * @JMS\Type("string") - */ - private $code; - /** * @var TranslatableString * diff --git a/module/category/src/Application/Controller/Api/CategoryController.php b/module/category/src/Application/Controller/Api/CategoryController.php index 26e32dc3f..4eb6d19f1 100644 --- a/module/category/src/Application/Controller/Api/CategoryController.php +++ b/module/category/src/Application/Controller/Api/CategoryController.php @@ -270,7 +270,7 @@ public function createCategory(Request $request): Response * in="body", * description="Category body", * required=true, - * @SWG\Schema(ref="#/definitions/category") + * @SWG\Schema(ref="#/definitions/category_upd") * ) * @SWG\Response( * response=204, From 48c42604b6b341a5218907b26370fc49c01f4723 Mon Sep 17 00:00:00 2001 From: "mateusz.kolankowski" Date: Mon, 9 Sep 2019 10:04:44 +0200 Subject: [PATCH 12/27] fixes after CR --- features/account.feature | 56 ++-------------------------------- features/category-tree.feature | 4 +-- features/category.feature | 4 +-- features/core.feature | 2 +- features/designer.feature | 6 ++-- features/product.feature | 6 +--- 6 files changed, 11 insertions(+), 67 deletions(-) diff --git a/features/account.feature b/features/account.feature index 5eaf6bf1a..8d3ca526e 100644 --- a/features/account.feature +++ b/features/account.feature @@ -76,32 +76,6 @@ Feature: Account module When I request "/api/v1/EN/roles" using HTTP POST Then validation error response is received - Scenario: Create role (wrong parameter - description) - Given current authentication token - Given the request body is: - """ - { - "name": "Test role (@@random_uuid@@)", - "test": "Test role", - "privileges": ["ATTRIBUTE_CREATE","ATTRIBUTE_UPDATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] - } - """ - When I request "/api/v1/EN/roles" using HTTP POST - Then validation error response is received - - Scenario: Create role (wrong parameter - privilages) - Given current authentication token - Given the request body is: - """ - { - "name": "Test role (@@random_uuid@@)", - "description": "Test role", - "test": ["ATTRIBUTE_CREATE","ATTRIBUTE_UPDATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] - } - """ - When I request "/api/v1/EN/roles" using HTTP POST - Then validation error response is received - Scenario: Create role (empty name) Given current authentication token Given the request body is: @@ -254,32 +228,6 @@ Feature: Account module When I request "/api/v1/EN/roles/@role@" using HTTP PUT Then validation error response is received - Scenario: Update role (wrong parameter - description) - Given current authentication token - Given the request body is: - """ - { - "name": "Test role (@@random_uuid@@)", - "test": "Test role", - "privileges": ["ATTRIBUTE_CREATE","ATTRIBUTE_UPDATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] - } - """ - When I request "/api/v1/EN/roles/@role@" using HTTP PUT - Then validation error response is received - - Scenario: Update role (wrong parameter - privilages) - Given current authentication token - Given the request body is: - """ - { - "name": "Test role (@@random_uuid@@)", - "description": "Test role", - "test": ["ATTRIBUTE_CREATE","ATTRIBUTE_UPDATE","ATTRIBUTE_READ","ATTRIBUTE_DELETE"] - } - """ - When I request "/api/v1/EN/roles/@role@" using HTTP PUT - Then validation error response is received - Scenario: Update role (empty name) Given current authentication token Given the request body is: @@ -461,7 +409,7 @@ Feature: Account module When I request "/api/v1/EN/accounts" using HTTP POST Then validation error response is received - Scenario: Create user (empty firsName + Scenario: Create user (empty firsName) Given current authentication token Given the request body is: """ @@ -723,7 +671,7 @@ Feature: Account module When I request "/api/v1/EN/accounts/@user@" using HTTP PUT Then validation error response is received - Scenario: Update user (empty firsName + Scenario: Update user (empty firsName) Given current authentication token Given the request body is: """ diff --git a/features/category-tree.feature b/features/category-tree.feature index ae27cbc16..637d79f90 100644 --- a/features/category-tree.feature +++ b/features/category-tree.feature @@ -91,7 +91,7 @@ Feature: Category tree module When I request "/api/v1/EN/trees" using HTTP POST Then validation error response is received - Scenario: Create category tree (name with wrong language code ) + Scenario: Create category tree (name with wrong language code) Given current authentication token Given the request body is: """ @@ -106,7 +106,7 @@ Feature: Category tree module When I request "/api/v1/EN/trees" using HTTP POST Then validation error response is received - Scenario: Create category tree (name with no existing language code ) + Scenario: Create category tree (name with no existing language code) Given current authentication token Given the request body is: """ diff --git a/features/category.feature b/features/category.feature index 14a7ff650..951ce3e1d 100644 --- a/features/category.feature +++ b/features/category.feature @@ -59,7 +59,7 @@ Feature: Category module When I request "/api/v1/EN/categories" using HTTP POST Then validation error response is received - Scenario: Create category (name with wrong language code ) + Scenario: Create category (name with wrong language code) Given current authentication token Given the request body is: """ @@ -74,7 +74,7 @@ Feature: Category module When I request "/api/v1/EN/categories" using HTTP POST Then validation error response is received - Scenario: Create category (name with no existing language code ) + Scenario: Create category (name with no existing language code) Given current authentication token Given the request body is: """ diff --git a/features/core.feature b/features/core.feature index eebe2517f..055b91d29 100644 --- a/features/core.feature +++ b/features/core.feature @@ -75,7 +75,7 @@ Feature: Core module When I request "/api/v1/EN/languages" using HTTP PUT Then validation error response is received - Scenario: Update language (wrong structure - bad request) + Scenario: Update language (wrong structure) Given current authentication token Given the request body is: """ diff --git a/features/designer.feature b/features/designer.feature index 18f3eca9e..c9953195e 100644 --- a/features/designer.feature +++ b/features/designer.feature @@ -401,7 +401,7 @@ Feature: Designer module When I request "/api/v1/EN/templates?field=name&order=ASC" using HTTP GET Then grid response is received - Scenario: Get templates (order DESC ) + Scenario: Get templates (order DESC) Given current authentication token When I request "/api/v1/EN/templates?field=name&order=DESC" using HTTP GET Then grid response is received @@ -455,7 +455,7 @@ Feature: Designer module When I request "/api/v1/EN/templates/groups?field=name&order=ASC" using HTTP GET Then grid response is received - Scenario: Get templates groups (order DESC ) + Scenario: Get templates groups (order DESC) Given current authentication token When I request "/api/v1/EN/templates/groups?field=name&order=DESC" using HTTP GET Then grid response is received @@ -525,7 +525,7 @@ Feature: Designer module When I request "/api/v1/EN/templates/types?field=type&order=ASC" using HTTP GET Then grid response is received - Scenario: Get templates types (order DESC ) + Scenario: Get templates types (order DESC) Given current authentication token When I request "/api/v1/EN/templates/types?field=type&order=DESC" using HTTP GET Then grid response is received diff --git a/features/product.feature b/features/product.feature index e8bd5c886..1ba981e7b 100644 --- a/features/product.feature +++ b/features/product.feature @@ -157,7 +157,6 @@ Feature: Product module When I request "/api/v1/EN/products" using HTTP POST Then created response is received - Scenario: Create product (categoryIds not UUID) Given current authentication token Given the request body is: @@ -194,8 +193,6 @@ Feature: Product module When I request "/api/v1/EN/products/@product@" using HTTP PUT Then empty response is received - - Scenario: Update product (not authorized) When I request "/api/v1/EN/products/@product@" using HTTP PUT Then unauthorized response is received @@ -205,7 +202,7 @@ Feature: Product module When I request "/api/v1/EN/products/@@static_uuid@@" using HTTP PUT Then not found response is received - Scenario: Update product (no categoryId) + Scenario: Update product (no content) Given current authentication token Given the request body is: """ @@ -215,7 +212,6 @@ Feature: Product module When I request "/api/v1/EN/products/@product@" using HTTP PUT Then validation error response is received - Scenario: Update product (categoryID not UUID) Given current authentication token Given the request body is: From 1419a678eba8bc152afaa206b962201e21e903e2 Mon Sep 17 00:00:00 2001 From: "mateusz.kolankowski" Date: Mon, 9 Sep 2019 10:20:22 +0200 Subject: [PATCH 13/27] fixes after CR --- features/core.feature | 16 ++++++++-------- features/workflow.feature | 1 - 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/features/core.feature b/features/core.feature index 055b91d29..57affe647 100644 --- a/features/core.feature +++ b/features/core.feature @@ -31,14 +31,14 @@ Feature: Core module Given current authentication token Given the request body is: """ - { - "collection":[ - { - "code":"EN", - "active":true - } - ] - } + { + "collection":[ + { + "code":"EN", + "active":true + } + ] + } """ When I request "/api/v1/EN/languages" using HTTP PUT Then the response code is 204 diff --git a/features/workflow.feature b/features/workflow.feature index 74c82196a..bb656d8eb 100644 --- a/features/workflow.feature +++ b/features/workflow.feature @@ -82,7 +82,6 @@ Feature: Workflow When I request "/api/v1/EN/workflow/default" using HTTP PUT Then empty response is received - Scenario: Update default workflow (wrong status) Given current authentication token Given the request body is: From 8a135c7f7ea99b788db22097e79631905534ca77 Mon Sep 17 00:00:00 2001 From: Sebastian Bielawski Date: Tue, 10 Sep 2019 10:25:51 +0200 Subject: [PATCH 14/27] #128 Profile and account log should take into attention the importer (#129) #128 Profile and account log should take into attention the importer --- .../Persistence/Dbal/Query/DbalLogQuery.php | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/module/account/src/Persistence/Dbal/Query/DbalLogQuery.php b/module/account/src/Persistence/Dbal/Query/DbalLogQuery.php index 670ac2fa8..96b42e25a 100644 --- a/module/account/src/Persistence/Dbal/Query/DbalLogQuery.php +++ b/module/account/src/Persistence/Dbal/Query/DbalLogQuery.php @@ -59,12 +59,26 @@ public function getDataSet(?UserId $id = null): DataSetInterface */ private function getQuery(): QueryBuilder { + $publicEventStoreBuilder = $this->connection->createQueryBuilder() + ->select('*') + ->from('public.event_store'); + + $importerEventStoreBuilder = $this->connection->createQueryBuilder() + ->select('*') + ->from('importer.event_store'); + + $union = sprintf( + '(%s UNION %s)', + $publicEventStoreBuilder->getSQL(), + $importerEventStoreBuilder->getSQL() + ); + return $this->connection->createQueryBuilder() - ->select('es.id, payload, recorded_at, recorded_by AS author_id') + ->select('es.id, es.payload, es.recorded_at, es.recorded_by AS author_id') ->addSelect('coalesce(u.first_name || \' \' || u.last_name, \'System\') AS author') ->addSelect('ese.translation_key as event') - ->from('event_store', 'es') - ->join('es', 'event_store_event', 'ese', 'es.event_id = ese.id') - ->leftJoin('es', 'users', 'u', 'u.id = es.recorded_by'); + ->from($union, 'es') + ->join('es', 'public.event_store_event', 'ese', 'es.event_id = ese.id') + ->leftJoin('es', 'public.users', 'u', 'u.id = es.recorded_by'); } } From b1fce5479084d41e7933205b0a91bde7a153a8c8 Mon Sep 17 00:00:00 2001 From: Sebastian Bielawski Date: Tue, 10 Sep 2019 11:24:14 +0200 Subject: [PATCH 15/27] #131 Workflow delete (#132) #131 Workflow delete --- features/workflow.feature | 16 ++++- .../Dbal/Repository/DbalRoleRepository.php | 1 - .../migrations/Version20190818160000.php | 1 + .../Controller/Api/WorkflowController.php | 51 ++++++++++++- .../ParamConverter/StatusParamConverter.php | 17 ++--- .../ParamConverter/WorkflowParamConverter.php | 71 +++++++++++++++++++ .../Event/Workflow/WorkflowDeletedEvent.php | 19 +++++ .../src/Domain/Provider/WorkflowProvider.php | 2 +- .../WorkflowRepositoryInterface.php | 7 +- .../Workflow/DeleteWorkflowCommandHandler.php | 43 +++++++++++ .../Repository/DbalWorkflowRepository.php | 22 ++++-- .../src/Resources/translations/log.en.yaml | 1 + .../src/Resources/translations/log.pl.yaml | 1 + 13 files changed, 228 insertions(+), 24 deletions(-) create mode 100644 module/workflow/src/Application/Request/ParamConverter/WorkflowParamConverter.php create mode 100644 module/workflow/src/Domain/Event/Workflow/WorkflowDeletedEvent.php create mode 100644 module/workflow/src/Infrastructure/Handler/Workflow/DeleteWorkflowCommandHandler.php diff --git a/features/workflow.feature b/features/workflow.feature index bb656d8eb..254a15732 100644 --- a/features/workflow.feature +++ b/features/workflow.feature @@ -95,8 +95,6 @@ Feature: Workflow When I request "/api/v1/EN/workflow/default" using HTTP PUT Then validation error response is received - - Scenario: Delete default status Given current authentication token When I request "/api/v1/EN/status/@workflow_status@" using HTTP DELETE @@ -163,3 +161,17 @@ Feature: Workflow Scenario: Get default workflow (not authorized) When I request "/api/v1/EN/workflow/default" using HTTP GET Then unauthorized response is received + + Scenario: Delete workflow (not found) + Given current authentication token + When I request "/api/v1/EN/workflow/@static_uuid@" using HTTP DELETE + Then not found response is received + + Scenario: Delete workflow (not authorized) + When I request "/api/v1/EN/workflow/@workflow@" using HTTP DELETE + Then unauthorized response is received + + Scenario: Delete workflow + Given current authentication token + When I request "/api/v1/EN/workflow/@workflow@" using HTTP DELETE + Then empty response is received diff --git a/module/account/src/Persistence/Dbal/Repository/DbalRoleRepository.php b/module/account/src/Persistence/Dbal/Repository/DbalRoleRepository.php index 72af2929a..5a0010fc9 100644 --- a/module/account/src/Persistence/Dbal/Repository/DbalRoleRepository.php +++ b/module/account/src/Persistence/Dbal/Repository/DbalRoleRepository.php @@ -89,7 +89,6 @@ public function save(AbstractAggregateRoot $aggregateRoot): void public function delete(AbstractAggregateRoot $aggregateRoot): void { $aggregateRoot->apply(new RoleDeletedEvent()); - $this->save($aggregateRoot); $this->eventStore->delete($aggregateRoot->getId()); diff --git a/module/workflow/migrations/Version20190818160000.php b/module/workflow/migrations/Version20190818160000.php index 2857748f1..74abb13eb 100644 --- a/module/workflow/migrations/Version20190818160000.php +++ b/module/workflow/migrations/Version20190818160000.php @@ -45,6 +45,7 @@ public function up(Schema $schema): void 'Ergonode\Workflow\Domain\Event\Workflow\WorkflowStatusRemovedEvent' => 'Deleted status from workflow', 'Ergonode\Workflow\Domain\Event\Workflow\WorkflowTransitionAddedEvent' => 'Added transition to workflow', 'Ergonode\Workflow\Domain\Event\Workflow\WorkflowTransitionRemovedEvent' => 'Deleted transition from workflow', + 'Ergonode\Workflow\Domain\Event\Workflow\WorkflowDeletedEvent' => 'Workflow deleted', ]); } diff --git a/module/workflow/src/Application/Controller/Api/WorkflowController.php b/module/workflow/src/Application/Controller/Api/WorkflowController.php index 6bc59860c..5d496570d 100644 --- a/module/workflow/src/Application/Controller/Api/WorkflowController.php +++ b/module/workflow/src/Application/Controller/Api/WorkflowController.php @@ -14,13 +14,16 @@ use Ergonode\Api\Application\Response\EmptyResponse; use Ergonode\Api\Application\Response\SuccessResponse; use Ergonode\Workflow\Domain\Command\Workflow\CreateWorkflowCommand; +use Ergonode\Workflow\Domain\Command\Workflow\DeleteWorkflowCommand; use Ergonode\Workflow\Domain\Command\Workflow\UpdateWorkflowCommand; +use Ergonode\Workflow\Domain\Entity\Workflow; use Ergonode\Workflow\Domain\Entity\WorkflowId; use Ergonode\Workflow\Domain\Provider\WorkflowProvider; use Ergonode\Workflow\Infrastructure\Builder\WorkflowValidatorBuilder; use JMS\Serializer\Serializer; use JMS\Serializer\SerializerInterface; use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Swagger\Annotations as SWG; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; @@ -139,8 +142,8 @@ public function getWorkflow(): Response * @SWG\Schema(ref="#/definitions/workflow") * ) * @SWG\Response( - * response=200, - * description="Returns attribute", + * response=201, + * description="Returns workflow ID", * ) * @SWG\Response( * response=400, @@ -227,4 +230,48 @@ public function updateWorkflow(Request $request): Response throw new ViolationsHttpException($violations); } + + /** + * @Route("/workflow/{workflow}", methods={"DELETE"}, requirements={"workflow"="[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"}) + * + * @IsGranted("WORKFLOW_DELETE") + * + * @SWG\Tag(name="Workflow") + * @SWG\Parameter( + * name="language", + * in="path", + * type="string", + * required=true, + * default="EN", + * description="Language Code", + * ) + * @SWG\Parameter( + * name="workflow", + * in="path", + * required=true, + * type="string", + * description="Workflow ID", + * ) + * @SWG\Response( + * response=204, + * description="Success" + * ) + * @SWG\Response( + * response=404, + * description="Not found", + * ) + * + * @ParamConverter(class="Ergonode\Workflow\Domain\Entity\Workflow") + * + * @param Workflow $workflow + * + * @return Response + */ + public function deleteWorkflow(Workflow $workflow): Response + { + $command = new DeleteWorkflowCommand($workflow->getId()); + $this->messageBus->dispatch($command); + + return new EmptyResponse(); + } } diff --git a/module/workflow/src/Application/Request/ParamConverter/StatusParamConverter.php b/module/workflow/src/Application/Request/ParamConverter/StatusParamConverter.php index 749a3899a..c0e068f59 100644 --- a/module/workflow/src/Application/Request/ParamConverter/StatusParamConverter.php +++ b/module/workflow/src/Application/Request/ParamConverter/StatusParamConverter.php @@ -28,18 +28,15 @@ class StatusParamConverter implements ParamConverterInterface private $repository; /** - * @param StatusRepositoryInterface $parameterRepository + * @param StatusRepositoryInterface $repository */ - public function __construct(StatusRepositoryInterface $parameterRepository) + public function __construct(StatusRepositoryInterface $repository) { - $this->repository = $parameterRepository; + $this->repository = $repository; } /** - * @param Request $request - * @param ParamConverter $configuration - * - * @return void + * {@inheritDoc} * * @throws \ReflectionException */ @@ -48,7 +45,7 @@ public function apply(Request $request, ParamConverter $configuration): void $parameter = $request->get('status'); if (null === $parameter) { - throw new BadRequestHttpException('Route parameter status is missing'); + throw new BadRequestHttpException('Route parameter "status" is missing'); } if (!StatusId::isValid($parameter)) { @@ -65,9 +62,7 @@ public function apply(Request $request, ParamConverter $configuration): void } /** - * @param ParamConverter $configuration - * - * @return bool + * {@inheritDoc} */ public function supports(ParamConverter $configuration): bool { diff --git a/module/workflow/src/Application/Request/ParamConverter/WorkflowParamConverter.php b/module/workflow/src/Application/Request/ParamConverter/WorkflowParamConverter.php new file mode 100644 index 000000000..9b9c09a22 --- /dev/null +++ b/module/workflow/src/Application/Request/ParamConverter/WorkflowParamConverter.php @@ -0,0 +1,71 @@ +repository = $repository; + } + + /** + * {@inheritDoc} + * + * @throws \ReflectionException + */ + public function apply(Request $request, ParamConverter $configuration): void + { + $parameter = $request->get('workflow'); + + if (null === $parameter) { + throw new BadRequestHttpException('Route parameter "workflow" is missing'); + } + + if (!WorkflowId::isValid($parameter)) { + throw new BadRequestHttpException('Invalid workflow ID format'); + } + + $entity = $this->repository->load(new WorkflowId($parameter)); + + if (null === $entity) { + throw new NotFoundHttpException(sprintf('Workflow by id "%s" not found', $parameter)); + } + + $request->attributes->set($configuration->getName(), $entity); + } + + /** + * {@inheritDoc} + */ + public function supports(ParamConverter $configuration): bool + { + return Workflow::class === $configuration->getClass(); + } +} diff --git a/module/workflow/src/Domain/Event/Workflow/WorkflowDeletedEvent.php b/module/workflow/src/Domain/Event/Workflow/WorkflowDeletedEvent.php new file mode 100644 index 000000000..d84e4b8d3 --- /dev/null +++ b/module/workflow/src/Domain/Event/Workflow/WorkflowDeletedEvent.php @@ -0,0 +1,19 @@ +repository->load($id); + $workflow = $this->repository->load($id); if (null === $workflow) { $workflow = $this->factory->create($id, $code); $this->repository->save($workflow); diff --git a/module/workflow/src/Domain/Repository/WorkflowRepositoryInterface.php b/module/workflow/src/Domain/Repository/WorkflowRepositoryInterface.php index 9631958f4..06b59f5dd 100644 --- a/module/workflow/src/Domain/Repository/WorkflowRepositoryInterface.php +++ b/module/workflow/src/Domain/Repository/WorkflowRepositoryInterface.php @@ -21,8 +21,6 @@ interface WorkflowRepositoryInterface * @param WorkflowId $id * * @return null|Workflow - * - * @throws \ReflectionException */ public function load(WorkflowId $id): ?AbstractAggregateRoot; @@ -30,4 +28,9 @@ public function load(WorkflowId $id): ?AbstractAggregateRoot; * @param AbstractAggregateRoot $aggregateRoot */ public function save(AbstractAggregateRoot $aggregateRoot): void; + + /** + * @param AbstractAggregateRoot $aggregateRoot + */ + public function delete(AbstractAggregateRoot $aggregateRoot): void; } diff --git a/module/workflow/src/Infrastructure/Handler/Workflow/DeleteWorkflowCommandHandler.php b/module/workflow/src/Infrastructure/Handler/Workflow/DeleteWorkflowCommandHandler.php new file mode 100644 index 000000000..3b7c88c5a --- /dev/null +++ b/module/workflow/src/Infrastructure/Handler/Workflow/DeleteWorkflowCommandHandler.php @@ -0,0 +1,43 @@ +repository = $repository; + } + + /** + * @param DeleteWorkflowCommand $command + */ + public function __invoke(DeleteWorkflowCommand $command) + { + $workflow = $this->repository->load($command->getId()); + Assert::notNull($workflow); + + $this->repository->delete($workflow); + } +} diff --git a/module/workflow/src/Persistence/Dbal/Repository/DbalWorkflowRepository.php b/module/workflow/src/Persistence/Dbal/Repository/DbalWorkflowRepository.php index 7ddb648d3..bd4d54d5c 100644 --- a/module/workflow/src/Persistence/Dbal/Repository/DbalWorkflowRepository.php +++ b/module/workflow/src/Persistence/Dbal/Repository/DbalWorkflowRepository.php @@ -14,6 +14,7 @@ use Ergonode\EventSourcing\Infrastructure\DomainEventStoreInterface; use Ergonode\Workflow\Domain\Entity\Workflow; use Ergonode\Workflow\Domain\Entity\WorkflowId; +use Ergonode\Workflow\Domain\Event\Workflow\WorkflowDeletedEvent; use Ergonode\Workflow\Domain\Repository\WorkflowRepositoryInterface; /** @@ -41,16 +42,14 @@ public function __construct(DomainEventStoreInterface $eventStore, DomainEventDi } /** - * @param WorkflowId $id - * - * @return Workflow|null + * {@inheritDoc} * * @throws \ReflectionException */ public function load(WorkflowId $id): ?AbstractAggregateRoot { $eventStream = $this->eventStore->load($id); - if (\count($eventStream) > 0) { + if (count($eventStream) > 0) { $class = new \ReflectionClass(Workflow::class); /** @var AbstractAggregateRoot $aggregate */ $aggregate = $class->newInstanceWithoutConstructor(); @@ -67,7 +66,7 @@ public function load(WorkflowId $id): ?AbstractAggregateRoot } /** - * @param AbstractAggregateRoot $aggregateRoot + * {@inheritDoc} */ public function save(AbstractAggregateRoot $aggregateRoot): void { @@ -78,4 +77,17 @@ public function save(AbstractAggregateRoot $aggregateRoot): void $this->eventDispatcher->dispatch($envelope); } } + + /** + * {@inheritDoc} + * + * @throws \Exception + */ + public function delete(AbstractAggregateRoot $aggregateRoot): void + { + $aggregateRoot->apply(new WorkflowDeletedEvent()); + $this->save($aggregateRoot); + + $this->eventStore->delete($aggregateRoot->getId()); + } } diff --git a/module/workflow/src/Resources/translations/log.en.yaml b/module/workflow/src/Resources/translations/log.en.yaml index bdc42c3b7..3840853ce 100644 --- a/module/workflow/src/Resources/translations/log.en.yaml +++ b/module/workflow/src/Resources/translations/log.en.yaml @@ -8,3 +8,4 @@ 'Deleted status from workflow': 'Deleted status from workflow' 'Added transition to workflow': 'Added transition to workflow' 'Deleted transition from workflow': 'Deleted transition from workflow' +'Workflow deleted': 'Workflow "%id%" deleted' diff --git a/module/workflow/src/Resources/translations/log.pl.yaml b/module/workflow/src/Resources/translations/log.pl.yaml index 48154b7cc..d99fa9bb1 100644 --- a/module/workflow/src/Resources/translations/log.pl.yaml +++ b/module/workflow/src/Resources/translations/log.pl.yaml @@ -8,3 +8,4 @@ 'Deleted status from workflow': 'Usunięto status z przepływu pracy' 'Added transition to workflow': 'Dodano transformację do przepływu pracy' 'Deleted transition from workflow': 'Usunięto transformację z przepływu pracy' +'Workflow deleted': 'Przepływ pracy "%id%" usunięto' From fa799c386d95c45b618347be00201a4405693400 Mon Sep 17 00:00:00 2001 From: Sebastian Bielawski Date: Tue, 10 Sep 2019 11:24:40 +0200 Subject: [PATCH 16/27] #133 Product delete (#134) * #133 Product delete --- features/product.feature | 15 ++++++ .../migrations/Version20180619083830.php | 1 + .../Controller/Api/ProductController.php | 44 ++++++++++++++++++ .../Domain/Command/DeleteProductCommand.php | 41 +++++++++++++++++ .../src/Domain/Event/ProductDeletedEvent.php | 19 ++++++++ .../Repository/ProductRepositoryInterface.php | 5 ++ .../Handler/DeleteProductCommandHandler.php | 46 +++++++++++++++++++ .../Dbal/Repository/DbalProductRepository.php | 20 ++++++-- .../src/Resources/translations/log.en.yaml | 1 + .../src/Resources/translations/log.pl.yaml | 1 + 10 files changed, 189 insertions(+), 4 deletions(-) create mode 100644 module/product/src/Domain/Command/DeleteProductCommand.php create mode 100644 module/product/src/Domain/Event/ProductDeletedEvent.php create mode 100644 module/product/src/Infrastructure/Handler/DeleteProductCommandHandler.php diff --git a/features/product.feature b/features/product.feature index 1ba981e7b..864ae617a 100644 --- a/features/product.feature +++ b/features/product.feature @@ -248,6 +248,21 @@ Feature: Product module When I request "/api/v1/EN/products/@@static_uuid@@" using HTTP GET Then not found response is received + Scenario: Delete product (not found) + Given current authentication token + When I request "/api/v1/EN/products/@static_uuid@" using HTTP DELETE + Then not found response is received + + Scenario: Delete product (not authorized) + Given current authentication token + When I request "/api/v1/EN/products/@product@" using HTTP DELETE + Then unauthorized response is received + + Scenario: Delete product + Given current authentication token + When I request "/api/v1/EN/products/@product@" using HTTP DELETE + Then empty response is received + Scenario: Get products (order by id) Given current authentication token When I request "/api/v1/EN/products?field=id" using HTTP GET diff --git a/module/product/migrations/Version20180619083830.php b/module/product/migrations/Version20180619083830.php index 43863b49e..aa96fb4a3 100644 --- a/module/product/migrations/Version20180619083830.php +++ b/module/product/migrations/Version20180619083830.php @@ -53,6 +53,7 @@ public function up(Schema $schema): void 'Ergonode\Product\Domain\Event\ProductValueAdded' => 'Product attribute value added', 'Ergonode\Product\Domain\Event\ProductValueChanged' => 'Product attribute value changed', 'Ergonode\Product\Domain\Event\ProductValueRemoved' => 'Product attribute value removed', + 'Ergonode\Product\Domain\Event\ProductDeletedEvent' => 'Product deleted', ]); } diff --git a/module/product/src/Application/Controller/Api/ProductController.php b/module/product/src/Application/Controller/Api/ProductController.php index 06b287425..c7a39b713 100644 --- a/module/product/src/Application/Controller/Api/ProductController.php +++ b/module/product/src/Application/Controller/Api/ProductController.php @@ -22,6 +22,7 @@ use Ergonode\Product\Application\Model\ProductCreateFormModel; use Ergonode\Product\Application\Model\ProductUpdateFormModel; use Ergonode\Product\Domain\Command\CreateProductCommand; +use Ergonode\Product\Domain\Command\DeleteProductCommand; use Ergonode\Product\Domain\Command\UpdateProductCommand; use Ergonode\Product\Domain\Entity\AbstractProduct; use Ergonode\Product\Domain\ValueObject\Sku; @@ -305,4 +306,47 @@ public function updateProduct(AbstractProduct $product, Request $request): Respo throw new FormValidationHttpException($form); } + + /** + * @Route("/products/{product}", methods={"DELETE"}, requirements={"product"="[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"}) + * + * @IsGranted("PRODUCT_DELETE") + * + * @SWG\Tag(name="Product") + * @SWG\Parameter( + * name="language", + * in="path", + * type="string", + * description="Language code", + * default="EN" + * ) + * @SWG\Parameter( + * name="product", + * in="path", + * required=true, + * type="string", + * description="Product ID", + * ) + * @SWG\Response( + * response=204, + * description="Success" + * ) + * @SWG\Response( + * response=404, + * description="Not found", + * ) + * + * @ParamConverter(class="Ergonode\Product\Domain\Entity\AbstractProduct") + * + * @param AbstractProduct $product + * + * @return Response + */ + public function deleteProduct(AbstractProduct $product): Response + { + $command = new DeleteProductCommand($product->getId()); + $this->messageBus->dispatch($command); + + return new EmptyResponse(); + } } diff --git a/module/product/src/Domain/Command/DeleteProductCommand.php b/module/product/src/Domain/Command/DeleteProductCommand.php new file mode 100644 index 000000000..1dc13b9bf --- /dev/null +++ b/module/product/src/Domain/Command/DeleteProductCommand.php @@ -0,0 +1,41 @@ +id = $id; + } + + /** + * @return ProductId + */ + public function getId(): ProductId + { + return $this->id; + } +} diff --git a/module/product/src/Domain/Event/ProductDeletedEvent.php b/module/product/src/Domain/Event/ProductDeletedEvent.php new file mode 100644 index 000000000..2574ebde8 --- /dev/null +++ b/module/product/src/Domain/Event/ProductDeletedEvent.php @@ -0,0 +1,19 @@ +repository = $repository; + } + + /** + * @param DeleteProductCommand $command + * + * @throws \Exception + */ + public function __invoke(DeleteProductCommand $command) + { + $product = $this->repository->load($command->getId()); + Assert::isInstanceOf($product, AbstractProduct::class, sprintf('Can\'t find product with id "%s"', $command->getId())); + + $this->repository->delete($product); + } +} diff --git a/module/product/src/Persistence/Dbal/Repository/DbalProductRepository.php b/module/product/src/Persistence/Dbal/Repository/DbalProductRepository.php index 830babfcc..4abb1282e 100644 --- a/module/product/src/Persistence/Dbal/Repository/DbalProductRepository.php +++ b/module/product/src/Persistence/Dbal/Repository/DbalProductRepository.php @@ -13,6 +13,7 @@ use Ergonode\EventSourcing\Infrastructure\DomainEventDispatcherInterface; use Ergonode\EventSourcing\Infrastructure\DomainEventStoreInterface; use Ergonode\Product\Domain\Entity\ProductId; +use Ergonode\Product\Domain\Event\ProductDeletedEvent; use Ergonode\Product\Domain\Repository\ProductRepositoryInterface; use Ergonode\ProductSimple\Domain\Entity\SimpleProduct; @@ -41,9 +42,7 @@ public function __construct(DomainEventStoreInterface $eventStore, DomainEventDi } /** - * @param ProductId $id - * - * @return AbstractAggregateRoot|null + * {@inheritDoc} * * @throws \ReflectionException */ @@ -69,7 +68,7 @@ public function load(ProductId $id): ?AbstractAggregateRoot } /** - * @param AbstractAggregateRoot $aggregateRoot + * {@inheritDoc} */ public function save(AbstractAggregateRoot $aggregateRoot): void { @@ -80,4 +79,17 @@ public function save(AbstractAggregateRoot $aggregateRoot): void $this->eventDispatcher->dispatch($envelope); } } + + /** + * {@inheritDoc} + * + * @throws \Exception + */ + public function delete(AbstractAggregateRoot $aggregateRoot): void + { + $aggregateRoot->apply(new ProductDeletedEvent()); + $this->save($aggregateRoot); + + $this->eventStore->delete($aggregateRoot->getId()); + } } diff --git a/module/product/src/Resources/translations/log.en.yaml b/module/product/src/Resources/translations/log.en.yaml index 1e3742283..470593f7b 100644 --- a/module/product/src/Resources/translations/log.en.yaml +++ b/module/product/src/Resources/translations/log.en.yaml @@ -4,3 +4,4 @@ 'Product attribute value added': 'Product value of attribute "%code%" added' 'Product attribute value changed': 'Product value of attribute "%code%" changed' 'Product attribute value removed': 'Product value of attribute "%code%" removed' +'Product deleted': 'Product "%id%" deleted' diff --git a/module/product/src/Resources/translations/log.pl.yaml b/module/product/src/Resources/translations/log.pl.yaml index 4196991db..c10ca02ab 100644 --- a/module/product/src/Resources/translations/log.pl.yaml +++ b/module/product/src/Resources/translations/log.pl.yaml @@ -4,3 +4,4 @@ 'Product attribute value added': 'Dodano wartość atrybutu "%code%"' 'Product attribute value changed': 'Zmieniono wartość atrybutu "%code%"' 'Product attribute value removed': 'Usunięto wartość atrybutu "%code%"' +'Product deleted': 'Produkt "%id%" usunięto' From 1aa9744d69c75273662602ada611977616bd4b1d Mon Sep 17 00:00:00 2001 From: Sebastian Bielawski Date: Tue, 10 Sep 2019 12:30:10 +0200 Subject: [PATCH 17/27] Category tree delete (#136) #135 Category tree delete --- features/category-tree.feature | 15 +++- .../migrations/Version20180619083700.php | 1 + .../Controller/Api/CategoryTreeController.php | 81 ++++++++++++++----- .../src/Domain/Command/DeleteTreeCommand.php | 41 ++++++++++ .../Domain/Event/CategoryTreeDeletedEvent.php | 18 +++++ .../Repository/TreeRepositoryInterface.php | 5 ++ .../Handler/CreateTreeCommandHandler.php | 2 + .../Handler/DeleteTreeCommandHandler.php | 46 +++++++++++ .../CategoryTreeDeletedEventProjector.php | 62 ++++++++++++++ .../Dbal/Repository/DbalTreeRepository.php | 24 ++++-- .../src/Resources/translations/log.en.yaml | 1 + .../src/Resources/translations/log.pl.yaml | 1 + .../migrations/Version20180619083830.php | 5 +- .../ProductDeletedEventProjector.php | 62 ++++++++++++++ 14 files changed, 337 insertions(+), 27 deletions(-) create mode 100644 module/category-tree/src/Domain/Command/DeleteTreeCommand.php create mode 100644 module/category-tree/src/Domain/Event/CategoryTreeDeletedEvent.php create mode 100644 module/category-tree/src/Infrastructure/Handler/DeleteTreeCommandHandler.php create mode 100644 module/category-tree/src/Persistence/Dbal/Projector/CategoryTreeDeletedEventProjector.php create mode 100644 module/product/src/Persistence/Dbal/Projector/ProductDeletedEventProjector.php diff --git a/features/category-tree.feature b/features/category-tree.feature index 637d79f90..6917de759 100644 --- a/features/category-tree.feature +++ b/features/category-tree.feature @@ -121,7 +121,6 @@ Feature: Category tree module When I request "/api/v1/EN/trees" using HTTP POST Then validation error response is received - Scenario: Update category tree Given current authentication token Given the request body is: @@ -430,6 +429,20 @@ Feature: Category tree module When I request "/api/v1/EN/trees/@@static_uuid@@" using HTTP GET Then not found response is received + Scenario: Delete category tree (not found) + Given current authentication token + When I request "/api/v1/EN/trees/@@static_uuid@@" using HTTP DELETE + Then not found response is received + + Scenario: Delete category tree (not authorized) + When I request "/api/v1/EN/trees/@category_tree@" using HTTP DELETE + Then unauthorized response is received + + Scenario: Delete category tree + Given current authentication token + When I request "/api/v1/EN/trees/@category_tree@" using HTTP DELETE + Then empty response is received + Scenario: Get category trees (order by name) Given current authentication token When I request "/api/v1/EN/trees?field=name" using HTTP GET diff --git a/module/category-tree/migrations/Version20180619083700.php b/module/category-tree/migrations/Version20180619083700.php index 9b7e8a806..81801b82d 100644 --- a/module/category-tree/migrations/Version20180619083700.php +++ b/module/category-tree/migrations/Version20180619083700.php @@ -38,6 +38,7 @@ public function up(Schema $schema): void 'Ergonode\CategoryTree\Domain\Event\CategoryTreeCategoryRemovedEvent' => 'Category removed from category tree', 'Ergonode\CategoryTree\Domain\Event\CategoryTreeCreatedEvent' => 'Category tree created', 'Ergonode\CategoryTree\Domain\Event\CategoryTreeNameChangedEvent' => 'Category tree name changed', + 'Ergonode\CategoryTree\Domain\Event\CategoryTreeDeletedEvent' => 'Category tree deleted', ]); } diff --git a/module/category-tree/src/Application/Controller/Api/CategoryTreeController.php b/module/category-tree/src/Application/Controller/Api/CategoryTreeController.php index dabd90ddf..e7a906b1b 100644 --- a/module/category-tree/src/Application/Controller/Api/CategoryTreeController.php +++ b/module/category-tree/src/Application/Controller/Api/CategoryTreeController.php @@ -20,6 +20,7 @@ use Ergonode\CategoryTree\Application\Model\CategoryTreeUpdateFormModel; use Ergonode\CategoryTree\Domain\Command\AddCategoryCommand; use Ergonode\CategoryTree\Domain\Command\CreateTreeCommand; +use Ergonode\CategoryTree\Domain\Command\DeleteTreeCommand; use Ergonode\CategoryTree\Domain\Command\UpdateTreeCommand; use Ergonode\CategoryTree\Domain\Entity\CategoryTree; use Ergonode\CategoryTree\Domain\Entity\CategoryTreeId; @@ -154,6 +155,56 @@ public function getCategories(Language $language, RequestGridConfiguration $conf return new GridResponse($this->grid, $configuration, $this->query->getDataSet($language), $language); } + /** + * @Route("/trees/{tree}", methods={"GET"}) + * + * @IsGranted("CATEGORY_TREE_READ") + * + * @SWG\Tag(name="Tree") + * @SWG\Parameter( + * name="language", + * in="path", + * type="string", + * required=true, + * default="EN", + * description="Language Code", + * ) + * + * @SWG\Parameter( + * name="tree", + * in="path", + * type="string", + * required=true, + * description="tree ID", + * ) + * @SWG\Parameter( + * name="language", + * in="path", + * type="string", + * required=true, + * description="Language", + * ) + * @SWG\Response( + * response=200, + * description="Returns category tree", + * ) + * @SWG\Response( + * response=404, + * description="Not found", + * ) + * + * @ParamConverter(class="Ergonode\CategoryTree\Domain\Entity\CategoryTree") + * + * @param CategoryTree $tree + * @param Language $language + * + * @return Response + */ + public function getTree(CategoryTree $tree, Language $language): Response + { + return new SuccessResponse($tree); + } + /** * @Route("/trees", methods={"POST"}) * @@ -282,7 +333,7 @@ public function addCategory(string $tree, string $category, Request $request): R } /** - * @Route("/trees/{tree}", methods={"PUT"}) + * @Route("/trees/{tree}", methods={"PUT"}, requirements={"tree"="[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"}) * * @IsGranted("CATEGORY_TREE_UPDATE") * @@ -349,9 +400,9 @@ public function putTree(CategoryTree $tree, Request $request): Response } /** - * @Route("/trees/{tree}", methods={"GET"}) + * @Route("/trees/{tree}", methods={"DELETE"}, requirements={"tree"="[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"}) * - * @IsGranted("CATEGORY_TREE_READ") + * @IsGranted("CATEGORY_TREE_DELETE") * * @SWG\Tag(name="Tree") * @SWG\Parameter( @@ -362,39 +413,33 @@ public function putTree(CategoryTree $tree, Request $request): Response * default="EN", * description="Language Code", * ) - * * @SWG\Parameter( * name="tree", * in="path", - * type="string", * required=true, - * description="tree ID", - * ) - * @SWG\Parameter( - * name="language", - * in="path", * type="string", - * required=true, - * description="Language", + * description="Tree ID", * ) * @SWG\Response( - * response=200, - * description="Returns import", + * response=204, + * description="Success" * ) * @SWG\Response( * response=404, - * description="Not found", + * description="Not found" * ) * * @ParamConverter(class="Ergonode\CategoryTree\Domain\Entity\CategoryTree") * * @param CategoryTree $tree - * @param Language $language * * @return Response */ - public function getTree(CategoryTree $tree, Language $language): Response + public function deleteTree(CategoryTree $tree): Response { - return new SuccessResponse($tree); + $command = new DeleteTreeCommand($tree->getId()); + $this->messageBus->dispatch($command); + + return new EmptyResponse(); } } diff --git a/module/category-tree/src/Domain/Command/DeleteTreeCommand.php b/module/category-tree/src/Domain/Command/DeleteTreeCommand.php new file mode 100644 index 000000000..a8236a0b9 --- /dev/null +++ b/module/category-tree/src/Domain/Command/DeleteTreeCommand.php @@ -0,0 +1,41 @@ +id = $id; + } + + /** + * @return CategoryTreeId + */ + public function getId(): CategoryTreeId + { + return $this->id; + } +} diff --git a/module/category-tree/src/Domain/Event/CategoryTreeDeletedEvent.php b/module/category-tree/src/Domain/Event/CategoryTreeDeletedEvent.php new file mode 100644 index 000000000..cac665864 --- /dev/null +++ b/module/category-tree/src/Domain/Event/CategoryTreeDeletedEvent.php @@ -0,0 +1,18 @@ +repository = $repository; + } + + /** + * @param DeleteTreeCommand $command + * + * @throws \Exception + */ + public function __invoke(DeleteTreeCommand $command) + { + $categoryTree = $this->repository->load($command->getId()); + Assert::isInstanceOf($categoryTree, CategoryTree::class, sprintf('Can\'t find category tree with id "%s"', $command->getId())); + + $this->repository->delete($categoryTree); + } +} diff --git a/module/category-tree/src/Persistence/Dbal/Projector/CategoryTreeDeletedEventProjector.php b/module/category-tree/src/Persistence/Dbal/Projector/CategoryTreeDeletedEventProjector.php new file mode 100644 index 000000000..b920013a9 --- /dev/null +++ b/module/category-tree/src/Persistence/Dbal/Projector/CategoryTreeDeletedEventProjector.php @@ -0,0 +1,62 @@ +connection = $connection; + } + + /** + * {@inheritDoc} + */ + public function support(DomainEventInterface $event): bool + { + return $event instanceof CategoryTreeDeletedEvent; + } + + /** + * {@inheritDoc} + */ + public function projection(AbstractId $aggregateId, DomainEventInterface $event): void + { + if (!$event instanceof CategoryTreeDeletedEvent) { + throw new UnsupportedEventException($event, CategoryTreeDeletedEvent::class); + } + + $this->connection->delete( + self::TABLE, + [ + 'id' => $aggregateId->getValue(), + ] + ); + } +} diff --git a/module/category-tree/src/Persistence/Dbal/Repository/DbalTreeRepository.php b/module/category-tree/src/Persistence/Dbal/Repository/DbalTreeRepository.php index eebc0104d..1a1b45bd3 100644 --- a/module/category-tree/src/Persistence/Dbal/Repository/DbalTreeRepository.php +++ b/module/category-tree/src/Persistence/Dbal/Repository/DbalTreeRepository.php @@ -11,6 +11,7 @@ use Ergonode\CategoryTree\Domain\Entity\CategoryTree; use Ergonode\CategoryTree\Domain\Entity\CategoryTreeId; +use Ergonode\CategoryTree\Domain\Event\CategoryTreeDeletedEvent; use Ergonode\CategoryTree\Domain\Repository\TreeRepositoryInterface; use Ergonode\EventSourcing\Domain\AbstractAggregateRoot; use Ergonode\EventSourcing\Infrastructure\DomainEventDispatcherInterface; @@ -41,9 +42,7 @@ public function __construct(DomainEventStoreInterface $eventStore, DomainEventDi } /** - * @param CategoryTreeId $id - * - * @return CategoryTree|null + * {@inheritDoc} * * @throws \ReflectionException */ @@ -68,9 +67,7 @@ public function load(CategoryTreeId $id): ?AbstractAggregateRoot } /** - * @param CategoryTreeId $id - * - * @return bool + * {@inheritDoc} */ public function exists(CategoryTreeId $id) : bool { @@ -80,7 +77,7 @@ public function exists(CategoryTreeId $id) : bool } /** - * @param AbstractAggregateRoot $aggregateRoot + * {@inheritDoc} */ public function save(AbstractAggregateRoot $aggregateRoot): void { @@ -91,4 +88,17 @@ public function save(AbstractAggregateRoot $aggregateRoot): void $this->eventDispatcher->dispatch($envelope); } } + + /** + * {@inheritDoc} + * + * @throws \Exception + */ + public function delete(AbstractAggregateRoot $aggregateRoot): void + { + $aggregateRoot->apply(new CategoryTreeDeletedEvent()); + $this->save($aggregateRoot); + + $this->eventStore->delete($aggregateRoot->getId()); + } } diff --git a/module/category-tree/src/Resources/translations/log.en.yaml b/module/category-tree/src/Resources/translations/log.en.yaml index b143fb8dc..9bccc0439 100644 --- a/module/category-tree/src/Resources/translations/log.en.yaml +++ b/module/category-tree/src/Resources/translations/log.en.yaml @@ -3,3 +3,4 @@ 'Category removed from category tree': 'Category removed from category tree' 'Category tree created': 'Category tree created' 'Category tree name changed': 'Category tree name changed' +'Category tree deleted': 'Category tree "%id%" deleted' diff --git a/module/category-tree/src/Resources/translations/log.pl.yaml b/module/category-tree/src/Resources/translations/log.pl.yaml index 502e50405..976dea321 100644 --- a/module/category-tree/src/Resources/translations/log.pl.yaml +++ b/module/category-tree/src/Resources/translations/log.pl.yaml @@ -3,3 +3,4 @@ 'Category removed from category tree': 'Usunięcie kategorii z drzewa kategorii' 'Category tree created': 'Stworzenie drzewa kategorii' 'Category tree name changed': 'Zmieniono nazwę drzewa kategorii' +'Category tree deleted': 'Drzewo kategorii "%id%" zostało usunięte' diff --git a/module/product/migrations/Version20180619083830.php b/module/product/migrations/Version20180619083830.php index aa96fb4a3..9dab127ad 100644 --- a/module/product/migrations/Version20180619083830.php +++ b/module/product/migrations/Version20180619083830.php @@ -30,10 +30,13 @@ public function up(Schema $schema): void PRIMARY KEY(id) ) '); - $this->addSql('CREATE UNIQUE INDEX product_sku_key ON product USING btree(sku)'); + $this->addSql('CREATE TABLE product_value (product_id UUID NOT NULL, attribute_id UUID NOT NULL, value_id UUID NOT NULL, PRIMARY KEY(product_id, attribute_id, value_id))'); + $this->addSql('ALTER TABLE product_value ADD CONSTRAINT product_value_product_id_fk FOREIGN KEY (product_id) references product on update cascade on delete cascade;'); + $this->addSql('CREATE TABLE product_category_product (category_id UUID NOT NULL, product_id UUID NOT NULL, PRIMARY KEY(category_id, product_id))'); + $this->addSql('ALTER TABLE product_category_product ADD CONSTRAINT product_category_product_product_id_fk FOREIGN KEY (product_id) references product on update cascade on delete cascade'); $this->addSql('CREATE TABLE product_status (code VARCHAR(32), name VARCHAR(64), PRIMARY KEY(code))'); $this->addSql('INSERT INTO product_status (code, name) VALUES(?, ?)', ['DRAFT', 'Draft']); diff --git a/module/product/src/Persistence/Dbal/Projector/ProductDeletedEventProjector.php b/module/product/src/Persistence/Dbal/Projector/ProductDeletedEventProjector.php new file mode 100644 index 000000000..dfb8cef29 --- /dev/null +++ b/module/product/src/Persistence/Dbal/Projector/ProductDeletedEventProjector.php @@ -0,0 +1,62 @@ +connection = $connection; + } + + /** + * {@inheritDoc} + */ + public function support(DomainEventInterface $event): bool + { + return $event instanceof ProductDeletedEvent; + } + + /** + * {@inheritDoc} + */ + public function projection(AbstractId $aggregateId, DomainEventInterface $event): void + { + if (!$event instanceof ProductDeletedEvent) { + throw new UnsupportedEventException($event, ProductDeletedEvent::class); + } + + $this->connection->delete( + self::TABLE, + [ + 'id' => $aggregateId->getValue(), + ] + ); + } +} From b0ab30053b8ce8c3cffdb987b9f02082203fc701 Mon Sep 17 00:00:00 2001 From: Mateusz Kolankowski Date: Tue, 10 Sep 2019 15:53:05 +0200 Subject: [PATCH 18/27] Creating product - templateId wrong UUID - no validation (#130) * Creating product - templateId wrong UUID - no validation * Creating product - templateId wrong UUID - no validation fixes * fixes after CR * fixes after CR --- .../Validator/TemplateExists.php | 23 +++++++ .../Validator/TemplateExistsValidator.php | 66 +++++++++++++++++++ .../Model/ProductCreateFormModel.php | 2 + 3 files changed, 91 insertions(+) create mode 100644 module/designer/src/Infrastructure/Validator/TemplateExists.php create mode 100644 module/designer/src/Infrastructure/Validator/TemplateExistsValidator.php diff --git a/module/designer/src/Infrastructure/Validator/TemplateExists.php b/module/designer/src/Infrastructure/Validator/TemplateExists.php new file mode 100644 index 000000000..28ea6f867 --- /dev/null +++ b/module/designer/src/Infrastructure/Validator/TemplateExists.php @@ -0,0 +1,23 @@ +templateRepository = $templateRepository; + } + + /** + * @param mixed $value + * @param Constraint $constraint + */ + public function validate($value, Constraint $constraint) + { + if (!$constraint instanceof TemplateExists) { + throw new UnexpectedTypeException($constraint, TemplateExists::class); + } + + if (null === $value || '' === $value) { + return; + } + + if (!is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) { + throw new UnexpectedTypeException($value, 'string'); + } + + $value = (string) $value; + $template = false; + + if (TemplateId::isValid($value)) { + $template = $this->templateRepository->load(new TemplateId($value)); + } + + if (!$template) { + $this->context->buildViolation($constraint->message) + ->setParameter('{{ value }}', $value) + ->addViolation(); + } + } +} diff --git a/module/product/src/Application/Model/ProductCreateFormModel.php b/module/product/src/Application/Model/ProductCreateFormModel.php index 799585119..de62ce853 100644 --- a/module/product/src/Application/Model/ProductCreateFormModel.php +++ b/module/product/src/Application/Model/ProductCreateFormModel.php @@ -9,6 +9,7 @@ namespace Ergonode\Product\Application\Model; +use Ergonode\Designer\Infrastructure\Validator\TemplateExists; use Ergonode\Product\Infrastructure\Validator\Sku; use Ergonode\Product\Infrastructure\Validator\SkuExists; use Symfony\Component\Validator\Constraints as Assert; @@ -36,6 +37,7 @@ class ProductCreateFormModel * * @Assert\NotBlank(message="Template is required") * @Assert\Uuid() + * @TemplateExists() */ public $template; From 78a44453a896cbefb4ad9a66b99252b5ac772027 Mon Sep 17 00:00:00 2001 From: Mateusz Kolankowski Date: Mon, 16 Sep 2019 13:28:01 +0200 Subject: [PATCH 19/27] Behat tests (#144) * categroy tree update fix (#141) * add category to category tree tests * little fix * revert --- features/category-tree.feature | 63 ++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/features/category-tree.feature b/features/category-tree.feature index 6917de759..a99405d48 100644 --- a/features/category-tree.feature +++ b/features/category-tree.feature @@ -438,6 +438,69 @@ Feature: Category tree module When I request "/api/v1/EN/trees/@category_tree@" using HTTP DELETE Then unauthorized response is received + Scenario: Add category to category tree + Given current authentication token + Given the following form parameters are set: + | name | value | + | child | @category_2@ | + When I request "/api/v1/EN/trees/@category_tree@/category/@category_1@/child" using HTTP POST + Then created response is received + + Scenario: Add category to category tree (not authorized) + Given the following form parameters are set: + | name | value | + | child | @category_2@ | + When I request "/api/v1/EN/trees/@category_tree@/category/@category_1@/child" using HTTP POST + Then unauthorized response is received + + Scenario: Add category to category tree (wrong child) + Given current authentication token + Given the following form parameters are set: + | name | value | + | child | @@static_uuid@@ | + When I request "/api/v1/EN/trees/@category_tree@/category/@category_1@/child" using HTTP POST + Then validation error response is received + + Scenario: Add category to category tree (not uuid child) + Given current authentication token + Given the following form parameters are set: + | name | value | + | child | 'test' | + When I request "/api/v1/EN/trees/@category_tree@/category/@category_1@/child" using HTTP POST + Then validation error response is received + + Scenario: Add category to category tree (wrong category tree) + Given current authentication token + Given the following form parameters are set: + | name | value | + | child | @category_2@ | + When I request "/api/v1/EN/trees/@@static_uuid@@/category/@category_1@/child" using HTTP POST + Then validation error response is received + + Scenario: Add category to category tree (not uuid child) + Given current authentication token + Given the following form parameters are set: + | name | value | + | child | @category_2@ | + When I request "/api/v1/EN/trees/test/category/@category_1@/child" using HTTP POST + Then validation error response is received + + Scenario: Add category to category tree (duplicated category) + Given current authentication token + Given the following form parameters are set: + | name | value | + | child | @category_2@ | + When I request "/api/v1/EN/trees/@category_tree@/category/@@static_uuid@@/child" using HTTP POST + Then validation error response is received + + Scenario: Add category to category tree (not uuid category) + Given current authentication token + Given the following form parameters are set: + | name | value | + | child | @category_1@ | + When I request "/api/v1/EN/trees/@category_tree@/category/test/child" using HTTP POST + Then validation error response is received + Scenario: Delete category tree Given current authentication token When I request "/api/v1/EN/trees/@category_tree@" using HTTP DELETE From cc4e8b21967d95fec9d1cf6e0c3ae1d8b23f73b7 Mon Sep 17 00:00:00 2001 From: Mateusz Kolankowski Date: Mon, 16 Sep 2019 13:34:29 +0200 Subject: [PATCH 20/27] Template wrong validation size position required (#140) * template - wrong validations * little fix --- features/designer.feature | 2 +- .../Transformer/PositionFormDataTransformer.php | 4 ++-- .../Form/Transformer/SizeFormDataTransformer.php | 4 ++-- .../src/Application/Form/Type/PositionFormType.php | 6 +++--- .../Properties/AttributeElementPropertyType.php | 13 ++----------- .../src/Application/Form/Type/SizeFormType.php | 6 +++--- 6 files changed, 13 insertions(+), 22 deletions(-) diff --git a/features/designer.feature b/features/designer.feature index c9953195e..a777dcd59 100644 --- a/features/designer.feature +++ b/features/designer.feature @@ -172,7 +172,7 @@ Feature: Designer module "elements": [ { "position": {"x": 0, "y": 0}, - "size": {"width": "test", "height": 1}, + "size": {"width": 2, "height": 1}, "variant": "attribute", "type": "text", "properties": { diff --git a/module/designer/src/Application/Form/Transformer/PositionFormDataTransformer.php b/module/designer/src/Application/Form/Transformer/PositionFormDataTransformer.php index 410bd6ccc..6cddfac7b 100644 --- a/module/designer/src/Application/Form/Transformer/PositionFormDataTransformer.php +++ b/module/designer/src/Application/Form/Transformer/PositionFormDataTransformer.php @@ -42,9 +42,9 @@ public function transform($value): ?array */ public function reverseTransform($value): ?Position { - if (is_array($value)) { + if (isset($value['x'], $value['y'])) { try { - return new Position((int) $value['x'], (int) $value['y']); + return new Position($value['x'], $value['y']); } catch (\InvalidArgumentException $e) { throw new TransformationFailedException(sprintf('invalid size %s value', implode(',', $value))); } diff --git a/module/designer/src/Application/Form/Transformer/SizeFormDataTransformer.php b/module/designer/src/Application/Form/Transformer/SizeFormDataTransformer.php index 79a6d7573..882334be3 100644 --- a/module/designer/src/Application/Form/Transformer/SizeFormDataTransformer.php +++ b/module/designer/src/Application/Form/Transformer/SizeFormDataTransformer.php @@ -42,9 +42,9 @@ public function transform($value): ?array */ public function reverseTransform($value): ?Size { - if (is_array($value)) { + if (isset($value['width'], $value['height'])) { try { - return new Size((int) $value['width'], (int) $value['height']); + return new Size($value['width'], $value['height']); } catch (\InvalidArgumentException $e) { throw new TransformationFailedException(sprintf('invalid size %s value', implode(',', $value))); } diff --git a/module/designer/src/Application/Form/Type/PositionFormType.php b/module/designer/src/Application/Form/Type/PositionFormType.php index 81823227a..eb5e8c9a4 100644 --- a/module/designer/src/Application/Form/Type/PositionFormType.php +++ b/module/designer/src/Application/Form/Type/PositionFormType.php @@ -11,7 +11,7 @@ use Ergonode\Designer\Application\Form\Transformer\PositionFormDataTransformer; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\Extension\Core\Type\IntegerType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -28,10 +28,10 @@ public function buildForm(FormBuilderInterface $builder, array $options): void $builder ->add( 'x', - TextType::class + IntegerType::class )->add( 'y', - TextType::class + IntegerType::class ); $builder->addModelTransformer(new PositionFormDataTransformer()); } diff --git a/module/designer/src/Application/Form/Type/Properties/AttributeElementPropertyType.php b/module/designer/src/Application/Form/Type/Properties/AttributeElementPropertyType.php index 1e498bba9..d03dd94b8 100644 --- a/module/designer/src/Application/Form/Type/Properties/AttributeElementPropertyType.php +++ b/module/designer/src/Application/Form/Type/Properties/AttributeElementPropertyType.php @@ -9,9 +9,9 @@ namespace Ergonode\Designer\Application\Form\Type\Properties; +use Ergonode\Core\Application\Form\Type\BooleanType; use Ergonode\Designer\Application\Model\Form\Type\Property\AttributeElementPropertyTypeModel; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -36,16 +36,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ) ->add( 'required', - CheckboxType::class, - [ - 'false_values' => [ - '0', - 'false', - '', - false, - ], - 'empty_data' => false, - ] + BooleanType::class ); } diff --git a/module/designer/src/Application/Form/Type/SizeFormType.php b/module/designer/src/Application/Form/Type/SizeFormType.php index d57e7bd13..c7c3df408 100644 --- a/module/designer/src/Application/Form/Type/SizeFormType.php +++ b/module/designer/src/Application/Form/Type/SizeFormType.php @@ -11,7 +11,7 @@ use Ergonode\Designer\Application\Form\Transformer\SizeFormDataTransformer; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\Extension\Core\Type\IntegerType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -28,10 +28,10 @@ public function buildForm(FormBuilderInterface $builder, array $options): void $builder ->add( 'width', - TextType::class + IntegerType::class )->add( 'height', - TextType::class + IntegerType::class ); $builder->addModelTransformer(new SizeFormDataTransformer()); } From c0a03709af2535602f42eaac195ad033150507cd Mon Sep 17 00:00:00 2001 From: Mateusz Kolankowski Date: Mon, 16 Sep 2019 13:35:47 +0200 Subject: [PATCH 21/27] Create boolean data transformer (#139) * creating Data Transformer * adding test to BooleanDataTransformer * fixing * adding data provider --- .../BooleanDataTransformer.php | 49 +++++++++ .../src/Application/Form/Type/BooleanType.php | 9 ++ .../BooleanDataTransformerTest.php | 103 ++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 module/core/src/Application/Form/DataTransformer/BooleanDataTransformer.php create mode 100644 module/core/tests/Application/Form/DataTransformer/BooleanDataTransformerTest.php diff --git a/module/core/src/Application/Form/DataTransformer/BooleanDataTransformer.php b/module/core/src/Application/Form/DataTransformer/BooleanDataTransformer.php new file mode 100644 index 000000000..2df5dc48a --- /dev/null +++ b/module/core/src/Application/Form/DataTransformer/BooleanDataTransformer.php @@ -0,0 +1,49 @@ +addViewTransformer(new BooleanDataTransformer()); + } /** * @param OptionsResolver $resolver */ diff --git a/module/core/tests/Application/Form/DataTransformer/BooleanDataTransformerTest.php b/module/core/tests/Application/Form/DataTransformer/BooleanDataTransformerTest.php new file mode 100644 index 000000000..834610155 --- /dev/null +++ b/module/core/tests/Application/Form/DataTransformer/BooleanDataTransformerTest.php @@ -0,0 +1,103 @@ +assertEquals($expected, $transformer->transform($value)); + } + + /** + * @dataProvider reverseDataProvider + * + * @param mixed $value + * @param string $expected + */ + public function testReverseTransform($value, string $expected): void + { + $transformer = new BooleanDataTransformer(); + $this->assertEquals($expected, $transformer->reverseTransform($value)); + } + + /** + */ + public function testReverseTransformException(): void + { + $transformer = new BooleanDataTransformer(); + $this->expectExceptionMessage('Expect boolean'); + $this->assertEquals('false', $transformer->reverseTransform('fadwwalse')); + } + + + /** + * @return array + */ + public function reverseDataProvider(): array + { + return [ + [ + 'value' => 'true', + 'expected' => 'true', + ], + [ + 'value' => true, + 'expected' => 'true', + ], + [ + 'value' => 1, + 'expected' => 'true', + ], + [ + 'value' => 0, + 'expected' => 'false', + ], + [ + 'value' => 'false', + 'expected' => 'false', + ], + [ + 'value' => false, + 'expected' => 'false', + ], + ]; + } + + /** + * @return array + */ + public function transformDataProvider(): array + { + return [ + [ + 'value' => 'true', + 'expected' => 1, + ], + [ + 'value' => 'false', + 'expected' => false, + ], + ]; + } +} From c8a1116c82620cd5c671f1c46ff1c1f1fce61661 Mon Sep 17 00:00:00 2001 From: Mateusz Kolankowski Date: Mon, 16 Sep 2019 14:34:28 +0200 Subject: [PATCH 22/27] adding not blank assertion (#145) --- .../account/src/Application/Form/Model/UpdateUserFormModel.php | 1 + 1 file changed, 1 insertion(+) diff --git a/module/account/src/Application/Form/Model/UpdateUserFormModel.php b/module/account/src/Application/Form/Model/UpdateUserFormModel.php index e82e866c2..b6b739efc 100644 --- a/module/account/src/Application/Form/Model/UpdateUserFormModel.php +++ b/module/account/src/Application/Form/Model/UpdateUserFormModel.php @@ -44,6 +44,7 @@ class UpdateUserFormModel /** * @var Password|null * + * @Assert\NotBlank(message="User password repeat is required") * @Assert\EqualTo(propertyPath="password", message="This value should be same as password") */ public $passwordRepeat; From af82670940a612b582f27487bcc174500bbb9948 Mon Sep 17 00:00:00 2001 From: Sebastian Bielawski Date: Tue, 17 Sep 2019 10:39:46 +0200 Subject: [PATCH 23/27] Segment conditions (#138) * #115 - add segment feature * #115 - add segment feature * #133 Product delete * move conditons and conditionsset t one module * #133 Product delete * stablization * Condition set create action * Condition set create action * Condition set get and update action * Condition set and segment fixes * Condition set validation * Condition set validation * Fix condition strategy inteface tag * Condition set persistance and validator * Condition set persistance fix * Condition set validator fix * condition validation * Condition set persistance fix * condition validation * condition validation * Condition set persistance fix * Condition set persistance fix * fix * Condition set persistance fix * Condition set persistance fix * fix * Condition set persistance fix * Condition set and segment persistance fix * Condition set additional tests * Condition set conditions fix * Condition set fixtures * Condition configuration strategy fix * Condition configuration strategy fix * Condition configuration strategy type uppercase fix * Create unique validators for segment code and condition set code * Workflow, product and account API tests fixes --- composer.json | 2 + config/bundles.php | 3 +- config/packages/nelmio_api_doc.yaml | 44 +- features/account.feature | 4 +- features/bootstrap/AttributeContext.php | 8 +- features/condition.feature | 733 ++++++++++++++++++ features/product.feature | 3 +- features/segment.feature | 252 ++++++ features/workflow.feature | 29 +- module/account/README.md | 2 +- .../Handler/ViolationsExceptionHandler.php | 18 +- .../Form/Model/AttributeOptionModel.php | 2 +- .../Form/Model/AttributeParametersModel.php | 1 - .../Form/Model/CreateAttributeFormModel.php | 4 +- .../Form/Model/UpdateAttributeFormModel.php | 6 +- .../Domain/Query/AttributeQueryInterface.php | 14 + .../Validator/AttributeExistsValidator.php | 5 +- .../Dbal/Query/DbalAttributeGroupQuery.php | 3 +- .../Dbal/Query/DbalAttributeQuery.php | 41 +- .../CacheAttributeQueryDecorator.php | 20 + module/condition/LICENSE.txt | 26 + module/condition/README.md | 19 + module/condition/composer.json | 31 + .../migrations/Version20190910151314.php | 67 ++ .../Controller/Api/ConditionController.php | 83 ++ .../Controller/Api/ConditionSetController.php | 402 ++++++++++ .../Controller/Api/DictionaryController.php | 68 ++ .../ConditionConfiguratorCompilerPass.php | 49 ++ .../ErgonodeConditionExtension.php | 42 + .../ConditionSetParamConverter.php | 69 ++ .../ConstraintAttributeExistsCondition.php | 25 + ...raintAttributeExistsConditionValidator.php | 64 ++ .../Command/CreateConditionSetCommand.php | 93 +++ .../Command/DeleteConditionSetCommand.php | 41 + .../Command/UpdateConditionSetCommand.php | 117 +++ .../Condition/AttributeExistsCondition.php | 54 ++ .../Domain/Condition/ConditionInterface.php | 20 + .../NumericAttributeValueCondition.php | 71 ++ .../OptionAttributeValueCondition.php | 71 ++ .../Condition/TextAttributeValueCondition.php | 71 ++ .../src/Domain/Entity/ConditionSet.php | 201 +++++ .../src/Domain/Entity/ConditionSetId.php | 32 + .../ConditionSetConditionsChangedEvent.php | 63 ++ .../Domain/Event/ConditionSetCreatedEvent.php | 117 +++ .../Domain/Event/ConditionSetDeletedEvent.php | 18 + .../ConditionSetDescriptionChangedEvent.php | 18 + .../Event/ConditionSetNameChangedEvent.php | 18 + .../ConditionStrategyNotFoundException.php | 25 + .../ConditionConfigurationProvider.php | 51 ++ .../Provider/ConditionDictionaryProvider.php | 50 ++ .../Query/ConditionSetQueryInterface.php | 33 + .../ConditionSetRepositoryInterface.php | 43 + .../Domain/Service/ConditionConfigurator.php | 48 ++ .../src/Domain/Service/ConditionVerifier.php | 66 ++ .../ConditionVerifierStrategyInterface.php | 33 + .../ConfigurationStrategyInterface.php | 31 + ...teExistsConditionConfigurationStrategy.php | 70 ++ ...tributeExistsConditionVerifierStrategy.php | 36 + ...uteValueConditionConfigurationStrategy.php | 87 +++ ...uteValueConditionConfigurationStrategy.php | 76 ++ ...uteValueConditionConfigurationStrategy.php | 84 ++ .../Domain/ValueObject/ConditionSetCode.php | 58 ++ .../condition/src/ErgonodeConditionBundle.php | 29 + ...tributeExistsConditionValidatorBuilder.php | 38 + ...ttributeValueConditionValidatorBuilder.php | 46 ++ ...ttributeValueConditionValidatorBuilder.php | 41 + ...ttributeValueConditionValidatorBuilder.php | 46 ++ .../ConditionValidatorBuilderInterface.php | 24 + .../CreateConditionSetValidatorBuilder.php | 55 ++ .../UpdateConditionSetValidatorBuilder.php | 97 +++ .../Infrastructure/Grid/ConditionSetGrid.php | 63 ++ .../CreateConditionSetCommandHandler.php | 49 ++ .../DeleteConditionSetCommandHandler.php | 45 ++ .../UpdateConditionSetCommandHandler.php | 55 ++ .../Handler/ConditionInterfaceHandler.php | 26 + .../Handler/ConditionSetCodeHandler.php | 75 ++ .../Handler/ConditionSetIdHandler.php | 75 ++ .../Resolver/ConditionConstraintResolver.php | 48 ++ .../Validator/UniqueConditionSetCode.php | 28 + .../UniqueConditionSetCodeValidator.php | 68 ++ ...tionSetConditionsChangedEventProjector.php | 80 ++ .../ConditionSetCreatedEventProjector.php | 79 ++ .../ConditionSetDeletedEventProjector.php | 62 ++ ...ionSetDescriptionChangedEventProjector.php | 80 ++ .../ConditionSetNameChangedEventProjector.php | 80 ++ .../Dbal/Query/DbalConditionSetQuery.php | 84 ++ .../Repository/DbalConditionSetRepository.php | 108 +++ .../condition/src/Resources/config/routes.yml | 14 + .../src/Resources/config/services.yml | 47 ++ .../Resources/translations/condition.en.yaml | 8 + .../Resources/translations/condition.pl.yaml | 8 + .../src/Resources/translations/grid.en.yaml | 5 + .../src/Resources/translations/grid.pl.yaml | 5 + .../src/Resources/translations/log.en.yaml | 5 + .../src/Resources/translations/log.pl.yaml | 5 + .../Resources/translations/privilege.en.yaml | 1 + .../Resources/translations/privilege.pl.yaml | 1 + .../Handler/AbstractInterfaceHandler.php | 12 + .../Exception/ProjectorException.php | 4 +- .../Faker/ConditionSetCodeFaker.php | 34 + .../Faker/ConditionSetIdFaker.php | 35 + .../fixtures/develop/conditions.yaml | 25 + .../Resources/fixtures/develop/fixture.yaml | 3 +- .../src/Resources/fixtures/develop/roles.yaml | 26 +- .../develop/{segment.yaml => segments.yaml} | 1 + .../src/Resources/fixtures/fixture.yaml | 20 + .../Handler/UpdateProductCommandHandler.php | 3 +- .../migrations/Version20190130104000.php | 11 +- .../Controller/Api/SegmentController.php | 67 +- .../ErgonodeSegmentExtension.php | 2 + .../Application/Form/CreateSegmentForm.php | 7 + .../Form/Model/CreateSegmentFormModel.php | 15 +- .../Form/Model/UpdateSegmentFormModel.php | 9 +- .../Application/Form/UpdateSegmentForm.php | 8 +- .../Domain/Command/CreateSegmentCommand.php | 20 +- .../Domain/Command/DeleteSegmentCommand.php | 40 + .../Domain/Command/UpdateSegmentCommand.php | 26 +- module/segment/src/Domain/Entity/Segment.php | 77 +- .../Event/SegmentConditionSetChangedEvent.php | 59 ++ .../src/Domain/Event/SegmentCreatedEvent.php | 50 +- .../src/Domain/Event/SegmentDeletedEvent.php | 18 + .../Event/SegmentSpecificationAddedEvent.php | 42 - .../Domain/Query/SegmentQueryInterface.php | 16 + .../Repository/SegmentRepositoryInterface.php | 5 + .../SegmentSpecificationInterface.php | 24 - .../src/Infrastructure/Grid/SegmentGrid.php | 10 +- .../Handler/CreateSegmentCommandHandler.php | 1 + .../Handler/DeleteSegmentCommandHandler.php | 45 ++ .../Handler/UpdateSegmentCommandHandler.php | 1 + .../Specification/AbstractSpecification.php | 25 - .../AttributeExistsSpecification.php | 45 -- .../AttributeValueSpecification.php | 62 -- .../Validator/UniqueSegmentCode.php | 28 + .../Validator/UniqueSegmentCodeValidator.php | 68 ++ ...gmentConditionSetChangedEventProjector.php | 72 ++ .../SegmentCreatedEventProjector.php | 33 +- .../SegmentDeletedEventProjector.php | 62 ++ ...egmentDescriptionChangedEventProjector.php | 80 ++ .../SegmentNameChangedEventProjector.php | 80 ++ .../SegmentStatusChangedEventProjector.php | 80 ++ .../Dbal/Query/DbalSegmentQuery.php | 45 +- .../Dbal/Repository/DbalSegmentRepository.php | 27 +- .../segment/src/Resources/config/routes.yml | 4 +- .../segment/src/Resources/config/services.yml | 4 + .../src/Resources/translations/grid.en.yaml | 5 + .../src/Resources/translations/grid.pl.yaml | 5 + .../src/Resources/translations/log.en.yaml | 2 + .../src/Resources/translations/log.pl.yaml | 2 + .../Resources/translations/privilege.en.yaml | 1 + .../Resources/translations/privilege.pl.yaml | 1 + .../Resources/translations/segment.en.yaml | 2 + .../Resources/translations/segment.pl.yaml | 2 + .../Command/CreateSegmentCommandTest.php | 6 +- .../Command/UpdateSegmentCommandTest.php | 7 +- .../tests/Domain/Entity/SegmentTest.php | 28 +- .../Domain/Event/SegmentCreatedEventTest.php | 11 +- module/translation-deepl/README.md | 2 +- .../Controller/Api/WorkflowController.php | 3 +- .../Workflow/UpdateWorkflowCommand.php | 1 - .../Builder/WorkflowValidatorBuilder.php | 1 - .../Validator/StatusNotExists.php | 2 +- .../Validator/StatusNotExistsValidator.php | 2 +- .../Validator/WorkflowExistsValidator.php | 5 +- symfony.lock | 9 - 164 files changed, 6672 insertions(+), 371 deletions(-) create mode 100644 features/condition.feature create mode 100644 features/segment.feature create mode 100644 module/condition/LICENSE.txt create mode 100644 module/condition/README.md create mode 100644 module/condition/composer.json create mode 100644 module/condition/migrations/Version20190910151314.php create mode 100644 module/condition/src/Application/Controller/Api/ConditionController.php create mode 100644 module/condition/src/Application/Controller/Api/ConditionSetController.php create mode 100644 module/condition/src/Application/Controller/Api/DictionaryController.php create mode 100644 module/condition/src/Application/DependencyInjection/CompilerPass/ConditionConfiguratorCompilerPass.php create mode 100644 module/condition/src/Application/DependencyInjection/ErgonodeConditionExtension.php create mode 100644 module/condition/src/Application/Request/ParamConverter/ConditionSetParamConverter.php create mode 100644 module/condition/src/Application/Validator/ConstraintAttributeExistsCondition.php create mode 100644 module/condition/src/Application/Validator/ConstraintAttributeExistsConditionValidator.php create mode 100644 module/condition/src/Domain/Command/CreateConditionSetCommand.php create mode 100644 module/condition/src/Domain/Command/DeleteConditionSetCommand.php create mode 100644 module/condition/src/Domain/Command/UpdateConditionSetCommand.php create mode 100644 module/condition/src/Domain/Condition/AttributeExistsCondition.php create mode 100644 module/condition/src/Domain/Condition/ConditionInterface.php create mode 100644 module/condition/src/Domain/Condition/NumericAttributeValueCondition.php create mode 100644 module/condition/src/Domain/Condition/OptionAttributeValueCondition.php create mode 100644 module/condition/src/Domain/Condition/TextAttributeValueCondition.php create mode 100644 module/condition/src/Domain/Entity/ConditionSet.php create mode 100644 module/condition/src/Domain/Entity/ConditionSetId.php create mode 100644 module/condition/src/Domain/Event/ConditionSetConditionsChangedEvent.php create mode 100644 module/condition/src/Domain/Event/ConditionSetCreatedEvent.php create mode 100644 module/condition/src/Domain/Event/ConditionSetDeletedEvent.php create mode 100644 module/condition/src/Domain/Event/ConditionSetDescriptionChangedEvent.php create mode 100644 module/condition/src/Domain/Event/ConditionSetNameChangedEvent.php create mode 100644 module/condition/src/Domain/Exception/ConditionStrategyNotFoundException.php create mode 100644 module/condition/src/Domain/Provider/ConditionConfigurationProvider.php create mode 100644 module/condition/src/Domain/Provider/ConditionDictionaryProvider.php create mode 100644 module/condition/src/Domain/Query/ConditionSetQueryInterface.php create mode 100644 module/condition/src/Domain/Repository/ConditionSetRepositoryInterface.php create mode 100644 module/condition/src/Domain/Service/ConditionConfigurator.php create mode 100644 module/condition/src/Domain/Service/ConditionVerifier.php create mode 100644 module/condition/src/Domain/Service/ConditionVerifierStrategyInterface.php create mode 100644 module/condition/src/Domain/Service/ConfigurationStrategyInterface.php create mode 100644 module/condition/src/Domain/Service/Strategy/AttributeExistsConditionConfigurationStrategy.php create mode 100644 module/condition/src/Domain/Service/Strategy/AttributeExistsConditionVerifierStrategy.php create mode 100644 module/condition/src/Domain/Service/Strategy/NumericAttributeValueConditionConfigurationStrategy.php create mode 100644 module/condition/src/Domain/Service/Strategy/OptionAttributeValueConditionConfigurationStrategy.php create mode 100644 module/condition/src/Domain/Service/Strategy/TextAttributeValueConditionConfigurationStrategy.php create mode 100644 module/condition/src/Domain/ValueObject/ConditionSetCode.php create mode 100644 module/condition/src/ErgonodeConditionBundle.php create mode 100644 module/condition/src/Infrastructure/Builder/Condition/AttributeExistsConditionValidatorBuilder.php create mode 100644 module/condition/src/Infrastructure/Builder/Condition/NumericAttributeValueConditionValidatorBuilder.php create mode 100644 module/condition/src/Infrastructure/Builder/Condition/OptionAttributeValueConditionValidatorBuilder.php create mode 100644 module/condition/src/Infrastructure/Builder/Condition/TextAttributeValueConditionValidatorBuilder.php create mode 100644 module/condition/src/Infrastructure/Builder/ConditionValidatorBuilderInterface.php create mode 100644 module/condition/src/Infrastructure/Builder/CreateConditionSetValidatorBuilder.php create mode 100644 module/condition/src/Infrastructure/Builder/UpdateConditionSetValidatorBuilder.php create mode 100644 module/condition/src/Infrastructure/Grid/ConditionSetGrid.php create mode 100644 module/condition/src/Infrastructure/Handler/CreateConditionSetCommandHandler.php create mode 100644 module/condition/src/Infrastructure/Handler/DeleteConditionSetCommandHandler.php create mode 100644 module/condition/src/Infrastructure/Handler/UpdateConditionSetCommandHandler.php create mode 100644 module/condition/src/Infrastructure/JMS/Serializer/Handler/ConditionInterfaceHandler.php create mode 100644 module/condition/src/Infrastructure/JMS/Serializer/Handler/ConditionSetCodeHandler.php create mode 100644 module/condition/src/Infrastructure/JMS/Serializer/Handler/ConditionSetIdHandler.php create mode 100644 module/condition/src/Infrastructure/Resolver/ConditionConstraintResolver.php create mode 100644 module/condition/src/Infrastructure/Validator/UniqueConditionSetCode.php create mode 100644 module/condition/src/Infrastructure/Validator/UniqueConditionSetCodeValidator.php create mode 100644 module/condition/src/Persistence/Dbal/Projector/ConditionSetConditionsChangedEventProjector.php create mode 100644 module/condition/src/Persistence/Dbal/Projector/ConditionSetCreatedEventProjector.php create mode 100644 module/condition/src/Persistence/Dbal/Projector/ConditionSetDeletedEventProjector.php create mode 100644 module/condition/src/Persistence/Dbal/Projector/ConditionSetDescriptionChangedEventProjector.php create mode 100644 module/condition/src/Persistence/Dbal/Projector/ConditionSetNameChangedEventProjector.php create mode 100644 module/condition/src/Persistence/Dbal/Query/DbalConditionSetQuery.php create mode 100644 module/condition/src/Persistence/Dbal/Repository/DbalConditionSetRepository.php create mode 100644 module/condition/src/Resources/config/routes.yml create mode 100644 module/condition/src/Resources/config/services.yml create mode 100644 module/condition/src/Resources/translations/condition.en.yaml create mode 100644 module/condition/src/Resources/translations/condition.pl.yaml create mode 100644 module/condition/src/Resources/translations/grid.en.yaml create mode 100644 module/condition/src/Resources/translations/grid.pl.yaml create mode 100644 module/condition/src/Resources/translations/log.en.yaml create mode 100644 module/condition/src/Resources/translations/log.pl.yaml create mode 100644 module/condition/src/Resources/translations/privilege.en.yaml create mode 100644 module/condition/src/Resources/translations/privilege.pl.yaml create mode 100644 module/fixture/src/Infrastructure/Faker/ConditionSetCodeFaker.php create mode 100644 module/fixture/src/Infrastructure/Faker/ConditionSetIdFaker.php create mode 100644 module/fixture/src/Resources/fixtures/develop/conditions.yaml rename module/fixture/src/Resources/fixtures/develop/{segment.yaml => segments.yaml} (92%) create mode 100644 module/segment/src/Domain/Command/DeleteSegmentCommand.php create mode 100644 module/segment/src/Domain/Event/SegmentConditionSetChangedEvent.php create mode 100644 module/segment/src/Domain/Event/SegmentDeletedEvent.php delete mode 100644 module/segment/src/Domain/Event/SegmentSpecificationAddedEvent.php delete mode 100644 module/segment/src/Domain/Specification/SegmentSpecificationInterface.php create mode 100644 module/segment/src/Infrastructure/Handler/DeleteSegmentCommandHandler.php delete mode 100644 module/segment/src/Infrastructure/Specification/AbstractSpecification.php delete mode 100644 module/segment/src/Infrastructure/Specification/AttributeExistsSpecification.php delete mode 100644 module/segment/src/Infrastructure/Specification/AttributeValueSpecification.php create mode 100644 module/segment/src/Infrastructure/Validator/UniqueSegmentCode.php create mode 100644 module/segment/src/Infrastructure/Validator/UniqueSegmentCodeValidator.php create mode 100644 module/segment/src/Persistence/Dbal/Projector/SegmentConditionSetChangedEventProjector.php create mode 100644 module/segment/src/Persistence/Dbal/Projector/SegmentDeletedEventProjector.php create mode 100644 module/segment/src/Persistence/Dbal/Projector/SegmentDescriptionChangedEventProjector.php create mode 100644 module/segment/src/Persistence/Dbal/Projector/SegmentNameChangedEventProjector.php create mode 100644 module/segment/src/Persistence/Dbal/Projector/SegmentStatusChangedEventProjector.php create mode 100644 module/segment/src/Resources/translations/grid.en.yaml create mode 100644 module/segment/src/Resources/translations/grid.pl.yaml create mode 100644 module/segment/src/Resources/translations/privilege.en.yaml create mode 100644 module/segment/src/Resources/translations/privilege.pl.yaml create mode 100644 module/segment/src/Resources/translations/segment.en.yaml create mode 100644 module/segment/src/Resources/translations/segment.pl.yaml diff --git a/composer.json b/composer.json index aebfbede7..31712680a 100644 --- a/composer.json +++ b/composer.json @@ -99,6 +99,7 @@ "Ergonode\\AttributeUnit\\": "module/attribute-unit/src", "Ergonode\\AttributeDate\\": "module/attribute-date/src", "Ergonode\\AttributeImage\\": "module/attribute-image/src", + "Ergonode\\Condition\\": "module/condition/src", "Ergonode\\Segment\\": "module/segment/src", "Ergonode\\Grid\\": "module/grid/src", "Ergonode\\Channel\\": "module/channel/src", @@ -136,6 +137,7 @@ "Ergonode\\AttributeUnit\\Tests\\": "module/attribute-unit/tests", "Ergonode\\AttributeDate\\Tests\\": "module/attribute-date/tests", "Ergonode\\AttributeImage\\Tests\\": "module/attribute-image/tests", + "Ergonode\\Condition\\Tests\\": "module/condition/tests", "Ergonode\\Segment\\Tests\\": "module/segment/tests", "Ergonode\\Grid\\Tests\\": "module/grid/tests", "Ergonode\\Channel\\Tests\\": "module/channel/tests", diff --git a/config/bundles.php b/config/bundles.php index c4e3784b8..5ef63ed0d 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -25,6 +25,7 @@ Ergonode\Importer\ErgonodeImporterBundle::class => ['all' => true], Ergonode\Reader\ErgonodeReaderBundle::class => ['all' => true], Ergonode\Transformer\ErgonodeTransformerBundle::class => ['all' => true], + Ergonode\Condition\ErgonodeConditionBundle::class => ['all' => true], Ergonode\Segment\ErgonodeSegmentBundle::class => ['all' => true], Ergonode\Category\ErgonodeCategoryBundle::class => ['all' => true], Ergonode\CategoryTree\ErgonodeCategoryTreeBundle::class => ['all' => true], @@ -44,6 +45,6 @@ Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], Vich\UploaderBundle\VichUploaderBundle::class => ['all' => true], Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], - Symfony\Bundle\WebServerBundle\WebServerBundle::class => ['dev' => true], Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true], + Symfony\Bundle\WebServerBundle\WebServerBundle::class => ['dev' => true], ]; diff --git a/config/packages/nelmio_api_doc.yaml b/config/packages/nelmio_api_doc.yaml index 033dcb08b..6871b2ffb 100644 --- a/config/packages/nelmio_api_doc.yaml +++ b/config/packages/nelmio_api_doc.yaml @@ -333,24 +333,29 @@ nelmio_api_doc: description: Additional format information (If used by attribute) example: YYYY-MM-DDDD segment: - type: object + type: 'object' properties: + condition_set_id: + type: 'string' + required: true + description: 'Condition set ID' code: - type: string + type: 'string' required: true - description: Segment unique code value + description: 'Segment unique code' name: $ref: "#/definitions/translation" description: $ref: "#/definitions/translation" example: - code: code_1 + condition_set_id: 'd1b9b64a-fef6-440c-9560-cf73daa4b4d6' + code: 'code' name: - PL: Name PL 1 - EN: Name EN 1 + PL: 'Nazwa' + EN: 'Name' description: - PL: Description PL 1 - EN: Description EN 1 + PL: 'Opis' + EN: 'Description' languages_req: type: object properties: @@ -479,7 +484,6 @@ nelmio_api_doc: items: type: 'string' example: 'Error message' - credentials: type: object properties: @@ -514,6 +518,28 @@ nelmio_api_doc: description: List of role privileges example: [ ATTRIBUTE_CREATE, ATTRIBUTE_READ, ATTRIBUTE_UPDATE, ATTRIBUTE_DELETE] + conditionset_create: + type: 'object' + properties: + code: + type: 'string' + required: true + description: 'Condition code' + example: 'CONDITION_CODE' + name: + $ref: '#/definitions/translation' + description: + $ref: '#/definitions/translation' + conditionset_update: + type: 'object' + properties: + name: + $ref: '#/definitions/translation' + description: + $ref: '#/definitions/translation' + conditions: + type: 'array' + example: [] authentication: type: object properties: diff --git a/features/account.feature b/features/account.feature index 8d3ca526e..24a75ee59 100644 --- a/features/account.feature +++ b/features/account.feature @@ -61,7 +61,7 @@ Feature: Account module } """ When I request "/api/v1/EN/roles" using HTTP POST - Then validation error response is received + Then created response is received Scenario: Create role (wrong parameter - name) Given current authentication token @@ -113,7 +113,7 @@ Feature: Account module } """ When I request "/api/v1/EN/roles" using HTTP POST - Then validation error response is received + Then created response is received Scenario: Create role (no existing privilages) Given current authentication token diff --git a/features/bootstrap/AttributeContext.php b/features/bootstrap/AttributeContext.php index 69e6bdedd..aaef6dd4d 100644 --- a/features/bootstrap/AttributeContext.php +++ b/features/bootstrap/AttributeContext.php @@ -38,11 +38,13 @@ public function gatherContexts(BeforeScenarioScope $scope): void } /** - * @Then remember first attribute group as "attribute_group" + * @param string $key + * + * @Then remember first attribute group as :key */ - public function rememberFirstAttributeGroup(): void + public function rememberFirstAttributeGroup(string $key): void { $response = $this->apiContext->getLastResponseBody(); - $this->storageContext->add('attribute_group', $response[count($response)-1]->id); + $this->storageContext->add($key, $response[key($response)]->id); } } diff --git a/features/condition.feature b/features/condition.feature new file mode 100644 index 000000000..a019f4c27 --- /dev/null +++ b/features/condition.feature @@ -0,0 +1,733 @@ +Feature: Condition + + Scenario: Get conditions dicitionary (not authorized) + When I request "/api/v1/EN/dictionary/conditions" using HTTP GET + Then unauthorized response is received + + Scenario: Get conditions dicitionary + Given current authentication token + When I request "/api/v1/EN/dictionary/conditions" using HTTP GET + Then the response code is 200 + + Scenario: Get condition (not found) + Given current authentication token + When I request "/api/v1/EN/conditions/asd" using HTTP GET + Then not found response is received + + Scenario: Get numeric condition (not authorized) + When I request "/api/v1/EN/conditions/NUMERIC_ATTRIBUTE_VALUE_CONDITION" using HTTP GET + Then unauthorized response is received + + Scenario: Get numeric condition + Given current authentication token + When I request "/api/v1/EN/conditions/NUMERIC_ATTRIBUTE_VALUE_CONDITION" using HTTP GET + Then the response code is 200 + + Scenario: Get option condition (not authorized) + When I request "/api/v1/EN/conditions/OPTION_ATTRIBUTE_VALUE_CONDITION" using HTTP GET + Then unauthorized response is received + + Scenario: Get option condition + Given current authentication token + When I request "/api/v1/EN/conditions/OPTION_ATTRIBUTE_VALUE_CONDITION" using HTTP GET + Then the response code is 200 + + Scenario: Get attribute exists condition (not authorized) + When I request "/api/v1/EN/conditions/ATTRIBUTE_EXISTS_CONDITION" using HTTP GET + Then unauthorized response is received + + Scenario: Get attribute exists condition + Given current authentication token + When I request "/api/v1/EN/conditions/ATTRIBUTE_EXISTS_CONDITION" using HTTP GET + Then the response code is 200 + + Scenario: Get text condition (not authorized) + When I request "/api/v1/EN/conditions/TEXT_ATTRIBUTE_VALUE_CONDITION" using HTTP GET + Then unauthorized response is received + + Scenario: Get text condition + Given current authentication token + When I request "/api/v1/EN/conditions/TEXT_ATTRIBUTE_VALUE_CONDITION" using HTTP GET + Then the response code is 200 + + Scenario: Get attribute groups dictionary + Given current authentication token + When I request "/api/v1/EN/dictionary/attributes/groups" using HTTP GET + Then the response code is 200 + And remember first attribute group as "condition_attribute_group" + + Scenario: Create text attribute + Given current authentication token + Given the request body is: + """ + { + "code": "CONDITION_@@random_code@@", + "type": "TEXT", + "label": {"PL": "Atrybut tekstowy", "EN": "Text attribute"}, + "groups": ["@condition_attribute_group@"], + "parameters": [] + } + """ + When I request "/api/v1/EN/attributes" using HTTP POST + Then created response is received + And remember response param "id" as "condition_text_attribute" + + Scenario: Create condition set (not authorized) + Given I request "/api/v1/EN/conditionsets" using HTTP POST + Then unauthorized response is received + + Scenario: Create condition set + Given current authentication token + Given remember param "condition_set_code" with value "CONDITION_@@random_uuid@@" + Given the request body is: + """ + { + "code": "@condition_set_code@", + "name": { + "PL": "Zbiór warunków", + "EN": "Condition set" + }, + "description": { + "PL": "Opis do zbioru warunków", + "EN": "Condition set description" + } + } + """ + Given I request "/api/v1/EN/conditionsets" using HTTP POST + Then created response is received + And remember response param "id" as "conditionset" + + Scenario: Create condition set (not unique code) + Given current authentication token + Given the request body is: + """ + { + "code": "@condition_set_code@" + } + """ + Given I request "/api/v1/EN/conditionsets" using HTTP POST + Then validation error response is received + + Scenario: Create condition set (without description) + Given current authentication token + Given the request body is: + """ + { + "code": "CONDITION_2_@@random_uuid@@", + "name": { + "PL": "Zbiór warunków", + "EN": "Condition set" + } + } + """ + Given I request "/api/v1/EN/conditionsets" using HTTP POST + Then created response is received + + Scenario: Create condition set (only code) + Given current authentication token + Given the request body is: + """ + { + "code": "CONDITION_3_@@random_uuid@@" + } + """ + Given I request "/api/v1/EN/conditionsets" using HTTP POST + Then created response is received + + Scenario: Create condition set (without code) + Given current authentication token + Given the request body is: + """ + { + "name": { + "PL": "Zbiór warunków", + "EN": "Condition set" + } + } + """ + Given I request "/api/v1/EN/conditionsets" using HTTP POST + Then validation error response is received + + Scenario: Create condition set (short name) + Given current authentication token + Given the request body is: + """ + { + "code": "CONDITION_@@random_uuid@@", + "name": { + "PL": "Z", + "EN": "C" + } + } + """ + Given I request "/api/v1/EN/conditionsets" using HTTP POST + Then validation error response is received + + Scenario: Create condition set (long name) + Given current authentication token + Given the request body is: + """ + { + "code": "CONDITION_@@random_uuid@@", + "name": { + "PL": "ceqvqEO1AsN92sTa0yn6vtYKc4Wkegfw7P5IQO34hhmtNWPYUKZXF8npJg55qGTUG4unmQPlaqRRvAzuaQLST2RP030V9gbqx5gekGPRnRqwVi03Cs0SDvmZe0jmMNm4lOm2w02kyHA1wtMapqgv3GGtQFTsXBegVFFu3aGlpZyfyWRl4TLSm4rTWMSRC89u2A3mxEAWv1AXn64ouBL4AoqwRGomgeU58ewRWiEwPv55BMmMfa0SxQOfiplqksmQ", + "EN": "Condition set" + } + } + """ + Given I request "/api/v1/EN/conditionsets" using HTTP POST + Then validation error response is received + + Scenario: Create condition set (long description) + Given current authentication token + Given the request body is: + """ + { + "code": "CONDITION_@@random_uuid@@", + "description": { + "PL": "Opis do zbioru warunków", + "EN": "ceqvqEO1AsN92sTa0yn6vtYKc4Wkegfw7P5IQO34hhmtNWPYUKZXF8npJg55qGTUG4unmQPlaqRRvAzuaQLST2RP030V9gbqx5gekGPRnRqwVi03Cs0SDvmZe0jmMNm4lOm2w02kyHA1wtMapqgv3GGtQFTsXBegVFFu3aGlpZyfyWRl4TLSm4rTWMSRC89u2A3mxEAWv1AXn64ouBL4AoqwRGomgeU58ewRWiEwPv55BMmMfa0SxQOfiplqksmQ" + } + } + """ + Given I request "/api/v1/EN/conditionsets" using HTTP POST + Then validation error response is received + + Scenario: Update condition set + Given current authentication token + Given the request body is: + """ + { + "name": { + "PL": "Zbiór warunków (changed)", + "EN": "Condition set (changed)" + }, + "description": { + "PL": "Opis do zbioru warunków (changed)", + "EN": "Condition set description (changed)" + }, + "conditions": [ + { + "type": "ATTRIBUTE_EXISTS_CONDITION", + "attribute": "@condition_text_attribute@" + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then empty response is received + + Scenario: Update condition set (without conditions) + Given current authentication token + Given the request body is: + """ + { + "name": { + "PL": "Zbiór warunków (changed)", + "EN": "Condition set (changed)" + }, + "description": { + "PL": "Opis do zbioru warunków (changed)", + "EN": "Condition set description (changed)" + } + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then validation error response is received + + Scenario: Update condition set (without name) + Given current authentication token + Given the request body is: + """ + { + "description": { + "PL": "Opis do zbioru warunków (changed)", + "EN": "Condition set description (changed)" + }, + "conditions": [ + { + "type": "ATTRIBUTE_EXISTS_CONDITION", + "attribute": "@condition_text_attribute@" + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then empty response is received + + Scenario: Update condition set (without description) + Given current authentication token + Given the request body is: + """ + { + "name": { + "PL": "Zbiór warunków (changed)", + "EN": "Condition set (changed)" + }, + "conditions": [ + { + "type": "ATTRIBUTE_EXISTS_CONDITION", + "attribute": "@condition_text_attribute@" + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then empty response is received + + Scenario: Update condition set (attribute not exists) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "ATTRIBUTE_EXISTS_CONDITION", + "attribute": "@@static_uuid@@" + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then validation error response is received + + Scenario: Update condition set (attribute not uuid) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "ATTRIBUTE_EXISTS_CONDITION", + "attribute": "abc" + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then validation error response is received + + Scenario: Update condition set (numeric attribute) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "NUMERIC_ATTRIBUTE_VALUE_CONDITION", + "attribute": "@condition_text_attribute@", + "operator": "=", + "value": 123 + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then empty response is received + + Scenario: Update condition set (numeric attribute with not uuid attribute) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "NUMERIC_ATTRIBUTE_VALUE_CONDITION", + "attribute": "abc", + "operator": "=", + "value": 123 + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then validation error response is received + + Scenario: Update condition set (numeric attribute without value) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "NUMERIC_ATTRIBUTE_VALUE_CONDITION", + "attribute": "@condition_text_attribute@", + "operator": "=" + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then validation error response is received + + Scenario: Update condition set (numeric attribute without operator) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "NUMERIC_ATTRIBUTE_VALUE_CONDITION", + "attribute": "@condition_text_attribute@", + "value": 123 + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then validation error response is received + + Scenario: Update condition set (numeric attribute invalid operator) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "NUMERIC_ATTRIBUTE_VALUE_CONDITION", + "attribute": "@condition_text_attribute@", + "operator": "123", + "value": 123 + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then validation error response is received + + Scenario: Update condition set (numeric attribute without attribute) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "NUMERIC_ATTRIBUTE_VALUE_CONDITION", + "operator": "=", + "value": 123 + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then validation error response is received + + Scenario: Update condition set (numeric attribute with not existing attribute) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "NUMERIC_ATTRIBUTE_VALUE_CONDITION", + "attribute": "@@static_uuid@@", + "operator": "=", + "value": 123 + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then validation error response is received + + Scenario: Update condition set (option attribute) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "OPTION_ATTRIBUTE_VALUE_CONDITION", + "attribute": "@condition_text_attribute@", + "value": 123 + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then empty response is received + + Scenario: Update condition set (option attribute with not uuid attribute) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "OPTION_ATTRIBUTE_VALUE_CONDITION", + "attribute": "abc", + "value": 123 + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then validation error response is received + + Scenario: Update condition set (option attribute without value) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "OPTION_ATTRIBUTE_VALUE_CONDITION", + "attribute": "@condition_text_attribute@" + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then validation error response is received + + Scenario: Update condition set (option attribute without attribute) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "NUMERIC_ATTRIBUTE_VALUE_CONDITION", + "value": 123 + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then validation error response is received + + Scenario: Update condition set (option attribute with not existing attribute) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "NUMERIC_ATTRIBUTE_VALUE_CONDITION", + "attribute": "@@static_uuid@@", + "value": 123 + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then validation error response is received + + Scenario: Update condition set (text attribute) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "TEXT_ATTRIBUTE_VALUE_CONDITION", + "attribute": "@condition_text_attribute@", + "operator": "=", + "value": 123 + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then empty response is received + + Scenario: Update condition set (text attribute with not uuid attribute) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "TEXT_ATTRIBUTE_VALUE_CONDITION", + "attribute": "abc", + "operator": "=", + "value": 123 + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then validation error response is received + + Scenario: Update condition set (text attribute without value) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "TEXT_ATTRIBUTE_VALUE_CONDITION", + "attribute": "@condition_text_attribute@", + "operator": "=" + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then validation error response is received + + Scenario: Update condition set (text attribute without operator) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "TEXT_ATTRIBUTE_VALUE_CONDITION", + "attribute": "@condition_text_attribute@", + "value": 123 + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then validation error response is received + + Scenario: Update condition set (text attribute invalid operator) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "TEXT_ATTRIBUTE_VALUE_CONDITION", + "attribute": "@condition_text_attribute@", + "operator": "123", + "value": 123 + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then validation error response is received + + Scenario: Update condition set (text attribute without attribute) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "TEXT_ATTRIBUTE_VALUE_CONDITION", + "operator": "=", + "value": 123 + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then validation error response is received + + Scenario: Update condition set (text attribute with not existing attribute) + Given current authentication token + Given the request body is: + """ + { + "conditions": [ + { + "type": "TEXT_ATTRIBUTE_VALUE_CONDITION", + "attribute": "@@static_uuid@@", + "operator": "=", + "value": 123 + } + ] + } + """ + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP PUT + Then validation error response is received + + Scenario: Get condition set (not authorized) + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP GET + Then unauthorized response is received + + Scenario: Get condition set (not found) + Given current authentication token + Given I request "/api/v1/EN/conditionsets/@@static_uuid@@" using HTTP GET + Then not found response is received + + Scenario: Get condition set + Given current authentication token + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP GET + Then the response code is 200 + + Scenario: Delete condition set (not authorized) + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP DELETE + Then unauthorized response is received + + Scenario: Delete condition set (not found) + Given current authentication token + Given I request "/api/v1/EN/conditionsets/@@static_uuid@@" using HTTP DELETE + Then not found response is received + + Scenario: Delete condition set + Given current authentication token + Given I request "/api/v1/EN/conditionsets/@conditionset@" using HTTP DELETE + Then empty response is received + + Scenario: Get condition sets + Given current authentication token + When I request "/api/v1/EN/conditionsets" using HTTP GET + Then grid response is received + + Scenario: Get condition sets (not authorized) + When I request "/api/v1/EN/conditionsets" using HTTP GET + Then unauthorized response is received + + Scenario: Get condition sets (order by code) + Given current authentication token + When I request "/api/v1/EN/conditionsets?field=code" using HTTP GET + Then grid response is received + + Scenario: Get condition sets (order by name) + Given current authentication token + When I request "/api/v1/EN/conditionsets?field=name" using HTTP GET + Then grid response is received + + Scenario: Get condition sets (order by description) + Given current authentication token + When I request "/api/v1/EN/conditionsets?field=description" using HTTP GET + Then grid response is received + + Scenario: Get condition sets (filter by code) + Given current authentication token + When I request "/api/v1/EN/conditionsets?limit=25&offset=0&filter=code%3Dsuper" using HTTP GET + Then grid response is received + + Scenario: Get condition sets (filter by name) + Given current authentication token + When I request "/api/v1/EN/conditionsets?limit=25&offset=0&filter=name%3Dsuper" using HTTP GET + Then grid response is received + + Scenario: Get condition sets (filter by description) + Given current authentication token + When I request "/api/v1/EN/conditionsets?limit=25&offset=0&filter=description%3Dsuper" using HTTP GET + Then grid response is received + + Scenario: Create condition set (for conflict delete) + Given current authentication token + Given the request body is: + """ + { + "code": "CONDITION_DELETE_@@random_uuid@@" + } + """ + Given I request "/api/v1/EN/conditionsets" using HTTP POST + Then created response is received + And remember response param "id" as "conditionset_delete" + + Scenario: Create segment (for relation to condition set conflict delete) + Given current authentication token + Given the request body is: + """ + { + "code": "SEG_REL_@@random_code@@", + "condition_set_id": "@conditionset_delete@" + } + """ + When I request "/api/v1/EN/segments" using HTTP POST + Then created response is received + + Scenario: Delete condition set (with conflict) + Given current authentication token + Given I request "/api/v1/EN/conditionsets/@conditionset_delete@" using HTTP DELETE + Then conflict response is received diff --git a/features/product.feature b/features/product.feature index 864ae617a..82ac3754c 100644 --- a/features/product.feature +++ b/features/product.feature @@ -250,11 +250,10 @@ Feature: Product module Scenario: Delete product (not found) Given current authentication token - When I request "/api/v1/EN/products/@static_uuid@" using HTTP DELETE + When I request "/api/v1/EN/products/@@static_uuid@@" using HTTP DELETE Then not found response is received Scenario: Delete product (not authorized) - Given current authentication token When I request "/api/v1/EN/products/@product@" using HTTP DELETE Then unauthorized response is received diff --git a/features/segment.feature b/features/segment.feature new file mode 100644 index 000000000..5decc307b --- /dev/null +++ b/features/segment.feature @@ -0,0 +1,252 @@ +Feature: Segment + + Scenario: Create condition set + Given current authentication token + Given the request body is: + """ + { + "code": "SEGMENT_CONDITION_@@random_uuid@@" + } + """ + Given I request "/api/v1/EN/conditionsets" using HTTP POST + Then created response is received + And remember response param "id" as "segment_conditionset" + + Scenario: Create segment (not authorized) + When I request "/api/v1/EN/segments" using HTTP POST + Then unauthorized response is received + + Scenario: Create segment + Given remember param "segment_code" with value "SEG_1_@@random_code@@" + Given current authentication token + Given the request body is: + """ + { + "code": "@segment_code@", + "condition_set_id": "@segment_conditionset@", + "name": { + "PL": "Segment", + "EN": "Segment" + }, + "description": { + "PL": "Opis segmentu", + "EN": "Segment description" + } + } + """ + When I request "/api/v1/EN/segments" using HTTP POST + Then created response is received + And remember response param "id" as "segment" + + Scenario: Create segment (not unique code) + Given current authentication token + Given the request body is: + """ + { + "code": "@segment_code@", + "condition_set_id": "@segment_conditionset@", + "description": { + "PL": "Opis segmentu", + "EN": "Segment description" + } + } + """ + When I request "/api/v1/EN/segments" using HTTP POST + Then validation error response is received + + Scenario: Create segment (without name) + Given current authentication token + Given the request body is: + """ + { + "code": "SEG_2_@@random_code@@", + "condition_set_id": "@segment_conditionset@", + "description": { + "PL": "Opis segmentu", + "EN": "Segment description" + } + } + """ + When I request "/api/v1/EN/segments" using HTTP POST + Then created response is received + + Scenario: Create segment (without description and name) + Given current authentication token + Given the request body is: + """ + { + "code": "SEG_2_@@random_code@@", + "condition_set_id": "@segment_conditionset@" + } + """ + When I request "/api/v1/EN/segments" using HTTP POST + Then created response is received + + Scenario: Create segment (without code) + Given current authentication token + Given the request body is: + """ + { + "condition_set_id": "@segment_conditionset@", + "name": { + "PL": "Segment", + "EN": "Segment" + }, + "description": { + "PL": "Opis segmentu", + "EN": "Segment description" + } + } + """ + When I request "/api/v1/EN/segments" using HTTP POST + Then validation error response is received + + Scenario: Create segment (without condition set) + Given current authentication token + Given the request body is: + """ + { + "code": "SEG_2_@@random_code@@" + } + """ + When I request "/api/v1/EN/segments" using HTTP POST + Then validation error response is received + + Scenario: Update segment (not authorized) + When I request "/api/v1/EN/segments/@segment@" using HTTP PUT + Then unauthorized response is received + + Scenario: Update segment (not found) + Given current authentication token + When I request "/api/v1/EN/segments/@@static_uuid@@" using HTTP PUT + Then not found response is received + + Scenario: Update segment + Given current authentication token + Given the request body is: + """ + { + "condition_set_id": "@segment_conditionset@", + "name": { + "PL": "Segment (changed)", + "EN": "Segment (changed)" + }, + "description": { + "PL": "Opis segmentu (changed)", + "EN": "Segment description (changed)" + } + } + """ + When I request "/api/v1/EN/segments/@segment@" using HTTP PUT + Then empty response is received + + Scenario: Update segment (without name) + Given current authentication token + Given the request body is: + """ + { + "condition_set_id": "@segment_conditionset@", + "description": { + "PL": "Opis segmentu (changed)", + "EN": "Segment description (changed)" + } + } + """ + When I request "/api/v1/EN/segments/@segment@" using HTTP PUT + Then empty response is received + + Scenario: Update segment (without name and description) + Given current authentication token + Given the request body is: + """ + { + "condition_set_id": "@segment_conditionset@" + } + """ + When I request "/api/v1/EN/segments/@segment@" using HTTP PUT + Then empty response is received + + Scenario: Update segment (without condition set) + Given current authentication token + Given the request body is: + """ + { + "name": { + "PL": "Segment (changed)", + "EN": "Segment (changed)" + }, + "description": { + "PL": "Opis segmentu (changed)", + "EN": "Segment description (changed)" + } + } + """ + When I request "/api/v1/EN/segments/@segment@" using HTTP PUT + Then validation error response is received + + Scenario: Get segment (not authorized) + When I request "/api/v1/EN/segments/@segment@" using HTTP GET + Then unauthorized response is received + + Scenario: Get segment (not found) + Given current authentication token + When I request "/api/v1/EN/segments/@@static_uuid@@" using HTTP GET + Then not found response is received + + Scenario: Get segment + Given current authentication token + When I request "/api/v1/EN/segments/@segment@" using HTTP GET + Then the response code is 200 + + Scenario: Get segments + Given current authentication token + When I request "/api/v1/EN/segments" using HTTP GET + Then grid response is received + + Scenario: Get segments (not authorized) + When I request "/api/v1/EN/segments" using HTTP GET + Then unauthorized response is received + + Scenario: Get segments (order by code) + Given current authentication token + When I request "/api/v1/EN/segments?field=code" using HTTP GET + Then grid response is received + + Scenario: Get segments (order by name) + Given current authentication token + When I request "/api/v1/EN/segments?field=name" using HTTP GET + Then grid response is received + + Scenario: Get segments (order by description) + Given current authentication token + When I request "/api/v1/EN/segments?field=description" using HTTP GET + Then grid response is received + + Scenario: Get segments (filter by code) + Given current authentication token + When I request "/api/v1/EN/segments?limit=25&offset=0&filter=code%3Dsuper" using HTTP GET + Then grid response is received + + Scenario: Get segments (filter by name) + Given current authentication token + When I request "/api/v1/EN/segments?limit=25&offset=0&filter=name%3Dsuper" using HTTP GET + Then grid response is received + + Scenario: Get segments (filter by description) + Given current authentication token + When I request "/api/v1/EN/segments?limit=25&offset=0&filter=description%3Dsuper" using HTTP GET + Then grid response is received + + Scenario: Delete segment (not authorized) + When I request "/api/v1/EN/segments/@segment@" using HTTP DELETE + Then unauthorized response is received + + Scenario: Delete segment (not found) + Given current authentication token + When I request "/api/v1/EN/segments/@@static_uuid@@" using HTTP DELETE + Then not found response is received + + Scenario: Delete segment + Given current authentication token + When I request "/api/v1/EN/segments/@segment@" using HTTP DELETE + Then empty response is received diff --git a/features/workflow.feature b/features/workflow.feature index 254a15732..ccb9e90b7 100644 --- a/features/workflow.feature +++ b/features/workflow.feature @@ -54,7 +54,6 @@ Feature: Workflow When I request "/api/v1/EN/status/@@static_uuid@@" using HTTP PUT Then not found response is received - Scenario: Get default status Given current authentication token When I request "/api/v1/EN/status/@workflow_status@" using HTTP GET @@ -95,20 +94,6 @@ Feature: Workflow When I request "/api/v1/EN/workflow/default" using HTTP PUT Then validation error response is received - Scenario: Delete default status - Given current authentication token - When I request "/api/v1/EN/status/@workflow_status@" using HTTP DELETE - Then empty response is received - - Scenario: Delete default status (not authorized) - When I request "/api/v1/EN/status/@workflow_status@" using HTTP DELETE - Then unauthorized response is received - - Scenario: Delete default status (not found) - Given current authentication token - When I request "/api/v1/EN/status/@@static_uuid@@" using HTTP DELETE - Then not found response is received - Scenario: Get default statuses Given current authentication token When I request "/api/v1/EN/status" using HTTP GET @@ -175,3 +160,17 @@ Feature: Workflow Given current authentication token When I request "/api/v1/EN/workflow/@workflow@" using HTTP DELETE Then empty response is received + + Scenario: Delete default status + Given current authentication token + When I request "/api/v1/EN/status/@workflow_status@" using HTTP DELETE + Then empty response is received + + Scenario: Delete default status (not authorized) + When I request "/api/v1/EN/status/@workflow_status@" using HTTP DELETE + Then unauthorized response is received + + Scenario: Delete default status (not found) + Given current authentication token + When I request "/api/v1/EN/status/@@static_uuid@@" using HTTP DELETE + Then not found response is received diff --git a/module/account/README.md b/module/account/README.md index a3da860f2..8af4522fe 100644 --- a/module/account/README.md +++ b/module/account/README.md @@ -1,4 +1,4 @@ -# Ergonode -Aaccount +# Ergonode - Account ## Documentation diff --git a/module/api/src/Infrastructure/JMS/Serializer/Handler/ViolationsExceptionHandler.php b/module/api/src/Infrastructure/JMS/Serializer/Handler/ViolationsExceptionHandler.php index 4715f180a..12716b89a 100644 --- a/module/api/src/Infrastructure/JMS/Serializer/Handler/ViolationsExceptionHandler.php +++ b/module/api/src/Infrastructure/JMS/Serializer/Handler/ViolationsExceptionHandler.php @@ -86,13 +86,23 @@ private function mapViolations(ConstraintViolationListInterface $violations): ar $errors = []; /** @var ConstraintViolationInterface $violation */ foreach ($violations as $violation) { - $field = substr($violation->getPropertyPath(), 1, -1); + $field = ltrim(str_replace(['[', ']'], ['.', ''], $violation->getPropertyPath()), '.'); + $path = explode('.', $field); - if (!array_key_exists($field, $errors)) { - $errors[$field] = []; + $pointer = &$errors; + foreach ($path as $key) { + if (ctype_digit($key)) { + $key = 'element-'.$key; + } + + if (!array_key_exists($key, $pointer)) { + $pointer[$key] = []; + } + + $pointer = &$pointer[$key]; } - $errors[$field][] = $violation->getMessage(); + $pointer[] = $violation->getMessage(); } return $errors; diff --git a/module/attribute/src/Application/Form/Model/AttributeOptionModel.php b/module/attribute/src/Application/Form/Model/AttributeOptionModel.php index c81e0d040..35b2ec36a 100644 --- a/module/attribute/src/Application/Form/Model/AttributeOptionModel.php +++ b/module/attribute/src/Application/Form/Model/AttributeOptionModel.php @@ -19,7 +19,7 @@ class AttributeOptionModel * @var string * * @Assert\NotBlank(message="Option code is required") - * @Assert\Length(max=128, maxMessage="Option code is to long,. It should have {{ limit }} character or less.") + * @Assert\Length(max=128, maxMessage="Option code is to long. It should have {{ limit }} character or less.") */ public $key; diff --git a/module/attribute/src/Application/Form/Model/AttributeParametersModel.php b/module/attribute/src/Application/Form/Model/AttributeParametersModel.php index 7b67345cc..f36a36917 100644 --- a/module/attribute/src/Application/Form/Model/AttributeParametersModel.php +++ b/module/attribute/src/Application/Form/Model/AttributeParametersModel.php @@ -10,7 +10,6 @@ namespace Ergonode\Attribute\Application\Form\Model; /** - * Class AttributeParametersModel */ class AttributeParametersModel { diff --git a/module/attribute/src/Application/Form/Model/CreateAttributeFormModel.php b/module/attribute/src/Application/Form/Model/CreateAttributeFormModel.php index edbf43dd1..292e43913 100644 --- a/module/attribute/src/Application/Form/Model/CreateAttributeFormModel.php +++ b/module/attribute/src/Application/Form/Model/CreateAttributeFormModel.php @@ -55,7 +55,7 @@ class CreateAttributeFormModel * * @Assert\All({ * @Assert\NotBlank(), - * @Assert\Length(max=4000, maxMessage="Attribute placeholder is to long, It should have {{ limit }} character or less.") + * @Assert\Length(max=4000, maxMessage="Attribute placeholder is to long. It should have {{ limit }} character or less.") * }) */ public $placeholder; @@ -65,7 +65,7 @@ class CreateAttributeFormModel * * @Assert\All({ * @Assert\NotBlank(), - * @Assert\Length(max=4000, maxMessage="Attribute hint is to long,. It should have {{ limit }} character or less.") + * @Assert\Length(max=4000, maxMessage="Attribute hint is to long. It should have {{ limit }} character or less.") * }) */ public $hint; diff --git a/module/attribute/src/Application/Form/Model/UpdateAttributeFormModel.php b/module/attribute/src/Application/Form/Model/UpdateAttributeFormModel.php index 14389e87d..da4d551b3 100644 --- a/module/attribute/src/Application/Form/Model/UpdateAttributeFormModel.php +++ b/module/attribute/src/Application/Form/Model/UpdateAttributeFormModel.php @@ -32,7 +32,7 @@ class UpdateAttributeFormModel * * @Assert\All({ * @Assert\NotBlank(), - * @Assert\Length(max=32, maxMessage="Attribute name is to long, It should have {{ limit }} character or less.") + * @Assert\Length(max=32, maxMessage="Attribute name is to long. It should have {{ limit }} character or less.") * }) */ public $label; @@ -42,7 +42,7 @@ class UpdateAttributeFormModel * * @Assert\All({ * @Assert\NotBlank(), - * @Assert\Length(max=4000, maxMessage="Attribute placeholder is to long, It should have {{ limit }} character or less.") + * @Assert\Length(max=4000, maxMessage="Attribute placeholder is to long. It should have {{ limit }} character or less.") * }) */ public $placeholder; @@ -52,7 +52,7 @@ class UpdateAttributeFormModel * * @Assert\All({ * @Assert\NotBlank(), - * @Assert\Length(max=4000, maxMessage="Attribute hint is to long,. It should have {{ limit }} character or less.") + * @Assert\Length(max=4000, maxMessage="Attribute hint is to long. It should have {{ limit }} character or less.") * }) */ public $hint; diff --git a/module/attribute/src/Domain/Query/AttributeQueryInterface.php b/module/attribute/src/Domain/Query/AttributeQueryInterface.php index 9e868046d..4f7f84986 100644 --- a/module/attribute/src/Domain/Query/AttributeQueryInterface.php +++ b/module/attribute/src/Domain/Query/AttributeQueryInterface.php @@ -51,6 +51,20 @@ public function getAttribute(AttributeId $attributeId): ?array; */ public function getAllAttributeCodes(): array; + /** + * @param array $types + * + * @return string[] + */ + public function getDictionary(array $types = []): array; + + /** + * @param array $types + * + * @return array + */ + public function getAttributeCodes(array $types = []): array; + /** * @param AttributeId $attributeId * diff --git a/module/attribute/src/Infrastructure/Validator/AttributeExistsValidator.php b/module/attribute/src/Infrastructure/Validator/AttributeExistsValidator.php index b870685f5..30a1fbddc 100644 --- a/module/attribute/src/Infrastructure/Validator/AttributeExistsValidator.php +++ b/module/attribute/src/Infrastructure/Validator/AttributeExistsValidator.php @@ -54,7 +54,10 @@ public function validate($value, Constraint $constraint): void $value = (string) $value; - $attribute = $this->attributeRepository->load(new AttributeId($value)); + $attribute = false; + if (AttributeId::isValid($value)) { + $attribute = $this->attributeRepository->load(new AttributeId($value)); + } if (!$attribute) { $this->context->buildViolation($constraint->message) diff --git a/module/attribute/src/Persistence/Dbal/Query/DbalAttributeGroupQuery.php b/module/attribute/src/Persistence/Dbal/Query/DbalAttributeGroupQuery.php index eab644269..e994815e3 100644 --- a/module/attribute/src/Persistence/Dbal/Query/DbalAttributeGroupQuery.php +++ b/module/attribute/src/Persistence/Dbal/Query/DbalAttributeGroupQuery.php @@ -54,7 +54,8 @@ public function getAttributeGroups(): array public function getDataSet(Language $language): DataSetInterface { $query = $this->connection->createQueryBuilder(); - $query->select('*') + $query + ->select('*') ->from(sprintf('(%s)', $this->getSQL()), 't'); return new DbalDataSet($query); diff --git a/module/attribute/src/Persistence/Dbal/Query/DbalAttributeQuery.php b/module/attribute/src/Persistence/Dbal/Query/DbalAttributeQuery.php index ef295b2ca..de714421b 100644 --- a/module/attribute/src/Persistence/Dbal/Query/DbalAttributeQuery.php +++ b/module/attribute/src/Persistence/Dbal/Query/DbalAttributeQuery.php @@ -198,12 +198,49 @@ public function checkAttributeExistsByCode(AttributeCode $code): bool */ public function getAllAttributeCodes(): array { - return $this->getQuery() - ->select('code') + return $this->getAttributeCodes(); + } + + /** + * @param array $types + * + * @return string[] + */ + public function getAttributeCodes(array $types = []): array + { + $qb = $this->getQuery() + ->select('code'); + + if ($types) { + $qb->andWhere($qb->expr()->in('type', ':types')) + ->setParameter(':types', $types, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY); + } + + return $qb ->execute() ->fetchAll(\PDO::FETCH_COLUMN); } + /** + * @param array $types + * + * @return string[] + */ + public function getDictionary(array $types = []): array + { + $qb = $this->getQuery() + ->select('id, code'); + + if ($types) { + $qb->andWhere($qb->expr()->in('type', ':types')) + ->setParameter(':types', $types, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY); + } + + return $qb + ->execute() + ->fetchAll(\PDO::FETCH_KEY_PAIR); + } + /** * @param AttributeId $attributeId * diff --git a/module/attribute/src/Persistence/Dbal/Query/Decorator/CacheAttributeQueryDecorator.php b/module/attribute/src/Persistence/Dbal/Query/Decorator/CacheAttributeQueryDecorator.php index f8fbe4e63..cdfaf6d66 100644 --- a/module/attribute/src/Persistence/Dbal/Query/Decorator/CacheAttributeQueryDecorator.php +++ b/module/attribute/src/Persistence/Dbal/Query/Decorator/CacheAttributeQueryDecorator.php @@ -113,6 +113,16 @@ public function getAllAttributeCodes(): array return $this->attributeQuery->getAllAttributeCodes(); } + /** + * @param array $types + * + * @return array + */ + public function getAttributeCodes(array $types = []): array + { + return $this->attributeQuery->getAttributeCodes($types); + } + /** * @param AttributeId $id * @param OptionKey $key @@ -123,4 +133,14 @@ public function findAttributeOption(AttributeId $id, OptionKey $key): ?OptionInt { return $this->attributeQuery->findAttributeOption($id, $key); } + + /** + * @param array $types + * + * @return array + */ + public function getDictionary(array $types = []): array + { + return $this->attributeQuery->getDictionary($types); + } } diff --git a/module/condition/LICENSE.txt b/module/condition/LICENSE.txt new file mode 100644 index 000000000..996dcc641 --- /dev/null +++ b/module/condition/LICENSE.txt @@ -0,0 +1,26 @@ +Open Software License ("OSL") v. 3.0 + +This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: +Licensed under the Open Software License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + a) to reproduce the Original Work in copies, either alone or as part of a collective work; + b) to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + c) to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; + d) to perform the Original Work publicly; and + e) to display the Original Work publicly. + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/module/condition/README.md b/module/condition/README.md new file mode 100644 index 000000000..8e93bc73a --- /dev/null +++ b/module/condition/README.md @@ -0,0 +1,19 @@ +# Ergonode - Condition + +## Documentation + +* Follow link to [**Ergonode Documentation**](https://docs.ergonode.com), + +## Community + +* Get Ergonode support on **Stack Overflow**, [**Slack**](https://ergonode.slack.com) and [**email**](team@ergonode.com). +* Follow us on [**GitHub**](https://github.com/ergonode), [**Twitter**](https://twitter.com/ergonode) and [**Facebook**](https://www.facebook.com/ergonode), + +## Contributing + +Ergonode is a Open Source. Join us as a [**contributor**](https://ergonode.com/contribution). + +## About Us + +Ergonode development is sponsored by Bold Brand Commerce Sp. z o.o., lead by **Eronode Core Team** and supported by Ergonode contributors. + diff --git a/module/condition/composer.json b/module/condition/composer.json new file mode 100644 index 000000000..eeca37d23 --- /dev/null +++ b/module/condition/composer.json @@ -0,0 +1,31 @@ +{ + "name": "ergonode/condition", + "type": "ergonode-module", + "description": "Ergonode - Condition", + "homepage": "https://ergonode.com", + "license": "OSL-3.0", + "require": { + "php": "^7.2", + "doctrine/dbal": "^2.9", + "ergonode/attribute": "^0.3.0", + "ergonode/product": "^0.3.0", + "ergonode/core": "^0.3.0", + "ergonode/es": "^0.3.0", + "ergonode/grid": "^0.3.0", + "ergonode/value": "^0.3.0", + "friendsofsymfony/rest-bundle": "^2.5", + "jms/serializer": "^3.1", + "nelmio/api-doc-bundle": "^3.4", + "ramsey/uuid": "^3.8", + "sensio/framework-extra-bundle": "^5.4", + "symfony/form": "^4.3", + "symfony/messenger": "^4.3", + "symfony/translation": "^4.3", + "symfony/validator": "^4.3" + }, + "autoload": { + "psr-4": { + "Ergonode\\Condition\\": "src/" + } + } +} diff --git a/module/condition/migrations/Version20190910151314.php b/module/condition/migrations/Version20190910151314.php new file mode 100644 index 000000000..925974eab --- /dev/null +++ b/module/condition/migrations/Version20190910151314.php @@ -0,0 +1,67 @@ +addSql(' + CREATE TABLE condition_set ( + id UUID NOT NULL, + code VARCHAR(100) NOT NULL, + name JSON NOT NULL, + description JSON NOT NULL, + conditions JSONB NOT NULL, + PRIMARY KEY(id) + ) + '); + $this->addSql('CREATE UNIQUE index condition_set_code_uindex ON condition_set (code)'); + + $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'CONDITION_CREATE', 'Condition']); + $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'CONDITION_READ', 'Condition']); + $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'CONDITION_UPDATE', 'Condition']); + $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'CONDITION_DELETE', 'Condition']); + + $this->createEventStoreEvents([ + 'Ergonode\Condition\Domain\Event\ConditionSetCreatedEvent' => 'Condition set created', + 'Ergonode\Condition\Domain\Event\ConditionSetDeletedEvent' => 'Condition set deleted', + 'Ergonode\Condition\Domain\Event\ConditionSetDescriptionChangedEvent' => 'Condition set description changed', + 'Ergonode\Condition\Domain\Event\ConditionSetNameChangedEvent' => 'Condition set name changed', + 'Ergonode\Condition\Domain\Event\ConditionSetConditionsChangedEvent' => 'Condition set conditions changed', + ]); + } + + /** + * @param array $collection + * + * @throws \Doctrine\DBAL\DBALException + */ + private function createEventStoreEvents(array $collection): void + { + foreach ($collection as $class => $translation) { + $this->connection->insert('event_store_event', [ + 'id' => Uuid::uuid4()->toString(), + 'event_class' => $class, + 'translation_key' => $translation, + ]); + } + } +} diff --git a/module/condition/src/Application/Controller/Api/ConditionController.php b/module/condition/src/Application/Controller/Api/ConditionController.php new file mode 100644 index 000000000..0b1df8a49 --- /dev/null +++ b/module/condition/src/Application/Controller/Api/ConditionController.php @@ -0,0 +1,83 @@ +provider = $provider; + } + + /** + * @Route("/conditions/{condition}", methods={"GET"}) + * + * @IsGranted("CONDITION_READ") + * + * @SWG\Tag(name="Condition") + * @SWG\Parameter( + * name="language", + * in="path", + * type="string", + * description="Language code", + * default="EN" + * ) + * @SWG\Parameter( + * name="condition", + * in="path", + * type="string", + * description="Condition ID" + * ) + * @SWG\Response( + * response=200, + * description="Returns condition" + * ) + * @SWG\Response( + * response=404, + * description="Not found" + * ) + * + * @param Language $language + * @param string $condition + * + * @return Response + */ + public function getCondition(Language $language, string $condition): Response + { + try { + $configuration = $this->provider->getConfiguration($language, $condition); + } catch (ConditionStrategyNotFoundException $exception) { + throw new NotFoundHttpException($exception->getMessage()); + } + + return new SuccessResponse($configuration); + } +} diff --git a/module/condition/src/Application/Controller/Api/ConditionSetController.php b/module/condition/src/Application/Controller/Api/ConditionSetController.php new file mode 100644 index 000000000..88101edd3 --- /dev/null +++ b/module/condition/src/Application/Controller/Api/ConditionSetController.php @@ -0,0 +1,402 @@ +validator = $validator; + $this->messageBus = $messageBus; + $this->serializer = $serializer; + $this->conditionSetGrid = $conditionSetGrid; + $this->conditionSetQuery = $conditionSetQuery; + $this->createConditionSetValidatorBuilder = $createConditionSetValidatorBuilder; + $this->updateConditionSetValidatorBuilder = $updateConditionSetValidatorBuilder; + $this->segmentQuery = $segmentQuery; + } + + /** + * @Route("/conditionsets", methods={"GET"}) + * + * @IsGranted("CONDITION_READ") + * + * @SWG\Tag(name="Condition") + * @SWG\Parameter( + * name="limit", + * in="query", + * type="integer", + * required=true, + * default="50", + * description="Number of returned lines", + * ) + * @SWG\Parameter( + * name="offset", + * in="query", + * type="integer", + * required=true, + * default="0", + * description="Number of start line", + * ) + * @SWG\Parameter( + * name="field", + * in="query", + * required=false, + * type="string", + * enum={"id", "code", "name", "description"}, + * description="Order field", + * ) + * @SWG\Parameter( + * name="order", + * in="query", + * required=false, + * type="string", + * enum={"ASC", "DESC"}, + * description="Order", + * ) + * @SWG\Parameter( + * name="filter", + * in="query", + * required=false, + * type="string", + * description="Filter" + * ) + * @SWG\Parameter( + * name="show", + * in="query", + * required=false, + * type="string", + * enum={"COLUMN", "DATA"}, + * description="Specify what response should containts" + * ) + * @SWG\Parameter( + * name="language", + * in="path", + * type="string", + * required=true, + * default="EN", + * description="Language Code", + * ) + * @SWG\Response( + * response=200, + * description="Returns condition set collection", + * ) + * + * @ParamConverter(class="Ergonode\Grid\RequestGridConfiguration") + * + * @param Language $language + * @param RequestGridConfiguration $configuration + * + * @return Response + */ + public function getConditionSets(Language $language, RequestGridConfiguration $configuration): Response + { + return new GridResponse( + $this->conditionSetGrid, + $configuration, + $this->conditionSetQuery->getDataSet($language), + $language + ); + } + + /** + * @Route("/conditionsets/{conditionSet}", methods={"GET"}, requirements={"conditionSet"="[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"}) + * + * @IsGranted("CONDITION_READ") + * + * @SWG\Tag(name="Condition") + * @SWG\Parameter( + * name="language", + * in="path", + * type="string", + * description="Language code", + * default="EN" + * ) + * @SWG\Parameter( + * name="conditionSet", + * in="path", + * type="string", + * description="Conditionset ID" + * ) + * @SWG\Response( + * response=200, + * description="Returns conditionset" + * ) + * @SWG\Response( + * response=404, + * description="Not found" + * ) + * + * @ParamConverter(class="Ergonode\Condition\Domain\Entity\ConditionSet") + * + * @param ConditionSet $conditionSet + * + * @return Response + */ + public function getConditionSet(ConditionSet $conditionSet): Response + { + return new SuccessResponse($conditionSet); + } + + /** + * @Route("/conditionsets", methods={"POST"}) + * + * @IsGranted("CONDITION_CREATE") + * + * @SWG\Tag(name="Condition") + * @SWG\Parameter( + * name="language", + * in="path", + * type="string", + * description="Language code", + * default="EN" + * ) + * @SWG\Parameter( + * name="body", + * in="body", + * description="Create condition set", + * required=true, + * @SWG\Schema(ref="#/definitions/conditionset_create") + * ) + * @SWG\Response( + * response=201, + * description="Returns condition ID" + * ) + * @SWG\Response( + * response=400, + * description="Validation error", + * @SWG\Schema(ref="#/definitions/validation_error_response") + * ) + * + * @param Request $request + * + * @return Response + */ + public function createConditionSet(Request $request): Response + { + $data = $request->request->all(); + + $violations = $this->validator->validate($data, $this->createConditionSetValidatorBuilder->build($data)); + if (0 === $violations->count()) { + $data['id'] = ConditionSetId::fromCode(new ConditionSetCode($data['code']))->getValue(); + $data['name'] = $data['name'] ?? []; + $data['description'] = $data['description'] ?? []; + + /** @var CreateConditionSetCommand $command */ + $command = $this->serializer->fromArray($data, CreateConditionSetCommand::class); + $this->messageBus->dispatch($command); + + return new CreatedResponse($command->getId()); + } + + throw new ViolationsHttpException($violations); + } + + /** + * @Route("/conditionsets/{conditionSet}", methods={"PUT"}, requirements={"conditionSet"="[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"}) + * + * @IsGranted("CONDITION_UPDATE") + * + * @SWG\Tag(name="Condition") + * @SWG\Parameter( + * name="language", + * in="path", + * type="string", + * description="Language code", + * default="EN" + * ) + * @SWG\Parameter( + * name="body", + * in="body", + * description="Update condition set", + * required=true, + * @SWG\Schema(ref="#/definitions/conditionset_update") + * ) + * @SWG\Response( + * response=204, + * description="Success" + * ) + * @SWG\Response( + * response=400, + * description="Validation error", + * @SWG\Schema(ref="#/definitions/validation_error_response") + * ) + * @SWG\Response( + * response=404, + * description="Not found" + * ) + * + * @ParamConverter(class="Ergonode\Condition\Domain\Entity\ConditionSet") + * + * @param ConditionSet $conditionSet + * @param Request $request + * + * @return Response + */ + public function updateConditionSet(ConditionSet $conditionSet, Request $request): Response + { + $data = $request->request->all(); + + $violations = $this->validator->validate($data, $this->updateConditionSetValidatorBuilder->build($data)); + if (0 === $violations->count()) { + $data['id'] = $conditionSet->getId()->getValue(); + + /** @var UpdateConditionSetCommand $command */ + $command = $this->serializer->fromArray($data, UpdateConditionSetCommand::class); + $this->messageBus->dispatch($command); + + return new EmptyResponse(); + } + + throw new ViolationsHttpException($violations); + } + + /** + * @Route("/conditionsets/{conditionSet}", methods={"DELETE"}, requirements={"conditionSet"="[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"}) + * + * @IsGranted("CONDITION_DELETE") + * + * @SWG\Tag(name="Condition") + * @SWG\Parameter( + * name="language", + * in="path", + * type="string", + * description="Language code", + * default="EN" + * ) + * @SWG\Parameter( + * name="conditionSet", + * in="path", + * required=true, + * type="string", + * description="Condition set ID", + * ) + * @SWG\Response( + * response=204, + * description="Success" + * ) + * @SWG\Response( + * response=404, + * description="Not found" + * ) + * @SWG\Response( + * response=409, + * description="Existing relations" + * ) + * + * @ParamConverter(class="Ergonode\Condition\Domain\Entity\ConditionSet") + * + * @param ConditionSet $conditionSet + * + * @return Response + */ + public function deleteConditionSet(ConditionSet $conditionSet): Response + { + $segments = $this->segmentQuery->findIdByConditionSetId($conditionSet->getId()); + if (0 !== count($segments)) { + throw new ConflictHttpException('Cannot delete condition set. Segments are assigned to it'); + } + + $command = new DeleteConditionSetCommand($conditionSet->getId()); + $this->messageBus->dispatch($command); + + return new EmptyResponse(); + } +} diff --git a/module/condition/src/Application/Controller/Api/DictionaryController.php b/module/condition/src/Application/Controller/Api/DictionaryController.php new file mode 100644 index 000000000..31b076e07 --- /dev/null +++ b/module/condition/src/Application/Controller/Api/DictionaryController.php @@ -0,0 +1,68 @@ +provider = $provider; + } + + /** + * @Route("conditions", methods={"GET"}) + * + * @SWG\Tag(name="Dictionary") + * @SWG\Parameter( + * name="language", + * in="path", + * type="string", + * required=true, + * default="EN", + * description="Language Code" + * ) + * @SWG\Response( + * response=200, + * description="Returns dictionary of available conditions" + * ) + * @SWG\Response( + * response=404, + * description="Not found" + * ) + * + * @param Language $language + * + * @return Response + */ + public function getDictionary(Language $language): Response + { + $dictionary = $this->provider->getDictionary($language); + + return new SuccessResponse($dictionary); + } +} diff --git a/module/condition/src/Application/DependencyInjection/CompilerPass/ConditionConfiguratorCompilerPass.php b/module/condition/src/Application/DependencyInjection/CompilerPass/ConditionConfiguratorCompilerPass.php new file mode 100644 index 000000000..36bcb17a4 --- /dev/null +++ b/module/condition/src/Application/DependencyInjection/CompilerPass/ConditionConfiguratorCompilerPass.php @@ -0,0 +1,49 @@ +has(ConditionConfigurationProvider::class)) { + $this->processTransformers($container); + } + } + + /** + * @param ContainerBuilder $container + */ + private function processTransformers(ContainerBuilder $container): void + { + $arguments = []; + $definition = $container->findDefinition(ConditionConfigurationProvider::class); + $strategies = $container->findTaggedServiceIds(self::TAG); + + foreach ($strategies as $id => $strategy) { + $arguments[] = new Reference($id); + } + + $definition->setArguments($arguments); + } +} diff --git a/module/condition/src/Application/DependencyInjection/ErgonodeConditionExtension.php b/module/condition/src/Application/DependencyInjection/ErgonodeConditionExtension.php new file mode 100644 index 000000000..ffef94940 --- /dev/null +++ b/module/condition/src/Application/DependencyInjection/ErgonodeConditionExtension.php @@ -0,0 +1,42 @@ +registerForAutoconfiguration(ConditionConfigurationStrategyInteface::class) + ->addTag(ConditionConfiguratorCompilerPass::TAG); + + $loader->load('services.yml'); + } +} diff --git a/module/condition/src/Application/Request/ParamConverter/ConditionSetParamConverter.php b/module/condition/src/Application/Request/ParamConverter/ConditionSetParamConverter.php new file mode 100644 index 000000000..1a1b54430 --- /dev/null +++ b/module/condition/src/Application/Request/ParamConverter/ConditionSetParamConverter.php @@ -0,0 +1,69 @@ +conditionSetRepository = $conditionSetRepository; + } + + /** + * {@inheritDoc} + */ + public function apply(Request $request, ParamConverter $configuration): void + { + $parameter = $request->get('conditionSet'); + + if (null === $parameter) { + throw new BadRequestHttpException('Route parameter "conditionSet" is missing'); + } + + if (!ConditionSetId::isValid($parameter)) { + throw new BadRequestHttpException('Invalid condition set ID format'); + } + + $entity = $this->conditionSetRepository->load(new ConditionSetId($parameter)); + + if (null === $entity) { + throw new NotFoundHttpException(sprintf('Condition set by ID "%s" not found', $parameter)); + } + + $request->attributes->set($configuration->getName(), $entity); + } + + /** + * {@inheritDoc} + */ + public function supports(ParamConverter $configuration): bool + { + return ConditionSet::class === $configuration->getClass(); + } +} diff --git a/module/condition/src/Application/Validator/ConstraintAttributeExistsCondition.php b/module/condition/src/Application/Validator/ConstraintAttributeExistsCondition.php new file mode 100644 index 000000000..ddb0869f2 --- /dev/null +++ b/module/condition/src/Application/Validator/ConstraintAttributeExistsCondition.php @@ -0,0 +1,25 @@ +attributeQuery = $attributeQuery; + } + + /** + * {@inheritDoc} + */ + public function validate($value, Constraint $constraint): void + { + if (!$constraint instanceof ConstraintAttributeExistsCondition) { + throw new UnexpectedTypeException($constraint, ConstraintAttributeExistsCondition::class); + } + + if (!is_array($value)) { + throw new UnexpectedValueException($value, 'array'); + } + + if (!array_key_exists('code', $value)) { + $this->context + ->buildViolation('Attribute code not set') + ->addViolation(); + } + + $code = new AttributeCode($value['code']); + if (null === $this->attributeQuery->findAttributeByCode($code)) { + $this->context + ->buildViolation('Attribute code "value" not found') + ->setParameter('value', $value['code']) + ->atPath('code') + ->addViolation(); + } + } +} diff --git a/module/condition/src/Domain/Command/CreateConditionSetCommand.php b/module/condition/src/Domain/Command/CreateConditionSetCommand.php new file mode 100644 index 000000000..e5e5ba4ca --- /dev/null +++ b/module/condition/src/Domain/Command/CreateConditionSetCommand.php @@ -0,0 +1,93 @@ +id = ConditionSetId::fromCode($code); + $this->code = $code; + $this->name = $name; + $this->description = $description; + } + + /** + * @return ConditionSetId + */ + public function getId(): ConditionSetId + { + return $this->id; + } + + /** + * @return ConditionSetCode + */ + public function getCode(): ConditionSetCode + { + return $this->code; + } + + /** + * @return TranslatableString + */ + public function getName(): TranslatableString + { + return $this->name; + } + + /** + * @return TranslatableString + */ + public function getDescription(): TranslatableString + { + return $this->description; + } +} diff --git a/module/condition/src/Domain/Command/DeleteConditionSetCommand.php b/module/condition/src/Domain/Command/DeleteConditionSetCommand.php new file mode 100644 index 000000000..3db9ef693 --- /dev/null +++ b/module/condition/src/Domain/Command/DeleteConditionSetCommand.php @@ -0,0 +1,41 @@ +id = $id; + } + + /** + * @return ConditionSetId + */ + public function getId(): ConditionSetId + { + return $this->id; + } +} diff --git a/module/condition/src/Domain/Command/UpdateConditionSetCommand.php b/module/condition/src/Domain/Command/UpdateConditionSetCommand.php new file mode 100644 index 000000000..09c1f3e6a --- /dev/null +++ b/module/condition/src/Domain/Command/UpdateConditionSetCommand.php @@ -0,0 +1,117 @@ +") + */ + private $conditions; + + /** + * @param ConditionSetId $id + * @param array $conditions + * @param TranslatableString|null $name + * @param TranslatableString|null $description + */ + public function __construct( + ConditionSetId $id, + array $conditions, + ?TranslatableString $name = null, + ?TranslatableString $description = null + ) { + Assert::allIsInstanceOf($conditions, ConditionInterface::class); + + $this->id = $id; + $this->name = $name; + $this->description = $description; + $this->conditions = $conditions; + } + + /** + * @return ConditionSetId + */ + public function getId(): ConditionSetId + { + return $this->id; + } + + /** + * @return bool + */ + public function hasName(): bool + { + return $this->name instanceof TranslatableString; + } + + /** + * @return TranslatableString|null + */ + public function getName(): ?TranslatableString + { + return $this->name; + } + + /** + * @return bool + */ + public function hasDescription(): bool + { + return $this->description instanceof TranslatableString; + } + + /** + * @return TranslatableString|null + */ + public function getDescription(): ?TranslatableString + { + return $this->description; + } + + /** + * @return ConditionInterface[] + */ + public function getConditions(): array + { + return $this->conditions; + } +} diff --git a/module/condition/src/Domain/Condition/AttributeExistsCondition.php b/module/condition/src/Domain/Condition/AttributeExistsCondition.php new file mode 100644 index 000000000..a2cebc3cc --- /dev/null +++ b/module/condition/src/Domain/Condition/AttributeExistsCondition.php @@ -0,0 +1,54 @@ +attribute = $attribute; + } + + /** + * {@inheritDoc} + * + * @JMS\VirtualProperty() + */ + public function getType(): string + { + return self::TYPE; + } + + /** + * @return AttributeId + */ + public function getAttribute(): AttributeId + { + return $this->attribute; + } +} diff --git a/module/condition/src/Domain/Condition/ConditionInterface.php b/module/condition/src/Domain/Condition/ConditionInterface.php new file mode 100644 index 000000000..172f005ba --- /dev/null +++ b/module/condition/src/Domain/Condition/ConditionInterface.php @@ -0,0 +1,20 @@ +attribute = $attribute; + $this->value = $value; + } + + /** + * {@inheritDoc} + * + * @JMS\VirtualProperty() + */ + public function getType(): string + { + return self::TYPE; + } + + /** + * @return AttributeId + */ + public function getAttribute(): AttributeId + { + return $this->attribute; + } + + /** + * @return string + */ + public function getValue(): string + { + return $this->value; + } +} diff --git a/module/condition/src/Domain/Condition/OptionAttributeValueCondition.php b/module/condition/src/Domain/Condition/OptionAttributeValueCondition.php new file mode 100644 index 000000000..a87cc9b9b --- /dev/null +++ b/module/condition/src/Domain/Condition/OptionAttributeValueCondition.php @@ -0,0 +1,71 @@ +attribute = $attribute; + $this->value = $value; + } + + /** + * {@inheritDoc} + * + * @JMS\VirtualProperty() + */ + public function getType(): string + { + return self::TYPE; + } + + /** + * @return AttributeId + */ + public function getAttribute(): AttributeId + { + return $this->attribute; + } + + /** + * @return string + */ + public function getValue(): string + { + return $this->value; + } +} diff --git a/module/condition/src/Domain/Condition/TextAttributeValueCondition.php b/module/condition/src/Domain/Condition/TextAttributeValueCondition.php new file mode 100644 index 000000000..3236ad990 --- /dev/null +++ b/module/condition/src/Domain/Condition/TextAttributeValueCondition.php @@ -0,0 +1,71 @@ +attribute = $attribute; + $this->value = $value; + } + + /** + * {@inheritDoc} + * + * @JMS\VirtualProperty() + */ + public function getType(): string + { + return self::TYPE; + } + + /** + * @return AttributeId + */ + public function getAttribute(): AttributeId + { + return $this->attribute; + } + + /** + * @return string + */ + public function getValue(): string + { + return $this->value; + } +} diff --git a/module/condition/src/Domain/Entity/ConditionSet.php b/module/condition/src/Domain/Entity/ConditionSet.php new file mode 100644 index 000000000..e4c246dff --- /dev/null +++ b/module/condition/src/Domain/Entity/ConditionSet.php @@ -0,0 +1,201 @@ +") + * @JMS\Expose() + */ + private $conditions; + + /** + * @param ConditionSetId $id + * @param ConditionSetCode $code + * @param TranslatableString $name + * @param TranslatableString $description + * @param array $conditions + * + * @throws \Exception + */ + public function __construct( + ConditionSetId $id, + ConditionSetCode $code, + TranslatableString $name, + TranslatableString $description, + array $conditions = [] + ) { + Assert::allIsInstanceOf($conditions, ConditionInterface::class); + + $this->apply(new ConditionSetCreatedEvent($id, $code, $name, $description, $conditions)); + } + + /** + * @return ConditionSetId|AbstractId + */ + public function getId(): AbstractId + { + return $this->id; + } + + /** + * @return ConditionSetCode + */ + public function getCode(): ConditionSetCode + { + return $this->code; + } + + /** + * @return TranslatableString + */ + public function getName(): TranslatableString + { + return $this->name; + } + + /** + * @return TranslatableString + */ + public function getDescription(): TranslatableString + { + return $this->description; + } + + /** + * @return ConditionInterface[] + */ + public function getConditions(): array + { + return $this->conditions; + } + + /** + * @param TranslatableString $name + * + * @throws \Exception + */ + public function changeName(TranslatableString $name): void + { + if (!$name->isEqual($this->name)) { + $this->apply(new ConditionSetNameChangedEvent($this->name, $name)); + } + } + + /** + * @param TranslatableString $description + * + * @throws \Exception + */ + public function changeDescription(TranslatableString $description): void + { + if (!$description->isEqual($this->description)) { + $this->apply(new ConditionSetDescriptionChangedEvent($this->description, $description)); + } + } + + /** + * @param array $conditions + * + * @throws \Exception + */ + public function changeConditons(array $conditions): void + { + if (sha1(serialize($this->conditions)) !== sha1(serialize($conditions))) { + $this->apply(new ConditionSetConditionsChangedEvent($this->conditions, $conditions)); + } + } + + /** + * @param ConditionSetCreatedEvent $event + */ + protected function applyConditionSetCreatedEvent(ConditionSetCreatedEvent $event): void + { + $this->id = $event->getId(); + $this->code = $event->getCode(); + $this->name = $event->getName(); + $this->description = $event->getDescription(); + $this->conditions = $event->getConditions(); + } + + /** + * @param ConditionSetNameChangedEvent $event + */ + protected function applyConditionSetNameChangedEvent(ConditionSetNameChangedEvent $event): void + { + $this->name = $event->getTo(); + } + + /** + * @param ConditionSetDescriptionChangedEvent $event + */ + protected function applyConditionSetDescriptionChangedEvent(ConditionSetDescriptionChangedEvent $event): void + { + $this->description = $event->getTo(); + } + + /** + * @param ConditionSetConditionsChangedEvent $event + */ + protected function applyConditionSetConditionsChangedEvent(ConditionSetConditionsChangedEvent $event): void + { + $this->conditions = $event->getTo(); + } +} diff --git a/module/condition/src/Domain/Entity/ConditionSetId.php b/module/condition/src/Domain/Entity/ConditionSetId.php new file mode 100644 index 000000000..df8ca94f3 --- /dev/null +++ b/module/condition/src/Domain/Entity/ConditionSetId.php @@ -0,0 +1,32 @@ +getValue())->toString()); + } +} diff --git a/module/condition/src/Domain/Event/ConditionSetConditionsChangedEvent.php b/module/condition/src/Domain/Event/ConditionSetConditionsChangedEvent.php new file mode 100644 index 000000000..cea8cf729 --- /dev/null +++ b/module/condition/src/Domain/Event/ConditionSetConditionsChangedEvent.php @@ -0,0 +1,63 @@ +") + */ + private $from; + + /** + * @var ConditionInterface[] + * + * @JMS\Type("array") + */ + private $to; + + /** + * @param array $from + * @param array $to + */ + public function __construct(array $from, array $to) + { + Assert::allIsInstanceOf($from, ConditionInterface::class); + Assert::allIsInstanceOf($to, ConditionInterface::class); + + $this->from = $from; + $this->to = $to; + } + + /** + * @return ConditionInterface[] + */ + public function getFrom(): array + { + return $this->from; + } + + /** + * @return ConditionInterface[] + */ + public function getTo(): array + { + return $this->to; + } +} diff --git a/module/condition/src/Domain/Event/ConditionSetCreatedEvent.php b/module/condition/src/Domain/Event/ConditionSetCreatedEvent.php new file mode 100644 index 000000000..b2577c397 --- /dev/null +++ b/module/condition/src/Domain/Event/ConditionSetCreatedEvent.php @@ -0,0 +1,117 @@ +") + */ + private $conditions = []; + + /** + * @param ConditionSetId $id + * @param ConditionSetCode $code + * @param TranslatableString $name + * @param TranslatableString $description + * @param array $conditions + */ + public function __construct( + ConditionSetId $id, + ConditionSetCode $code, + TranslatableString $name, + TranslatableString $description, + array $conditions = [] + ) { + $this->id = $id; + $this->code = $code; + $this->name = $name; + $this->description = $description; + $this->conditions = $conditions; + } + + /** + * @return ConditionSetId + */ + public function getId(): ConditionSetId + { + return $this->id; + } + + /** + * @return ConditionSetCode + */ + public function getCode(): ConditionSetCode + { + return $this->code; + } + + /** + * @return TranslatableString + */ + public function getName(): TranslatableString + { + return $this->name; + } + + /** + * @return TranslatableString + */ + public function getDescription(): TranslatableString + { + return $this->description; + } + + /** + * @return array + */ + public function getConditions(): array + { + return $this->conditions; + } +} diff --git a/module/condition/src/Domain/Event/ConditionSetDeletedEvent.php b/module/condition/src/Domain/Event/ConditionSetDeletedEvent.php new file mode 100644 index 000000000..f4364f38b --- /dev/null +++ b/module/condition/src/Domain/Event/ConditionSetDeletedEvent.php @@ -0,0 +1,18 @@ +strategies = $strategies; + } + + /** + * @param Language $language + * @param string $type + * + * @return array + * + * @throws ConditionStrategyNotFoundException + */ + public function getConfiguration(Language $language, string $type): array + { + foreach ($this->strategies as $strategy) { + if ($strategy->isSupportedBy($type)) { + return $strategy->getConfiguration($language); + } + } + + throw new ConditionStrategyNotFoundException($type); + } +} diff --git a/module/condition/src/Domain/Provider/ConditionDictionaryProvider.php b/module/condition/src/Domain/Provider/ConditionDictionaryProvider.php new file mode 100644 index 000000000..a79543c6f --- /dev/null +++ b/module/condition/src/Domain/Provider/ConditionDictionaryProvider.php @@ -0,0 +1,50 @@ +translator = $translator; + } + + /** + * @param Language $language + * + * @return array + */ + public function getDictionary(Language $language): array + { + return [ + AttributeExistsCondition::TYPE => $this->translator->trans(AttributeExistsCondition::TYPE, [], 'condition', $language->getCode()), + TextAttributeValueCondition::TYPE => $this->translator->trans(TextAttributeValueCondition::TYPE, [], 'condition', $language->getCode()), + OptionAttributeValueCondition::TYPE => $this->translator->trans(OptionAttributeValueCondition::TYPE, [], 'condition', $language->getCode()), + NumericAttributeValueCondition::TYPE => $this->translator->trans(NumericAttributeValueCondition::TYPE, [], 'condition', $language->getCode()), + ]; + } +} diff --git a/module/condition/src/Domain/Query/ConditionSetQueryInterface.php b/module/condition/src/Domain/Query/ConditionSetQueryInterface.php new file mode 100644 index 000000000..33a5c914f --- /dev/null +++ b/module/condition/src/Domain/Query/ConditionSetQueryInterface.php @@ -0,0 +1,33 @@ +strategies = $strategies; + } + + /** + * @param ConditionInterface $condition + * @param Language $language + * + * @return array + */ + public function getConfiguration(ConditionInterface $condition, Language $language): array + { + foreach ($this->strategies as $strategy) { + if ($strategy->isSupportedBy($condition->getType())) { + return $strategy->getConfiguration($language); + } + } + + throw new \RuntimeException(sprintf('Can\'t find strategy for "%s" condition', get_class($condition))); + } +} diff --git a/module/condition/src/Domain/Service/ConditionVerifier.php b/module/condition/src/Domain/Service/ConditionVerifier.php new file mode 100644 index 000000000..89a513958 --- /dev/null +++ b/module/condition/src/Domain/Service/ConditionVerifier.php @@ -0,0 +1,66 @@ +strategies = $strategies; + } + + /** + * @param ConditionSet $conditionSet + * @param AbstractProduct $product + * + * @return bool + */ + public function verify(ConditionSet $conditionSet, AbstractProduct $product): bool + { + foreach ($conditionSet->getConditions() as $condition) { + $strategy = $this->getStrategy($condition); + if (!$strategy->verify($product, $condition)) { + return false; + } + } + + return true; + } + + /** + * @param ConditionInterface $condition + * + * @return ConditionVerifierStrategyInterface + */ + private function getStrategy(ConditionInterface $condition): ConditionVerifierStrategyInterface + { + foreach ($this->strategies as $strategy) { + if ($strategy->isSupportedBy($condition->getType())) { + return $strategy; + } + } + + throw new \RuntimeException(sprintf('Can\'t find strategy for "%s" condition', get_class($condition))); + } +} diff --git a/module/condition/src/Domain/Service/ConditionVerifierStrategyInterface.php b/module/condition/src/Domain/Service/ConditionVerifierStrategyInterface.php new file mode 100644 index 000000000..b17fcdb5e --- /dev/null +++ b/module/condition/src/Domain/Service/ConditionVerifierStrategyInterface.php @@ -0,0 +1,33 @@ +query = $query; + $this->translator = $translator; + } + + /** + * {@inheritDoc} + */ + public function isSupportedBy(string $type): bool + { + return AttributeExistsCondition::TYPE === $type; + } + + /** + * {@inheritDoc} + */ + public function getConfiguration(Language $language): array + { + $codes = $this->query->getDictionary(); + + return [ + 'type' => AttributeExistsCondition::TYPE, + 'name' => $this->translator->trans(AttributeExistsCondition::TYPE, [], 'condition', $language->getCode()), + 'phrase' => $this->translator->trans(AttributeExistsCondition::PHRASE, [], 'condition', $language->getCode()), + 'parameters' => [ + [ + 'name' => 'attribute', + 'type' => 'SELECT', + 'options' => $codes, + ], + ], + ]; + } +} diff --git a/module/condition/src/Domain/Service/Strategy/AttributeExistsConditionVerifierStrategy.php b/module/condition/src/Domain/Service/Strategy/AttributeExistsConditionVerifierStrategy.php new file mode 100644 index 000000000..a44a6cc4a --- /dev/null +++ b/module/condition/src/Domain/Service/Strategy/AttributeExistsConditionVerifierStrategy.php @@ -0,0 +1,36 @@ +hasAttribute($configuration->getCode()); + } +} diff --git a/module/condition/src/Domain/Service/Strategy/NumericAttributeValueConditionConfigurationStrategy.php b/module/condition/src/Domain/Service/Strategy/NumericAttributeValueConditionConfigurationStrategy.php new file mode 100644 index 000000000..ecb805283 --- /dev/null +++ b/module/condition/src/Domain/Service/Strategy/NumericAttributeValueConditionConfigurationStrategy.php @@ -0,0 +1,87 @@ +query = $query; + $this->translator = $translator; + } + + /** + * {@inheritDoc} + */ + public function isSupportedBy(string $type): bool + { + return NumericAttributeValueCondition::TYPE === $type; + } + + /** + * {@inheritDoc} + */ + public function getConfiguration(Language $language): array + { + $codes = $this->query->getDictionary([NumericAttribute::TYPE]); + + return [ + 'type' => NumericAttributeValueCondition::TYPE, + 'name' => $this->translator->trans(NumericAttributeValueCondition::TYPE, [], 'condition', $language->getCode()), + 'phrase' => $this->translator->trans(NumericAttributeValueCondition::PHRASE, [], 'condition', $language->getCode()), + 'parameters' => [ + [ + 'name' => 'attribute', + 'type' => 'SELECT', + 'options' => $codes, + ], + [ + 'name' => 'operator', + 'type' => 'SELECT', + 'options' => [ + '=' => '=', + '<>' => '<>', + '>' => '>', + '<' => '<', + '>=' => '>=', + '<=' => '<=', + ], + ], + [ + 'name' => 'value', + 'type' => 'TEXT', + ], + ], + ]; + } +} diff --git a/module/condition/src/Domain/Service/Strategy/OptionAttributeValueConditionConfigurationStrategy.php b/module/condition/src/Domain/Service/Strategy/OptionAttributeValueConditionConfigurationStrategy.php new file mode 100644 index 000000000..004af2ff4 --- /dev/null +++ b/module/condition/src/Domain/Service/Strategy/OptionAttributeValueConditionConfigurationStrategy.php @@ -0,0 +1,76 @@ +query = $query; + $this->translator = $translator; + } + + /** + * {@inheritDoc} + */ + public function isSupportedBy(string $type): bool + { + return OptionAttributeValueCondition::TYPE === $type; + } + + /** + * {@inheritDoc} + */ + public function getConfiguration(Language $language): array + { + $codes = $this->query->getDictionary([SelectAttribute::TYPE, MultiSelectAttribute::TYPE]); + + return [ + 'type' => OptionAttributeValueCondition::TYPE, + 'name' => $this->translator->trans(OptionAttributeValueCondition::TYPE, [], 'condition', $language->getCode()), + 'phrase' => $this->translator->trans(OptionAttributeValueCondition::PHRASE, [], 'condition', $language->getCode()), + 'parameters' => [ + [ + 'name' => 'attribute', + 'type' => 'SELECT', + 'options' => $codes, + ], + [ + 'name' => 'value', + 'type' => 'TEXT', + ], + ], + ]; + } +} diff --git a/module/condition/src/Domain/Service/Strategy/TextAttributeValueConditionConfigurationStrategy.php b/module/condition/src/Domain/Service/Strategy/TextAttributeValueConditionConfigurationStrategy.php new file mode 100644 index 000000000..d1749e1a0 --- /dev/null +++ b/module/condition/src/Domain/Service/Strategy/TextAttributeValueConditionConfigurationStrategy.php @@ -0,0 +1,84 @@ +query = $query; + $this->translator = $translator; + } + + /** + * {@inheritDoc} + */ + public function isSupportedBy(string $type): bool + { + return TextAttributeValueCondition::TYPE === $type; + } + + /** + * {@inheritDoc} + */ + public function getConfiguration(Language $language): array + { + $codes = $this->query->getDictionary([TextAttribute::TYPE, TextareaAttribute::TYPE]); + + return [ + 'type' => TextAttributeValueCondition::TYPE, + 'name' => $this->translator->trans(TextAttributeValueCondition::TYPE, [], 'condition', $language->getCode()), + 'phrase' => $this->translator->trans(TextAttributeValueCondition::PHRASE, [], 'condition', $language->getCode()), + 'parameters' => [ + [ + 'name' => 'attribute', + 'type' => 'SELECT', + 'options' => $codes, + ], + [ + 'name' => 'operator', + 'type' => 'SELECT', + 'options' => [ + '=' => $this->translator->trans('Is equal', [], 'condition', $language->getCode()), + '~' => $this->translator->trans('Has', [], 'condition', $language->getCode()), + ], + ], + [ + 'name' => 'value', + 'type' => 'TEXT', + ], + ], + ]; + } +} diff --git a/module/condition/src/Domain/ValueObject/ConditionSetCode.php b/module/condition/src/Domain/ValueObject/ConditionSetCode.php new file mode 100644 index 000000000..1360a8ed1 --- /dev/null +++ b/module/condition/src/Domain/ValueObject/ConditionSetCode.php @@ -0,0 +1,58 @@ +value = $value; + } + + /** + * @return string + */ + public function getValue(): string + { + return $this->value; + } + + /** + * @return string + */ + public function __toString(): string + { + return $this->value; + } + + /** + * @param string $value + * + * @return bool + */ + public static function isValid(string $value): bool + { + return strlen($value) <= 100; + } +} diff --git a/module/condition/src/ErgonodeConditionBundle.php b/module/condition/src/ErgonodeConditionBundle.php new file mode 100644 index 000000000..1dc4bf6f6 --- /dev/null +++ b/module/condition/src/ErgonodeConditionBundle.php @@ -0,0 +1,29 @@ +addCompilerPass(new ConditionConfiguratorCompilerPass()); + } +} diff --git a/module/condition/src/Infrastructure/Builder/Condition/AttributeExistsConditionValidatorBuilder.php b/module/condition/src/Infrastructure/Builder/Condition/AttributeExistsConditionValidatorBuilder.php new file mode 100644 index 000000000..da8ee0aaf --- /dev/null +++ b/module/condition/src/Infrastructure/Builder/Condition/AttributeExistsConditionValidatorBuilder.php @@ -0,0 +1,38 @@ + [ + new NotBlank(), + new AttributeExists(), + ], + ] + ); + } +} diff --git a/module/condition/src/Infrastructure/Builder/Condition/NumericAttributeValueConditionValidatorBuilder.php b/module/condition/src/Infrastructure/Builder/Condition/NumericAttributeValueConditionValidatorBuilder.php new file mode 100644 index 000000000..4db05d826 --- /dev/null +++ b/module/condition/src/Infrastructure/Builder/Condition/NumericAttributeValueConditionValidatorBuilder.php @@ -0,0 +1,46 @@ + [ + new NotBlank(), + new AttributeExists(), + ], + 'operator' => [ + new NotBlank(), + new Choice(['=', '<>', '>', '<', '>=', '<=']), + ], + 'value' => [ + new NotBlank(), + ], + ] + ); + } +} diff --git a/module/condition/src/Infrastructure/Builder/Condition/OptionAttributeValueConditionValidatorBuilder.php b/module/condition/src/Infrastructure/Builder/Condition/OptionAttributeValueConditionValidatorBuilder.php new file mode 100644 index 000000000..0e8e1e700 --- /dev/null +++ b/module/condition/src/Infrastructure/Builder/Condition/OptionAttributeValueConditionValidatorBuilder.php @@ -0,0 +1,41 @@ + [ + new NotBlank(), + new AttributeExists(), + ], + 'value' => [ + new NotBlank(), + ], + ] + ); + } +} diff --git a/module/condition/src/Infrastructure/Builder/Condition/TextAttributeValueConditionValidatorBuilder.php b/module/condition/src/Infrastructure/Builder/Condition/TextAttributeValueConditionValidatorBuilder.php new file mode 100644 index 000000000..a31da2603 --- /dev/null +++ b/module/condition/src/Infrastructure/Builder/Condition/TextAttributeValueConditionValidatorBuilder.php @@ -0,0 +1,46 @@ + [ + new NotBlank(), + new AttributeExists(), + ], + 'operator' => [ + new NotBlank(), + new Choice(['=', '~']), + ], + 'value' => [ + new NotBlank(), + ], + ] + ); + } +} diff --git a/module/condition/src/Infrastructure/Builder/ConditionValidatorBuilderInterface.php b/module/condition/src/Infrastructure/Builder/ConditionValidatorBuilderInterface.php new file mode 100644 index 000000000..cb3679c0a --- /dev/null +++ b/module/condition/src/Infrastructure/Builder/ConditionValidatorBuilderInterface.php @@ -0,0 +1,24 @@ + [ + 'code' => [ + new NotBlank(), + new Length(['min' => 2, 'max' => 100]), + new UniqueConditionSetCode(), + ], + 'name' => [ + new Optional([ + new All([ + new Length(['min' => 2, 'max' => 255]), + ]), + ]), + ], + 'description' => [ + new Optional([ + new All([ + new Length(['max' => 255]), + ]), + ]), + ], + ], + ]); + } +} diff --git a/module/condition/src/Infrastructure/Builder/UpdateConditionSetValidatorBuilder.php b/module/condition/src/Infrastructure/Builder/UpdateConditionSetValidatorBuilder.php new file mode 100644 index 000000000..168cd1ace --- /dev/null +++ b/module/condition/src/Infrastructure/Builder/UpdateConditionSetValidatorBuilder.php @@ -0,0 +1,97 @@ +conditionConstraintResolver = $conditionConstraintResolver; + } + + /** + * @param array $data + * + * @return Constraint + */ + public function build(array $data): Constraint + { + $resolver = function ($data, ExecutionContextInterface $context, $payload) { + foreach ($data as $index => $condition) { + if (!is_array($condition)) { + throw new \InvalidArgumentException('Condition in condition set must be array type'); + } + + if (!array_key_exists('type', $condition)) { + throw new \InvalidArgumentException('Type not found in condition'); + } + + $constraint = $this->conditionConstraintResolver->resolve($condition['type'])->build($condition); + unset($condition['type']); + $violations = $context->getValidator()->validate($condition, $constraint); + if (0 !== $violations->count()) { + /** @var ConstraintViolation $violation */ + foreach ($violations as $violation) { + $path = sprintf('[%d]%s', $index, $violation->getPropertyPath()); + $context + ->buildViolation($violation->getMessage(), $violation->getParameters()) + ->atPath($path) + ->addViolation(); + } + } + } + }; + + return new Collection([ + 'fields' => [ + 'name' => [ + new Optional([ + new NotBlank(), + new All([ + new Length(['min' => 2, 'max' => 255]), + ]), + ]), + ], + 'description' => [ + new Optional([ + new NotBlank(), + new All([ + new Length(['max' => 255]), + ]), + ]), + ], + 'conditions' => [ + new Callback(['callback' => $resolver]), + ], + ], + ]); + } +} diff --git a/module/condition/src/Infrastructure/Grid/ConditionSetGrid.php b/module/condition/src/Infrastructure/Grid/ConditionSetGrid.php new file mode 100644 index 000000000..4ccfd304c --- /dev/null +++ b/module/condition/src/Infrastructure/Grid/ConditionSetGrid.php @@ -0,0 +1,63 @@ +translator = $translator; + } + + /** + * {@inheritDoc} + */ + public function init(GridConfigurationInterface $configuration, Language $language): void + { + $filters = $configuration->getFilters(); + + $id = new TextColumn('id', $this->trans('Id')); + $id->setVisible(false); + $this->addColumn('id', $id); + $this->addColumn('code', new TextColumn('code', $this->trans('Code'), new TextFilter($filters->getString('code')))); + $this->addColumn('name', new TextColumn('name', $this->trans('Name'), new TextFilter($filters->getString('name')))); + $this->addColumn('description', new TextColumn('description', $this->trans('Description'), new TextFilter($filters->getString('description')))); + $this->addColumn('edit', new ActionColumn('edit')); + } + + /** + * @param string $id + * @param array $parameters + * + * @return string + */ + private function trans(string $id, array $parameters = []): string + { + return $this->translator->trans($id, $parameters, 'grid'); + } +} diff --git a/module/condition/src/Infrastructure/Handler/CreateConditionSetCommandHandler.php b/module/condition/src/Infrastructure/Handler/CreateConditionSetCommandHandler.php new file mode 100644 index 000000000..5543f1c7d --- /dev/null +++ b/module/condition/src/Infrastructure/Handler/CreateConditionSetCommandHandler.php @@ -0,0 +1,49 @@ +repository = $repository; + } + + /** + * @param CreateConditionSetCommand $command + * + * @throws \Exception + */ + public function __invoke(CreateConditionSetCommand $command) + { + $segment = new ConditionSet( + $command->getId(), + $command->getCode(), + $command->getName(), + $command->getDescription() + ); + + $this->repository->save($segment); + } +} diff --git a/module/condition/src/Infrastructure/Handler/DeleteConditionSetCommandHandler.php b/module/condition/src/Infrastructure/Handler/DeleteConditionSetCommandHandler.php new file mode 100644 index 000000000..767f2fa91 --- /dev/null +++ b/module/condition/src/Infrastructure/Handler/DeleteConditionSetCommandHandler.php @@ -0,0 +1,45 @@ +repository = $repository; + } + + /** + * @param DeleteConditionSetCommand $command + * + * @throws \Exception + */ + public function __invoke(DeleteConditionSetCommand $command) + { + $conditionSet = $this->repository->load($command->getId()); + Assert::notNull($conditionSet); + + $this->repository->delete($conditionSet); + } +} diff --git a/module/condition/src/Infrastructure/Handler/UpdateConditionSetCommandHandler.php b/module/condition/src/Infrastructure/Handler/UpdateConditionSetCommandHandler.php new file mode 100644 index 000000000..9264aeec2 --- /dev/null +++ b/module/condition/src/Infrastructure/Handler/UpdateConditionSetCommandHandler.php @@ -0,0 +1,55 @@ +repository = $repository; + } + + /** + * @param UpdateConditionSetCommand $command + * + * @throws \Exception + */ + public function __invoke(UpdateConditionSetCommand $command) + { + $conditionSet = $this->repository->load($command->getId()); + Assert::notNull($conditionSet); + + $conditionSet->changeConditons($command->getConditions()); + + if ($command->hasName()) { + $conditionSet->changeName($command->getName()); + } + + if ($command->hasDescription()) { + $conditionSet->changeDescription($command->getDescription()); + } + + $this->repository->save($conditionSet); + } +} diff --git a/module/condition/src/Infrastructure/JMS/Serializer/Handler/ConditionInterfaceHandler.php b/module/condition/src/Infrastructure/JMS/Serializer/Handler/ConditionInterfaceHandler.php new file mode 100644 index 000000000..549c37fb1 --- /dev/null +++ b/module/condition/src/Infrastructure/JMS/Serializer/Handler/ConditionInterfaceHandler.php @@ -0,0 +1,26 @@ + GraphNavigatorInterface::DIRECTION_SERIALIZATION, + 'type' => ConditionSetCode::class, + 'format' => $format, + 'method' => 'serialize', + ]; + + $methods[] = [ + 'direction' => GraphNavigatorInterface::DIRECTION_DESERIALIZATION, + 'type' => ConditionSetCode::class, + 'format' => $format, + 'method' => 'deserialize', + ]; + } + + return $methods; + } + + /** + * @param SerializationVisitorInterface $visitor + * @param ConditionSetCode $status + * @param array $type + * @param Context $context + * + * @return string + */ + public function serialize(SerializationVisitorInterface $visitor, ConditionSetCode $status, array $type, Context $context): string + { + return (string) $status; + } + + /** + * @param DeserializationVisitorInterface $visitor + * @param mixed $data + * @param array $type + * @param Context $context + * + * @return ConditionSetCode + */ + public function deserialize(DeserializationVisitorInterface $visitor, $data, array $type, Context $context): ConditionSetCode + { + return new ConditionSetCode($data); + } +} diff --git a/module/condition/src/Infrastructure/JMS/Serializer/Handler/ConditionSetIdHandler.php b/module/condition/src/Infrastructure/JMS/Serializer/Handler/ConditionSetIdHandler.php new file mode 100644 index 000000000..fc687db07 --- /dev/null +++ b/module/condition/src/Infrastructure/JMS/Serializer/Handler/ConditionSetIdHandler.php @@ -0,0 +1,75 @@ + GraphNavigatorInterface::DIRECTION_SERIALIZATION, + 'type' => ConditionSetId::class, + 'format' => $format, + 'method' => 'serialize', + ]; + + $methods[] = [ + 'direction' => GraphNavigatorInterface::DIRECTION_DESERIALIZATION, + 'type' => ConditionSetId::class, + 'format' => $format, + 'method' => 'deserialize', + ]; + } + + return $methods; + } + + /** + * @param SerializationVisitorInterface $visitor + * @param ConditionSetId $id + * @param array $type + * @param Context $context + * + * @return string + */ + public function serialize(SerializationVisitorInterface $visitor, ConditionSetId $id, array $type, Context $context): string + { + return $id->getValue(); + } + + /** + * @param DeserializationVisitorInterface $visitor + * @param mixed $data + * @param array $type + * @param Context $context + * + * @return ConditionSetId + */ + public function deserialize(DeserializationVisitorInterface $visitor, $data, array $type, Context $context): ConditionSetId + { + return new ConditionSetId($data); + } +} diff --git a/module/condition/src/Infrastructure/Resolver/ConditionConstraintResolver.php b/module/condition/src/Infrastructure/Resolver/ConditionConstraintResolver.php new file mode 100644 index 000000000..32d539949 --- /dev/null +++ b/module/condition/src/Infrastructure/Resolver/ConditionConstraintResolver.php @@ -0,0 +1,48 @@ +constraints[$type] = $constraintClass; + } + + /** + * @param string $type + * + * @return ConditionValidatorBuilderInterface + * + * @throws \OutOfBoundsException + */ + public function resolve(string $type): ConditionValidatorBuilderInterface + { + if (!array_key_exists($type, $this->constraints)) { + throw new \OutOfBoundsException(sprintf('Constraint by condition type "%s" not found', $type)); + } + + return $this->constraints[$type]; + } +} diff --git a/module/condition/src/Infrastructure/Validator/UniqueConditionSetCode.php b/module/condition/src/Infrastructure/Validator/UniqueConditionSetCode.php new file mode 100644 index 000000000..8e0f5d07d --- /dev/null +++ b/module/condition/src/Infrastructure/Validator/UniqueConditionSetCode.php @@ -0,0 +1,28 @@ +query = $query; + } + + /** + * @param mixed $value + * @param UniqueConditionSetCode|Constraint $constraint + */ + public function validate($value, Constraint $constraint): void + { + if (!$constraint instanceof UniqueConditionSetCode) { + throw new UnexpectedTypeException($constraint, UniqueConditionSetCode::class); + } + + if (null === $value || '' === $value) { + return; + } + + if (!is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) { + throw new UnexpectedTypeException($value, 'string'); + } + + $value = (string) $value; + + if (!ConditionSetCode::isValid($value)) { + $this->context->buildViolation($constraint->validMessage) + ->setParameter('{{ value }}', $value) + ->addViolation(); + + return; + } + + if ($this->query->isExistsByCode(new ConditionSetCode($value))) { + $this->context->buildViolation($constraint->uniqueMessage) + ->addViolation(); + } + } +} diff --git a/module/condition/src/Persistence/Dbal/Projector/ConditionSetConditionsChangedEventProjector.php b/module/condition/src/Persistence/Dbal/Projector/ConditionSetConditionsChangedEventProjector.php new file mode 100644 index 000000000..e0b6bceba --- /dev/null +++ b/module/condition/src/Persistence/Dbal/Projector/ConditionSetConditionsChangedEventProjector.php @@ -0,0 +1,80 @@ +connection = $connection; + $this->serializer = $serializer; + } + + /** + * @param DomainEventInterface $event + * + * @return bool + */ + public function support(DomainEventInterface $event): bool + { + return $event instanceof ConditionSetConditionsChangedEvent; + } + + /** + * @param AbstractId $aggregateId + * @param DomainEventInterface $event + * + * @throws UnsupportedEventException + * @throws DBALException + */ + public function projection(AbstractId $aggregateId, DomainEventInterface $event): void + { + if (!$event instanceof ConditionSetConditionsChangedEvent) { + throw new UnsupportedEventException($event, ConditionSetConditionsChangedEvent::class); + } + + $this->connection->update( + self::TABLE, + [ + 'conditions' => $this->serializer->serialize($event->getTo(), 'json'), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); + } +} diff --git a/module/condition/src/Persistence/Dbal/Projector/ConditionSetCreatedEventProjector.php b/module/condition/src/Persistence/Dbal/Projector/ConditionSetCreatedEventProjector.php new file mode 100644 index 000000000..825e28613 --- /dev/null +++ b/module/condition/src/Persistence/Dbal/Projector/ConditionSetCreatedEventProjector.php @@ -0,0 +1,79 @@ +connection = $connection; + $this->serializer = $serializer; + } + + /** + * {@inheritDoc} + */ + public function support(DomainEventInterface $event): bool + { + return $event instanceof ConditionSetCreatedEvent; + } + + /** + * @param AbstractId $aggregateId + * @param DomainEventInterface $event + * + * @throws UnsupportedEventException + * @throws DBALException + */ + public function projection(AbstractId $aggregateId, DomainEventInterface $event): void + { + if (!$event instanceof ConditionSetCreatedEvent) { + throw new UnsupportedEventException($event, ConditionSetCreatedEvent::class); + } + + $this->connection->insert( + self::TABLE, + [ + 'id' => $event->getId()->getValue(), + 'code' => $event->getCode(), + 'name' => $this->serializer->serialize($event->getName(), 'json'), + 'description' => $this->serializer->serialize($event->getDescription(), 'json'), + 'conditions' => $this->serializer->serialize($event->getConditions(), 'json'), + ] + ); + } +} diff --git a/module/condition/src/Persistence/Dbal/Projector/ConditionSetDeletedEventProjector.php b/module/condition/src/Persistence/Dbal/Projector/ConditionSetDeletedEventProjector.php new file mode 100644 index 000000000..ea869432a --- /dev/null +++ b/module/condition/src/Persistence/Dbal/Projector/ConditionSetDeletedEventProjector.php @@ -0,0 +1,62 @@ +connection = $connection; + } + + /** + * {@inheritDoc} + */ + public function support(DomainEventInterface $event): bool + { + return $event instanceof ConditionSetDeletedEvent; + } + + /** + * {@inheritDoc} + */ + public function projection(AbstractId $aggregateId, DomainEventInterface $event): void + { + if (!$event instanceof ConditionSetDeletedEvent) { + throw new UnsupportedEventException($event, ConditionSetDeletedEvent::class); + } + + $this->connection->delete( + self::TABLE, + [ + 'id' => $aggregateId->getValue(), + ] + ); + } +} diff --git a/module/condition/src/Persistence/Dbal/Projector/ConditionSetDescriptionChangedEventProjector.php b/module/condition/src/Persistence/Dbal/Projector/ConditionSetDescriptionChangedEventProjector.php new file mode 100644 index 000000000..44a17e5b8 --- /dev/null +++ b/module/condition/src/Persistence/Dbal/Projector/ConditionSetDescriptionChangedEventProjector.php @@ -0,0 +1,80 @@ +connection = $connection; + $this->serializer = $serializer; + } + + /** + * @param DomainEventInterface $event + * + * @return bool + */ + public function support(DomainEventInterface $event): bool + { + return $event instanceof ConditionSetDescriptionChangedEvent; + } + + /** + * @param AbstractId $aggregateId + * @param DomainEventInterface $event + * + * @throws UnsupportedEventException + * @throws DBALException + */ + public function projection(AbstractId $aggregateId, DomainEventInterface $event): void + { + if (!$event instanceof ConditionSetDescriptionChangedEvent) { + throw new UnsupportedEventException($event, ConditionSetDescriptionChangedEvent::class); + } + + $this->connection->update( + self::TABLE, + [ + 'description' => $this->serializer->serialize($event->getTo(), 'json'), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); + } +} diff --git a/module/condition/src/Persistence/Dbal/Projector/ConditionSetNameChangedEventProjector.php b/module/condition/src/Persistence/Dbal/Projector/ConditionSetNameChangedEventProjector.php new file mode 100644 index 000000000..769425c7b --- /dev/null +++ b/module/condition/src/Persistence/Dbal/Projector/ConditionSetNameChangedEventProjector.php @@ -0,0 +1,80 @@ +connection = $connection; + $this->serializer = $serializer; + } + + /** + * @param DomainEventInterface $event + * + * @return bool + */ + public function support(DomainEventInterface $event): bool + { + return $event instanceof ConditionSetNameChangedEvent; + } + + /** + * @param AbstractId $aggregateId + * @param DomainEventInterface $event + * + * @throws UnsupportedEventException + * @throws DBALException + */ + public function projection(AbstractId $aggregateId, DomainEventInterface $event): void + { + if (!$event instanceof ConditionSetNameChangedEvent) { + throw new UnsupportedEventException($event, ConditionSetNameChangedEvent::class); + } + + $this->connection->update( + self::TABLE, + [ + 'name' => $this->serializer->serialize($event->getTo(), 'json'), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); + } +} diff --git a/module/condition/src/Persistence/Dbal/Query/DbalConditionSetQuery.php b/module/condition/src/Persistence/Dbal/Query/DbalConditionSetQuery.php new file mode 100644 index 000000000..14db6e155 --- /dev/null +++ b/module/condition/src/Persistence/Dbal/Query/DbalConditionSetQuery.php @@ -0,0 +1,84 @@ +connection = $connection; + } + + /** + * {@inheritDoc} + */ + public function getDataSet(Language $language): DbalDataSet + { + $query = $this->getQuery(); + $query->addSelect(sprintf('(name->>\'%s\') AS name', $language->getCode())); + $query->addSelect(sprintf('(description->>\'%s\') AS description', $language->getCode())); + + $result = $this->connection->createQueryBuilder(); + $result->select('*'); + $result->from(sprintf('(%s)', $query->getSQL()), 't'); + + return new DbalDataSet($result); + } + + /** + * {@inheritDoc} + */ + public function isExistsByCode(ConditionSetCode $conditionSetCode): bool + { + $queryBuilder = $this->connection->createQueryBuilder() + ->select('id') + ->from(self::TABLE) + ->where('code = :code') + ->setParameter('code', $conditionSetCode->getValue()) + ->setMaxResults(1); + + $result = $queryBuilder->execute()->fetchColumn(); + + return !empty($result); + } + + /** + * @return QueryBuilder + */ + private function getQuery(): QueryBuilder + { + return $this->connection->createQueryBuilder() + ->select(self::FIELDS) + ->from(self::TABLE, 't'); + } +} diff --git a/module/condition/src/Persistence/Dbal/Repository/DbalConditionSetRepository.php b/module/condition/src/Persistence/Dbal/Repository/DbalConditionSetRepository.php new file mode 100644 index 000000000..117dbbaa2 --- /dev/null +++ b/module/condition/src/Persistence/Dbal/Repository/DbalConditionSetRepository.php @@ -0,0 +1,108 @@ +eventStore = $eventStore; + $this->eventDispatcher = $eventDispatcher; + } + + /** + * @param ConditionSetId $id + * + * @return AbstractAggregateRoot|null + * + * @throws \ReflectionException + */ + public function load(ConditionSetId $id): ?AbstractAggregateRoot + { + $eventStream = $this->eventStore->load($id); + + if (\count($eventStream) > 0) { + $class = new \ReflectionClass(ConditionSet::class); + /** @var AbstractAggregateRoot $aggregate */ + $aggregate = $class->newInstanceWithoutConstructor(); + if (!$aggregate instanceof AbstractAggregateRoot) { + throw new \LogicException(sprintf('Impossible to initialize "%s"', $class)); + } + + $aggregate->initialize($eventStream); + + return $aggregate; + } + + return null; + } + + /** + * @param AbstractAggregateRoot $aggregateRoot + */ + public function save(AbstractAggregateRoot $aggregateRoot): void + { + $events = $aggregateRoot->popEvents(); + + $this->eventStore->append($aggregateRoot->getId(), $events); + foreach ($events as $envelope) { + $this->eventDispatcher->dispatch($envelope); + } + } + + /** + * @param ConditionSetId $id + * + * @return bool + */ + public function exists(ConditionSetId $id): bool + { + $eventStream = $this->eventStore->load($id); + + return \count($eventStream) > 0; + } + + /** + * @param AbstractAggregateRoot $aggregateRoot + * + * @throws \Exception + */ + public function delete(AbstractAggregateRoot $aggregateRoot): void + { + $aggregateRoot->apply(new ConditionSetDeletedEvent()); + $this->save($aggregateRoot); + + $this->eventStore->delete($aggregateRoot->getId()); + } +} diff --git a/module/condition/src/Resources/config/routes.yml b/module/condition/src/Resources/config/routes.yml new file mode 100644 index 000000000..bd2e16589 --- /dev/null +++ b/module/condition/src/Resources/config/routes.yml @@ -0,0 +1,14 @@ +ergonode_condition_dictionary: + resource: '../../Application/Controller/Api/Dictionary*' + type: 'annotation' + prefix: '/api/v1/{language}/dictionary' + +ergonode_condition_api: + resource: '../../Application/Controller/Api/Condition*' + type: 'annotation' + prefix: '/api/v1/{language}' + +ergonode_condition_set_api: + resource: '../../Application/Controller/Api/ConditionSet*' + type: 'annotation' + prefix: '/api/v1/{language}' diff --git a/module/condition/src/Resources/config/services.yml b/module/condition/src/Resources/config/services.yml new file mode 100644 index 000000000..6c0ce0884 --- /dev/null +++ b/module/condition/src/Resources/config/services.yml @@ -0,0 +1,47 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + _instanceof: + Ergonode\Condition\Domain\Service\ConfigurationStrategyInterface: + tags: ['component.condition.condition_set.configurator_interface'] + + Ergonode\Condition\Application\: + resource: '../../Application/*' + + Ergonode\Condition\Persistence\: + resource: '../../Persistence/*' + + Ergonode\Condition\Domain\: + resource: '../../Domain/*' + exclude: '../../Domain/{Entity,ValueObject,Condition}' + + Ergonode\Condition\Infrastructure\: + resource: '../../Infrastructure/*' + + Ergonode\Condition\Infrastructure\Handler\: + resource: '../../Infrastructure/Handler/*' + exclude: '../../Infrastructure/Handler/{Strategy}' + tags: ['messenger.message_handler'] + + Ergonode\Condition\Infrastructure\JMS\Serializer\Handler\: + resource: '../../Infrastructure/JMS/Serializer/Handler/*' + tags: ['jms_serializer.subscribing_handler'] + + Ergonode\Condition\Infrastructure\Resolver\ConditionConstraintResolver: + calls: + - ['set', ['ATTRIBUTE_EXISTS_CONDITION', '@Ergonode\Condition\Infrastructure\Builder\Condition\AttributeExistsConditionValidatorBuilder']] + - ['set', ['TEXT_ATTRIBUTE_VALUE_CONDITION', '@Ergonode\Condition\Infrastructure\Builder\Condition\TextAttributeValueConditionValidatorBuilder']] + - ['set', ['OPTION_ATTRIBUTE_VALUE_CONDITION', '@Ergonode\Condition\Infrastructure\Builder\Condition\OptionAttributeValueConditionValidatorBuilder']] + - ['set', ['NUMERIC_ATTRIBUTE_VALUE_CONDITION', '@Ergonode\Condition\Infrastructure\Builder\Condition\NumericAttributeValueConditionValidatorBuilder']] + + Ergonode\Condition\Infrastructure\JMS\Serializer\Handler\ConditionInterfaceHandler: + calls: + - ['set', ['Ergonode\Condition\Domain\Condition\AttributeExistsCondition']] + - ['set', ['Ergonode\Condition\Domain\Condition\NumericAttributeValueCondition']] + - ['set', ['Ergonode\Condition\Domain\Condition\OptionAttributeValueCondition']] + - ['set', ['Ergonode\Condition\Domain\Condition\TextAttributeValueCondition']] + + Ergonode\Condition\Domain\Query\ConditionSetQueryInterface: '@Ergonode\Condition\Persistence\Dbal\Query\DbalConditionSetQuery' + Ergonode\Condition\Domain\Repository\ConditionSetRepositoryInterface: '@Ergonode\Condition\Persistence\Dbal\Repository\DbalConditionSetRepository' diff --git a/module/condition/src/Resources/translations/condition.en.yaml b/module/condition/src/Resources/translations/condition.en.yaml new file mode 100644 index 000000000..c2966f2b5 --- /dev/null +++ b/module/condition/src/Resources/translations/condition.en.yaml @@ -0,0 +1,8 @@ +ATTRIBUTE_EXISTS_CONDITION: Attribute exists +ATTRIBUTE_EXISTS_CONDITION_PHRASE: Attribute [attribute] exists +NUMERIC_ATTRIBUTE_VALUE_CONDITION: Numeric attribute has value +NUMERIC_ATTRIBUTE_VALUE_CONDITION_PHRASE: Attribute [attribute] has value [operator] [value] +OPTION_ATTRIBUTE_VALUE_CONDITION: Option attribute has value +OPTION_ATTRIBUTE_VALUE_CONDITION_PHRASE: Attribute [attribute] has option [value] +TEXT_ATTRIBUTE_VALUE_CONDITION: Text attribute has value +TEXT_ATTRIBUTE_VALUE_CONDITION_PHRASE: Attribute [attribute] has value [operator] [value] diff --git a/module/condition/src/Resources/translations/condition.pl.yaml b/module/condition/src/Resources/translations/condition.pl.yaml new file mode 100644 index 000000000..ef6a249b4 --- /dev/null +++ b/module/condition/src/Resources/translations/condition.pl.yaml @@ -0,0 +1,8 @@ +ATTRIBUTE_EXISTS_CONDITION: Posiada attrybut +ATTRIBUTE_EXISTS_CONDITION_PHRASE: Posiada attrybut [attribute] +NUMERIC_ATTRIBUTE_VALUE_CONDITION: Atrybut liczbowy ma wartość +NUMERIC_ATTRIBUTE_VALUE_CONDITION_PHRASE: Atrybut [attribute] ma wartość [operator] [value] +OPTION_ATTRIBUTE_VALUE_CONDITION: Atrybut posiada opcję o wartości +OPTION_ATTRIBUTE_VALUE_CONDITION_PHRASE: Atrybut [attribute] ma opcję o wartości [value] +TEXT_ATTRIBUTE_VALUE_CONDITION: Atrybut textowy ma wartość +TEXT_ATTRIBUTE_VALUE_CONDITION_PHRASE: Atrybut [attribute] ma wartość [operator] [value] diff --git a/module/condition/src/Resources/translations/grid.en.yaml b/module/condition/src/Resources/translations/grid.en.yaml new file mode 100644 index 000000000..6d06acbd8 --- /dev/null +++ b/module/condition/src/Resources/translations/grid.en.yaml @@ -0,0 +1,5 @@ +Code: Code +Name: Name +Description: Description +Edit: Edit +Status: Status diff --git a/module/condition/src/Resources/translations/grid.pl.yaml b/module/condition/src/Resources/translations/grid.pl.yaml new file mode 100644 index 000000000..d52851ca9 --- /dev/null +++ b/module/condition/src/Resources/translations/grid.pl.yaml @@ -0,0 +1,5 @@ +Code: Kod +Name: Nazwa +Description: Opis +Edit: Edycja +Status: Status diff --git a/module/condition/src/Resources/translations/log.en.yaml b/module/condition/src/Resources/translations/log.en.yaml new file mode 100644 index 000000000..b549e0b41 --- /dev/null +++ b/module/condition/src/Resources/translations/log.en.yaml @@ -0,0 +1,5 @@ +'Condition set created': 'Condition set "%code%" created' +'Condition set deleted': 'Condition set "%id%" deleted' +'Condition set description changed': 'Condition set description changed' +'Condition set name changed': 'Condition set name changed' +'Condition set conditions changed': 'Condition set conditions changed' diff --git a/module/condition/src/Resources/translations/log.pl.yaml b/module/condition/src/Resources/translations/log.pl.yaml new file mode 100644 index 000000000..aade6db69 --- /dev/null +++ b/module/condition/src/Resources/translations/log.pl.yaml @@ -0,0 +1,5 @@ +'Condition set created': 'Zbiór warunków "%code%" utworzony' +'Condition set deleted': 'Zbiór warunków "%id%" usunięty' +'Condition set description changed': 'Zmieniono opis w zbiorze warunków' +'Condition set name changed': 'Zmieniono nazwe w zbiorze warunków' +'Condition set conditions changed': 'Zmineniono warunki w zbiorze warunków' diff --git a/module/condition/src/Resources/translations/privilege.en.yaml b/module/condition/src/Resources/translations/privilege.en.yaml new file mode 100644 index 000000000..f78984bce --- /dev/null +++ b/module/condition/src/Resources/translations/privilege.en.yaml @@ -0,0 +1 @@ +Condition: Conditions diff --git a/module/condition/src/Resources/translations/privilege.pl.yaml b/module/condition/src/Resources/translations/privilege.pl.yaml new file mode 100644 index 000000000..7a93c6bad --- /dev/null +++ b/module/condition/src/Resources/translations/privilege.pl.yaml @@ -0,0 +1 @@ +Condition: Warunki diff --git a/module/core/src/Infrastructure/JMS/Serializer/Handler/AbstractInterfaceHandler.php b/module/core/src/Infrastructure/JMS/Serializer/Handler/AbstractInterfaceHandler.php index 3ac9bc23e..c456cc95d 100644 --- a/module/core/src/Infrastructure/JMS/Serializer/Handler/AbstractInterfaceHandler.php +++ b/module/core/src/Infrastructure/JMS/Serializer/Handler/AbstractInterfaceHandler.php @@ -86,6 +86,8 @@ public function deserialize(DeserializationVisitorInterface $visitor, array $dat { $typeField = strtolower($this->constant); + $data = $this->prepareData($data); + if (!array_key_exists($data[$typeField], $this->map)) { throw new \OutOfBoundsException(sprintf('Value type "%s" not mapped', $data[$typeField])); } @@ -127,4 +129,14 @@ public function deserialize(DeserializationVisitorInterface $visitor, array $dat return $object; } + + /** + * @param array $data + * + * @return array + */ + protected function prepareData(array $data): array + { + return $data; + } } diff --git a/module/event-sourcing/src/Infrastructure/Exception/ProjectorException.php b/module/event-sourcing/src/Infrastructure/Exception/ProjectorException.php index f96afe006..93d24ec1a 100644 --- a/module/event-sourcing/src/Infrastructure/Exception/ProjectorException.php +++ b/module/event-sourcing/src/Infrastructure/Exception/ProjectorException.php @@ -15,7 +15,7 @@ */ class ProjectorException extends \Exception { - public const MESSAGE = 'Can\'t project event %s'; + public const MESSAGE = 'Can\'t project event "%s"'; /** * @param DomainEventInterface $event @@ -23,6 +23,6 @@ class ProjectorException extends \Exception */ public function __construct(DomainEventInterface $event, \Throwable $previous = null) { - parent::__construct(sprintf(self::MESSAGE, \get_class($event)), 0, $previous); + parent::__construct(sprintf(self::MESSAGE, get_class($event)), 0, $previous); } } diff --git a/module/fixture/src/Infrastructure/Faker/ConditionSetCodeFaker.php b/module/fixture/src/Infrastructure/Faker/ConditionSetCodeFaker.php new file mode 100644 index 000000000..004fe7547 --- /dev/null +++ b/module/fixture/src/Infrastructure/Faker/ConditionSetCodeFaker.php @@ -0,0 +1,34 @@ +' + +Ergonode\Core\Domain\ValueObject\TranslatableString: + condition_set_name: + __construct: + - + 'PL': 'Zbiór warunków' + 'EN': 'Condition set' + condition_set_description: + __construct: + - + 'PL': 'Opis do zbioru warunków' + 'EN': 'Description of condition set' + +Ergonode\Condition\Domain\Entity\ConditionSet: + condition_set: + __construct: + - '' + - '' + - '@condition_set_name' + - '@condition_set_description' + - ['@condition_attribute_exists'] diff --git a/module/fixture/src/Resources/fixtures/develop/fixture.yaml b/module/fixture/src/Resources/fixtures/develop/fixture.yaml index a3099b59c..91fd91f12 100644 --- a/module/fixture/src/Resources/fixtures/develop/fixture.yaml +++ b/module/fixture/src/Resources/fixtures/develop/fixture.yaml @@ -8,4 +8,5 @@ include: - 'templates.yaml' - 'categories.yaml' - 'products.yaml' - - 'segment.yaml' + - 'segments.yaml' + - 'conditions.yaml' diff --git a/module/fixture/src/Resources/fixtures/develop/roles.yaml b/module/fixture/src/Resources/fixtures/develop/roles.yaml index fed4da07c..e28e4db8a 100644 --- a/module/fixture/src/Resources/fixtures/develop/roles.yaml +++ b/module/fixture/src/Resources/fixtures/develop/roles.yaml @@ -59,6 +59,18 @@ Ergonode\Account\Domain\Entity\Role: - '' - '' - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' admin_role: __construct: - '@admin_role_id' @@ -105,6 +117,18 @@ Ergonode\Account\Domain\Entity\Role: - '' - '' - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' data_inputer_role: __construct: - '@data_inputer_role_id' @@ -174,4 +198,4 @@ Ergonode\Account\Domain\Entity\Role: - '' - '' - '' - - '' \ No newline at end of file + - '' diff --git a/module/fixture/src/Resources/fixtures/develop/segment.yaml b/module/fixture/src/Resources/fixtures/develop/segments.yaml similarity index 92% rename from module/fixture/src/Resources/fixtures/develop/segment.yaml rename to module/fixture/src/Resources/fixtures/develop/segments.yaml index 1096aa877..d9913674e 100644 --- a/module/fixture/src/Resources/fixtures/develop/segment.yaml +++ b/module/fixture/src/Resources/fixtures/develop/segments.yaml @@ -17,5 +17,6 @@ Ergonode\Segment\Domain\Entity\Segment: __construct: - '' - '' + - '' - '@segment_name' - '@segment_description' diff --git a/module/fixture/src/Resources/fixtures/fixture.yaml b/module/fixture/src/Resources/fixtures/fixture.yaml index 75122cd89..8e66ce667 100644 --- a/module/fixture/src/Resources/fixtures/fixture.yaml +++ b/module/fixture/src/Resources/fixtures/fixture.yaml @@ -75,6 +75,14 @@ Ergonode\Account\Domain\Entity\Role: - '' - '' - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' admin_role: __construct: - '@admin_role_id' @@ -121,6 +129,18 @@ Ergonode\Account\Domain\Entity\Role: - '' - '' - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' data_inputer_role: __construct: - '@data_inputer_role_id' diff --git a/module/product/src/Infrastructure/Handler/UpdateProductCommandHandler.php b/module/product/src/Infrastructure/Handler/UpdateProductCommandHandler.php index 902730d89..0d263207e 100644 --- a/module/product/src/Infrastructure/Handler/UpdateProductCommandHandler.php +++ b/module/product/src/Infrastructure/Handler/UpdateProductCommandHandler.php @@ -16,7 +16,6 @@ use Webmozart\Assert\Assert; /** - * Class UpdateProductCommandHandler */ class UpdateProductCommandHandler { @@ -42,6 +41,8 @@ public function __construct(ProductRepositoryInterface $productRepository, Categ /** * @param UpdateProductCommand $command + * + * @throws \Exception */ public function __invoke(UpdateProductCommand $command) { diff --git a/module/segment/migrations/Version20190130104000.php b/module/segment/migrations/Version20190130104000.php index 8ef2790c6..c0a97c8af 100644 --- a/module/segment/migrations/Version20190130104000.php +++ b/module/segment/migrations/Version20190130104000.php @@ -29,10 +29,17 @@ public function up(Schema $schema): void code VARCHAR(100) NOT NULL, name JSON NOT NULL, description JSON NOT NULL, - status VARCHAR(32) NOT NULL, + status VARCHAR(32) NOT NULL, + condition_set_id UUID DEFAULT NULL, PRIMARY KEY(id) ) '); + $this->addSql('CREATE UNIQUE index segment_code_uindex ON segment (code)'); + + $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'SEGMENT_CREATE', 'Segment']); + $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'SEGMENT_READ', 'Segment']); + $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'SEGMENT_UPDATE', 'Segment']); + $this->addSql('INSERT INTO privileges (id, code, area) VALUES (?, ?, ?)', [Uuid::uuid4()->toString(), 'SEGMENT_DELETE', 'Segment']); $this->createEventStoreEvents([ 'Ergonode\Segment\Domain\Event\SegmentCreatedEvent' => 'Segment created', @@ -40,6 +47,8 @@ public function up(Schema $schema): void 'Ergonode\Segment\Domain\Event\SegmentNameChangedEvent' => 'Segment name changed', 'Ergonode\Segment\Domain\Event\SegmentSpecificationAddedEvent' => 'Segment specification added', 'Ergonode\Segment\Domain\Event\SegmentStatusChangedEvent' => 'Segment status changed', + 'Ergonode\Segment\Domain\Event\SegmentDeletedEvent' => 'Segment deleted', + 'Ergonode\Segment\Domain\Event\SegmentConditionSetChangedEvent' => 'Segment condition set changed', ]); } diff --git a/module/segment/src/Application/Controller/Api/SegmentController.php b/module/segment/src/Application/Controller/Api/SegmentController.php index b4133b797..2dbf2c871 100644 --- a/module/segment/src/Application/Controller/Api/SegmentController.php +++ b/module/segment/src/Application/Controller/Api/SegmentController.php @@ -11,7 +11,9 @@ use Ergonode\Api\Application\Exception\FormValidationHttpException; use Ergonode\Api\Application\Response\CreatedResponse; +use Ergonode\Api\Application\Response\EmptyResponse; use Ergonode\Api\Application\Response\SuccessResponse; +use Ergonode\Condition\Domain\Entity\ConditionSetId; use Ergonode\Core\Domain\ValueObject\Language; use Ergonode\Core\Domain\ValueObject\TranslatableString; use Ergonode\Grid\RequestGridConfiguration; @@ -21,18 +23,20 @@ use Ergonode\Segment\Application\Form\Model\UpdateSegmentFormModel; use Ergonode\Segment\Application\Form\UpdateSegmentForm; use Ergonode\Segment\Domain\Command\CreateSegmentCommand; +use Ergonode\Segment\Domain\Command\DeleteSegmentCommand; use Ergonode\Segment\Domain\Command\UpdateSegmentCommand; use Ergonode\Segment\Domain\Entity\Segment; use Ergonode\Segment\Domain\Query\SegmentQueryInterface; use Ergonode\Segment\Domain\ValueObject\SegmentCode; use Ergonode\Segment\Infrastructure\Grid\SegmentGrid; +use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; +use Swagger\Annotations as SWG; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Routing\Annotation\Route; -use Swagger\Annotations as SWG; /** */ @@ -71,6 +75,8 @@ public function __construct( /** * @Route("segments", methods={"GET"}) * + * @IsGranted("SEGMENT_READ") + * * @SWG\Tag(name="Segment") * @SWG\Parameter( * name="language", @@ -146,6 +152,8 @@ public function getSegments(Language $language, RequestGridConfiguration $config /** * @Route("/segments/{segment}", methods={"GET"}, requirements={"segment" = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"}) * + * @IsGranted("SEGMENT_READ") + * * @SWG\Tag(name="Segment") * @SWG\Parameter( * name="language", @@ -158,7 +166,7 @@ public function getSegments(Language $language, RequestGridConfiguration $config * name="segment", * in="path", * type="string", - * description="Segment id", + * description="Segment ID", * ) * @SWG\Response( * response=200, @@ -183,6 +191,8 @@ public function getSegment(Segment $segment): Response /** * @Route("/segments", methods={"POST"}) * + * @IsGranted("SEGMENT_CREATE") + * * @SWG\Tag(name="Segment") * @SWG\Parameter( * name="language", @@ -224,6 +234,7 @@ public function createSegment(Request $request): Response $command = new CreateSegmentCommand( new SegmentCode($data->code), + new ConditionSetId($data->conditionSetId), new TranslatableString($data->name), new TranslatableString($data->description) ); @@ -236,7 +247,9 @@ public function createSegment(Request $request): Response } /** - * @Route("/segments/{segment}", methods={"PUT"}) + * @Route("/segments/{segment}", methods={"PUT"}, requirements={"segment"="[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"}) + * + * @IsGranted("SEGMENT_UPDATE") * * @SWG\Tag(name="Segment") * @SWG\Parameter( @@ -288,13 +301,57 @@ public function updateSegment(Segment $segment, Request $request): Response $command = new UpdateSegmentCommand( $segment->getId(), new TranslatableString($data->name), - new TranslatableString($data->description) + new TranslatableString($data->description), + new ConditionSetId($data->conditionSetId) ); $this->messageBus->dispatch($command); - return new CreatedResponse($command->getId()); + return new EmptyResponse(); } throw new FormValidationHttpException($form); } + + /** + * @Route("/segments/{segment}", methods={"DELETE"}, requirements={"segment"="[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"}) + * + * @IsGranted("CONDITION_DELETE") + * + * @SWG\Tag(name="Segment") + * @SWG\Parameter( + * name="language", + * in="path", + * type="string", + * description="Language code", + * default="EN" + * ) + * @SWG\Parameter( + * name="segment", + * in="path", + * required=true, + * type="string", + * description="Segment ID", + * ) + * @SWG\Response( + * response=204, + * description="Success" + * ) + * @SWG\Response( + * response=404, + * description="Not found" + * ) + * + * @ParamConverter(class="Ergonode\Segment\Domain\Entity\Segment") + * + * @param Segment $segment + * + * @return Response + */ + public function deleteSegment(Segment $segment): Response + { + $command = new DeleteSegmentCommand($segment->getId()); + $this->messageBus->dispatch($command); + + return new EmptyResponse(); + } } diff --git a/module/segment/src/Application/DependencyInjection/ErgonodeSegmentExtension.php b/module/segment/src/Application/DependencyInjection/ErgonodeSegmentExtension.php index 2ce2769f5..2b3011db3 100644 --- a/module/segment/src/Application/DependencyInjection/ErgonodeSegmentExtension.php +++ b/module/segment/src/Application/DependencyInjection/ErgonodeSegmentExtension.php @@ -9,7 +9,9 @@ namespace Ergonode\Segment\Application\DependencyInjection; +use Ergonode\Segment\Application\DependencyInjection\CompilerPass\SegmentConditionConfiguratorCompilerPass; use Ergonode\Segment\Application\DependencyInjection\CompilerPass\SegmentGeneratorCompilerPass; +use Ergonode\Segment\Domain\Service\SegmentConfigurationStrategyInterface; use Ergonode\Segment\Infrastructure\Generator\SegmentGeneratorInterface; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; diff --git a/module/segment/src/Application/Form/CreateSegmentForm.php b/module/segment/src/Application/Form/CreateSegmentForm.php index 9746f0a3e..4351f1d97 100644 --- a/module/segment/src/Application/Form/CreateSegmentForm.php +++ b/module/segment/src/Application/Form/CreateSegmentForm.php @@ -27,6 +27,13 @@ class CreateSegmentForm extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options): void { $builder + ->add( + 'condition_set_id', + TextType::class, + [ + 'property_path' => 'conditionSetId', + ] + ) ->add( 'code', TextType::class diff --git a/module/segment/src/Application/Form/Model/CreateSegmentFormModel.php b/module/segment/src/Application/Form/Model/CreateSegmentFormModel.php index 94071e819..98813c4ce 100644 --- a/module/segment/src/Application/Form/Model/CreateSegmentFormModel.php +++ b/module/segment/src/Application/Form/Model/CreateSegmentFormModel.php @@ -9,6 +9,7 @@ namespace Ergonode\Segment\Application\Form\Model; +use Ergonode\Segment\Infrastructure\Validator\UniqueSegmentCode; use Symfony\Component\Validator\Constraints as Assert; /** @@ -18,8 +19,17 @@ class CreateSegmentFormModel /** * @var string * - * @Assert\NotBlank(message="Segment name is required") - * @Assert\Length(max=32) + * @Assert\NotBlank(message="Condition set ID is required") + * @Assert\Uuid() + */ + public $conditionSetId; + + /** + * @var string + * + * @Assert\NotBlank(message="Segment code is required") + * @Assert\Length(max=100) + * @UniqueSegmentCode() */ public $code; @@ -34,7 +44,6 @@ class CreateSegmentFormModel public $description; /** - * CreateSegmentFormModel constructor. */ public function __construct() { diff --git a/module/segment/src/Application/Form/Model/UpdateSegmentFormModel.php b/module/segment/src/Application/Form/Model/UpdateSegmentFormModel.php index afc05c60a..b4967fa1c 100644 --- a/module/segment/src/Application/Form/Model/UpdateSegmentFormModel.php +++ b/module/segment/src/Application/Form/Model/UpdateSegmentFormModel.php @@ -15,6 +15,14 @@ */ class UpdateSegmentFormModel { + /** + * @var string + * + * @Assert\NotBlank(message="Condition set ID is required") + * @Assert\Uuid() + */ + public $conditionSetId; + /** * @var array */ @@ -26,7 +34,6 @@ class UpdateSegmentFormModel public $description; /** - * CreateSegmentFormModel constructor. */ public function __construct() { diff --git a/module/segment/src/Application/Form/UpdateSegmentForm.php b/module/segment/src/Application/Form/UpdateSegmentForm.php index a8647d3b2..ef52c35ed 100644 --- a/module/segment/src/Application/Form/UpdateSegmentForm.php +++ b/module/segment/src/Application/Form/UpdateSegmentForm.php @@ -10,7 +10,6 @@ namespace Ergonode\Segment\Application\Form; use Ergonode\Core\Application\Form\Type\TranslationType; -use Ergonode\Segment\Application\Form\Model\CreateSegmentFormModel; use Ergonode\Segment\Application\Form\Model\UpdateSegmentFormModel; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\TextType; @@ -28,6 +27,13 @@ class UpdateSegmentForm extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options): void { $builder + ->add( + 'condition_set_id', + TextType::class, + [ + 'property_path' => 'conditionSetId', + ] + ) ->add( 'name', TranslationType::class diff --git a/module/segment/src/Domain/Command/CreateSegmentCommand.php b/module/segment/src/Domain/Command/CreateSegmentCommand.php index bbf4a5167..e3b814070 100644 --- a/module/segment/src/Domain/Command/CreateSegmentCommand.php +++ b/module/segment/src/Domain/Command/CreateSegmentCommand.php @@ -9,6 +9,7 @@ namespace Ergonode\Segment\Domain\Command; +use Ergonode\Condition\Domain\Entity\ConditionSetId; use Ergonode\Core\Domain\ValueObject\TranslatableString; use Ergonode\Segment\Domain\Entity\SegmentId; use Ergonode\Segment\Domain\ValueObject\SegmentCode; @@ -32,6 +33,13 @@ class CreateSegmentCommand */ private $code; + /** + * @var ConditionSetId + * + * @JMS\Type("Ergonode\Condition\Domain\Entity\ConditionSetId") + */ + private $conditionSetId; + /** * @var TranslatableString * @@ -48,12 +56,14 @@ class CreateSegmentCommand /** * @param SegmentCode $code + * @param ConditionSetId $conditionSetId * @param TranslatableString $name * @param TranslatableString $description */ - public function __construct(SegmentCode $code, TranslatableString $name, TranslatableString $description) + public function __construct(SegmentCode $code, ConditionSetId $conditionSetId, TranslatableString $name, TranslatableString $description) { $this->id = SegmentId::fromCode($code); + $this->conditionSetId = $conditionSetId; $this->code = $code; $this->name = $name; $this->description = $description; @@ -67,6 +77,14 @@ public function getId(): SegmentId return $this->id; } + /** + * @return ConditionSetId + */ + public function getConditionSetId(): ConditionSetId + { + return $this->conditionSetId; + } + /** * @return SegmentCode */ diff --git a/module/segment/src/Domain/Command/DeleteSegmentCommand.php b/module/segment/src/Domain/Command/DeleteSegmentCommand.php new file mode 100644 index 000000000..d8c39f50d --- /dev/null +++ b/module/segment/src/Domain/Command/DeleteSegmentCommand.php @@ -0,0 +1,40 @@ +id = $id; + } + + /** + * @return SegmentId + */ + public function getId(): SegmentId + { + return $this->id; + } +} diff --git a/module/segment/src/Domain/Command/UpdateSegmentCommand.php b/module/segment/src/Domain/Command/UpdateSegmentCommand.php index 9e8816f62..fe7a7b185 100644 --- a/module/segment/src/Domain/Command/UpdateSegmentCommand.php +++ b/module/segment/src/Domain/Command/UpdateSegmentCommand.php @@ -9,6 +9,7 @@ namespace Ergonode\Segment\Domain\Command; +use Ergonode\Condition\Domain\Entity\ConditionSetId; use Ergonode\Core\Domain\ValueObject\TranslatableString; use Ergonode\Segment\Domain\Entity\SegmentId; use JMS\Serializer\Annotation as JMS; @@ -38,16 +39,27 @@ class UpdateSegmentCommand */ private $description; + /** + * @var ConditionSetId + */ + private $conditionSetId; + /** * @param SegmentId $id * @param TranslatableString $name * @param TranslatableString $description + * @param ConditionSetId $conditionSetId */ - public function __construct(SegmentId $id, TranslatableString $name, TranslatableString $description) - { + public function __construct( + SegmentId $id, + TranslatableString $name, + TranslatableString $description, + ConditionSetId $conditionSetId + ) { $this->id = $id; $this->name = $name; $this->description = $description; + $this->conditionSetId = $conditionSetId; } /** @@ -58,7 +70,7 @@ public function getId(): SegmentId return $this->id; } - /** + /** * @return TranslatableString */ public function getName(): TranslatableString @@ -73,4 +85,12 @@ public function getDescription(): TranslatableString { return $this->description; } + + /** + * @return ConditionSetId + */ + public function getConditionSetId(): ConditionSetId + { + return $this->conditionSetId; + } } diff --git a/module/segment/src/Domain/Entity/Segment.php b/module/segment/src/Domain/Entity/Segment.php index d59b68821..089c9642c 100644 --- a/module/segment/src/Domain/Entity/Segment.php +++ b/module/segment/src/Domain/Entity/Segment.php @@ -9,18 +9,18 @@ namespace Ergonode\Segment\Domain\Entity; -use Ergonode\Segment\Domain\ValueObject\SegmentCode; +use Ergonode\Condition\Domain\Entity\ConditionSetId; +use Ergonode\Core\Domain\Entity\AbstractId; +use Ergonode\Core\Domain\ValueObject\TranslatableString; use Ergonode\EventSourcing\Domain\AbstractAggregateRoot; +use Ergonode\Segment\Domain\Event\SegmentConditionSetChangedEvent; +use Ergonode\Segment\Domain\Event\SegmentCreatedEvent; use Ergonode\Segment\Domain\Event\SegmentDescriptionChangedEvent; use Ergonode\Segment\Domain\Event\SegmentNameChangedEvent; use Ergonode\Segment\Domain\Event\SegmentStatusChangedEvent; -use Ergonode\Segment\Domain\Event\SegmentSpecificationAddedEvent; -use Ergonode\Core\Domain\Entity\AbstractId; -use Ergonode\Segment\Domain\Event\SegmentCreatedEvent; -use Ergonode\Segment\Domain\Specification\SegmentSpecificationInterface; +use Ergonode\Segment\Domain\ValueObject\SegmentCode; use Ergonode\Segment\Domain\ValueObject\SegmentStatus; use JMS\Serializer\Annotation as JMS; -use Ergonode\Core\Domain\ValueObject\TranslatableString; /** * @JMS\ExclusionPolicy("all") @@ -68,22 +68,31 @@ class Segment extends AbstractAggregateRoot private $description; /** - * @var SegmentSpecificationInterface[] + * @var ConditionSetId + * + * @JMS\Type("Ergonode\Condition\Domain\Entity\ConditionSetId") + * @JMS\Expose() */ - private $specifications; + private $conditionSetId; /** * @param SegmentId $id * @param SegmentCode $code + * @param ConditionSetId $conditionSetId * @param TranslatableString $name - * * @param TranslatableString $description * * @throws \Exception */ - public function __construct(SegmentId $id, SegmentCode $code, TranslatableString $name, TranslatableString $description) - { - $this->apply(new SegmentCreatedEvent($id, $code, $name, $description)); + public function __construct( + SegmentId $id, + SegmentCode $code, + ConditionSetId $conditionSetId, + TranslatableString $name, + TranslatableString $description + ) { + $this->status = new SegmentStatus(SegmentStatus::NEW); + $this->apply(new SegmentCreatedEvent($id, $code, $conditionSetId, $name, $description, $this->status)); } /** @@ -102,6 +111,14 @@ public function getCode(): SegmentCode return $this->code; } + /** + * @return ConditionSetId + */ + public function getConditionSetId(): ConditionSetId + { + return $this->conditionSetId; + } + /** * @return TranslatableString */ @@ -163,21 +180,15 @@ public function changeDescription(TranslatableString $description): void } /** - * @param SegmentSpecificationInterface $specification + * @param ConditionSetId $conditionSetId * * @throws \Exception */ - public function addSpecification(SegmentSpecificationInterface $specification): void + public function changeConditionSet(ConditionSetId $conditionSetId): void { - $this->apply(new SegmentSpecificationAddedEvent($specification)); - } - - /** - * @return SegmentSpecificationInterface[] - */ - public function getSpecifications(): array - { - return $this->specifications; + if (!$conditionSetId->isEqual($this->conditionSetId)) { + $this->apply(new SegmentConditionSetChangedEvent($this->conditionSetId, $conditionSetId)); + } } /** @@ -187,18 +198,10 @@ protected function applySegmentCreatedEvent(SegmentCreatedEvent $event): void { $this->id = $event->getId(); $this->code = $event->getCode(); + $this->conditionSetId = $event->getConditionSetId(); $this->name = $event->getName(); $this->description = $event->getDescription(); - $this->status = new SegmentStatus(); - $this->specifications = []; - } - - /** - * @param SegmentSpecificationAddedEvent $event - */ - protected function applySegmentSpecificationAddedEvent(SegmentSpecificationAddedEvent $event): void - { - $this->specifications[] = $event->getSpecification(); + $this->status = $event->getStatus(); } /** @@ -224,4 +227,12 @@ protected function applySegmentDescriptionChangedEvent(SegmentDescriptionChanged { $this->description = $event->getTo(); } + + /** + * @param SegmentConditionSetChangedEvent $event + */ + protected function applySegmentConditionSetChangedEvent(SegmentConditionSetChangedEvent $event): void + { + $this->conditionSetId = $event->getTo(); + } } diff --git a/module/segment/src/Domain/Event/SegmentConditionSetChangedEvent.php b/module/segment/src/Domain/Event/SegmentConditionSetChangedEvent.php new file mode 100644 index 000000000..1f9ce772d --- /dev/null +++ b/module/segment/src/Domain/Event/SegmentConditionSetChangedEvent.php @@ -0,0 +1,59 @@ +from = $from; + $this->to = $to; + } + + /** + * @return ConditionSetId + */ + public function getFrom(): ConditionSetId + { + return $this->from; + } + + /** + * @return ConditionSetId + */ + public function getTo(): ConditionSetId + { + return $this->to; + } +} diff --git a/module/segment/src/Domain/Event/SegmentCreatedEvent.php b/module/segment/src/Domain/Event/SegmentCreatedEvent.php index 4c5407c72..f91c46e03 100644 --- a/module/segment/src/Domain/Event/SegmentCreatedEvent.php +++ b/module/segment/src/Domain/Event/SegmentCreatedEvent.php @@ -9,10 +9,12 @@ namespace Ergonode\Segment\Domain\Event; -use Ergonode\Segment\Domain\ValueObject\SegmentCode; +use Ergonode\Condition\Domain\Entity\ConditionSetId; use Ergonode\Core\Domain\ValueObject\TranslatableString; -use Ergonode\Segment\Domain\Entity\SegmentId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; +use Ergonode\Segment\Domain\Entity\SegmentId; +use Ergonode\Segment\Domain\ValueObject\SegmentCode; +use Ergonode\Segment\Domain\ValueObject\SegmentStatus; use JMS\Serializer\Annotation as JMS; /** @@ -33,6 +35,13 @@ class SegmentCreatedEvent implements DomainEventInterface */ private $code; + /** + * @var ConditionSetId + * + * @JMS\Type("Ergonode\Condition\Domain\Entity\ConditionSetId") + */ + private $conditionSetId; + /** * @var TranslatableString * @@ -47,18 +56,35 @@ class SegmentCreatedEvent implements DomainEventInterface */ private $description; + /** + * @var SegmentStatus + * + * @JMS\Type("Ergonode\Segment\Domain\ValueObject\SegmentStatus") + */ + private $status; + /** * @param SegmentId $id * @param SegmentCode $code + * @param ConditionSetId $conditionSetId * @param TranslatableString $name * @param TranslatableString $description + * @param SegmentStatus $status */ - public function __construct(SegmentId $id, SegmentCode $code, TranslatableString $name, TranslatableString $description) - { + public function __construct( + SegmentId $id, + SegmentCode $code, + ConditionSetId $conditionSetId, + TranslatableString $name, + TranslatableString $description, + SegmentStatus $status + ) { $this->id = $id; $this->code = $code; + $this->conditionSetId = $conditionSetId; $this->name = $name; $this->description = $description; + $this->status = $status; } /** @@ -77,6 +103,14 @@ public function getCode(): SegmentCode return $this->code; } + /** + * @return ConditionSetId + */ + public function getConditionSetId(): ConditionSetId + { + return $this->conditionSetId; + } + /** * @return TranslatableString */ @@ -92,4 +126,12 @@ public function getDescription(): TranslatableString { return $this->description; } + + /** + * @return SegmentStatus + */ + public function getStatus(): SegmentStatus + { + return $this->status; + } } diff --git a/module/segment/src/Domain/Event/SegmentDeletedEvent.php b/module/segment/src/Domain/Event/SegmentDeletedEvent.php new file mode 100644 index 000000000..be3a8625b --- /dev/null +++ b/module/segment/src/Domain/Event/SegmentDeletedEvent.php @@ -0,0 +1,18 @@ +specification = $specification; - } - - /** - * @return SegmentSpecificationInterface - */ - public function getSpecification(): SegmentSpecificationInterface - { - return $this->specification; - } -} diff --git a/module/segment/src/Domain/Query/SegmentQueryInterface.php b/module/segment/src/Domain/Query/SegmentQueryInterface.php index f6232dfd2..3704a0dff 100644 --- a/module/segment/src/Domain/Query/SegmentQueryInterface.php +++ b/module/segment/src/Domain/Query/SegmentQueryInterface.php @@ -9,8 +9,10 @@ namespace Ergonode\Segment\Domain\Query; +use Ergonode\Condition\Domain\Entity\ConditionSetId; use Ergonode\Core\Domain\ValueObject\Language; use Ergonode\Grid\DbalDataSet; +use Ergonode\Segment\Domain\ValueObject\SegmentCode; /** */ @@ -22,4 +24,18 @@ interface SegmentQueryInterface * @return DbalDataSet */ public function getDataSet(Language $language): DbalDataSet; + + /** + * @param ConditionSetId $conditionSetId + * + * @return array + */ + public function findIdByConditionSetId(ConditionSetId $conditionSetId): array; + + /** + * @param SegmentCode $segmentCode + * + * @return bool + */ + public function isExistsByCode(SegmentCode $segmentCode): bool; } diff --git a/module/segment/src/Domain/Repository/SegmentRepositoryInterface.php b/module/segment/src/Domain/Repository/SegmentRepositoryInterface.php index cbb333a5f..2e1628d31 100644 --- a/module/segment/src/Domain/Repository/SegmentRepositoryInterface.php +++ b/module/segment/src/Domain/Repository/SegmentRepositoryInterface.php @@ -35,4 +35,9 @@ public function save(AbstractAggregateRoot $aggregateRoot): void; * @return bool */ public function exists(SegmentId $id): bool; + + /** + * @param AbstractAggregateRoot $aggregateRoot + */ + public function delete(AbstractAggregateRoot $aggregateRoot): void; } diff --git a/module/segment/src/Domain/Specification/SegmentSpecificationInterface.php b/module/segment/src/Domain/Specification/SegmentSpecificationInterface.php deleted file mode 100644 index 616e6747f..000000000 --- a/module/segment/src/Domain/Specification/SegmentSpecificationInterface.php +++ /dev/null @@ -1,24 +0,0 @@ -getFilters(); + $statuses = array_combine(SegmentStatus::AVAILABLE, SegmentStatus::AVAILABLE); + $id = new TextColumn('id', $this->trans('Id'), new TextFilter()); $id->setVisible(false); $this->addColumn('id', $id); $this->addColumn('code', new TextColumn('name', $this->trans('Code'), new TextFilter($filters->getString('code')))); + $this->addColumn('status', new SelectColumn('status', $this->trans('Status'), new SelectFilter($statuses, $filters->getString('status')))); $this->addColumn('name', new TextColumn('name', $this->trans('Name'), new TextFilter($filters->getString('name')))); - $this->addColumn('status', new TextColumn('status', $this->trans('Status'), new TextFilter($filters->getString('status')))); $this->addColumn('description', new TextColumn('description', $this->trans('Description'), new TextFilter($filters->getString('description')))); + $this->addColumn('edit', new ActionColumn('edit')); + $this->setConfiguration(self::PARAMETER_ALLOW_COLUMN_RESIZE, true); $this->orderBy('id', 'DESC'); } diff --git a/module/segment/src/Infrastructure/Handler/CreateSegmentCommandHandler.php b/module/segment/src/Infrastructure/Handler/CreateSegmentCommandHandler.php index 7ec17682e..011eeab58 100644 --- a/module/segment/src/Infrastructure/Handler/CreateSegmentCommandHandler.php +++ b/module/segment/src/Infrastructure/Handler/CreateSegmentCommandHandler.php @@ -40,6 +40,7 @@ public function __invoke(CreateSegmentCommand $command) $segment = new Segment( $command->getId(), $command->getCode(), + $command->getConditionSetId(), $command->getName(), $command->getDescription() ); diff --git a/module/segment/src/Infrastructure/Handler/DeleteSegmentCommandHandler.php b/module/segment/src/Infrastructure/Handler/DeleteSegmentCommandHandler.php new file mode 100644 index 000000000..1738b25ca --- /dev/null +++ b/module/segment/src/Infrastructure/Handler/DeleteSegmentCommandHandler.php @@ -0,0 +1,45 @@ +repository = $repository; + } + + /** + * @param DeleteSegmentCommand $command + * + * @throws \Exception + */ + public function __invoke(DeleteSegmentCommand $command) + { + $conditionSet = $this->repository->load($command->getId()); + Assert::notNull($conditionSet); + + $this->repository->delete($conditionSet); + } +} diff --git a/module/segment/src/Infrastructure/Handler/UpdateSegmentCommandHandler.php b/module/segment/src/Infrastructure/Handler/UpdateSegmentCommandHandler.php index e09844aba..b60bf3974 100644 --- a/module/segment/src/Infrastructure/Handler/UpdateSegmentCommandHandler.php +++ b/module/segment/src/Infrastructure/Handler/UpdateSegmentCommandHandler.php @@ -43,6 +43,7 @@ public function __invoke(UpdateSegmentCommand $command) $segment->changeName($command->getName()); $segment->changeDescription($command->getDescription()); + $segment->changeConditionSet($command->getConditionSetId()); $this->repository->save($segment); } diff --git a/module/segment/src/Infrastructure/Specification/AbstractSpecification.php b/module/segment/src/Infrastructure/Specification/AbstractSpecification.php deleted file mode 100644 index 24f2be3ab..000000000 --- a/module/segment/src/Infrastructure/Specification/AbstractSpecification.php +++ /dev/null @@ -1,25 +0,0 @@ -code = $code; - } - - /** - * @param AbstractProduct $product - * - * @return bool - */ - public function isSatisfiedBy(AbstractProduct $product): bool - { - return $product->hasAttribute($this->code); - } -} diff --git a/module/segment/src/Infrastructure/Specification/AttributeValueSpecification.php b/module/segment/src/Infrastructure/Specification/AttributeValueSpecification.php deleted file mode 100644 index 79b3b0aa5..000000000 --- a/module/segment/src/Infrastructure/Specification/AttributeValueSpecification.php +++ /dev/null @@ -1,62 +0,0 @@ -code = $code; - $this->value = $value; - } - - /** - * @param AbstractProduct $product - * - * @return bool - */ - public function isSatisfiedBy(AbstractProduct $product): bool - { - if ($product->hasAttribute($this->code)) { - $value = $product->getAttribute($this->code); - if ($value->getValue() === $this->value->getValue()) { - return true; - } - } - - return false; - } -} diff --git a/module/segment/src/Infrastructure/Validator/UniqueSegmentCode.php b/module/segment/src/Infrastructure/Validator/UniqueSegmentCode.php new file mode 100644 index 000000000..18c8ec34b --- /dev/null +++ b/module/segment/src/Infrastructure/Validator/UniqueSegmentCode.php @@ -0,0 +1,28 @@ +query = $query; + } + + /** + * @param mixed $value + * @param UniqueSegmentCode|Constraint $constraint + */ + public function validate($value, Constraint $constraint): void + { + if (!$constraint instanceof UniqueSegmentCode) { + throw new UnexpectedTypeException($constraint, UniqueSegmentCode::class); + } + + if (null === $value || '' === $value) { + return; + } + + if (!is_scalar($value) && !(\is_object($value) && method_exists($value, '__toString'))) { + throw new UnexpectedTypeException($value, 'string'); + } + + $value = (string) $value; + + if (!SegmentCode::isValid($value)) { + $this->context->buildViolation($constraint->validMessage) + ->setParameter('{{ value }}', $value) + ->addViolation(); + + return; + } + + if ($this->query->isExistsByCode(new SegmentCode($value))) { + $this->context->buildViolation($constraint->uniqueMessage) + ->addViolation(); + } + } +} diff --git a/module/segment/src/Persistence/Dbal/Projector/SegmentConditionSetChangedEventProjector.php b/module/segment/src/Persistence/Dbal/Projector/SegmentConditionSetChangedEventProjector.php new file mode 100644 index 000000000..6b1ddb5f7 --- /dev/null +++ b/module/segment/src/Persistence/Dbal/Projector/SegmentConditionSetChangedEventProjector.php @@ -0,0 +1,72 @@ +connection = $connection; + } + + /** + * @param DomainEventInterface $event + * + * @return bool + */ + public function support(DomainEventInterface $event): bool + { + return $event instanceof SegmentConditionSetChangedEvent; + } + + /** + * @param AbstractId $aggregateId + * @param DomainEventInterface $event + * + * @throws UnsupportedEventException + * @throws DBALException + */ + public function projection(AbstractId $aggregateId, DomainEventInterface $event): void + { + if (!$this->support($event)) { + throw new UnsupportedEventException($event, SegmentConditionSetChangedEvent::class); + } + + $this->connection->update( + self::TABLE, + [ + 'condition_set_id' => $event->getTo()->getValue(), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); + } +} diff --git a/module/segment/src/Persistence/Dbal/Projector/SegmentCreatedEventProjector.php b/module/segment/src/Persistence/Dbal/Projector/SegmentCreatedEventProjector.php index dbe473c03..9d4e44ec1 100644 --- a/module/segment/src/Persistence/Dbal/Projector/SegmentCreatedEventProjector.php +++ b/module/segment/src/Persistence/Dbal/Projector/SegmentCreatedEventProjector.php @@ -10,9 +10,9 @@ namespace Ergonode\Segment\Persistence\Dbal\Projector; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\DBALException; use Ergonode\Core\Domain\Entity\AbstractId; use Ergonode\EventSourcing\Infrastructure\DomainEventInterface; -use Ergonode\EventSourcing\Infrastructure\Exception\ProjectorException; use Ergonode\EventSourcing\Infrastructure\Exception\UnsupportedEventException; use Ergonode\EventSourcing\Infrastructure\Projector\DomainEventProjectorInterface; use Ergonode\Segment\Domain\Event\SegmentCreatedEvent; @@ -59,9 +59,8 @@ public function support(DomainEventInterface $event): bool * @param AbstractId $aggregateId * @param DomainEventInterface $event * - * @throws ProjectorException * @throws UnsupportedEventException - * @throws \Doctrine\DBAL\ConnectionException + * @throws DBALException */ public function projection(AbstractId $aggregateId, DomainEventInterface $event): void { @@ -69,22 +68,16 @@ public function projection(AbstractId $aggregateId, DomainEventInterface $event) throw new UnsupportedEventException($event, SegmentCreatedEvent::class); } - try { - $this->connection->beginTransaction(); - $this->connection->insert( - self::TABLE, - [ - 'id' => $event->getId()->getValue(), - 'code' => $event->getCode(), - 'name' => $this->serializer->serialize($event->getName(), 'json'), - 'description' => $this->serializer->serialize($event->getDescription(), 'json'), - 'status' => SegmentStatus::NEW, - ] - ); - $this->connection->commit(); - } catch (\Throwable $exception) { - $this->connection->rollBack(); - throw new ProjectorException($event, $exception); - } + $this->connection->insert( + self::TABLE, + [ + 'id' => $event->getId()->getValue(), + 'code' => $event->getCode(), + 'name' => $this->serializer->serialize($event->getName(), 'json'), + 'description' => $this->serializer->serialize($event->getDescription(), 'json'), + 'status' => SegmentStatus::NEW, + 'condition_set_id' => $event->getConditionSetId()->getValue(), + ] + ); } } diff --git a/module/segment/src/Persistence/Dbal/Projector/SegmentDeletedEventProjector.php b/module/segment/src/Persistence/Dbal/Projector/SegmentDeletedEventProjector.php new file mode 100644 index 000000000..f3fff888c --- /dev/null +++ b/module/segment/src/Persistence/Dbal/Projector/SegmentDeletedEventProjector.php @@ -0,0 +1,62 @@ +connection = $connection; + } + + /** + * {@inheritDoc} + */ + public function support(DomainEventInterface $event): bool + { + return $event instanceof SegmentDeletedEvent; + } + + /** + * {@inheritDoc} + */ + public function projection(AbstractId $aggregateId, DomainEventInterface $event): void + { + if (!$this->support($event)) { + throw new UnsupportedEventException($event, SegmentDeletedEvent::class); + } + + $this->connection->delete( + self::TABLE, + [ + 'id' => $aggregateId->getValue(), + ] + ); + } +} diff --git a/module/segment/src/Persistence/Dbal/Projector/SegmentDescriptionChangedEventProjector.php b/module/segment/src/Persistence/Dbal/Projector/SegmentDescriptionChangedEventProjector.php new file mode 100644 index 000000000..6900d412d --- /dev/null +++ b/module/segment/src/Persistence/Dbal/Projector/SegmentDescriptionChangedEventProjector.php @@ -0,0 +1,80 @@ +connection = $connection; + $this->serializer = $serializer; + } + + /** + * @param DomainEventInterface $event + * + * @return bool + */ + public function support(DomainEventInterface $event): bool + { + return $event instanceof SegmentDescriptionChangedEvent; + } + + /** + * @param AbstractId $aggregateId + * @param DomainEventInterface $event + * + * @throws UnsupportedEventException + * @throws DBALException + */ + public function projection(AbstractId $aggregateId, DomainEventInterface $event): void + { + if (!$event instanceof SegmentDescriptionChangedEvent) { + throw new UnsupportedEventException($event, SegmentDescriptionChangedEvent::class); + } + + $this->connection->update( + self::TABLE, + [ + 'description' => $this->serializer->serialize($event->getTo(), 'json'), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); + } +} diff --git a/module/segment/src/Persistence/Dbal/Projector/SegmentNameChangedEventProjector.php b/module/segment/src/Persistence/Dbal/Projector/SegmentNameChangedEventProjector.php new file mode 100644 index 000000000..52736982d --- /dev/null +++ b/module/segment/src/Persistence/Dbal/Projector/SegmentNameChangedEventProjector.php @@ -0,0 +1,80 @@ +connection = $connection; + $this->serializer = $serializer; + } + + /** + * @param DomainEventInterface $event + * + * @return bool + */ + public function support(DomainEventInterface $event): bool + { + return $event instanceof SegmentNameChangedEvent; + } + + /** + * @param AbstractId $aggregateId + * @param DomainEventInterface $event + * + * @throws UnsupportedEventException + * @throws DBALException + */ + public function projection(AbstractId $aggregateId, DomainEventInterface $event): void + { + if (!$event instanceof SegmentNameChangedEvent) { + throw new UnsupportedEventException($event, SegmentNameChangedEvent::class); + } + + $this->connection->update( + self::TABLE, + [ + 'name' => $this->serializer->serialize($event->getTo(), 'json'), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); + } +} diff --git a/module/segment/src/Persistence/Dbal/Projector/SegmentStatusChangedEventProjector.php b/module/segment/src/Persistence/Dbal/Projector/SegmentStatusChangedEventProjector.php new file mode 100644 index 000000000..ef87d4fa2 --- /dev/null +++ b/module/segment/src/Persistence/Dbal/Projector/SegmentStatusChangedEventProjector.php @@ -0,0 +1,80 @@ +connection = $connection; + $this->serializer = $serializer; + } + + /** + * @param DomainEventInterface $event + * + * @return bool + */ + public function support(DomainEventInterface $event): bool + { + return $event instanceof SegmentStatusChangedEvent; + } + + /** + * @param AbstractId $aggregateId + * @param DomainEventInterface $event + * + * @throws UnsupportedEventException + * @throws DBALException + */ + public function projection(AbstractId $aggregateId, DomainEventInterface $event): void + { + if (!$event instanceof SegmentStatusChangedEvent) { + throw new UnsupportedEventException($event, SegmentStatusChangedEvent::class); + } + + $this->connection->update( + self::TABLE, + [ + 'status' => (string) $event->getTo(), + ], + [ + 'id' => $aggregateId->getValue(), + ] + ); + } +} diff --git a/module/segment/src/Persistence/Dbal/Query/DbalSegmentQuery.php b/module/segment/src/Persistence/Dbal/Query/DbalSegmentQuery.php index 33050437b..74645e7e6 100644 --- a/module/segment/src/Persistence/Dbal/Query/DbalSegmentQuery.php +++ b/module/segment/src/Persistence/Dbal/Query/DbalSegmentQuery.php @@ -11,9 +11,11 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Query\QueryBuilder; +use Ergonode\Condition\Domain\Entity\ConditionSetId; use Ergonode\Core\Domain\ValueObject\Language; use Ergonode\Grid\DbalDataSet; use Ergonode\Segment\Domain\Query\SegmentQueryInterface; +use Ergonode\Segment\Domain\ValueObject\SegmentCode; /** */ @@ -40,15 +42,11 @@ public function __construct(Connection $connection) } /** - * @param Language $language - * - * @return DbalDataSet + * {@inheritDoc} */ public function getDataSet(Language $language): DbalDataSet { $query = $this->getQuery(); - $query->addSelect('id'); - $query->addSelect('code'); $query->addSelect(sprintf('(name->>\'%s\') AS name', $language->getCode())); $query->addSelect(sprintf('(description->>\'%s\') AS description', $language->getCode())); @@ -59,6 +57,43 @@ public function getDataSet(Language $language): DbalDataSet return new DbalDataSet($result); } + /** + * {@inheritDoc} + */ + public function findIdByConditionSetId(ConditionSetId $conditionSetId): array + { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder + ->select('id') + ->from(self::TABLE) + ->where('condition_set_id = :id') + ->setParameter('id', $conditionSetId->getValue()); + + $result = $queryBuilder->execute()->fetchAll(\PDO::FETCH_COLUMN); + if (false === $result) { + $result = []; + } + + return $result; + } + + /** + * {@inheritDoc} + */ + public function isExistsByCode(SegmentCode $segmentCode): bool + { + $queryBuilder = $this->connection->createQueryBuilder() + ->select('id') + ->from(self::TABLE) + ->where('code = :code') + ->setParameter('code', $segmentCode->getValue()) + ->setMaxResults(1); + + $result = $queryBuilder->execute()->fetchColumn(); + + return !empty($result); + } + /** * @return QueryBuilder */ diff --git a/module/segment/src/Persistence/Dbal/Repository/DbalSegmentRepository.php b/module/segment/src/Persistence/Dbal/Repository/DbalSegmentRepository.php index 3e1a03f19..9fd4437cb 100644 --- a/module/segment/src/Persistence/Dbal/Repository/DbalSegmentRepository.php +++ b/module/segment/src/Persistence/Dbal/Repository/DbalSegmentRepository.php @@ -10,10 +10,11 @@ namespace Ergonode\Segment\Persistence\Dbal\Repository; use Ergonode\EventSourcing\Domain\AbstractAggregateRoot; -use Ergonode\Segment\Domain\Entity\Segment; -use Ergonode\Segment\Domain\Entity\SegmentId; use Ergonode\EventSourcing\Infrastructure\DomainEventDispatcherInterface; use Ergonode\EventSourcing\Infrastructure\DomainEventStoreInterface; +use Ergonode\Segment\Domain\Entity\Segment; +use Ergonode\Segment\Domain\Entity\SegmentId; +use Ergonode\Segment\Domain\Event\SegmentDeletedEvent; use Ergonode\Segment\Domain\Repository\SegmentRepositoryInterface; /** @@ -41,9 +42,8 @@ public function __construct(DomainEventStoreInterface $eventStore, DomainEventDi } /** - * @param SegmentId $id + * {@inheritDoc} * - * @return Segment|null * @throws \ReflectionException */ public function load(SegmentId $id): ?AbstractAggregateRoot @@ -67,7 +67,7 @@ public function load(SegmentId $id): ?AbstractAggregateRoot } /** - * @param AbstractAggregateRoot $aggregateRoot + * {@inheritDoc} */ public function save(AbstractAggregateRoot $aggregateRoot): void { @@ -80,9 +80,7 @@ public function save(AbstractAggregateRoot $aggregateRoot): void } /** - * @param SegmentId $id - * - * @return bool + * {@inheritDoc} */ public function exists(SegmentId $id): bool { @@ -90,4 +88,17 @@ public function exists(SegmentId $id): bool return \count($eventStream) > 0; } + + /** + * {@inheritDoc} + * + * @throws \Exception + */ + public function delete(AbstractAggregateRoot $aggregateRoot): void + { + $aggregateRoot->apply(new SegmentDeletedEvent()); + $this->save($aggregateRoot); + + $this->eventStore->delete($aggregateRoot->getId()); + } } diff --git a/module/segment/src/Resources/config/routes.yml b/module/segment/src/Resources/config/routes.yml index dac9b2caa..6087d5e64 100644 --- a/module/segment/src/Resources/config/routes.yml +++ b/module/segment/src/Resources/config/routes.yml @@ -1,4 +1,4 @@ -ergonode_channel_api: +ergonode_segment_api: resource: '../../Application/Controller/Api/Segment*' type: annotation - prefix: /api/v1/{language} + prefix: /api/v1/{language} \ No newline at end of file diff --git a/module/segment/src/Resources/config/services.yml b/module/segment/src/Resources/config/services.yml index 629c25d8e..331c79dc4 100644 --- a/module/segment/src/Resources/config/services.yml +++ b/module/segment/src/Resources/config/services.yml @@ -10,6 +10,10 @@ services: Ergonode\Segment\Persistence\: resource: '../../Persistence/*' + Ergonode\Segment\Domain\: + resource: '../../Domain/*' + exclude: '../../Domain/{Entity,ValueObject,Type,Condition}' + Ergonode\Segment\Infrastructure\: resource: '../../Infrastructure/*' exclude: '../../Infrastructure/{Specification}' diff --git a/module/segment/src/Resources/translations/grid.en.yaml b/module/segment/src/Resources/translations/grid.en.yaml new file mode 100644 index 000000000..6d06acbd8 --- /dev/null +++ b/module/segment/src/Resources/translations/grid.en.yaml @@ -0,0 +1,5 @@ +Code: Code +Name: Name +Description: Description +Edit: Edit +Status: Status diff --git a/module/segment/src/Resources/translations/grid.pl.yaml b/module/segment/src/Resources/translations/grid.pl.yaml new file mode 100644 index 000000000..d52851ca9 --- /dev/null +++ b/module/segment/src/Resources/translations/grid.pl.yaml @@ -0,0 +1,5 @@ +Code: Kod +Name: Nazwa +Description: Opis +Edit: Edycja +Status: Status diff --git a/module/segment/src/Resources/translations/log.en.yaml b/module/segment/src/Resources/translations/log.en.yaml index 1589c9bd6..5c198c19a 100644 --- a/module/segment/src/Resources/translations/log.en.yaml +++ b/module/segment/src/Resources/translations/log.en.yaml @@ -3,3 +3,5 @@ 'Segment name changed': 'Segment name changed' 'Segment specification added': 'Segment specification added' 'Segment status changed': 'Segment status changed' +'Segment deleted': 'Segment "%id%" deleted' +'Segment condition set changed': 'Segment condition set changed' diff --git a/module/segment/src/Resources/translations/log.pl.yaml b/module/segment/src/Resources/translations/log.pl.yaml index 3297e1439..d29a99762 100644 --- a/module/segment/src/Resources/translations/log.pl.yaml +++ b/module/segment/src/Resources/translations/log.pl.yaml @@ -3,3 +3,5 @@ 'Segment name changed': 'Zmieniono nazwę segmentu' 'Segment specification added': 'Dodano specyfikację do segmentu' 'Segment status changed': 'Zmieniono status segmentu' +'Segment deleted': 'Segment "%id%" został usunięty' +'Segment condition set changed': 'Zmieniono przypisanie do zbioru warunków w segmencie' diff --git a/module/segment/src/Resources/translations/privilege.en.yaml b/module/segment/src/Resources/translations/privilege.en.yaml new file mode 100644 index 000000000..8d2cd6f43 --- /dev/null +++ b/module/segment/src/Resources/translations/privilege.en.yaml @@ -0,0 +1 @@ +Segment: Segments diff --git a/module/segment/src/Resources/translations/privilege.pl.yaml b/module/segment/src/Resources/translations/privilege.pl.yaml new file mode 100644 index 000000000..fbf341d29 --- /dev/null +++ b/module/segment/src/Resources/translations/privilege.pl.yaml @@ -0,0 +1 @@ +Segment: Segmenty diff --git a/module/segment/src/Resources/translations/segment.en.yaml b/module/segment/src/Resources/translations/segment.en.yaml new file mode 100644 index 000000000..b1c8e8813 --- /dev/null +++ b/module/segment/src/Resources/translations/segment.en.yaml @@ -0,0 +1,2 @@ +ATTRIBUTE_EXISTS_CONDITION: Attribute exists +ATTRIBUTE_EXISTS_CONDITION_PHRASE: Attribute [attribute] exists diff --git a/module/segment/src/Resources/translations/segment.pl.yaml b/module/segment/src/Resources/translations/segment.pl.yaml new file mode 100644 index 000000000..345fca8d6 --- /dev/null +++ b/module/segment/src/Resources/translations/segment.pl.yaml @@ -0,0 +1,2 @@ +ATTRIBUTE_EXISTS_CONDITION: Posiada attrybut +ATTRIBUTE_EXISTS_CONDITION_PHRASE: Posiada attrybut [attribute] diff --git a/module/segment/tests/Domain/Command/CreateSegmentCommandTest.php b/module/segment/tests/Domain/Command/CreateSegmentCommandTest.php index 626c888f2..ee0530852 100644 --- a/module/segment/tests/Domain/Command/CreateSegmentCommandTest.php +++ b/module/segment/tests/Domain/Command/CreateSegmentCommandTest.php @@ -9,6 +9,7 @@ namespace Ergonode\Segment\Tests\Domain\Command; +use Ergonode\Condition\Domain\Entity\ConditionSetId; use Ergonode\Core\Domain\ValueObject\TranslatableString; use Ergonode\Segment\Domain\Command\CreateSegmentCommand; use Ergonode\Segment\Domain\ValueObject\SegmentCode; @@ -24,6 +25,8 @@ class CreateSegmentCommandTest extends TestCase */ public function testCommand(): void { + /** @var ConditionSetId|MockObject $conditionSetId */ + $conditionSetId = $this->createMock(ConditionSetId::class); /** @var SegmentCode|MockObject $code */ $code = $this->createMock(SegmentCode::class); /** @var TranslatableString $name */ @@ -31,9 +34,10 @@ public function testCommand(): void /** @var TranslatableString $description */ $description = $this->createMock(TranslatableString::class); - $command = new CreateSegmentCommand($code, $name, $description); + $command = new CreateSegmentCommand($code, $conditionSetId, $name, $description); $this->assertEquals($code, $command->getCode()); $this->assertEquals($name, $command->getName()); $this->assertEquals($description, $command->getDescription()); + $this->assertEquals($conditionSetId, $command->getConditionSetId()); } } diff --git a/module/segment/tests/Domain/Command/UpdateSegmentCommandTest.php b/module/segment/tests/Domain/Command/UpdateSegmentCommandTest.php index 71967be7a..aa8fc3fe7 100644 --- a/module/segment/tests/Domain/Command/UpdateSegmentCommandTest.php +++ b/module/segment/tests/Domain/Command/UpdateSegmentCommandTest.php @@ -9,10 +9,10 @@ namespace Ergonode\Segment\Tests\Domain\Command; +use Ergonode\Condition\Domain\Entity\ConditionSetId; use Ergonode\Core\Domain\ValueObject\TranslatableString; use Ergonode\Segment\Domain\Command\UpdateSegmentCommand; use Ergonode\Segment\Domain\Entity\SegmentId; -use Ergonode\Segment\Domain\ValueObject\SegmentCode; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -29,10 +29,13 @@ public function testCommand(): void $name = $this->createMock(TranslatableString::class); /** @var TranslatableString $description */ $description = $this->createMock(TranslatableString::class); + /** @var ConditionSetId $conditionSetId */ + $conditionSetId = $this->createMock(ConditionSetId::class); - $command = new UpdateSegmentCommand($id, $name, $description); + $command = new UpdateSegmentCommand($id, $name, $description, $conditionSetId); $this->assertEquals($id, $command->getId()); $this->assertEquals($name, $command->getName()); $this->assertEquals($description, $command->getDescription()); + $this->assertEquals($conditionSetId, $command->getConditionSetId()); } } diff --git a/module/segment/tests/Domain/Entity/SegmentTest.php b/module/segment/tests/Domain/Entity/SegmentTest.php index 204d16261..277469564 100644 --- a/module/segment/tests/Domain/Entity/SegmentTest.php +++ b/module/segment/tests/Domain/Entity/SegmentTest.php @@ -9,10 +9,11 @@ namespace Ergonode\Segment\Tests\Domain\Entity; -use Ergonode\Segment\Domain\ValueObject\SegmentCode; +use Ergonode\Condition\Domain\Entity\ConditionSetId; use Ergonode\Core\Domain\ValueObject\TranslatableString; use Ergonode\Segment\Domain\Entity\Segment; use Ergonode\Segment\Domain\Entity\SegmentId; +use Ergonode\Segment\Domain\ValueObject\SegmentCode; use Ergonode\Segment\Domain\ValueObject\SegmentStatus; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -41,6 +42,11 @@ class SegmentTest extends TestCase */ private $description; + /** + * @var ConditionSetId|MockObject + */ + private $conditionSetId; + /** */ protected function setUp() @@ -49,6 +55,7 @@ protected function setUp() $this->code = $this->createMock(SegmentCode::class); $this->name = $this->createMock(TranslatableString::class); $this->description = $this->createMock(TranslatableString::class); + $this->conditionSetId = $this->createMock(ConditionSetId::class); } /** @@ -56,18 +63,13 @@ protected function setUp() */ public function testSegmentCreation():void { - /** @var TranslatableString $name */ - $name = $this->createMock(TranslatableString::class); - /** @var TranslatableString $description */ - $description = $this->createMock(TranslatableString::class); - /** @var SegmentStatus $name */ - $status = $this->createMock(SegmentStatus::class); + $segment = new Segment($this->id, $this->code, $this->conditionSetId, $this->name, $this->description); - $segment = new Segment($this->id, $this->code, $this->name, $this->description); $this->assertEquals($this->id, $segment->getId()); $this->assertEquals($this->code, $segment->getCode()); $this->assertEquals($this->name, $segment->getName()); $this->assertEquals($this->description, $segment->getDescription()); + $this->assertEquals($this->conditionSetId, $segment->getConditionSetId()); $this->assertEquals(new SegmentStatus(SegmentStatus::NEW), $segment->getStatus()); } @@ -79,20 +81,28 @@ public function testSegmentManipulation():void /** @var TranslatableString|MockObject $name */ $name = $this->createMock(TranslatableString::class); $name->method('isEqual')->willReturn(false); + /** @var TranslatableString|MockObject $description */ $description = $this->createMock(TranslatableString::class); $description->method('isEqual')->willReturn(false); + /** @var SegmentStatus|MockObject $status */ $status = $this->createMock(SegmentStatus::class); $status->method('isEqual')->willReturn(false); - $segment = new Segment($this->id, $this->code, $this->name, $this->description); + /** @var ConditionSetId|MockObject $conditionSet */ + $conditionSetId = $this->createMock(ConditionSetId::class); + $conditionSetId->method('isEqual')->willReturn(false); + + $segment = new Segment($this->id, $this->code, $this->conditionSetId, $this->name, $this->description); $segment->changeStatus($status); $segment->changeName($name); $segment->changeDescription($description); + $segment->changeConditionSet($conditionSetId); $this->assertSame($name, $segment->getName()); $this->assertSame($description, $segment->getDescription()); $this->assertSame($status, $segment->getStatus()); + $this->assertSame($conditionSetId, $segment->getConditionSetId()); } } diff --git a/module/segment/tests/Domain/Event/SegmentCreatedEventTest.php b/module/segment/tests/Domain/Event/SegmentCreatedEventTest.php index aebb10ba0..ab4b3ec26 100644 --- a/module/segment/tests/Domain/Event/SegmentCreatedEventTest.php +++ b/module/segment/tests/Domain/Event/SegmentCreatedEventTest.php @@ -9,10 +9,12 @@ namespace Ergonode\Segment\Tests\Domain\Event; +use Ergonode\Condition\Domain\Entity\ConditionSetId; use Ergonode\Core\Domain\ValueObject\TranslatableString; use Ergonode\Segment\Domain\Entity\SegmentId; use Ergonode\Segment\Domain\Event\SegmentCreatedEvent; use Ergonode\Segment\Domain\ValueObject\SegmentCode; +use Ergonode\Segment\Domain\ValueObject\SegmentStatus; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -32,11 +34,18 @@ public function testEventCreation(): void $description = $this->createMock(TranslatableString::class); /** @var SegmentCode|MockObject $code */ $code = $this->createMock(SegmentCode::class); + /** @var ConditionSetId|MockObject $conditionSetId */ + $conditionSetId = $this->createMock(ConditionSetId::class); + /** @var SegmentStatus|MockObject $status */ + $status = $this->createMock(SegmentStatus::class); + + $event = new SegmentCreatedEvent($id, $code, $conditionSetId, $name, $description, $status); - $event = new SegmentCreatedEvent($id, $code, $name, $description); $this->assertSame($id, $event->getId()); $this->assertSame($code, $event->getCode()); $this->assertSame($name, $event->getName()); $this->assertSame($description, $event->getDescription()); + $this->assertSame($conditionSetId, $event->getConditionSetId()); + $this->assertSame($status, $event->getStatus()); } } diff --git a/module/translation-deepl/README.md b/module/translation-deepl/README.md index 59cdabdbc..916de1004 100644 --- a/module/translation-deepl/README.md +++ b/module/translation-deepl/README.md @@ -1,4 +1,4 @@ -# Ergonode - transformer +# Ergonode - Deepl translation ## Documentation diff --git a/module/workflow/src/Application/Controller/Api/WorkflowController.php b/module/workflow/src/Application/Controller/Api/WorkflowController.php index 5d496570d..565fb37d6 100644 --- a/module/workflow/src/Application/Controller/Api/WorkflowController.php +++ b/module/workflow/src/Application/Controller/Api/WorkflowController.php @@ -161,7 +161,7 @@ public function createWorkflow(Request $request): Response { $data = $request->request->all(); - $violations = $this->validator->validate($data, $this->builder->build($data), [WorkflowValidatorBuilder::UNIQUE_WORKFLOW]); + $violations = $this->validator->validate($data, $this->builder->build($data), ['Default', WorkflowValidatorBuilder::UNIQUE_WORKFLOW]); if (0 === $violations->count()) { $data['id'] = WorkflowId::fromCode($data['code'])->getValue(); @@ -218,7 +218,6 @@ public function updateWorkflow(Request $request): Response $workflow = $this->provider->provide(); $violations = $this->validator->validate($data, $this->builder->build($data)); - if (0 === $violations->count()) { $data['id'] = $workflow->getId()->getValue(); $command = $this->serializer->fromArray($data, UpdateWorkflowCommand::class); diff --git a/module/workflow/src/Domain/Command/Workflow/UpdateWorkflowCommand.php b/module/workflow/src/Domain/Command/Workflow/UpdateWorkflowCommand.php index 10c301d02..e3c442e04 100644 --- a/module/workflow/src/Domain/Command/Workflow/UpdateWorkflowCommand.php +++ b/module/workflow/src/Domain/Command/Workflow/UpdateWorkflowCommand.php @@ -11,7 +11,6 @@ use Ergonode\Workflow\Domain\Entity\StatusId; use Ergonode\Workflow\Domain\Entity\WorkflowId; -use Ergonode\Workflow\Domain\ValueObject\Status; use Ergonode\Workflow\Domain\ValueObject\Transition; use JMS\Serializer\Annotation as JMS; use Webmozart\Assert\Assert; diff --git a/module/workflow/src/Infrastructure/Builder/WorkflowValidatorBuilder.php b/module/workflow/src/Infrastructure/Builder/WorkflowValidatorBuilder.php index bb2dfd779..e601a6d0d 100644 --- a/module/workflow/src/Infrastructure/Builder/WorkflowValidatorBuilder.php +++ b/module/workflow/src/Infrastructure/Builder/WorkflowValidatorBuilder.php @@ -68,7 +68,6 @@ public function build(array $data): Constraint }; $uniqueTransition = static function ($data, ExecutionContextInterface $context, $payload) { - $result = []; foreach ($data as $transition) { $key = $transition['source'].$transition['destination']; diff --git a/module/workflow/src/Infrastructure/Validator/StatusNotExists.php b/module/workflow/src/Infrastructure/Validator/StatusNotExists.php index 37e9b5609..f5b8f9790 100644 --- a/module/workflow/src/Infrastructure/Validator/StatusNotExists.php +++ b/module/workflow/src/Infrastructure/Validator/StatusNotExists.php @@ -19,5 +19,5 @@ class StatusNotExists extends Constraint /** * @var string */ - public $message = 'Status {{ value }} not exists.'; + public $message = 'Status {{ value }} not exists'; } diff --git a/module/workflow/src/Infrastructure/Validator/StatusNotExistsValidator.php b/module/workflow/src/Infrastructure/Validator/StatusNotExistsValidator.php index e2994c836..f5ae8e4e1 100644 --- a/module/workflow/src/Infrastructure/Validator/StatusNotExistsValidator.php +++ b/module/workflow/src/Infrastructure/Validator/StatusNotExistsValidator.php @@ -54,7 +54,7 @@ public function validate($value, Constraint $constraint): void $value = (string) $value; - $workflow = null; + $workflow = false; if (StatusId::isValid($value)) { $workflow = $this->repository->load(new StatusId($value)); } diff --git a/module/workflow/src/Infrastructure/Validator/WorkflowExistsValidator.php b/module/workflow/src/Infrastructure/Validator/WorkflowExistsValidator.php index 471add847..7495c2b77 100644 --- a/module/workflow/src/Infrastructure/Validator/WorkflowExistsValidator.php +++ b/module/workflow/src/Infrastructure/Validator/WorkflowExistsValidator.php @@ -33,10 +33,9 @@ public function __construct(WorkflowRepositoryInterface $repository) } /** - * @param mixed $value - * @param WorkflowExists|Constraint $constraint + * {@inheritDoc} * - * @throws \ReflectionException + * @throws \Exception */ public function validate($value, Constraint $constraint): void { diff --git a/symfony.lock b/symfony.lock index 04fbaa9a3..df4ccedfe 100644 --- a/symfony.lock +++ b/symfony.lock @@ -104,15 +104,6 @@ "firebase/php-jwt": { "version": "v5.0.0" }, - "friendsofsymfony/rest-bundle": { - "version": "2.2", - "recipe": { - "repo": "github.com/symfony/recipes-contrib", - "branch": "master", - "version": "2.2", - "ref": "258300d52be6ad59b32a888d5ddafbf9638540ff" - } - }, "fzaninotto/faker": { "version": "v1.7.1" }, From bf4d1996228e5d3dd5154addd19d9bd7dab1fda2 Mon Sep 17 00:00:00 2001 From: Mateusz Kolankowski Date: Tue, 17 Sep 2019 11:18:15 +0200 Subject: [PATCH 24/27] Adding NotBlank assertion (#148) --- .../src/Application/Model/CategoryTreeUpdateFormModel.php | 1 + 1 file changed, 1 insertion(+) diff --git a/module/category-tree/src/Application/Model/CategoryTreeUpdateFormModel.php b/module/category-tree/src/Application/Model/CategoryTreeUpdateFormModel.php index b7e16b028..5807f2747 100644 --- a/module/category-tree/src/Application/Model/CategoryTreeUpdateFormModel.php +++ b/module/category-tree/src/Application/Model/CategoryTreeUpdateFormModel.php @@ -28,6 +28,7 @@ class CategoryTreeUpdateFormModel /** * @var TreeNodeFormModel[] * + * @Assert\NotBlank() * @Assert\Valid() */ public $categories; From 8929548b6156d3bc8380342167994465882c67b1 Mon Sep 17 00:00:00 2001 From: Mateusz Kolankowski Date: Tue, 17 Sep 2019 12:17:38 +0200 Subject: [PATCH 25/27] Add category to category tree validation errors (#147) * adding not blank assertion * little fix --- features/category-tree.feature | 63 --------------- .../Controller/Api/CategoryTreeController.php | 69 ---------------- .../src/Domain/Command/AddCategoryCommand.php | 78 ------------------- .../Handler/AddCategoryCommandHandler.php | 45 ----------- 4 files changed, 255 deletions(-) delete mode 100644 module/category-tree/src/Domain/Command/AddCategoryCommand.php delete mode 100644 module/category-tree/src/Infrastructure/Handler/AddCategoryCommandHandler.php diff --git a/features/category-tree.feature b/features/category-tree.feature index a99405d48..6917de759 100644 --- a/features/category-tree.feature +++ b/features/category-tree.feature @@ -438,69 +438,6 @@ Feature: Category tree module When I request "/api/v1/EN/trees/@category_tree@" using HTTP DELETE Then unauthorized response is received - Scenario: Add category to category tree - Given current authentication token - Given the following form parameters are set: - | name | value | - | child | @category_2@ | - When I request "/api/v1/EN/trees/@category_tree@/category/@category_1@/child" using HTTP POST - Then created response is received - - Scenario: Add category to category tree (not authorized) - Given the following form parameters are set: - | name | value | - | child | @category_2@ | - When I request "/api/v1/EN/trees/@category_tree@/category/@category_1@/child" using HTTP POST - Then unauthorized response is received - - Scenario: Add category to category tree (wrong child) - Given current authentication token - Given the following form parameters are set: - | name | value | - | child | @@static_uuid@@ | - When I request "/api/v1/EN/trees/@category_tree@/category/@category_1@/child" using HTTP POST - Then validation error response is received - - Scenario: Add category to category tree (not uuid child) - Given current authentication token - Given the following form parameters are set: - | name | value | - | child | 'test' | - When I request "/api/v1/EN/trees/@category_tree@/category/@category_1@/child" using HTTP POST - Then validation error response is received - - Scenario: Add category to category tree (wrong category tree) - Given current authentication token - Given the following form parameters are set: - | name | value | - | child | @category_2@ | - When I request "/api/v1/EN/trees/@@static_uuid@@/category/@category_1@/child" using HTTP POST - Then validation error response is received - - Scenario: Add category to category tree (not uuid child) - Given current authentication token - Given the following form parameters are set: - | name | value | - | child | @category_2@ | - When I request "/api/v1/EN/trees/test/category/@category_1@/child" using HTTP POST - Then validation error response is received - - Scenario: Add category to category tree (duplicated category) - Given current authentication token - Given the following form parameters are set: - | name | value | - | child | @category_2@ | - When I request "/api/v1/EN/trees/@category_tree@/category/@@static_uuid@@/child" using HTTP POST - Then validation error response is received - - Scenario: Add category to category tree (not uuid category) - Given current authentication token - Given the following form parameters are set: - | name | value | - | child | @category_1@ | - When I request "/api/v1/EN/trees/@category_tree@/category/test/child" using HTTP POST - Then validation error response is received - Scenario: Delete category tree Given current authentication token When I request "/api/v1/EN/trees/@category_tree@" using HTTP DELETE diff --git a/module/category-tree/src/Application/Controller/Api/CategoryTreeController.php b/module/category-tree/src/Application/Controller/Api/CategoryTreeController.php index e7a906b1b..d438df598 100644 --- a/module/category-tree/src/Application/Controller/Api/CategoryTreeController.php +++ b/module/category-tree/src/Application/Controller/Api/CategoryTreeController.php @@ -13,12 +13,10 @@ use Ergonode\Api\Application\Response\CreatedResponse; use Ergonode\Api\Application\Response\EmptyResponse; use Ergonode\Api\Application\Response\SuccessResponse; -use Ergonode\Category\Domain\Entity\CategoryId; use Ergonode\CategoryTree\Application\Form\CategoryTreeCreateForm; use Ergonode\CategoryTree\Application\Form\CategoryTreeUpdateForm; use Ergonode\CategoryTree\Application\Model\CategoryTreeCreateFormModel; use Ergonode\CategoryTree\Application\Model\CategoryTreeUpdateFormModel; -use Ergonode\CategoryTree\Domain\Command\AddCategoryCommand; use Ergonode\CategoryTree\Domain\Command\CreateTreeCommand; use Ergonode\CategoryTree\Domain\Command\DeleteTreeCommand; use Ergonode\CategoryTree\Domain\Command\UpdateTreeCommand; @@ -265,73 +263,6 @@ public function createTree(Request $request): Response throw new FormValidationHttpException($form); } - - /** - * @Route("/trees/{tree}/category/{category}/child", methods={"POST"}) - * - * @IsGranted("CATEGORY_CREATE") - * - * @SWG\Tag(name="Tree") - * @SWG\Parameter( - * name="language", - * in="path", - * type="string", - * required=true, - * default="EN", - * description="Language Code", - * ) - * @SWG\Parameter( - * name="tree", - * in="path", - * type="string", - * required=true, - * description="Id of category tree", - * ) - * @SWG\Parameter( - * name="category", - * in="path", - * type="string", - * required=true, - * description="Id of category in tree", - * ) - * @SWG\Parameter( - * name="child", - * in="formData", - * type="string", - * required=true, - * description="Id of added child category", - * ) - * @SWG\Response( - * response=202, - * description="Action accepted", - * ) - * @SWG\Response( - * response=400, - * description="Validation error", - * @SWG\Schema(ref="#/definitions/error_response") - * ) - * - * @param string $tree - * @param string $category - * @param Request $request - * - * @return Response - * @throws \Exception - */ - public function addCategory(string $tree, string $category, Request $request): Response - { - $child = $request->request->get('child'); - - if ($child) { - $command = new AddCategoryCommand(new CategoryTreeId($tree), new CategoryId($category), new CategoryId($child)); - $this->messageBus->dispatch($command); - - return new CreatedResponse($command->getCategoryId()); - } - - throw new BadRequestHttpException(); - } - /** * @Route("/trees/{tree}", methods={"PUT"}, requirements={"tree"="[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"}) * diff --git a/module/category-tree/src/Domain/Command/AddCategoryCommand.php b/module/category-tree/src/Domain/Command/AddCategoryCommand.php deleted file mode 100644 index 25b5c279f..000000000 --- a/module/category-tree/src/Domain/Command/AddCategoryCommand.php +++ /dev/null @@ -1,78 +0,0 @@ -treeId = $treeId; - $this->categoryId = $categoryId; - $this->parentId = $parentId; - } - - /** - * @return CategoryTreeId - */ - public function getTreeId(): CategoryTreeId - { - return $this->treeId; - } - - /** - * @return CategoryId - */ - public function getCategoryId(): CategoryId - { - return $this->categoryId; - } - - /** - * @return CategoryId - */ - public function getParentId(): CategoryId - { - return $this->parentId; - } -} diff --git a/module/category-tree/src/Infrastructure/Handler/AddCategoryCommandHandler.php b/module/category-tree/src/Infrastructure/Handler/AddCategoryCommandHandler.php deleted file mode 100644 index 51c3bb761..000000000 --- a/module/category-tree/src/Infrastructure/Handler/AddCategoryCommandHandler.php +++ /dev/null @@ -1,45 +0,0 @@ -repository = $repository; - } - - /** - * @param AddCategoryCommand $command - */ - public function __invoke(AddCategoryCommand $command) - { - $tree = $this->repository->load($command->getTreeId()); - - Assert::notNull($tree); - $tree->addCategory($command->getCategoryId(), $command->getParentId()); - - $this->repository->save($tree); - } -} From 4baca5ee8c01f0f06e5d9566c723dc23235022b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Prz=C4=99dzik?= <46092482+rprzedzik@users.noreply.github.com> Date: Fri, 27 Sep 2019 11:03:06 +0200 Subject: [PATCH 26/27] Release/v0.5.0 fixes (#165) Release/v0.5.0 fixes --- .../src/Domain/ValueObject/DateFormat.php | 18 +++---- .../Factory/MultiSelectAttributeFactory.php | 3 +- .../Domain/Factory/SelectAttributeFactory.php | 3 +- .../NumericAttributeValueCondition.php | 23 ++++++-- .../Condition/TextAttributeValueCondition.php | 20 ++++++- ...ttributeValueConditionValidatorBuilder.php | 2 + .../Infrastructure/Grid/ConditionSetGrid.php | 1 + module/grid/src/Renderer/GridRenderer.php | 24 +++++++-- module/grid/src/RequestGridConfiguration.php | 5 ++ .../Domain/Query/ProductQueryInterface.php | 8 +++ .../Dbal/Query/DbalProductQuery.php | 37 +++++++++++++ .../Decorator/CacheProductQueryDecorator.php | 11 ++++ .../src/Infrastructure/Grid/SegmentGrid.php | 3 +- .../Controller/Api/StatusController.php | 53 +++++++++++++------ 14 files changed, 173 insertions(+), 38 deletions(-) diff --git a/module/attribute-date/src/Domain/ValueObject/DateFormat.php b/module/attribute-date/src/Domain/ValueObject/DateFormat.php index f3db75ece..c53c07d3a 100644 --- a/module/attribute-date/src/Domain/ValueObject/DateFormat.php +++ b/module/attribute-date/src/Domain/ValueObject/DateFormat.php @@ -13,15 +13,15 @@ */ class DateFormat { - public const YYYY_MM_DD = 'YYYY-MM-DD'; - public const YY_MM_DD = 'YY-MM-DD'; - public const DD_MM_YY = 'DD.MM.YY'; - public const DD_MM_YYYY = 'DD.MM.YYYY'; - public const MM_DD_YY = 'MM/DD/YY'; - public const MM_DD_YYYY = 'MM/DD/YYYY'; - public const MMMM_DD_YYYY = 'MMMM DD, YYYY'; - public const DD_MMMM_YYYY = 'DD MMMM YYYY'; - public const DD_MMM_YYYY = 'DD MMM YYYY'; + public const YYYY_MM_DD = 'yyyy-MM-dd'; + public const YY_MM_DD = 'yy-MM-dd'; + public const DD_MM_YY = 'dd.MM.yy'; + public const DD_MM_YYYY = 'dd.MM.yyyy'; + public const MM_DD_YY = 'MM/dd/yy'; + public const MM_DD_YYYY = 'MM/dd/yyyy'; + public const MMMM_DD_YYYY = 'MMMM dd, yyyy'; + public const DD_MMMM_YYYY = 'dd MMMM yyyy'; + public const DD_MMM_YYYY = 'dd MMM yyyy'; public const AVAILABLE = [ self::YYYY_MM_DD, diff --git a/module/attribute/src/Domain/Factory/MultiSelectAttributeFactory.php b/module/attribute/src/Domain/Factory/MultiSelectAttributeFactory.php index 6d36914dd..0706426d7 100644 --- a/module/attribute/src/Domain/Factory/MultiSelectAttributeFactory.php +++ b/module/attribute/src/Domain/Factory/MultiSelectAttributeFactory.php @@ -56,7 +56,8 @@ public function create(CreateAttributeCommand $command): AbstractAttribute $option = new StringOption(''); } } - $attribute->addOption(new OptionKey($key), $option); + + $attribute->addOption(new OptionKey((string) $key), $option); } return $attribute; diff --git a/module/attribute/src/Domain/Factory/SelectAttributeFactory.php b/module/attribute/src/Domain/Factory/SelectAttributeFactory.php index 4245135e6..06d255c81 100644 --- a/module/attribute/src/Domain/Factory/SelectAttributeFactory.php +++ b/module/attribute/src/Domain/Factory/SelectAttributeFactory.php @@ -56,7 +56,8 @@ public function create(CreateAttributeCommand $command): AbstractAttribute $option = new StringOption(''); } } - $attribute->addOption(new OptionKey($key), $option); + + $attribute->addOption(new OptionKey((string) $key), $option); } return $attribute; diff --git a/module/condition/src/Domain/Condition/NumericAttributeValueCondition.php b/module/condition/src/Domain/Condition/NumericAttributeValueCondition.php index b794a0acd..a1f7f4b72 100644 --- a/module/condition/src/Domain/Condition/NumericAttributeValueCondition.php +++ b/module/condition/src/Domain/Condition/NumericAttributeValueCondition.php @@ -31,15 +31,24 @@ class NumericAttributeValueCondition implements ConditionInterface * * @JMS\Type("string") */ + private $operator; + + /** + * @var float + * + * @JMS\Type("float") + */ private $value; /** * @param AttributeId $attribute - * @param string $value + * @param string $operator + * @param float $value */ - public function __construct(AttributeId $attribute, string $value) + public function __construct(AttributeId $attribute, string $operator, float $value) { $this->attribute = $attribute; + $this->operator = $operator; $this->value = $value; } @@ -64,7 +73,15 @@ public function getAttribute(): AttributeId /** * @return string */ - public function getValue(): string + public function getOption(): string + { + return $this->operator; + } + + /** + * @return float + */ + public function getValue(): float { return $this->value; } diff --git a/module/condition/src/Domain/Condition/TextAttributeValueCondition.php b/module/condition/src/Domain/Condition/TextAttributeValueCondition.php index 3236ad990..0181a58d2 100644 --- a/module/condition/src/Domain/Condition/TextAttributeValueCondition.php +++ b/module/condition/src/Domain/Condition/TextAttributeValueCondition.php @@ -26,6 +26,13 @@ class TextAttributeValueCondition implements ConditionInterface */ private $attribute; + /** + * @var string + * + * @JMS\Type("string") + */ + private $operator; + /** * @var string * @@ -35,11 +42,13 @@ class TextAttributeValueCondition implements ConditionInterface /** * @param AttributeId $attribute + * @param string $operator * @param string $value */ - public function __construct(AttributeId $attribute, string $value) + public function __construct(AttributeId $attribute, string $operator, string $value) { $this->attribute = $attribute; + $this->operator = $operator; $this->value = $value; } @@ -53,6 +62,7 @@ public function getType(): string return self::TYPE; } + /** * @return AttributeId */ @@ -61,6 +71,14 @@ public function getAttribute(): AttributeId return $this->attribute; } + /** + * @return string + */ + public function getOption(): string + { + return $this->operator; + } + /** * @return string */ diff --git a/module/condition/src/Infrastructure/Builder/Condition/NumericAttributeValueConditionValidatorBuilder.php b/module/condition/src/Infrastructure/Builder/Condition/NumericAttributeValueConditionValidatorBuilder.php index 4db05d826..9c623b13f 100644 --- a/module/condition/src/Infrastructure/Builder/Condition/NumericAttributeValueConditionValidatorBuilder.php +++ b/module/condition/src/Infrastructure/Builder/Condition/NumericAttributeValueConditionValidatorBuilder.php @@ -15,6 +15,7 @@ use Symfony\Component\Validator\Constraints\Choice; use Symfony\Component\Validator\Constraints\Collection; use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\Validator\Constraints\Type; /** */ @@ -39,6 +40,7 @@ public function build(array $data): Constraint ], 'value' => [ new NotBlank(), + new Type('numeric'), ], ] ); diff --git a/module/condition/src/Infrastructure/Grid/ConditionSetGrid.php b/module/condition/src/Infrastructure/Grid/ConditionSetGrid.php index 4ccfd304c..79d09936d 100644 --- a/module/condition/src/Infrastructure/Grid/ConditionSetGrid.php +++ b/module/condition/src/Infrastructure/Grid/ConditionSetGrid.php @@ -48,6 +48,7 @@ public function init(GridConfigurationInterface $configuration, Language $langua $this->addColumn('name', new TextColumn('name', $this->trans('Name'), new TextFilter($filters->getString('name')))); $this->addColumn('description', new TextColumn('description', $this->trans('Description'), new TextFilter($filters->getString('description')))); $this->addColumn('edit', new ActionColumn('edit')); + $this->setConfiguration(self::PARAMETER_ALLOW_COLUMN_RESIZE, true); } /** diff --git a/module/grid/src/Renderer/GridRenderer.php b/module/grid/src/Renderer/GridRenderer.php index 616a58151..05cddbda5 100644 --- a/module/grid/src/Renderer/GridRenderer.php +++ b/module/grid/src/Renderer/GridRenderer.php @@ -53,11 +53,27 @@ public function render(AbstractGrid $grid, GridConfigurationInterface $configura $field = $configuration->getField(); $order = $configuration->getOrder(); $records = $dataSet->getItems($grid->getColumns(), $configuration->getLimit(), $configuration->getOffset(), $field, $order); - $result = []; - $result['configuration'] = $grid->getConfiguration(); - $result['columns'] = $this->columnRenderer->render($grid, []); - $result['collection'] = []; + $result = [ + 'configuration' => $grid->getConfiguration(), + 'columns' => $this->columnRenderer->render($grid, []), + 'collection' => [], + ]; + + // @todo HAX for column ordering (we need to refactor whole gird) + if (!empty($configuration->getColumns())) { + $columnsOrdered = []; + foreach (array_keys($configuration->getColumns()) as $name) { + foreach ($result['columns'] as $key => $column) { + if ($name === $column['id']) { + $columnsOrdered[] = $result['columns'][$key]; + break; + } + } + } + + $result['columns'] = $columnsOrdered; + } foreach ($records as $row) { $result['collection'][] = $this->rowRenderer->render($grid, $row); diff --git a/module/grid/src/RequestGridConfiguration.php b/module/grid/src/RequestGridConfiguration.php index 519cf7aca..c17eddb74 100644 --- a/module/grid/src/RequestGridConfiguration.php +++ b/module/grid/src/RequestGridConfiguration.php @@ -83,10 +83,15 @@ public function __construct(Request $request) foreach ($columns as $column) { $data = explode(':', $column); $key = $data[0]; + if (empty($key)) { + continue; + } + $language = null; if (isset($data[1])) { $language = new Language($data[1]); } + $this->columns[$column] = new RequestColumn($key, $language); } } diff --git a/module/product/src/Domain/Query/ProductQueryInterface.php b/module/product/src/Domain/Query/ProductQueryInterface.php index d0a102b96..b3dadb21c 100644 --- a/module/product/src/Domain/Query/ProductQueryInterface.php +++ b/module/product/src/Domain/Query/ProductQueryInterface.php @@ -13,6 +13,7 @@ use Ergonode\Designer\Domain\Entity\TemplateId; use Ergonode\Product\Domain\Entity\ProductId; use Ergonode\Product\Domain\ValueObject\Sku; +use Ergonode\Workflow\Domain\Entity\StatusId; /** */ @@ -43,4 +44,11 @@ public function findProductIdByCategoryId(CategoryId $categoryId): array; * @return array */ public function findProductIdByTemplateId(TemplateId $templateId): array; + + /** + * @param StatusId $statusId + * + * @return array + */ + public function findProductIdByStatusId(StatusId $statusId): array; } diff --git a/module/product/src/Persistence/Dbal/Query/DbalProductQuery.php b/module/product/src/Persistence/Dbal/Query/DbalProductQuery.php index eec9298be..cbabc6d61 100644 --- a/module/product/src/Persistence/Dbal/Query/DbalProductQuery.php +++ b/module/product/src/Persistence/Dbal/Query/DbalProductQuery.php @@ -16,6 +16,7 @@ use Ergonode\Product\Domain\Entity\ProductId; use Ergonode\Product\Domain\Query\ProductQueryInterface; use Ergonode\Product\Domain\ValueObject\Sku; +use Ergonode\Workflow\Domain\Entity\StatusId; /** */ @@ -110,6 +111,42 @@ public function findProductIdByTemplateId(TemplateId $templateId): array ->fetchAll(\PDO::FETCH_COLUMN); } + /** + * {@inheritDoc} + */ + public function findProductIdByStatusId(StatusId $statusId): array + { + $statusCode = $this->connection->createQueryBuilder() + ->select('s.code') + ->from('public.status', 's') + ->where('s.id = :id') + ->setParameter('id', $statusId->getValue()) + ->setMaxResults(1) + ->execute()->fetchColumn(); + + // @todo This is unacceptable! + $attributeId = $this->connection->createQueryBuilder() + ->select('a.id') + ->from('public.attribute', 'a') + ->where('a.code = \'esa_status\'') + ->setMaxResults(1) + ->execute()->fetchColumn(); + + // @todo That's too! + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder + ->select('p.id') + ->from(self::PRODUCT_TABLE, 'p') + ->join('p', 'designer.draft', 'd', 'p.id = d.product_id') + ->join('d', 'designer.draft_value', 'dv', 'd.id = dv.draft_id') + ->where('dv.value = :statusCode') + ->andWhere('dv.element_id = :attributeId') + ->setParameter('statusCode', $statusCode) + ->setParameter('attributeId', $attributeId); + + return $queryBuilder->execute()->fetchAll(\PDO::FETCH_COLUMN); + } + /** * @return QueryBuilder */ diff --git a/module/product/src/Persistence/Dbal/Query/Decorator/CacheProductQueryDecorator.php b/module/product/src/Persistence/Dbal/Query/Decorator/CacheProductQueryDecorator.php index 1b596a975..2ff04e298 100644 --- a/module/product/src/Persistence/Dbal/Query/Decorator/CacheProductQueryDecorator.php +++ b/module/product/src/Persistence/Dbal/Query/Decorator/CacheProductQueryDecorator.php @@ -14,6 +14,7 @@ use Ergonode\Product\Domain\Entity\ProductId; use Ergonode\Product\Domain\Query\ProductQueryInterface; use Ergonode\Product\Domain\ValueObject\Sku; +use Ergonode\Workflow\Domain\Entity\StatusId; /** */ @@ -79,4 +80,14 @@ public function findProductIdByTemplateId(TemplateId $templateId): array { return $this->query->findProductIdByTemplateId($templateId); } + + /** + * @param StatusId $statusId + * + * @return array + */ + public function findProductIdByStatusId(StatusId $statusId): array + { + return $this->query->findProductIdByStatusId($statusId); + } } diff --git a/module/segment/src/Infrastructure/Grid/SegmentGrid.php b/module/segment/src/Infrastructure/Grid/SegmentGrid.php index 3f6762113..810c3ab4a 100644 --- a/module/segment/src/Infrastructure/Grid/SegmentGrid.php +++ b/module/segment/src/Infrastructure/Grid/SegmentGrid.php @@ -12,7 +12,6 @@ use Ergonode\Core\Domain\ValueObject\Language; use Ergonode\Grid\AbstractGrid; use Ergonode\Grid\Column\ActionColumn; -use Ergonode\Grid\Column\SelectColumn; use Ergonode\Grid\Column\TextColumn; use Ergonode\Grid\Filter\SelectFilter; use Ergonode\Grid\Filter\TextFilter; @@ -51,7 +50,7 @@ public function init(GridConfigurationInterface $configuration, Language $langua $id->setVisible(false); $this->addColumn('id', $id); $this->addColumn('code', new TextColumn('name', $this->trans('Code'), new TextFilter($filters->getString('code')))); - $this->addColumn('status', new SelectColumn('status', $this->trans('Status'), new SelectFilter($statuses, $filters->getString('status')))); + $this->addColumn('status', new TextColumn('status', $this->trans('Status'), new SelectFilter($statuses, $filters->getString('status')))); $this->addColumn('name', new TextColumn('name', $this->trans('Name'), new TextFilter($filters->getString('name')))); $this->addColumn('description', new TextColumn('description', $this->trans('Description'), new TextFilter($filters->getString('description')))); $this->addColumn('edit', new ActionColumn('edit')); diff --git a/module/workflow/src/Application/Controller/Api/StatusController.php b/module/workflow/src/Application/Controller/Api/StatusController.php index 58e17a9f7..8b01053c6 100644 --- a/module/workflow/src/Application/Controller/Api/StatusController.php +++ b/module/workflow/src/Application/Controller/Api/StatusController.php @@ -17,6 +17,7 @@ use Ergonode\Core\Domain\ValueObject\TranslatableString; use Ergonode\Grid\RequestGridConfiguration; use Ergonode\Grid\Response\GridResponse; +use Ergonode\Product\Domain\Query\ProductQueryInterface; use Ergonode\Workflow\Application\Form\Model\StatusFormModel; use Ergonode\Workflow\Application\Form\StatusForm; use Ergonode\Workflow\Domain\Command\Status\CreateStatusCommand; @@ -32,6 +33,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\ConflictHttpException; use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException; use Symfony\Component\Routing\Annotation\Route; @@ -56,18 +58,26 @@ class StatusController extends AbstractController private $grid; /** - * @param MessageBusInterface $messageBus - * @param StatusQueryInterface $query - * @param StatusGrid $grid + * @var ProductQueryInterface + */ + private $productQuery; + + /** + * @param MessageBusInterface $messageBus + * @param StatusQueryInterface $query + * @param StatusGrid $grid + * @param ProductQueryInterface $productQuery */ public function __construct( MessageBusInterface $messageBus, StatusQueryInterface $query, - StatusGrid $grid + StatusGrid $grid, + ProductQueryInterface $productQuery ) { $this->messageBus = $messageBus; $this->query = $query; $this->grid = $grid; + $this->productQuery = $productQuery; } /** @@ -82,7 +92,7 @@ public function __construct( * type="string", * required=true, * default="EN", - * description="Language Code", + * description="Language Code" * ) * @SWG\Parameter( * name="limit", @@ -90,7 +100,7 @@ public function __construct( * type="integer", * required=true, * default="50", - * description="Number of returned lines", + * description="Number of returned lines" * ) * @SWG\Parameter( * name="offset", @@ -98,14 +108,14 @@ public function __construct( * type="integer", * required=true, * default="0", - * description="Number of start line", + * description="Number of start line" * ) * @SWG\Parameter( * name="field", * in="query", * required=false, * type="string", - * description="Order field", + * description="Order field" * ) * @SWG\Parameter( * name="order", @@ -113,7 +123,7 @@ public function __construct( * required=false, * type="string", * enum={"ASC","DESC"}, - * description="Order", + * description="Order" * ) * @SWG\Parameter( * name="filter", @@ -132,7 +142,7 @@ public function __construct( * ) * @SWG\Response( * response=200, - * description="Returns statuses collection", + * description="Returns statuses collection" * ) * * @ParamConverter(class="Ergonode\Grid\RequestGridConfiguration") @@ -158,7 +168,7 @@ public function getStatuses(Language $language, RequestGridConfiguration $config * in="path", * type="string", * required=true, - * description="Status id", + * description="Status id" * ) * @SWG\Parameter( * name="language", @@ -166,15 +176,15 @@ public function getStatuses(Language $language, RequestGridConfiguration $config * type="string", * required=true, * default="EN", - * description="Language Code", + * description="Language Code" * ) * @SWG\Response( * response=200, - * description="Returns status", + * description="Returns status" * ) * @SWG\Response( * response=404, - * description="Not found", + * description="Not found" * ) * * @ParamConverter(class="Ergonode\Workflow\Domain\Entity\Status") @@ -210,7 +220,7 @@ public function getStatus(Status $status): Response * ) * @SWG\Response( * response=201, - * description="Returns status ID", + * description="Returns status ID" * ) * @SWG\Response( * response=400, @@ -264,7 +274,7 @@ public function createStatus(Request $request): Response * in="path", * type="string", * required=true, - * description="Status code", + * description="Status code" * ) * @SWG\Parameter( * name="language", @@ -338,7 +348,7 @@ public function updateStatus(Status $status, Request $request): Response * in="path", * type="string", * required=true, - * description="Status code", + * description="Status code" * ) * @SWG\Parameter( * name="language", @@ -359,6 +369,10 @@ public function updateStatus(Status $status, Request $request): Response * response=404, * description="Status not found" * ) + * @SWG\Response( + * response=409, + * description="Can't remove" + * ) * * @ParamConverter(class="Ergonode\Workflow\Domain\Entity\Status") * @@ -370,6 +384,11 @@ public function updateStatus(Status $status, Request $request): Response */ public function deleteStatus(Status $status): Response { + $relationships = $this->productQuery->findProductIdByStatusId($status->getId()); + if (0 !== count($relationships)) { + throw new ConflictHttpException('Can\'t remove status, it has relations to products'); + } + $command = new DeleteStatusCommand($status->getId()); $this->messageBus->dispatch($command); From bc76e559047d1faca8201ae7ddde344b35995cd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Prz=C4=99dzik?= Date: Tue, 8 Oct 2019 11:45:24 +0200 Subject: [PATCH 27/27] preparation of release 0.5.0 --- CHANGELOG.md | 5 +++++ README.md | 4 ++-- module/account/composer.json | 13 ++++++------- module/api/composer.json | 2 +- module/attribute-date/composer.json | 4 ++-- module/attribute-image/composer.json | 4 ++-- module/attribute-price/composer.json | 8 ++++---- module/attribute-unit/composer.json | 4 ++-- module/attribute/composer.json | 12 ++++++------ module/authentication/composer.json | 4 ++-- module/category-tree/composer.json | 8 ++++---- module/category/composer.json | 12 ++++++------ module/completeness/composer.json | 8 ++++---- module/condition/composer.json | 13 ++++++------- module/core/composer.json | 5 ++--- module/designer/composer.json | 11 +++++------ module/editor/composer.json | 15 +++++++-------- module/event-sourcing/composer.json | 4 ++-- module/fixture/composer.json | 2 +- module/grid/composer.json | 4 ++-- module/importer/composer.json | 11 +++++------ module/multimedia/composer.json | 7 +++---- module/product-simple/composer.json | 2 +- module/product/composer.json | 17 ++++++++--------- module/reader/composer.json | 11 +++++------ module/segment/composer.json | 13 ++++++------- module/transformer/composer.json | 9 ++++----- module/translation-deepl/composer.json | 4 ++-- module/value/composer.json | 6 +++--- module/workflow/composer.json | 8 ++++---- 30 files changed, 112 insertions(+), 118 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff6e1fe0a..d58ccfd9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## CHANGELOG FOR 0.5.0 + - feature [#115](https://github.com/ergonode/backend/issues/115) Product segment functionality (rprzedzik) + - feature [#118](https://github.com/ergonode/backend/issues/118) Event store history (BastekBielawski) + - feature [#124](https://github.com/ergonode/backend/issues/124) Register events in database (BastekBielawski) + ## CHANGELOG FOR 0.4.0 - feature [#104](https://github.com/ergonode/backend/issues/104) Multiple category trees (wiewiurdp) diff --git a/README.md b/README.md index 84480ee4e..1e7c7a388 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@

- Version + Version Code Version @@ -96,7 +96,7 @@ On the front side we've used headless approach with Vue.js application. Thanks t #### Backend Technologies - PHP 7.2 -- Symfony 4.2 +- Symfony 4.3 - Postgres 9.6 (uuid-ossp, ltree) - RabbitMQ (optional) - Redis (optional) diff --git a/module/account/composer.json b/module/account/composer.json index 4446e74f0..b9d5045df 100644 --- a/module/account/composer.json +++ b/module/account/composer.json @@ -7,13 +7,12 @@ "require": { "php": "^7.2", "doctrine/dbal": "^2.9", - "ergonode/api": "^0.4.0", - "ergonode/core": "^0.4.0", - "ergonode/es": "^0.4.0", - "ergonode/grid": "^0.4.0", - "ergonode/migration": "^0.4.0", - "ergonode/multimedia": "^0.4.0", - "friendsofsymfony/rest-bundle": "^2.5", + "ergonode/api": "^0.5.0", + "ergonode/core": "^0.5.0", + "ergonode/es": "^0.5.0", + "ergonode/grid": "^0.5.0", + "ergonode/migration": "^0.5.0", + "ergonode/multimedia": "^0.5.0", "jms/serializer": "^3.1", "nelmio/api-doc-bundle": "^3.4", "ramsey/uuid": "^3.8", diff --git a/module/api/composer.json b/module/api/composer.json index f1d12f7e6..5b0763394 100644 --- a/module/api/composer.json +++ b/module/api/composer.json @@ -7,7 +7,7 @@ "require": { "php": "^7.2", "doctrine/dbal": "^2.9", - "ergonode/core": "^0.4.0", + "ergonode/core": "^0.5.0", "jms/serializer": "^3.1", "nelmio/api-doc-bundle": "^3.4", "ramsey/uuid": "^3.8", diff --git a/module/attribute-date/composer.json b/module/attribute-date/composer.json index 9c133ce0a..20ee6cd51 100644 --- a/module/attribute-date/composer.json +++ b/module/attribute-date/composer.json @@ -6,8 +6,8 @@ "license": "OSL-3.0", "require": { "php": "^7.2", - "ergonode/api": "^0.4.0", - "ergonode/attribute": "^0.4.0", + "ergonode/api": "^0.5.0", + "ergonode/attribute": "^0.5.0", "jms/serializer": "^3.1", "nelmio/api-doc-bundle": "^3.4", "symfony/form": "^4.3", diff --git a/module/attribute-image/composer.json b/module/attribute-image/composer.json index add4f831a..50e4484e9 100644 --- a/module/attribute-image/composer.json +++ b/module/attribute-image/composer.json @@ -7,8 +7,8 @@ "require": { "php": "^7.2", "doctrine/collections": "^1.6", - "ergonode/api": "^0.4.0", - "ergonode/attribute": "^0.4.0", + "ergonode/api": "^0.5.0", + "ergonode/attribute": "^0.5.0", "jms/serializer": "^3.1", "nelmio/api-doc-bundle": "^3.4", "symfony/http-foundation": "^4.3" diff --git a/module/attribute-price/composer.json b/module/attribute-price/composer.json index 8d5d7d7e4..01eb26d63 100644 --- a/module/attribute-price/composer.json +++ b/module/attribute-price/composer.json @@ -7,10 +7,10 @@ "require": { "php": "^7.2", "doctrine/dbal": "^2.9", - "ergonode/api": "^0.4.0", - "ergonode/attribute": "^0.4.0", - "ergonode/core": "^0.4.0", - "ergonode/es": "^0.4.0", + "ergonode/api": "^0.5.0", + "ergonode/attribute": "^0.5.0", + "ergonode/core": "^0.5.0", + "ergonode/es": "^0.5.0", "jms/serializer": "^3.1", "moneyphp/money": "^3.2", "nelmio/api-doc-bundle": "^3.4", diff --git a/module/attribute-unit/composer.json b/module/attribute-unit/composer.json index 7ee8cc537..53e58453e 100644 --- a/module/attribute-unit/composer.json +++ b/module/attribute-unit/composer.json @@ -8,8 +8,8 @@ "php": "^7.2", "doctrine/collections": "^1.6", "doctrine/dbal": "^2.9", - "ergonode/api": "^0.4.0", - "ergonode/attribute": "^0.4.0", + "ergonode/api": "^0.5.0", + "ergonode/attribute": "^0.5.0", "jms/serializer": "^3.1", "nelmio/api-doc-bundle": "^3.4", "symfony/form": "^4.3", diff --git a/module/attribute/composer.json b/module/attribute/composer.json index 3dc20a84d..896c64c31 100644 --- a/module/attribute/composer.json +++ b/module/attribute/composer.json @@ -6,12 +6,12 @@ "license": "OSL-3.0", "require": { "php": "^7.2", - "ergonode/api": "^0.4.0", - "ergonode/core": "^0.4.0", - "ergonode/es": "^0.4.0", - "ergonode/grid": "^0.4.0", - "ergonode/importer": "^0.4.0", - "ergonode/value": "^0.4.0", + "ergonode/api": "^0.5.0", + "ergonode/core": "^0.5.0", + "ergonode/es": "^0.5.0", + "ergonode/grid": "^0.5.0", + "ergonode/importer": "^0.5.0", + "ergonode/value": "^0.5.0", "jms/serializer": "^3.1", "nelmio/api-doc-bundle": "^3.4", "ramsey/uuid": "^3.8", diff --git a/module/authentication/composer.json b/module/authentication/composer.json index b4b93bc9f..d513eed49 100644 --- a/module/authentication/composer.json +++ b/module/authentication/composer.json @@ -8,8 +8,8 @@ "php": "^7.2", "doctrine/dbal": "^2.9", "doctrine/orm": "^2.6", - "ergonode/api": "^0.4.0", - "ergonode/core": "^0.4.0", + "ergonode/api": "^0.5.0", + "ergonode/core": "^0.5.0", "friendsofsymfony/rest-bundle": "^2.5", "jms/serializer": "^3.1", "nelmio/api-doc-bundle": "^3.4", diff --git a/module/category-tree/composer.json b/module/category-tree/composer.json index d925afa33..79898791c 100644 --- a/module/category-tree/composer.json +++ b/module/category-tree/composer.json @@ -7,10 +7,10 @@ "require": { "php": "^7.2", "doctrine/dbal": "^2.9", - "ergonode/api": "^0.4.0", - "ergonode/category": "^0.4.0", - "ergonode/es": "^0.4.0", - "ergonode/migration": "^0.4.0", + "ergonode/api": "^0.5.0", + "ergonode/category": "^0.5.0", + "ergonode/es": "^0.5.0", + "ergonode/migration": "^0.5.0", "friendsofsymfony/rest-bundle": "^2.5", "jms/serializer": "^3.1", "nelmio/api-doc-bundle": "^3.4", diff --git a/module/category/composer.json b/module/category/composer.json index 91bd8dbce..3ba439d6f 100644 --- a/module/category/composer.json +++ b/module/category/composer.json @@ -7,12 +7,12 @@ "require": { "php": "^7.2", "doctrine/dbal": "^2.9", - "ergonode/api": "^0.4.0", - "ergonode/attribute": "^0.4.0", - "ergonode/core": "^0.4.0", - "ergonode/es": "^0.4.0", - "ergonode/grid": "^0.4.0", - "ergonode/value": "^0.4.0", + "ergonode/api": "^0.5.0", + "ergonode/attribute": "^0.5.0", + "ergonode/core": "^0.5.0", + "ergonode/es": "^0.5.0", + "ergonode/grid": "^0.5.0", + "ergonode/value": "^0.5.0", "friendsofsymfony/rest-bundle": "^2.5", "jms/serializer": "^3.1", "nelmio/api-doc-bundle": "^3.4", diff --git a/module/completeness/composer.json b/module/completeness/composer.json index 43c70657c..af6c05a8f 100644 --- a/module/completeness/composer.json +++ b/module/completeness/composer.json @@ -7,10 +7,10 @@ "require": { "php": "^7.2", "doctrine/dbal": "^2.9", - "ergonode/api": "^0.4.0", - "ergonode/attribute": "^0.4.0", - "ergonode/core": "^0.4.0", - "ergonode/editor": "^0.4.0", + "ergonode/api": "^0.5.0", + "ergonode/attribute": "^0.5.0", + "ergonode/core": "^0.5.0", + "ergonode/editor": "^0.5.0", "friendsofsymfony/rest-bundle": "^2.5", "jms/serializer": "^3.1", "nelmio/api-doc-bundle": "^3.4", diff --git a/module/condition/composer.json b/module/condition/composer.json index eeca37d23..e300606f1 100644 --- a/module/condition/composer.json +++ b/module/condition/composer.json @@ -7,13 +7,12 @@ "require": { "php": "^7.2", "doctrine/dbal": "^2.9", - "ergonode/attribute": "^0.3.0", - "ergonode/product": "^0.3.0", - "ergonode/core": "^0.3.0", - "ergonode/es": "^0.3.0", - "ergonode/grid": "^0.3.0", - "ergonode/value": "^0.3.0", - "friendsofsymfony/rest-bundle": "^2.5", + "ergonode/attribute": "^0.5.0", + "ergonode/product": "^0.5.0", + "ergonode/core": "^0.5.0", + "ergonode/es": "^0.5.0", + "ergonode/grid": "^0.5.0", + "ergonode/value": "^0.5.0", "jms/serializer": "^3.1", "nelmio/api-doc-bundle": "^3.4", "ramsey/uuid": "^3.8", diff --git a/module/core/composer.json b/module/core/composer.json index b2d6fc112..1064a2e21 100644 --- a/module/core/composer.json +++ b/module/core/composer.json @@ -7,9 +7,8 @@ "require": { "php": "^7.2", "doctrine/dbal": "^2.9", - "ergonode/api": "^0.4.0", - "ergonode/migration": "^0.4.0", - "friendsofsymfony/rest-bundle": "^2.5", + "ergonode/api": "^0.5.0", + "ergonode/migration": "^0.5.0", "jms/serializer": "^3.1", "moneyphp/money": "^3.2", "nelmio/api-doc-bundle": "^3.4", diff --git a/module/designer/composer.json b/module/designer/composer.json index 3a0cfa757..08a449b23 100644 --- a/module/designer/composer.json +++ b/module/designer/composer.json @@ -7,12 +7,11 @@ "require": { "php": "^7.2", "doctrine/dbal": "^2.9", - "ergonode/api": "^0.4.0", - "ergonode/core": "^0.4.0", - "ergonode/es": "^0.4.0", - "ergonode/grid": "^0.4.0", - "ergonode/multimedia": "^0.4.0", - "friendsofsymfony/rest-bundle": "^2.5", + "ergonode/api": "^0.5.0", + "ergonode/core": "^0.5.0", + "ergonode/es": "^0.5.0", + "ergonode/grid": "^0.5.0", + "ergonode/multimedia": "^0.5.0", "jms/serializer": "^3.1", "nelmio/api-doc-bundle": "^3.4", "ramsey/uuid": "^3.8", diff --git a/module/editor/composer.json b/module/editor/composer.json index afa367ca6..bb698aa66 100644 --- a/module/editor/composer.json +++ b/module/editor/composer.json @@ -7,14 +7,13 @@ "require": { "php": "^7.2", "doctrine/dbal": "^2.9", - "ergonode/api": "^0.4.0", - "ergonode/attribute": "^0.4.0", - "ergonode/core": "^0.4.0", - "ergonode/es": "^0.4.0", - "ergonode/grid": "^0.4.0", - "ergonode/product": "^0.4.0", - "ergonode/value": "^0.4.0", - "friendsofsymfony/rest-bundle": "^2.5", + "ergonode/api": "^0.5.0", + "ergonode/attribute": "^0.5.0", + "ergonode/core": "^0.5.0", + "ergonode/es": "^0.5.0", + "ergonode/grid": "^0.5.0", + "ergonode/product": "^0.5.0", + "ergonode/value": "^0.5.0", "jms/serializer": "^3.1", "nelmio/api-doc-bundle": "^3.4", "ramsey/uuid": "^3.8", diff --git a/module/event-sourcing/composer.json b/module/event-sourcing/composer.json index 54c24ea15..e5e410561 100644 --- a/module/event-sourcing/composer.json +++ b/module/event-sourcing/composer.json @@ -7,8 +7,8 @@ "require": { "php": "^7.2", "doctrine/dbal": "^2.9", - "ergonode/api": "^0.4.0", - "ergonode/core": "^0.4.0", + "ergonode/api": "^0.5.0", + "ergonode/core": "^0.5.0", "jms/serializer": "^3.1", "symfony/cache": "^4.3", "symfony/config": "^4.3", diff --git a/module/fixture/composer.json b/module/fixture/composer.json index 991430429..d7e4bb297 100644 --- a/module/fixture/composer.json +++ b/module/fixture/composer.json @@ -6,7 +6,7 @@ "license": "OSL-3.0", "require": { "php": "^7.2", - "ergonode/es": "^0.4.0", + "ergonode/es": "^0.5.0", "nelmio/alice": "^3.5", "symfony/config": "^4.3", "symfony/dependency-injection": "^4.3", diff --git a/module/grid/composer.json b/module/grid/composer.json index eb9261df2..d4ed73060 100644 --- a/module/grid/composer.json +++ b/module/grid/composer.json @@ -9,8 +9,8 @@ "ext-json": "*", "doctrine/collections": "^1.6", "doctrine/dbal": "^2.9", - "ergonode/api": "^0.4.0", - "ergonode/core": "^0.4.0", + "ergonode/api": "^0.5.0", + "ergonode/core": "^0.5.0", "symfony/http-foundation": "^4.3" }, "autoload": { diff --git a/module/importer/composer.json b/module/importer/composer.json index 9b0748bea..8cdd3ba12 100644 --- a/module/importer/composer.json +++ b/module/importer/composer.json @@ -7,12 +7,11 @@ "require": { "php": "^7.2", "doctrine/dbal": "^2.9", - "ergonode/api": "^0.4.0", - "ergonode/core": "^0.4.0", - "ergonode/grid": "^0.4.0", - "ergonode/reader": "^0.4.0", - "ergonode/transformer": "^0.4.0", - "friendsofsymfony/rest-bundle": "^2.5", + "ergonode/api": "^0.5.0", + "ergonode/core": "^0.5.0", + "ergonode/grid": "^0.5.0", + "ergonode/reader": "^0.5.0", + "ergonode/transformer": "^0.5.0", "jms/serializer": "^3.1", "nelmio/api-doc-bundle": "^3.4", "ramsey/uuid": "^3.8", diff --git a/module/multimedia/composer.json b/module/multimedia/composer.json index 22cce29bb..036428e51 100644 --- a/module/multimedia/composer.json +++ b/module/multimedia/composer.json @@ -7,10 +7,9 @@ "require": { "php": "^7.2", "doctrine/dbal": "^2.9", - "ergonode/api": "^0.4.0", - "ergonode/core": "^0.4.0", - "ergonode/es": "^0.4.0", - "friendsofsymfony/rest-bundle": "^2.5", + "ergonode/api": "^0.5.0", + "ergonode/core": "^0.5.0", + "ergonode/es": "^0.5.0", "jms/serializer": "^3.1", "nelmio/api-doc-bundle": "^3.4", "ramsey/uuid": "^3.8", diff --git a/module/product-simple/composer.json b/module/product-simple/composer.json index 59d7fa8ab..f4779ad50 100644 --- a/module/product-simple/composer.json +++ b/module/product-simple/composer.json @@ -13,7 +13,7 @@ }, "require": { "php": "^7.2", - "ergonode/product": "^0.4.0" + "ergonode/product": "^0.5.0" }, "autoload": { "psr-4": { diff --git a/module/product/composer.json b/module/product/composer.json index 8906ad156..c671e5dbf 100644 --- a/module/product/composer.json +++ b/module/product/composer.json @@ -7,15 +7,14 @@ "require": { "php": "^7.2", "doctrine/dbal": "^2.9", - "ergonode/api": "^0.4.0", - "ergonode/category": "^0.4.0", - "ergonode/core": "^0.4.0", - "ergonode/es": "^0.4.0", - "ergonode/grid": "^0.4.0", - "ergonode/migration": "^0.4.0", - "ergonode/workflow": "^0.4.0", - "ergonode/value": "^0.4.0", - "friendsofsymfony/rest-bundle": "^2.5", + "ergonode/api": "^0.5.0", + "ergonode/category": "^0.5.0", + "ergonode/core": "^0.5.0", + "ergonode/es": "^0.5.0", + "ergonode/grid": "^0.5.0", + "ergonode/migration": "^0.5.0", + "ergonode/workflow": "^0.5.0", + "ergonode/value": "^0.5.0", "jms/serializer": "^3.1", "nelmio/api-doc-bundle": "^3.4", "ramsey/uuid": "^3.8", diff --git a/module/reader/composer.json b/module/reader/composer.json index 973829b89..a1e4f71dd 100644 --- a/module/reader/composer.json +++ b/module/reader/composer.json @@ -7,12 +7,11 @@ "require": { "php": "^7.2", "doctrine/dbal": "^2.9", - "ergonode/api": "^0.4.0", - "ergonode/core": "^0.4.0", - "ergonode/es": "^0.4.0", - "ergonode/grid": "^0.4.0", - "ergonode/migration": "^0.4.0", - "friendsofsymfony/rest-bundle": "^2.5", + "ergonode/api": "^0.5.0", + "ergonode/core": "^0.5.0", + "ergonode/es": "^0.5.0", + "ergonode/grid": "^0.5.0", + "ergonode/migration": "^0.5.0", "jms/serializer": "^3.1", "nelmio/api-doc-bundle": "^3.4", "ramsey/uuid": "^3.8", diff --git a/module/segment/composer.json b/module/segment/composer.json index 3e4cc5a62..600eea0a8 100644 --- a/module/segment/composer.json +++ b/module/segment/composer.json @@ -7,13 +7,12 @@ "require": { "php": "^7.2", "doctrine/dbal": "^2.9", - "ergonode/attribute": "^0.3.0", - "ergonode/product": "^0.3.0", - "ergonode/core": "^0.3.0", - "ergonode/es": "^0.3.0", - "ergonode/grid": "^0.3.0", - "ergonode/value": "^0.3.0", - "friendsofsymfony/rest-bundle": "^2.5", + "ergonode/attribute": "^0.5.0", + "ergonode/product": "^0.5.0", + "ergonode/core": "^0.5.0", + "ergonode/es": "^0.5.0", + "ergonode/grid": "^0.5.0", + "ergonode/value": "^0.5.0", "jms/serializer": "^3.1", "nelmio/api-doc-bundle": "^3.4", "ramsey/uuid": "^3.8", diff --git a/module/transformer/composer.json b/module/transformer/composer.json index b987a61f7..ced41e10e 100644 --- a/module/transformer/composer.json +++ b/module/transformer/composer.json @@ -7,11 +7,10 @@ "require": { "php": "^7.2", "doctrine/dbal": "^2.9", - "ergonode/api": "^0.4.0", - "ergonode/core": "^0.4.0", - "ergonode/es": "^0.4.0", - "ergonode/value": "^0.4.0", - "friendsofsymfony/rest-bundle": "^2.5", + "ergonode/api": "^0.5.0", + "ergonode/core": "^0.5.0", + "ergonode/es": "^0.5.0", + "ergonode/value": "^0.5.0", "jms/serializer": "^3.1", "nelmio/api-doc-bundle": "^3.4", "ramsey/uuid": "^3.8", diff --git a/module/translation-deepl/composer.json b/module/translation-deepl/composer.json index 0c405be44..b64d76e10 100644 --- a/module/translation-deepl/composer.json +++ b/module/translation-deepl/composer.json @@ -9,8 +9,8 @@ "ext-json": "*", "doctrine/collections": "^1.6", "doctrine/dbal": "^2.9", - "ergonode/api": "^0.4.0", - "ergonode/core": "^0.4.0", + "ergonode/api": "^0.5.0", + "ergonode/core": "^0.5.0", "symfony/http-foundation": "^4.3", "scn/deepl-api-connector": "^2.0" }, diff --git a/module/value/composer.json b/module/value/composer.json index bb1b563e7..3268c59c5 100644 --- a/module/value/composer.json +++ b/module/value/composer.json @@ -7,9 +7,9 @@ "require": { "php": "^7.2", "doctrine/dbal": "^2.9", - "ergonode/attribute": "^0.4.0", - "ergonode/core": "^0.4.0", - "ergonode/es": "^0.4.0", + "ergonode/attribute": "^0.5.0", + "ergonode/core": "^0.5.0", + "ergonode/es": "^0.5.0", "jms/serializer": "^3.1", "ramsey/uuid": "^3.8" }, diff --git a/module/workflow/composer.json b/module/workflow/composer.json index 1a6eeb76a..656dd4bd1 100644 --- a/module/workflow/composer.json +++ b/module/workflow/composer.json @@ -6,10 +6,10 @@ "license": "OSL-3.0", "require": { "php": "^7.2", - "ergonode/api": "^0.4.0", - "ergonode/core": "^0.4.0", - "ergonode/es": "^0.4.0", - "ergonode/grid": "^0.4.0", + "ergonode/api": "^0.5.0", + "ergonode/core": "^0.5.0", + "ergonode/es": "^0.5.0", + "ergonode/grid": "^0.5.0", "doctrine/dbal": "^2.9", "jms/serializer": "^3.1", "ramsey/uuid": "^3.8"