diff --git a/databox/api/migrations/Version20241028171322.php b/databox/api/migrations/Version20241028171322.php new file mode 100644 index 000000000..caee96234 --- /dev/null +++ b/databox/api/migrations/Version20241028171322.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE job_state ADD number SMALLINT NOT NULL DEFAULT 0'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE job_state DROP number'); + } +} diff --git a/databox/api/src/Controller/Admin/JobStateCrudController.php b/databox/api/src/Controller/Admin/JobStateCrudController.php index 65853eb6d..670071ddf 100644 --- a/databox/api/src/Controller/Admin/JobStateCrudController.php +++ b/databox/api/src/Controller/Admin/JobStateCrudController.php @@ -19,9 +19,12 @@ use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField; use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField; use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField; +use EasyCorp\Bundle\EasyAdminBundle\Field\NumberField; use EasyCorp\Bundle\EasyAdminBundle\Field\TextField; use EasyCorp\Bundle\EasyAdminBundle\Filter\ChoiceFilter; use EasyCorp\Bundle\EasyAdminBundle\Filter\DateTimeFilter; +use EasyCorp\Bundle\EasyAdminBundle\Filter\NumericFilter; +use EasyCorp\Bundle\EasyAdminBundle\Filter\TextFilter; use Symfony\Component\HttpFoundation\RedirectResponse; class JobStateCrudController extends AbstractAdminCrudController @@ -109,6 +112,7 @@ public function configureFilters(Filters $filters): Filters ->add(AssociationIdentifierFilter::new('workflow')) ->add(DateTimeFilter::new('startedAt')) ->add(DateTimeFilter::new('endedAt')) + ->add(NumericFilter::new('number')) ; } @@ -119,6 +123,7 @@ public function configureFields(string $pageName): iterable yield TextField::new('jobId', 'Job ID'); yield DateTimeField::new('triggeredAt', 'Triggered At'); yield DateTimeField::new('startedAt', 'Started At'); + yield NumberField::new('number'); yield ChoiceField::new('status', 'Status') ->setChoices([ 'TRIGGERED' => ModelJobState::STATUS_TRIGGERED, diff --git a/databox/api/tests/Rendition/Phraseanet/PhraseanetRenditionApiV3SubDefMethodTest.php b/databox/api/tests/Rendition/Phraseanet/PhraseanetRenditionApiV3SubDefMethodTest.php index 08e8cbeb9..b597b49fa 100644 --- a/databox/api/tests/Rendition/Phraseanet/PhraseanetRenditionApiV3SubDefMethodTest.php +++ b/databox/api/tests/Rendition/Phraseanet/PhraseanetRenditionApiV3SubDefMethodTest.php @@ -99,7 +99,7 @@ public function testApiV3SubDefIsTriggered(): void $envelope = $inMemoryTransport->get()[0]; $eventMessage = $envelope->getMessage(); self::assertInstanceOf(JobConsumer::class, $eventMessage); - self::assertEquals(PhraseanetRenditionIntegration::getName().':'.$integration->getId().':api', $eventMessage->getJobId()); + self::assertEquals(PhraseanetRenditionIntegration::getName().':'.$integration->getId().':api', $eventMessage->getJobStateId()); $this->consumeEvent($envelope); self::assertEquals('POST', $mockResponse->getRequestMethod()); diff --git a/databox/api/tests/Rendition/Phraseanet/PhraseanetRenditionEnqueueMethodTest.php b/databox/api/tests/Rendition/Phraseanet/PhraseanetRenditionEnqueueMethodTest.php index 7ed0807bf..b1fd5920e 100644 --- a/databox/api/tests/Rendition/Phraseanet/PhraseanetRenditionEnqueueMethodTest.php +++ b/databox/api/tests/Rendition/Phraseanet/PhraseanetRenditionEnqueueMethodTest.php @@ -103,7 +103,7 @@ public function testEnqueueIsTriggered(): void $eventMessage = $envelope->getMessage(); self::assertInstanceOf(JobConsumer::class, $eventMessage); $workflowId = $eventMessage->getWorkflowId(); - self::assertEquals(PhraseanetRenditionIntegration::getName().':'.$integration->getId().':enqueue', $eventMessage->getJobId()); + self::assertEquals(PhraseanetRenditionIntegration::getName().':'.$integration->getId().':enqueue', $eventMessage->getJobStateId()); $this->consumeEvent($envelope); self::assertEquals('POST', $mockResponse->getRequestMethod()); diff --git a/lib/php/workflow-bundle/Resources/config/doctrine/JobState.orm.yml b/lib/php/workflow-bundle/Resources/config/doctrine/JobState.orm.yml index 59e624858..8e9b16386 100644 --- a/lib/php/workflow-bundle/Resources/config/doctrine/JobState.orm.yml +++ b/lib/php/workflow-bundle/Resources/config/doctrine/JobState.orm.yml @@ -37,3 +37,7 @@ Alchemy\Workflow\Doctrine\Entity\JobState: name: status type: smallint nullable: false + number: + name: status + type: smallint + nullable: false diff --git a/lib/php/workflow/composer.json b/lib/php/workflow/composer.json index c78c90f8c..bb8b1bd57 100644 --- a/lib/php/workflow/composer.json +++ b/lib/php/workflow/composer.json @@ -27,16 +27,17 @@ "require": { "php": "^8.3", "ext-json": "*", - "symfony/console": "^5.4 || ^6", - "symfony/yaml": "^6.2", + "symfony/console": "^5.4 || ^6 || ^7", + "symfony/yaml": "^6.2 || ^7", "ramsey/uuid": "^4.2", - "symfony/process": "^6.3", - "symfony/expression-language": "^5.2 || ^6.2", - "symfony/property-access": "^5.2 || ^6.2" + "symfony/process": "^6.3 || ^7", + "symfony/expression-language": "^5.2 || ^6.2 || ^7", + "symfony/property-access": "^5.2 || ^6.2 || ^7" }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.17", "phpunit/phpunit": "^9.5", + "symfony/http-kernel": "^6 || ^7", "symfony/var-dumper": "^5.4", "doctrine/orm": "^2.14", "symfony/messenger": "^6.4", diff --git a/lib/php/workflow/composer.lock b/lib/php/workflow/composer.lock index ed001d79b..1e244d5e2 100644 --- a/lib/php/workflow/composer.lock +++ b/lib/php/workflow/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9180388da9ea6ef37dac9183a749b6e8", + "content-hash": "caa1343f3850fb8c05b2e114b1230026", "packages": [ { "name": "brick/math", @@ -401,16 +401,16 @@ }, { "name": "symfony/cache", - "version": "v6.4.12", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "a463451b7f6ac4a47b98dbfc78ec2d3560c759d8" + "reference": "8079a3006f53805e7771d086b62428b7cac481dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/a463451b7f6ac4a47b98dbfc78ec2d3560c759d8", - "reference": "a463451b7f6ac4a47b98dbfc78ec2d3560c759d8", + "url": "https://api.github.com/repos/symfony/cache/zipball/8079a3006f53805e7771d086b62428b7cac481dd", + "reference": "8079a3006f53805e7771d086b62428b7cac481dd", "shasum": "" }, "require": { @@ -477,7 +477,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v6.4.12" + "source": "https://github.com/symfony/cache/tree/v6.4.13" }, "funding": [ { @@ -493,7 +493,7 @@ "type": "tidelift" } ], - "time": "2024-09-16T16:01:33+00:00" + "time": "2024-10-25T15:39:47+00:00" }, { "name": "symfony/cache-contracts", @@ -573,47 +573,46 @@ }, { "name": "symfony/console", - "version": "v6.4.12", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "72d080eb9edf80e36c19be61f72c98ed8273b765" + "reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/72d080eb9edf80e36c19be61f72c98ed8273b765", - "reference": "72d080eb9edf80e36c19be61f72c98ed8273b765", + "url": "https://api.github.com/repos/symfony/console/zipball/bb5192af6edc797cbab5c8e8ecfea2fe5f421e57", + "reference": "bb5192af6edc797cbab5c8e8ecfea2fe5f421e57", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.2", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0|^7.0" + "symfony/string": "^6.4|^7.0" }, "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" }, "provide": { "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", "symfony/http-foundation": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0", - "symfony/var-dumper": "^5.4|^6.0|^7.0" + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -647,7 +646,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.12" + "source": "https://github.com/symfony/console/tree/v7.1.6" }, "funding": [ { @@ -663,7 +662,7 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:15:52+00:00" + "time": "2024-10-09T08:46:59+00:00" }, { "name": "symfony/deprecation-contracts", @@ -734,21 +733,21 @@ }, { "name": "symfony/expression-language", - "version": "v6.4.11", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/expression-language.git", - "reference": "564e109c40d3637053c942a29a58e9434592a8bf" + "reference": "c3a1224bc144b36cd79149b42c1aecd5f81395a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/expression-language/zipball/564e109c40d3637053c942a29a58e9434592a8bf", - "reference": "564e109c40d3637053c942a29a58e9434592a8bf", + "url": "https://api.github.com/repos/symfony/expression-language/zipball/c3a1224bc144b36cd79149b42c1aecd5f81395a5", + "reference": "c3a1224bc144b36cd79149b42c1aecd5f81395a5", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/cache": "^5.4|^6.0|^7.0", + "php": ">=8.2", + "symfony/cache": "^6.4|^7.0", "symfony/deprecation-contracts": "^2.5|^3", "symfony/service-contracts": "^2.5|^3" }, @@ -778,7 +777,7 @@ "description": "Provides an engine that can compile and evaluate expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/expression-language/tree/v6.4.11" + "source": "https://github.com/symfony/expression-language/tree/v7.1.6" }, "funding": [ { @@ -794,7 +793,7 @@ "type": "tidelift" } ], - "time": "2024-08-12T09:55:28+00:00" + "time": "2024-10-09T08:46:59+00:00" }, { "name": "symfony/polyfill-ctype", @@ -1116,20 +1115,20 @@ }, { "name": "symfony/process", - "version": "v6.4.12", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "3f94e5f13ff58df371a7ead461b6e8068900fbb3" + "reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/3f94e5f13ff58df371a7ead461b6e8068900fbb3", - "reference": "3f94e5f13ff58df371a7ead461b6e8068900fbb3", + "url": "https://api.github.com/repos/symfony/process/zipball/6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e", + "reference": "6aaa189ddb4ff6b5de8fa3210f2fb42c87b4d12e", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "type": "library", "autoload": { @@ -1157,7 +1156,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.4.12" + "source": "https://github.com/symfony/process/tree/v7.1.6" }, "funding": [ { @@ -1173,29 +1172,28 @@ "type": "tidelift" } ], - "time": "2024-09-17T12:47:12+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/property-access", - "version": "v6.4.11", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/property-access.git", - "reference": "866f6cd84f2094cbc6f66ce9752faf749916e2a9" + "reference": "975d7f7fd8fcb952364c6badc46d01a580532bf9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/866f6cd84f2094cbc6f66ce9752faf749916e2a9", - "reference": "866f6cd84f2094cbc6f66ce9752faf749916e2a9", + "url": "https://api.github.com/repos/symfony/property-access/zipball/975d7f7fd8fcb952364c6badc46d01a580532bf9", + "reference": "975d7f7fd8fcb952364c6badc46d01a580532bf9", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/property-info": "^5.4|^6.0|^7.0" + "php": ">=8.2", + "symfony/property-info": "^6.4|^7.0" }, "require-dev": { - "symfony/cache": "^5.4|^6.0|^7.0" + "symfony/cache": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -1234,7 +1232,7 @@ "reflection" ], "support": { - "source": "https://github.com/symfony/property-access/tree/v6.4.11" + "source": "https://github.com/symfony/property-access/tree/v7.1.6" }, "funding": [ { @@ -1250,20 +1248,20 @@ "type": "tidelift" } ], - "time": "2024-08-30T16:10:11+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/property-info", - "version": "v7.1.3", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/property-info.git", - "reference": "88a279df2db5b7919cac6f35d6a5d1d7147e6a9b" + "reference": "6b630ff585d9fdc72f50369885ad4364a849cf02" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/88a279df2db5b7919cac6f35d6a5d1d7147e6a9b", - "reference": "88a279df2db5b7919cac6f35d6a5d1d7147e6a9b", + "url": "https://api.github.com/repos/symfony/property-info/zipball/6b630ff585d9fdc72f50369885ad4364a849cf02", + "reference": "6b630ff585d9fdc72f50369885ad4364a849cf02", "shasum": "" }, "require": { @@ -1318,7 +1316,7 @@ "validator" ], "support": { - "source": "https://github.com/symfony/property-info/tree/v7.1.3" + "source": "https://github.com/symfony/property-info/tree/v7.1.6" }, "funding": [ { @@ -1334,7 +1332,7 @@ "type": "tidelift" } ], - "time": "2024-07-26T07:36:36+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/service-contracts", @@ -1421,16 +1419,16 @@ }, { "name": "symfony/string", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306" + "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/d66f9c343fa894ec2037cc928381df90a7ad4306", - "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306", + "url": "https://api.github.com/repos/symfony/string/zipball/61b72d66bf96c360a727ae6232df5ac83c71f626", + "reference": "61b72d66bf96c360a727ae6232df5ac83c71f626", "shasum": "" }, "require": { @@ -1488,7 +1486,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.5" + "source": "https://github.com/symfony/string/tree/v7.1.6" }, "funding": [ { @@ -1504,20 +1502,20 @@ "type": "tidelift" } ], - "time": "2024-09-20T08:28:38+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/type-info", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/type-info.git", - "reference": "9f6094aa900d2c06bd61576a6f279d4ac441515f" + "reference": "a13032128c307470955c45c99201349b15cd7f4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/type-info/zipball/9f6094aa900d2c06bd61576a6f279d4ac441515f", - "reference": "9f6094aa900d2c06bd61576a6f279d4ac441515f", + "url": "https://api.github.com/repos/symfony/type-info/zipball/a13032128c307470955c45c99201349b15cd7f4a", + "reference": "a13032128c307470955c45c99201349b15cd7f4a", "shasum": "" }, "require": { @@ -1570,7 +1568,7 @@ "type" ], "support": { - "source": "https://github.com/symfony/type-info/tree/v7.1.5" + "source": "https://github.com/symfony/type-info/tree/v7.1.6" }, "funding": [ { @@ -1586,20 +1584,20 @@ "type": "tidelift" } ], - "time": "2024-09-19T21:48:23+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/var-exporter", - "version": "v7.1.2", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "b80a669a2264609f07f1667f891dbfca25eba44c" + "reference": "90173ef89c40e7c8c616653241048705f84130ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/b80a669a2264609f07f1667f891dbfca25eba44c", - "reference": "b80a669a2264609f07f1667f891dbfca25eba44c", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/90173ef89c40e7c8c616653241048705f84130ef", + "reference": "90173ef89c40e7c8c616653241048705f84130ef", "shasum": "" }, "require": { @@ -1646,7 +1644,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v7.1.2" + "source": "https://github.com/symfony/var-exporter/tree/v7.1.6" }, "funding": [ { @@ -1662,32 +1660,31 @@ "type": "tidelift" } ], - "time": "2024-06-28T08:00:31+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/yaml", - "version": "v6.4.12", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "762ee56b2649659380e0ef4d592d807bc17b7971" + "reference": "3ced3f29e4f0d6bce2170ff26719f1fe9aacc671" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/762ee56b2649659380e0ef4d592d807bc17b7971", - "reference": "762ee56b2649659380e0ef4d592d807bc17b7971", + "url": "https://api.github.com/repos/symfony/yaml/zipball/3ced3f29e4f0d6bce2170ff26719f1fe9aacc671", + "reference": "3ced3f29e4f0d6bce2170ff26719f1fe9aacc671", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", + "php": ">=8.2", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<5.4" + "symfony/console": "<6.4" }, "require-dev": { - "symfony/console": "^5.4|^6.0|^7.0" + "symfony/console": "^6.4|^7.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -1718,7 +1715,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v6.4.12" + "source": "https://github.com/symfony/yaml/tree/v7.1.6" }, "funding": [ { @@ -1734,7 +1731,7 @@ "type": "tidelift" } ], - "time": "2024-09-17T12:47:12+00:00" + "time": "2024-09-25T14:20:29+00:00" } ], "packages-dev": [ @@ -2288,16 +2285,16 @@ }, { "name": "doctrine/common", - "version": "3.4.4", + "version": "3.4.5", "source": { "type": "git", "url": "https://github.com/doctrine/common.git", - "reference": "0aad4b7ab7ce8c6602dfbb1e1a24581275fb9d1a" + "reference": "6c8fef961f67b8bc802ce3e32e3ebd1022907286" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/common/zipball/0aad4b7ab7ce8c6602dfbb1e1a24581275fb9d1a", - "reference": "0aad4b7ab7ce8c6602dfbb1e1a24581275fb9d1a", + "url": "https://api.github.com/repos/doctrine/common/zipball/6c8fef961f67b8bc802ce3e32e3ebd1022907286", + "reference": "6c8fef961f67b8bc802ce3e32e3ebd1022907286", "shasum": "" }, "require": { @@ -2359,7 +2356,7 @@ ], "support": { "issues": "https://github.com/doctrine/common/issues", - "source": "https://github.com/doctrine/common/tree/3.4.4" + "source": "https://github.com/doctrine/common/tree/3.4.5" }, "funding": [ { @@ -2375,20 +2372,20 @@ "type": "tidelift" } ], - "time": "2024-04-16T13:35:33+00:00" + "time": "2024-10-08T15:53:43+00:00" }, { "name": "doctrine/dbal", - "version": "3.9.1", + "version": "3.9.3", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "d7dc08f98cba352b2bab5d32c5e58f7e745c11a7" + "reference": "61446f07fcb522414d6cfd8b1c3e5f9e18c579ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/d7dc08f98cba352b2bab5d32c5e58f7e745c11a7", - "reference": "d7dc08f98cba352b2bab5d32c5e58f7e745c11a7", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/61446f07fcb522414d6cfd8b1c3e5f9e18c579ba", + "reference": "61446f07fcb522414d6cfd8b1c3e5f9e18c579ba", "shasum": "" }, "require": { @@ -2404,7 +2401,7 @@ "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "1.12.0", + "phpstan/phpstan": "1.12.6", "phpstan/phpstan-strict-rules": "^1.6", "phpunit/phpunit": "9.6.20", "psalm/plugin-phpunit": "0.18.4", @@ -2472,7 +2469,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.9.1" + "source": "https://github.com/doctrine/dbal/tree/3.9.3" }, "funding": [ { @@ -2488,7 +2485,7 @@ "type": "tidelift" } ], - "time": "2024-09-01T13:49:23+00:00" + "time": "2024-10-10T17:56:43+00:00" }, { "name": "doctrine/deprecations", @@ -2868,16 +2865,16 @@ }, { "name": "doctrine/orm", - "version": "2.19.7", + "version": "2.20.0", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "168ac31084226f94d42e7461a40ff5607a56bd35" + "reference": "8ed6c2234aba019f9737a6bcc9516438e62da27c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/168ac31084226f94d42e7461a40ff5607a56bd35", - "reference": "168ac31084226f94d42e7461a40ff5607a56bd35", + "url": "https://api.github.com/repos/doctrine/orm/zipball/8ed6c2234aba019f9737a6bcc9516438e62da27c", + "reference": "8ed6c2234aba019f9737a6bcc9516438e62da27c", "shasum": "" }, "require": { @@ -2906,7 +2903,9 @@ "doctrine/annotations": "^1.13 || ^2", "doctrine/coding-standard": "^9.0.2 || ^12.0", "phpbench/phpbench": "^0.16.10 || ^1.0", - "phpstan/phpstan": "~1.4.10 || 1.11.1", + "phpstan/extension-installer": "~1.1.0 || ^1.4", + "phpstan/phpstan": "~1.4.10 || 1.12.6", + "phpstan/phpstan-deprecation-rules": "^1", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", "psr/log": "^1 || ^2 || ^3", "squizlabs/php_codesniffer": "3.7.2", @@ -2963,9 +2962,9 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/2.19.7" + "source": "https://github.com/doctrine/orm/tree/2.20.0" }, - "time": "2024-08-23T06:54:57+00:00" + "time": "2024-10-11T11:47:24+00:00" }, { "name": "doctrine/persistence", @@ -3337,16 +3336,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.3.0", + "version": "v5.3.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "3abf7425cd284141dc5d8d14a9ee444de3345d1a" + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3abf7425cd284141dc5d8d14a9ee444de3345d1a", - "reference": "3abf7425cd284141dc5d8d14a9ee444de3345d1a", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", + "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", "shasum": "" }, "require": { @@ -3389,9 +3388,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" }, - "time": "2024-09-29T13:56:26+00:00" + "time": "2024-10-08T18:51:32+00:00" }, { "name": "phar-io/manifest", @@ -3513,16 +3512,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.12.6", + "version": "1.12.7", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "dc4d2f145a88ea7141ae698effd64d9df46527ae" + "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc4d2f145a88ea7141ae698effd64d9df46527ae", - "reference": "dc4d2f145a88ea7141ae698effd64d9df46527ae", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", + "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", "shasum": "" }, "require": { @@ -3567,7 +3566,7 @@ "type": "github" } ], - "time": "2024-10-06T15:03:59+00:00" + "time": "2024-10-18T11:12:07+00:00" }, { "name": "phpunit/php-code-coverage", @@ -4621,16 +4620,16 @@ }, { "name": "rector/rector", - "version": "1.2.6", + "version": "1.2.8", "source": { "type": "git", "url": "https://github.com/rectorphp/rector.git", - "reference": "6ca85da28159dbd3bb36211c5104b7bc91278e99" + "reference": "05755bf43617449c08ee8e50fb840c85ad3b1240" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/6ca85da28159dbd3bb36211c5104b7bc91278e99", - "reference": "6ca85da28159dbd3bb36211c5104b7bc91278e99", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/05755bf43617449c08ee8e50fb840c85ad3b1240", + "reference": "05755bf43617449c08ee8e50fb840c85ad3b1240", "shasum": "" }, "require": { @@ -4668,7 +4667,7 @@ ], "support": { "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/1.2.6" + "source": "https://github.com/rectorphp/rector/tree/1.2.8" }, "funding": [ { @@ -4676,7 +4675,7 @@ "type": "github" } ], - "time": "2024-10-03T08:56:44+00:00" + "time": "2024-10-18T11:54:27+00:00" }, { "name": "sebastian/cli-parser", @@ -5643,16 +5642,16 @@ }, { "name": "symfony/clock", - "version": "v7.1.1", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", - "reference": "3dfc8b084853586de51dd1441c6242c76a28cbe7" + "reference": "97bebc53548684c17ed696bc8af016880f0f098d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/3dfc8b084853586de51dd1441c6242c76a28cbe7", - "reference": "3dfc8b084853586de51dd1441c6242c76a28cbe7", + "url": "https://api.github.com/repos/symfony/clock/zipball/97bebc53548684c17ed696bc8af016880f0f098d", + "reference": "97bebc53548684c17ed696bc8af016880f0f098d", "shasum": "" }, "require": { @@ -5697,7 +5696,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v7.1.1" + "source": "https://github.com/symfony/clock/tree/v7.1.6" }, "funding": [ { @@ -5713,28 +5712,102 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v6.3.12", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "93a8400a7eaaaf385b2d6f71e30e064baa639629" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/93a8400a7eaaaf385b2d6f71e30e064baa639629", + "reference": "93a8400a7eaaaf385b2d6f71e30e064baa639629", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^5.4|^6.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5" + }, + "require-dev": { + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/serializer": "^5.4|^6.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v6.3.12" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T14:35:58+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.1.1", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7" + "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", - "reference": "9fa7f7a21beb22a39a8f3f28618b29e50d7a55a7", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", + "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.1", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<6.4", + "symfony/dependency-injection": "<5.4", "symfony/service-contracts": "<2.5" }, "provide": { @@ -5743,13 +5816,13 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/error-handler": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0" + "symfony/stopwatch": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -5777,7 +5850,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.1.1" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.13" }, "funding": [ { @@ -5793,7 +5866,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -5873,16 +5946,16 @@ }, { "name": "symfony/filesystem", - "version": "v7.1.5", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a" + "reference": "c835867b3c62bb05c7fe3d637c871c7ae52024d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/61fe0566189bf32e8cfee78335d8776f64a66f5a", - "reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/c835867b3c62bb05c7fe3d637c871c7ae52024d4", + "reference": "c835867b3c62bb05c7fe3d637c871c7ae52024d4", "shasum": "" }, "require": { @@ -5919,7 +5992,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.1.5" + "source": "https://github.com/symfony/filesystem/tree/v7.1.6" }, "funding": [ { @@ -5935,20 +6008,20 @@ "type": "tidelift" } ], - "time": "2024-09-17T09:16:35+00:00" + "time": "2024-10-25T15:11:02+00:00" }, { "name": "symfony/finder", - "version": "v7.1.4", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "d95bbf319f7d052082fb7af147e0f835a695e823" + "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/d95bbf319f7d052082fb7af147e0f835a695e823", - "reference": "d95bbf319f7d052082fb7af147e0f835a695e823", + "url": "https://api.github.com/repos/symfony/finder/zipball/2cb89664897be33f78c65d3d2845954c8d7a43b8", + "reference": "2cb89664897be33f78c65d3d2845954c8d7a43b8", "shasum": "" }, "require": { @@ -5983,7 +6056,196 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.1.4" + "source": "https://github.com/symfony/finder/tree/v7.1.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-01T08:31:23+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v6.4.13", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "4c0341b3e0a7291e752c69d2a1ed9a84b68d604c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/4c0341b3e0a7291e752c69d2a1ed9a84b68d604c", + "reference": "4c0341b3e0a7291e752c69d2a1ed9a84b68d604c", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php83": "^1.27" + }, + "conflict": { + "symfony/cache": "<6.3" + }, + "require-dev": { + "doctrine/dbal": "^2.13.1|^3|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.3|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4|^7.0", + "symfony/mime": "^5.4|^6.0|^7.0", + "symfony/rate-limiter": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v6.4.13" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-10-11T19:20:58+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v6.2.14", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "d05cebbc07478d37ff1e0f0079f06298a096b870" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/d05cebbc07478d37ff1e0f0079f06298a096b870", + "reference": "d05cebbc07478d37ff1e0f0079f06298a096b870", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/error-handler": "^6.1", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/http-foundation": "^5.4.21|^6.2.7", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/browser-kit": "<5.4", + "symfony/cache": "<5.4", + "symfony/config": "<6.1", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<6.2", + "symfony/doctrine-bridge": "<5.4", + "symfony/form": "<5.4", + "symfony/http-client": "<5.4", + "symfony/mailer": "<5.4", + "symfony/messenger": "<5.4", + "symfony/translation": "<5.4", + "symfony/twig-bridge": "<5.4", + "symfony/validator": "<5.4", + "twig/twig": "<2.13" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^5.4|^6.0", + "symfony/config": "^6.1", + "symfony/console": "^5.4|^6.0", + "symfony/css-selector": "^5.4|^6.0", + "symfony/dependency-injection": "^6.2", + "symfony/dom-crawler": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/http-client-contracts": "^1.1|^2|^3", + "symfony/process": "^5.4|^6.0", + "symfony/routing": "^5.4|^6.0", + "symfony/stopwatch": "^5.4|^6.0", + "symfony/translation": "^5.4|^6.0", + "symfony/translation-contracts": "^1.1|^2|^3", + "symfony/uid": "^5.4|^6.0", + "symfony/var-exporter": "^6.2", + "twig/twig": "^2.13|^3.0.4" + }, + "suggest": { + "symfony/browser-kit": "", + "symfony/config": "", + "symfony/console": "", + "symfony/dependency-injection": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v6.2.14" }, "funding": [ { @@ -5999,20 +6261,20 @@ "type": "tidelift" } ], - "time": "2024-08-13T14:28:19+00:00" + "time": "2023-07-31T10:40:35+00:00" }, { "name": "symfony/messenger", - "version": "v6.4.12", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/messenger.git", - "reference": "05035355ef94de2cb054f8697e65d82f67bf89d4" + "reference": "5cd75048611f7a86a7dca7f1cf7089d0d7ef90ff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/messenger/zipball/05035355ef94de2cb054f8697e65d82f67bf89d4", - "reference": "05035355ef94de2cb054f8697e65d82f67bf89d4", + "url": "https://api.github.com/repos/symfony/messenger/zipball/5cd75048611f7a86a7dca7f1cf7089d0d7ef90ff", + "reference": "5cd75048611f7a86a7dca7f1cf7089d0d7ef90ff", "shasum": "" }, "require": { @@ -6070,7 +6332,7 @@ "description": "Helps applications send and receive messages to/from other applications or via message queues", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/messenger/tree/v6.4.12" + "source": "https://github.com/symfony/messenger/tree/v6.4.13" }, "funding": [ { @@ -6086,20 +6348,20 @@ "type": "tidelift" } ], - "time": "2024-09-08T12:31:10+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.1.1", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55" + "reference": "85e95eeede2d41cd146146e98c9c81d9214cae85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/47aa818121ed3950acd2b58d1d37d08a94f9bf55", - "reference": "47aa818121ed3950acd2b58d1d37d08a94f9bf55", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/85e95eeede2d41cd146146e98c9c81d9214cae85", + "reference": "85e95eeede2d41cd146146e98c9c81d9214cae85", "shasum": "" }, "require": { @@ -6137,7 +6399,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.1.1" + "source": "https://github.com/symfony/options-resolver/tree/v7.1.6" }, "funding": [ { @@ -6153,7 +6415,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/polyfill-php72", @@ -6454,16 +6716,16 @@ }, { "name": "symfony/stopwatch", - "version": "v7.1.1", + "version": "v7.1.6", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "5b75bb1ac2ba1b9d05c47fc4b3046a625377d23d" + "reference": "8b4a434e6e7faf6adedffb48783a5c75409a1a05" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5b75bb1ac2ba1b9d05c47fc4b3046a625377d23d", - "reference": "5b75bb1ac2ba1b9d05c47fc4b3046a625377d23d", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/8b4a434e6e7faf6adedffb48783a5c75409a1a05", + "reference": "8b4a434e6e7faf6adedffb48783a5c75409a1a05", "shasum": "" }, "require": { @@ -6496,7 +6758,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v7.1.1" + "source": "https://github.com/symfony/stopwatch/tree/v7.1.6" }, "funding": [ { @@ -6512,20 +6774,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:57:53+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/var-dumper", - "version": "v5.4.43", + "version": "v5.4.45", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "6be6a6a8af4818564e3726fc65cf936f34743cef" + "reference": "c4a5a08fe8d836a1aeec59eeee9697457fd28723" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/6be6a6a8af4818564e3726fc65cf936f34743cef", - "reference": "6be6a6a8af4818564e3726fc65cf936f34743cef", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c4a5a08fe8d836a1aeec59eeee9697457fd28723", + "reference": "c4a5a08fe8d836a1aeec59eeee9697457fd28723", "shasum": "" }, "require": { @@ -6585,7 +6847,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v5.4.43" + "source": "https://github.com/symfony/var-dumper/tree/v5.4.45" }, "funding": [ { @@ -6601,7 +6863,7 @@ "type": "tidelift" } ], - "time": "2024-08-30T16:01:46+00:00" + "time": "2024-09-25T14:11:13+00:00" }, { "name": "theseer/tokenizer", diff --git a/lib/php/workflow/src/Doctrine/Entity/JobState.php b/lib/php/workflow/src/Doctrine/Entity/JobState.php index 8e9edf6d9..80b79626c 100644 --- a/lib/php/workflow/src/Doctrine/Entity/JobState.php +++ b/lib/php/workflow/src/Doctrine/Entity/JobState.php @@ -9,14 +9,13 @@ use Doctrine\DBAL\Types\Types; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping as ORM; -use Ramsey\Uuid\Uuid; class JobState { - protected string $id; - protected ?string $state = null; + protected int $number; + protected int $status; #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: false)] @@ -30,9 +29,12 @@ class JobState protected ?ModelJobState $jobState = null; - public function __construct(protected ?WorkflowState $workflow, protected string $jobId) + public function __construct( + protected readonly string $id, + protected ?WorkflowState $workflow, + protected string $jobId, + ) { - $this->id = Uuid::uuid4()->toString(); } public function getId(): string @@ -62,6 +64,7 @@ public function setState(ModelJobState $state, EntityManagerInterface $em): void $this->endedAt = $state->getEndedAt()?->getDateTimeObject(); $this->startedAt = $state->getStartedAt()?->getDateTimeObject(); $this->status = $state->getStatus(); + $this->number = $state->getNumber(); } public function getJobState(): ModelJobState @@ -117,4 +120,9 @@ public function getJobId(): string { return $this->jobId; } + + public function getNumber(): int + { + return $this->number; + } } diff --git a/lib/php/workflow/src/Executor/JobExecutor.php b/lib/php/workflow/src/Executor/JobExecutor.php index 439814e39..60e6422db 100644 --- a/lib/php/workflow/src/Executor/JobExecutor.php +++ b/lib/php/workflow/src/Executor/JobExecutor.php @@ -68,21 +68,12 @@ private function shouldBeSkipped(JobExecutionContext $context, Job $job): bool return false; } - public function executeJob(WorkflowState $workflowState, Job $job, array $env = []): void + public function executeJob(WorkflowState $workflowState, Job $job, JobState $jobState, array $env = []): void { $workflowId = $workflowState->getId(); $jobId = $job->getId(); - if ($this->stateRepository instanceof LockAwareStateRepositoryInterface) { - $this->stateRepository->acquireJobLock($workflowId, $jobId); - } - try { - $jobState = $this->stateRepository->getJobState($workflowId, $jobId); - - if (null === $jobState) { - throw new \InvalidArgumentException(sprintf('State of job "%s" does not exists for workflow "%s"', $jobId, $workflowId)); - } $status = $jobState->getStatus(); if (JobState::STATUS_TRIGGERED !== $status) { @@ -132,7 +123,7 @@ public function executeJob(WorkflowState $workflowState, Job $job, array $env = } catch (\Throwable $e) { try { if ($this->stateRepository instanceof LockAwareStateRepositoryInterface) { - $this->stateRepository->releaseJobLock($workflowId, $jobId); + $this->stateRepository->releaseJobLock($workflowId, $jobState->getId()); } } catch (\Throwable $e2) { throw new \RuntimeException(sprintf('Error while releasing job lock after another error: %s (First error was: %s)', $e2->getMessage(), $e->getMessage()), 0, $e); @@ -149,10 +140,10 @@ private function persistJobState(JobState $jobState): void $this->stateRepository->persistJobState($jobState); if ($this->stateRepository instanceof LockAwareStateRepositoryInterface) { - $this->stateRepository->releaseJobLock($jobState->getWorkflowId(), $jobState->getJobId()); + $this->stateRepository->releaseJobLock($jobState->getWorkflowId(), $jobState->getId()); } - $this->eventDispatcher->dispatch(new JobUpdateEvent($jobState->getWorkflowId(), $jobState->getJobId(), $jobState->getStatus())); + $this->eventDispatcher->dispatch(new JobUpdateEvent($jobState->getWorkflowId(), $jobState->getJobId(), $jobState->getId(), $jobState->getStatus())); } private function runJob(JobExecutionContext $context, Job $job): void diff --git a/lib/php/workflow/src/Executor/PlanExecutor.php b/lib/php/workflow/src/Executor/PlanExecutor.php index b08d085ac..37524ab19 100644 --- a/lib/php/workflow/src/Executor/PlanExecutor.php +++ b/lib/php/workflow/src/Executor/PlanExecutor.php @@ -6,6 +6,7 @@ use Alchemy\Workflow\Planner\WorkflowPlanner; use Alchemy\Workflow\Repository\WorkflowRepositoryInterface; +use Alchemy\Workflow\State\Repository\LockAwareStateRepositoryInterface; use Alchemy\Workflow\State\Repository\StateRepositoryInterface; final readonly class PlanExecutor @@ -17,10 +18,21 @@ public function __construct( ) { } - public function executePlan(string $workflowId, string $jobId): void + public function executePlan(string $workflowId, string $jobStateId): void { $workflowState = $this->stateRepository->getWorkflowState($workflowId); + if ($this->stateRepository instanceof LockAwareStateRepositoryInterface) { + $this->stateRepository->acquireJobLock($workflowId, $jobStateId); + } + + $jobState = $this->stateRepository->getJobState($workflowId, $jobStateId); + if (null === $jobState) { + throw new \InvalidArgumentException(sprintf('Job state "%s" does not exists for workflow "%s"', $jobStateId, $workflowId)); + } + + $jobId = $jobState->getJobId(); + $event = $workflowState->getEvent(); $workflow = $this->workflowRepository->loadWorkflowByName($workflowState->getWorkflowName()); if (null === $workflow) { @@ -30,6 +42,8 @@ public function executePlan(string $workflowId, string $jobId): void $planner = new WorkflowPlanner([$workflow]); $plan = null === $event ? $planner->planAll() : $planner->planEvent($event); - $this->jobExecutor->executeJob($workflowState, $plan->getJob($jobId), $workflow->getEnv()->getArrayCopy()); + $this->jobExecutor->executeJob($workflowState, $plan->getJob($jobId), $jobState, $workflow->getEnv()->getArrayCopy()); + + } } diff --git a/lib/php/workflow/src/Listener/JobUpdateEvent.php b/lib/php/workflow/src/Listener/JobUpdateEvent.php index c0fab0ff2..1c754f1b4 100644 --- a/lib/php/workflow/src/Listener/JobUpdateEvent.php +++ b/lib/php/workflow/src/Listener/JobUpdateEvent.php @@ -7,6 +7,7 @@ public function __construct( private string $workflowId, private string $jobId, + private string $jobStateId, private int $status, ) { } @@ -21,6 +22,11 @@ public function getJobId(): string return $this->jobId; } + public function getJobStateId(): string + { + return $this->jobStateId; + } + public function getStatus(): int { return $this->status; diff --git a/lib/php/workflow/src/Message/JobConsumer.php b/lib/php/workflow/src/Message/JobConsumer.php index 5d17f5e0c..9b08ef566 100644 --- a/lib/php/workflow/src/Message/JobConsumer.php +++ b/lib/php/workflow/src/Message/JobConsumer.php @@ -4,7 +4,7 @@ final readonly class JobConsumer { - public function __construct(private string $workflowId, private string $jobId) + public function __construct(private string $workflowId, private string $jobStateId) { } @@ -13,8 +13,8 @@ public function getWorkflowId(): string return $this->workflowId; } - public function getJobId(): string + public function getJobStateId(): string { - return $this->jobId; + return $this->jobStateId; } } diff --git a/lib/php/workflow/src/Message/JobConsumerHandler.php b/lib/php/workflow/src/Message/JobConsumerHandler.php index dfc2d833f..59d0acaa5 100644 --- a/lib/php/workflow/src/Message/JobConsumerHandler.php +++ b/lib/php/workflow/src/Message/JobConsumerHandler.php @@ -17,7 +17,7 @@ public function __construct( public function __invoke(JobConsumer $message): void { - $this->planExecutor->executePlan($message->getWorkflowId(), $message->getJobId()); + $this->planExecutor->executePlan($message->getWorkflowId(), $message->getJobStateId()); $this->orchestrator->continueWorkflow($message->getWorkflowId()); } } diff --git a/lib/php/workflow/src/Runner/RunnerInterface.php b/lib/php/workflow/src/Runner/RunnerInterface.php index bb9290ee7..2e6451ea7 100644 --- a/lib/php/workflow/src/Runner/RunnerInterface.php +++ b/lib/php/workflow/src/Runner/RunnerInterface.php @@ -6,5 +6,5 @@ interface RunnerInterface { - public function run(string $workflowId, string $jobId): void; + public function run(string $workflowId, string $jobStateId): void; } diff --git a/lib/php/workflow/src/Runner/RuntimeRunner.php b/lib/php/workflow/src/Runner/RuntimeRunner.php index 8d618a199..482c2571f 100644 --- a/lib/php/workflow/src/Runner/RuntimeRunner.php +++ b/lib/php/workflow/src/Runner/RuntimeRunner.php @@ -12,8 +12,8 @@ public function __construct(private PlanExecutor $planExecutor) { } - public function run(string $workflowId, string $jobId): void + public function run(string $workflowId, string $jobStateId): void { - $this->planExecutor->executePlan($workflowId, $jobId); + $this->planExecutor->executePlan($workflowId, $jobStateId); } } diff --git a/lib/php/workflow/src/State/JobState.php b/lib/php/workflow/src/State/JobState.php index 6dccb13ca..bf51f255b 100644 --- a/lib/php/workflow/src/State/JobState.php +++ b/lib/php/workflow/src/State/JobState.php @@ -5,18 +5,19 @@ namespace Alchemy\Workflow\State; use Alchemy\Workflow\Date\MicroDateTime; +use Ramsey\Uuid\Uuid; -class JobState +final class JobState { - final public const STATUS_TRIGGERED = 0; - final public const STATUS_SUCCESS = 1; - final public const STATUS_FAILURE = 2; - final public const STATUS_SKIPPED = 3; - final public const STATUS_RUNNING = 4; - final public const STATUS_ERROR = 5; - final public const STATUS_CANCELLED = 6; - - final public const STATUS_LABELS = [ + final public const int STATUS_TRIGGERED = 0; + final public const int STATUS_SUCCESS = 1; + final public const int STATUS_FAILURE = 2; + final public const int STATUS_SKIPPED = 3; + final public const int STATUS_RUNNING = 4; + final public const int STATUS_ERROR = 5; + final public const int STATUS_CANCELLED = 6; + + final public const array STATUS_LABELS = [ self::STATUS_TRIGGERED => 'triggered', self::STATUS_SUCCESS => 'success', self::STATUS_FAILURE => 'failure', @@ -25,6 +26,9 @@ class JobState self::STATUS_ERROR => 'error', self::STATUS_CANCELLED => 'cancelled', ]; + + private readonly string $id; + private array $errors = []; private Outputs $outputs; private ?Inputs $inputs = null; @@ -37,12 +41,23 @@ class JobState private ?MicroDateTime $startedAt = null; private ?MicroDateTime $endedAt = null; - public function __construct(private string $workflowId, private string $jobId, private int $status) - { + public function __construct( + private readonly string $workflowId, + private readonly string $jobId, + private int $status = self::STATUS_TRIGGERED, + ?string $id = null, + private readonly int $number = 0, + ) { + $this->id = $id ?? Uuid::uuid4()->toString(); $this->triggeredAt = new MicroDateTime(); $this->outputs = new Outputs(); } + public function getId(): string + { + return $this->id; + } + public function getStatus(): int { return $this->status; @@ -139,11 +154,14 @@ public function __serialize(): array 'startedAt' => $this->startedAt, 'endedAt' => $this->endedAt, 'errors' => $this->errors, + 'id' => $this->id, + 'number' => $this->number, ]; } public function __unserialize(array $data): void { + $this->id = $data['id'] ?? 'unset'; $this->workflowId = $data['workflowId']; $this->jobId = $data['jobId']; $this->status = $data['status']; @@ -153,6 +171,7 @@ public function __unserialize(array $data): void $this->startedAt = $data['startedAt'] ?? null; $this->endedAt = $data['endedAt'] ?? null; $this->errors = $data['errors'] ?? []; + $this->number = $data['number'] ?? 0; } public function getSteps(): array @@ -171,4 +190,9 @@ public function setInputs(?Inputs $inputs): JobState return $this; } + + public function getNumber(): int + { + return $this->number; + } } diff --git a/lib/php/workflow/src/State/Repository/DoctrineStateRepository.php b/lib/php/workflow/src/State/Repository/DoctrineStateRepository.php index b3ffed877..9ddbf4bc5 100644 --- a/lib/php/workflow/src/State/Repository/DoctrineStateRepository.php +++ b/lib/php/workflow/src/State/Repository/DoctrineStateRepository.php @@ -16,12 +16,13 @@ use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent; use Symfony\Component\Messenger\Event\WorkerMessageHandledEvent; -#[AsEventListener(event: WorkerMessageHandledEvent::class, method: 'clear')] -#[AsEventListener(event: WorkerMessageFailedEvent::class, method: 'clear')] -#[AsEventListener(event: KernelEvents::TERMINATE, method: 'clear')] +#[AsEventListener(event: WorkerMessageHandledEvent::class, method: 'flush')] +#[AsEventListener(event: WorkerMessageFailedEvent::class, method: 'flush')] +#[AsEventListener(event: KernelEvents::TERMINATE, method: 'flush')] class DoctrineStateRepository implements LockAwareStateRepositoryInterface { - private array $jobs = []; + use JobStatusCacheTrait; + private readonly string $workflowStateEntity; private readonly string $jobStateEntity; @@ -34,11 +35,6 @@ public function __construct( $this->jobStateEntity = $jobStateEntity ?? JobStateEntity::class; } - public function clear(): void - { - $this->jobs = []; - } - public function getWorkflowState(string $id): WorkflowState { $entity = $this->em->getRepository($this->workflowStateEntity)->find($id); @@ -52,6 +48,27 @@ public function getWorkflowState(string $id): WorkflowState return $state; } + public function flush(): void + { + $this->clearCache(); + } + + public function createJobState(string $workflowId, string $jobId): JobState + { + $number = $this->createListQueryBuilder($workflowId, $jobId) + ->select('MAX(t.number) + 1') + ->getQuery() + ->getSingleScalarResult(); + + return new JobState( + $workflowId, + $jobId, + JobState::STATUS_TRIGGERED, + null, + $number ?? 0, + ); + } + public function persistWorkflowState(WorkflowState $state): void { $workflowStateEntity = $this->workflowStateEntity; @@ -66,24 +83,34 @@ public function persistWorkflowState(WorkflowState $state): void $this->em->flush($entity); } - public function getJobState(string $workflowId, string $jobId): ?JobState + public function getJobState(string $workflowId, string $jobStateId): ?JobState { - $entity = $this->fetchJobEntity($workflowId, $jobId); - if (!$entity instanceof JobStateEntity) { - return null; + return $this->fetchJobState($workflowId, $jobStateId)?->getJobState(); + } + + public function getLastJobState(string $workflowId, string $jobId): ?JobState + { + return $this->fetchLastJobState($workflowId, $jobId)?->getJobState(); + } + + public function getJobStates(string $workflowId, string $jobId): array + { + $entities = $this->fetchJobStates($workflowId, $jobId); + if (empty($entities)) { + return []; } - return $entity->getJobState(); + return array_map(fn (JobStateEntity $state) => $state->getJobState(), $entities); } - public function removeJobState(string $workflowId, string $jobId): void + public function removeJobState(string $workflowId, string $jobStateId): void { - $entity = $this->fetchJobEntity($workflowId, $jobId); + $entity = $this->fetchJobState($workflowId, $jobStateId); if ($entity instanceof JobStateEntity) { $this->em->remove($entity); $this->em->flush(); - unset($this->jobs[$workflowId][$jobId]); + $this->removeJobStateFromCache($workflowId, $jobStateId); } } @@ -91,19 +118,17 @@ public function resetJobState(string $workflowId, string $jobId): void { } - public function acquireJobLock(string $workflowId, string $jobId): void + public function acquireJobLock(string $workflowId, string $jobStateId): void { $this->em->beginTransaction(); try { - $entity = $this->createQueryBuilder($workflowId, $jobId) - ->getQuery() - ->setLockMode(LockMode::PESSIMISTIC_WRITE) - ->getOneOrNullResult(); + $entity = $this->em->getRepository($this->jobStateEntity) + ->find($jobStateId, LockMode::PESSIMISTIC_WRITE); if ($entity instanceof JobStateEntity) { - $this->jobs[$workflowId][$jobId] = $entity; + $this->statuses[$jobStateId] = $entity; } else { - unset($this->jobs[$workflowId][$jobId]); + unset($this->statuses[$jobStateId]); } } catch (\Throwable $e) { $this->em->rollback(); @@ -111,7 +136,7 @@ public function acquireJobLock(string $workflowId, string $jobId): void } } - private function createQueryBuilder(string $workflowId, string $jobId): QueryBuilder + private function createListQueryBuilder(string $workflowId, string $jobId): QueryBuilder { return $this->em->getRepository($this->jobStateEntity) ->createQueryBuilder('t') @@ -121,12 +146,10 @@ private function createQueryBuilder(string $workflowId, string $jobId): QueryBui ->setParameters([ 'w' => $workflowId, 'j' => $jobId, - ]) - ->addOrderBy('t.triggeredAt', 'DESC') - ->setMaxResults(1); + ]); } - public function releaseJobLock(string $workflowId, string $jobId): void + public function releaseJobLock(string $workflowId, string $jobStateId): void { $this->em->commit(); } @@ -135,16 +158,18 @@ public function persistJobState(JobState $state): void { $entity = null; if (JobState::STATUS_TRIGGERED !== $state->getStatus()) { - $entity = $this->fetchJobEntity($state->getWorkflowId(), $state->getJobId()); + $entity = $this->fetchJobState($state->getWorkflowId(), $state->getId()); } if (!$entity instanceof JobStateEntity) { $jobStateEntity = $this->jobStateEntity; $entity = new $jobStateEntity( + $state->getId(), $this->em->getReference($this->workflowStateEntity, $state->getWorkflowId()), $state->getJobId() ); - $this->jobs[$entity->getWorkflow()->getId()][$entity->getJobId()] = $entity; + $this->statuses[$state->getId()] = $state; + $this->statuses[$entity->getWorkflow()->getId()][$entity->getJobId()][] = $entity; } $entity->setState($state, $this->em); @@ -153,22 +178,58 @@ public function persistJobState(JobState $state): void $this->em->flush($entity); } - private function fetchJobEntity(string $workflowId, string $jobId): ?JobStateEntity + private function fetchJobState(string $workflowId, string $jobStateId): ?JobStateEntity { - if (isset($this->jobs[$workflowId][$jobId])) { - return $this->jobs[$workflowId][$jobId]; + $state = $this->statuses[$jobStateId] ?? null; + if ($state instanceof JobStateEntity) { + return $state; } - $entity = $this->createQueryBuilder($workflowId, $jobId) + $state = $this->em->getRepository($this->jobStateEntity) + ->findOneBy(['id' => $jobStateId]); + + $this->cacheJobState($workflowId, $jobStateId, $state); + + return $state; + } + + private function fetchLastJobState(string $workflowId, string $jobId): ?JobStateEntity + { + $state = $this->lastByJobId[$workflowId][$jobId] ?? null; + if ($state instanceof JobStateEntity) { + return $state; + } + + $state = $this->createListQueryBuilder($workflowId, $jobId) + ->addOrderBy('t.triggeredAt', 'DESC') + ->setMaxResults(1) ->getQuery() ->getOneOrNullResult(); - if ($entity) { - $this->jobs[$workflowId][$jobId] = $entity; - } else { - unset($this->jobs[$workflowId][$jobId]); + if ($state) { + $this->cacheLastJobState($workflowId, $jobId, $state); } - return $entity; + return $state; + } + + /** + * @return JobStateEntity[] + */ + private function fetchJobStates(string $workflowId, string $jobId): array + { + $statuses = $this->statusesByJobId[$workflowId][$jobId] ?? null; + if (null !== $statuses) { + return $statuses; + } + + $states = $this->createListQueryBuilder($workflowId, $jobId) + ->addOrderBy('t.triggeredAt', 'DESC') + ->getQuery() + ->getResult(); + + $this->cacheJobStates($workflowId, $jobId, $states); + + return $states; } } diff --git a/lib/php/workflow/src/State/Repository/FileSystemStateRepository.php b/lib/php/workflow/src/State/Repository/FileSystemStateRepository.php index 3e4089455..dadbef6bc 100644 --- a/lib/php/workflow/src/State/Repository/FileSystemStateRepository.php +++ b/lib/php/workflow/src/State/Repository/FileSystemStateRepository.php @@ -43,6 +43,38 @@ public function getWorkflowState(string $id): WorkflowState return $state; } + public function getJobState(string $workflowId, string $jobStateId): ?JobState + { + $fd = $this->fileDescriptors[$workflowId][$jobStateId] ?? null; + if (null === $fd) { + return $this->readJobState($workflowId, $jobStateId); + } + + fseek($fd, 0); + $content = ''; + while (!feof($fd)) { + $content .= fgets($fd, 4096); + } + + if (empty($content)) { + return null; + } + + return unserialize($content); + } + + public function getLastJobState(string $workflowId, string $jobId): ?JobState + { + $jobStateId = $this->getStateId($jobId); + + return $this->getJobState($workflowId, $jobStateId); + } + + public function createJobState(string $workflowId, string $jobId): JobState + { + return new JobState($workflowId, $jobId, id: $this->getStateId($jobId)); + } + public function persistWorkflowState(WorkflowState $state): void { $path = $this->getWorkflowPath($state->getId(), self::WORKFLOW_FILENAME); @@ -55,29 +87,25 @@ public function persistWorkflowState(WorkflowState $state): void file_put_contents($path, serialize($state)); } - public function getJobState(string $workflowId, string $jobId): ?JobState + private function getStateId(string $jobId): string { - $fd = $this->fileDescriptors[$workflowId][$jobId] ?? null; - if (null === $fd) { - return $this->readJobState($workflowId, $jobId); - } - - fseek($fd, 0); - $content = ''; - while (!feof($fd)) { - $content .= fgets($fd, 4096); - } + return sprintf('%s-0', $jobId); + } - if (empty($content)) { - return null; + public function getJobStates(string $workflowId, string $jobId): array + { + $jobStateId = $this->getStateId($jobId); + $state = $this->getJobState($workflowId, $jobStateId); + if (null === $state) { + return []; } - return unserialize($content); + return [$state]; } - private function readJobState(string $workflowId, string $jobId): ?JobState + private function readJobState(string $workflowId, string $jobStateId): ?JobState { - $path = $this->getJobPath($workflowId, $jobId); + $path = $this->getJobPath($workflowId, $jobStateId); if (file_exists($path)) { return unserialize(file_get_contents($path)); } @@ -85,47 +113,51 @@ private function readJobState(string $workflowId, string $jobId): ?JobState return null; } - public function acquireJobLock(string $workflowId, string $jobId): void + public function acquireJobLock(string $workflowId, string $jobStateId): void { - $path = $this->getJobPath($workflowId, $jobId); + $path = $this->getJobPath($workflowId, $jobStateId); $fd = fopen($path, 'c+'); if (!flock($fd, LOCK_EX)) { throw new LockException(sprintf('Cannot acquire lock on "%s"', $path)); } - $this->fileDescriptors[$workflowId][$jobId] = $fd; + $this->fileDescriptors[$workflowId][$jobStateId] = $fd; } - public function releaseJobLock(string $workflowId, string $jobId): void + public function releaseJobLock(string $workflowId, string $jobStateId): void { - $fd = $this->fileDescriptors[$workflowId][$jobId] ?? null; + $fd = $this->fileDescriptors[$workflowId][$jobStateId] ?? null; if ($fd) { flock($fd, LOCK_UN); fclose($fd); } - unset($this->fileDescriptors[$workflowId][$jobId]); + unset($this->fileDescriptors[$workflowId][$jobStateId]); } public function persistJobState(JobState $state): void { - $fd = $this->fileDescriptors[$state->getWorkflowId()][$state->getJobId()] ?? null; - if (null === $fd) { - $path = $this->getJobPath($state->getWorkflowId(), $state->getJobId()); - $fd = fopen($path, 'r+'); + $jobStateId = $state->getId(); + $fd = $this->fileDescriptors[$state->getWorkflowId()][$jobStateId] ?? null; + $hadLock = null === $fd; + if ($hadLock) { + $path = $this->getJobPath($state->getWorkflowId(), $jobStateId); + $fd = fopen($path, 'c+'); } ftruncate($fd, 0); fseek($fd, 0); fwrite($fd, serialize($state)); fflush($fd); - flock($fd, LOCK_UN); + if ($hadLock) { + flock($fd, LOCK_UN); + } } - public function removeJobState(string $workflowId, string $jobId): void + public function removeJobState(string $workflowId, string $jobStateId): void { - $path = $this->getJobPath($workflowId, $jobId); + $path = $this->getJobPath($workflowId, $jobStateId); if (file_exists($path)) { unlink($path); } @@ -141,8 +173,8 @@ private function getWorkflowPath(string $id, string $filename): string return $this->path.DIRECTORY_SEPARATOR.$id.DIRECTORY_SEPARATOR.$filename.'.state'; } - private function getJobPath(string $workflowId, string $jobId): string + private function getJobPath(string $workflowId, string $jobStateId): string { - return $this->getWorkflowPath($workflowId, self::JOB_PREFIX.$jobId); + return $this->getWorkflowPath($workflowId, self::JOB_PREFIX.$jobStateId); } } diff --git a/lib/php/workflow/src/State/Repository/JobStatusCacheTrait.php b/lib/php/workflow/src/State/Repository/JobStatusCacheTrait.php new file mode 100644 index 000000000..c9587eb3d --- /dev/null +++ b/lib/php/workflow/src/State/Repository/JobStatusCacheTrait.php @@ -0,0 +1,105 @@ + + */ + protected array $statuses = []; + + /** + * @var array> + */ + protected array $statusesByJobId = []; + + /** + * @var array> + */ + protected array $lastByJobId = []; + + public function clearCache(): void + { + $this->statuses = []; + $this->statusesByJobId = []; + } + + protected function cacheJobState(string $workflowId, string $jobStateId, ?object $state): void + { + if (null === $state) { + $this->removeJobStateFromCache($workflowId, $jobStateId); + } else { + $this->statuses[$state->getId()] = $state; + + if (isset($this->lastByJobId[$workflowId])) { + foreach ($this->lastByJobId[$workflowId] as $jobId => $state) { + if ($state->getId() === $jobStateId) { + $this->lastByJobId[$workflowId][$jobId] = $state; + } + } + } + + if (isset($this->statusesByJobId[$workflowId][$state->getJobId()])) { + $this->statusesByJobId[$workflowId][$state->getJobId()] = array_map( + function (object $s) use ($state): object { + if ($s->getId() === $state->getId()) { + return $state; + } else { + return $s; + } + }, + $this->statusesByJobId[$workflowId][$state->getJobId()] + ); + } + } + } + + protected function cacheJobStates(string $workflowId, string $jobId, array $states): void + { + $this->statusesByJobId[$workflowId][$jobId] = $states; + foreach ($states as $state) { + $this->statuses[$state->getId()] = $state; + } + } + + protected function cacheLastJobState(string $workflowId, string $jobId, object $state): void + { + $this->lastByJobId[$workflowId][$jobId] = $state; + $this->statuses[$state->getId()] = $state; + } + + public function resetJobStateFromCache(string $workflowId, string $jobId): void + { + foreach ($this->statusesByJobId[$workflowId][$jobId] ?? [] as $jobState) { + unset($this->statuses[$jobState->getId()]); + } + + $this->statusesByJobId[$workflowId][$jobId]=[]; + } + + protected function removeJobStateFromCache(string $workflowId, string $jobStateId): void + { + unset($this->statuses[$jobStateId]); + + if (isset($this->statusesByJobId[$workflowId])) { + foreach ($this->statusesByJobId[$workflowId] as $jobId => $states) { + $this->statusesByJobId[$workflowId][$jobId] = array_filter( + $states, + fn (object $s): bool => $s->getId() !== $jobStateId + ); + } + } + + if (isset($this->lastByJobId[$workflowId])) { + foreach ($this->lastByJobId[$workflowId] as $jobId => $state) { + if ($state->getId() === $jobStateId) { + unset($this->lastByJobId[$workflowId][$jobId]); + } + } + } + } +} diff --git a/lib/php/workflow/src/State/Repository/LockAwareStateRepositoryInterface.php b/lib/php/workflow/src/State/Repository/LockAwareStateRepositoryInterface.php index a7192b2de..39a5f7b4c 100644 --- a/lib/php/workflow/src/State/Repository/LockAwareStateRepositoryInterface.php +++ b/lib/php/workflow/src/State/Repository/LockAwareStateRepositoryInterface.php @@ -4,9 +4,11 @@ namespace Alchemy\Workflow\State\Repository; +use Alchemy\Workflow\State\JobState; + interface LockAwareStateRepositoryInterface extends StateRepositoryInterface { - public function acquireJobLock(string $workflowId, string $jobId): void; + public function acquireJobLock(string $workflowId, string $jobStateId): void; - public function releaseJobLock(string $workflowId, string $jobId): void; + public function releaseJobLock(string $workflowId, string $jobStateId): void; } diff --git a/lib/php/workflow/src/State/Repository/MemoryStateRepository.php b/lib/php/workflow/src/State/Repository/MemoryStateRepository.php index 067524379..d9e69a920 100644 --- a/lib/php/workflow/src/State/Repository/MemoryStateRepository.php +++ b/lib/php/workflow/src/State/Repository/MemoryStateRepository.php @@ -6,18 +6,24 @@ use Alchemy\Workflow\State\JobState; use Alchemy\Workflow\State\WorkflowState; +use function array_filter; class MemoryStateRepository implements StateRepositoryInterface { /** - * @var array + * @var array */ - private array $workflows = []; + protected array $statuses = []; + + /** + * @var array> + */ + protected array $statusesByJobId = []; /** - * @var array + * @var array */ - private array $jobs = []; + private array $workflows = []; public function getWorkflowState(string $id): WorkflowState { @@ -32,14 +38,46 @@ public function persistWorkflowState(WorkflowState $state): void { $id = $state->getId(); $this->workflows[$id] = $state; - $this->jobs[$id] ??= []; + $this->statusesByJobId[$id] ??= []; + } + + public function getJobState(string $workflowId, string $jobStateId): ?JobState + { + $this->ensureWorkflowExists($workflowId); + + return $this->statuses[$jobStateId] ?? null; + } + + public function getLastJobState(string $workflowId, string $jobId): ?JobState + { + $this->ensureWorkflowExists($workflowId); + + $states = $this->statusesByJobId[$workflowId][$jobId] ?? []; + + return end($states) ?: null; } - public function getJobState(string $workflowId, string $jobId): ?JobState + public function createJobState(string $workflowId, string $jobId): JobState + { + $number = count($this->statusesByJobId[$workflowId][$jobId] ?? []); + + $state = new JobState( + $workflowId, + $jobId, + id: sprintf('%s-%d', $jobId, $number), + number: $number, + ); + + $this->persistJobState($state); + + return $state; + } + + public function getJobStates(string $workflowId, string $jobId): array { $this->ensureWorkflowExists($workflowId); - return $this->jobs[$workflowId][$jobId] ?? null; + return $this->statusesByJobId[$workflowId][$jobId] ?? []; } public function persistJobState(JobState $state): void @@ -47,22 +85,51 @@ public function persistJobState(JobState $state): void $workflowId = $state->getWorkflowId(); $this->ensureWorkflowExists($workflowId); - $this->jobs[$workflowId][$state->getJobId()] = $state; + $exists = isset($this->statuses[$state->getId()]); + $this->statuses[$state->getId()] = $state; + $this->statusesByJobId[$workflowId][$state->getJobId()] ??= []; + + if ($exists) { + $this->statusesByJobId[$workflowId][$state->getJobId()] = array_map( + function (object $s) use ($state, &$found): object { + if ($s->getId() === $state->getId()) { + $found = true; + return $state; + } else { + return $s; + } + }, + $this->statusesByJobId[$workflowId][$state->getJobId()] + ); + } else { + $this->statusesByJobId[$workflowId][$state->getJobId()][] = $state; + } } - public function removeJobState(string $workflowId, string $jobId): void + public function removeJobState(string $workflowId, string $jobStateId): void { - unset($this->jobs[$workflowId][$jobId]); + unset($this->statuses[$jobStateId]); + + foreach ($this->statusesByJobId[$workflowId] as $jobId => $states) { + $this->statusesByJobId[$workflowId][$jobId] = array_filter( + $states, + fn(object $s): bool => $s->getId() !== $jobStateId + ); + } } public function resetJobState(string $workflowId, string $jobId): void { - $this->removeJobState($workflowId, $jobId); + foreach ($this->statusesByJobId[$workflowId][$jobId] ?? [] as $jobState) { + unset($this->statuses[$jobState->getId()]); + } + + $this->statusesByJobId[$workflowId][$jobId]=[]; } private function ensureWorkflowExists(string $workflowId): void { - if (!isset($this->jobs[$workflowId])) { + if (!isset($this->statusesByJobId[$workflowId])) { throw new \LogicException(sprintf('Job container for workflow "%s" was not created. Please ensure the WorkflowState is persisted before.', $workflowId)); } } diff --git a/lib/php/workflow/src/State/Repository/StateRepositoryInterface.php b/lib/php/workflow/src/State/Repository/StateRepositoryInterface.php index 8d62b12cd..9a6236e74 100644 --- a/lib/php/workflow/src/State/Repository/StateRepositoryInterface.php +++ b/lib/php/workflow/src/State/Repository/StateRepositoryInterface.php @@ -16,11 +16,17 @@ public function getWorkflowState(string $id): WorkflowState; public function persistWorkflowState(WorkflowState $state): void; - public function getJobState(string $workflowId, string $jobId): ?JobState; + public function getJobState(string $workflowId, string $jobStateId): ?JobState; + + public function getJobStates(string $workflowId, string $jobId): array; + + public function getLastJobState(string $workflowId, string $jobId): ?JobState; public function persistJobState(JobState $state): void; - public function removeJobState(string $workflowId, string $jobId): void; + public function removeJobState(string $workflowId, string $jobStateId): void; public function resetJobState(string $workflowId, string $jobId): void; + + public function createJobState(string $workflowId, string $jobId): JobState; } diff --git a/lib/php/workflow/src/State/WorkflowState.php b/lib/php/workflow/src/State/WorkflowState.php index 4286bed9c..e63641345 100644 --- a/lib/php/workflow/src/State/WorkflowState.php +++ b/lib/php/workflow/src/State/WorkflowState.php @@ -9,24 +9,24 @@ use Alchemy\Workflow\State\Repository\StateRepositoryInterface; use Ramsey\Uuid\Uuid; -class WorkflowState +final class WorkflowState { - final public const STATUS_STARTED = 0; - final public const STATUS_SUCCESS = 1; - final public const STATUS_FAILURE = 2; - final public const STATUS_CANCELLED = 3; - - private string $id; - private Context $context; - private ?MicroDateTime $startedAt = null; + final public const int STATUS_STARTED = 0; + final public const int STATUS_SUCCESS = 1; + final public const int STATUS_FAILURE = 2; + final public const int STATUS_CANCELLED = 3; + + private readonly string $id; + private readonly Context $context; + private readonly ?MicroDateTime $startedAt; private ?MicroDateTime $endedAt = null; private ?MicroDateTime $cancelledAt = null; private int $status = self::STATUS_STARTED; public function __construct( private StateRepositoryInterface $stateRepository, - private string $workflowName, - private ?WorkflowEvent $event, + private readonly string $workflowName, + private readonly ?WorkflowEvent $event, ?string $id = null, array $context = [], ) { @@ -57,7 +57,13 @@ public function getStartedAt(): MicroDateTime public function getJobState(string $jobId): ?JobState { - return $this->stateRepository->getJobState($this->id, $jobId); + $states = $this->stateRepository->getJobStates($this->id, $jobId); + + if (empty($states)) { + return null; + } + + return $states[array_key_last($states)]; } public function getDuration(): ?float diff --git a/lib/php/workflow/src/Trigger/JobTriggerInterface.php b/lib/php/workflow/src/Trigger/JobTriggerInterface.php index 364e1ceac..93a12b0de 100644 --- a/lib/php/workflow/src/Trigger/JobTriggerInterface.php +++ b/lib/php/workflow/src/Trigger/JobTriggerInterface.php @@ -9,5 +9,5 @@ interface JobTriggerInterface /** * @return bool whether workflow should continue in the process */ - public function triggerJob(string $workflowId, string $jobId): bool; + public function triggerJob(string $workflowId, string $jobStateId): bool; } diff --git a/lib/php/workflow/src/Trigger/MessengerJobTrigger.php b/lib/php/workflow/src/Trigger/MessengerJobTrigger.php index 59bc1c003..bdf660d4c 100644 --- a/lib/php/workflow/src/Trigger/MessengerJobTrigger.php +++ b/lib/php/workflow/src/Trigger/MessengerJobTrigger.php @@ -13,9 +13,9 @@ public function __construct(private MessageBusInterface $bus) { } - public function triggerJob(string $workflowId, string $jobId): bool + public function triggerJob(string $workflowId, string $jobStateId): bool { - $this->bus->dispatch(new JobConsumer($workflowId, $jobId)); + $this->bus->dispatch(new JobConsumer($workflowId, $jobStateId)); return true; } diff --git a/lib/php/workflow/src/Trigger/RuntimeJobTrigger.php b/lib/php/workflow/src/Trigger/RuntimeJobTrigger.php index 350576b44..5d064de89 100644 --- a/lib/php/workflow/src/Trigger/RuntimeJobTrigger.php +++ b/lib/php/workflow/src/Trigger/RuntimeJobTrigger.php @@ -12,9 +12,9 @@ public function __construct(private RunnerInterface $runner) { } - public function triggerJob(string $workflowId, string $jobId): bool + public function triggerJob(string $workflowId, string $jobStateId): bool { - $this->runner->run($workflowId, $jobId); + $this->runner->run($workflowId, $jobStateId); return true; } diff --git a/lib/php/workflow/src/Trigger/SymfonyConsoleJobTrigger.php b/lib/php/workflow/src/Trigger/SymfonyConsoleJobTrigger.php index ce6d20495..78a0ecf64 100644 --- a/lib/php/workflow/src/Trigger/SymfonyConsoleJobTrigger.php +++ b/lib/php/workflow/src/Trigger/SymfonyConsoleJobTrigger.php @@ -6,9 +6,9 @@ class SymfonyConsoleJobTrigger implements JobTriggerInterface { - public function triggerJob(string $workflowId, string $jobId): bool + public function triggerJob(string $workflowId, string $jobStateId): bool { - exec(sprintf('bin/console workflow:run "%s"', escapeshellarg($jobId))); + exec(sprintf('bin/console workflow:run "%s"', escapeshellarg($jobStateId))); return false; } diff --git a/lib/php/workflow/src/WorkflowOrchestrator.php b/lib/php/workflow/src/WorkflowOrchestrator.php index da5383285..c60d2b761 100644 --- a/lib/php/workflow/src/WorkflowOrchestrator.php +++ b/lib/php/workflow/src/WorkflowOrchestrator.php @@ -13,7 +13,6 @@ use Alchemy\Workflow\Repository\WorkflowRepositoryInterface; use Alchemy\Workflow\State\Inputs; use Alchemy\Workflow\State\JobState; -use Alchemy\Workflow\State\Repository\LockAwareStateRepositoryInterface; use Alchemy\Workflow\State\Repository\StateRepositoryInterface; use Alchemy\Workflow\State\WorkflowState; use Alchemy\Workflow\Trigger\JobTriggerInterface; @@ -71,7 +70,7 @@ public function startWorkflow(string $workflowName, ?WorkflowEvent $event = null $this->stateRepository->persistWorkflowState($workflowState); - $this->continueWorkflow($workflowState->getId(), $workflowState); + $this->doContinueWorkflow($workflowState); return $workflowState; } @@ -94,39 +93,31 @@ public function cancelWorkflow(string $workflowId): void $workflow = $this->loadWorkflowByName($workflowState->getWorkflowName()); foreach ($workflow->getJobIds() as $jobId) { - if ($this->stateRepository instanceof LockAwareStateRepositoryInterface) { - $this->stateRepository->acquireJobLock($workflowId, $jobId); - } - - try { - $jobState = $this->stateRepository->getJobState($workflowId, $jobId); - if (null !== $jobState && in_array($jobState->getStatus(), [ - JobState::STATUS_TRIGGERED, - JobState::STATUS_RUNNING, - ], true)) { - $jobState->setStatus(JobState::STATUS_CANCELLED); - $this->stateRepository->persistJobState($jobState); - } - } finally { - if ($this->stateRepository instanceof LockAwareStateRepositoryInterface) { - $this->stateRepository->releaseJobLock($workflowId, $jobId); - } + $jobState = $this->stateRepository->getLastJobState($workflowId, $jobId); + if (null !== $jobState && in_array($jobState->getStatus(), [ + JobState::STATUS_TRIGGERED, + JobState::STATUS_RUNNING, + ], true)) { + $jobState->setStatus(JobState::STATUS_CANCELLED); + $this->stateRepository->persistJobState($jobState); } } $this->stateRepository->persistWorkflowState($workflowState); } - public function continueWorkflow(string $workflowId, ?WorkflowState $workflowState = null): void + public function continueWorkflow(string $workflowId): void { - if (null === $workflowState) { - $workflowState = $this->stateRepository->getWorkflowState($workflowId); - } - + $workflowState = $this->stateRepository->getWorkflowState($workflowId); if ($workflowState->isCancelled()) { return; } + $this->doContinueWorkflow($workflowState); + } + + private function doContinueWorkflow(WorkflowState $workflowState): void + { $event = $workflowState->getEvent(); $planner = new WorkflowPlanner([$this->loadWorkflowByName($workflowState->getWorkflowName())]); $plan = null === $event ? $planner->planAll() : $planner->planEvent($event); @@ -190,22 +181,10 @@ public function rerunJobs(string $workflowId, ?string $jobIdFilter = null, ?arra continue; } - if ($this->stateRepository instanceof LockAwareStateRepositoryInterface) { - $this->stateRepository->acquireJobLock($workflowId, $jobId); - } - - try { - $jobState = $this->stateRepository->getJobState($workflowId, $jobId); - if (null !== $jobState && (null === $expectedStatuses || in_array($jobState->getStatus(), $expectedStatuses, true))) { - $this->stateRepository->resetJobState($workflowId, $jobId); - - if (!$run->getJob()->isDisabled()) { - $jobsToTrigger[] = $jobId; - } - } - } finally { - if ($this->stateRepository instanceof LockAwareStateRepositoryInterface) { - $this->stateRepository->releaseJobLock($workflowId, $jobId); + $jobState = $this->stateRepository->getLastJobState($workflowId, $jobId); + if (null !== $jobState && (null === $expectedStatuses || in_array($jobState->getStatus(), $expectedStatuses, true))) { + if (!$run->getJob()->isDisabled()) { + $jobsToTrigger[] = $jobId; } } @@ -256,7 +235,7 @@ private function getNextJob(Plan $plan, WorkflowState $state): array continue; } - $jobState = $this->stateRepository->getJobState($state->getId(), $jobId); + $jobState = $this->stateRepository->getLastJobState($state->getId(), $jobId); if (null === $jobState) { if ($this->satisfiesAllNeeds($statuses, $job)) { return [$jobId, null]; @@ -302,25 +281,17 @@ private function satisfiesAllNeeds(array $states, Job $job): bool return true; } - private function triggerJob(WorkflowState $state, string $jobId, ?array $jobInputs = null): bool + private function triggerJob(WorkflowState $workflowState, string $jobId, ?array $jobInputs = null): bool { - $workflowId = $state->getId(); + $workflowId = $workflowState->getId(); - if ($this->stateRepository instanceof LockAwareStateRepositoryInterface) { - $this->stateRepository->acquireJobLock($workflowId, $jobId); - } - - $jobState = new JobState($workflowId, $jobId, JobState::STATUS_TRIGGERED); + $jobState = $this->stateRepository->createJobState($workflowId, $jobId); if (null !== $jobInputs) { $inputs = $jobState->getInputs() ?? new Inputs(); $jobState->setInputs($inputs->mergeWith($jobInputs)); } $this->stateRepository->persistJobState($jobState); - if ($this->stateRepository instanceof LockAwareStateRepositoryInterface) { - $this->stateRepository->releaseJobLock($workflowId, $jobId); - } - - return $this->trigger->triggerJob($state->getId(), $jobId); + return $this->trigger->triggerJob($workflowState->getId(), $jobState->getId()); } } diff --git a/lib/php/workflow/tests/AbstractWorkflowTest.php b/lib/php/workflow/tests/AbstractWorkflowTest.php index ac16d5745..e2a272dd7 100644 --- a/lib/php/workflow/tests/AbstractWorkflowTest.php +++ b/lib/php/workflow/tests/AbstractWorkflowTest.php @@ -85,9 +85,9 @@ protected function assertJobResultsStates(array $expected, StateRepositoryInterf { foreach ($expected as $jobId => $result) { if (null === $result) { - $this->assertNull($repository->getJobState($workflowId, $jobId)); + $this->assertNull($repository->getJobStates($workflowId, $jobId)); } else { - $this->assertEquals($result, $repository->getJobState($workflowId, $jobId)->getStatus(), $jobId); + $this->assertEquals($result, $repository->getJobStates($workflowId, $jobId)->getStatus(), $jobId); } } } diff --git a/lib/php/workflow/tests/Dumper/AbstractDumperTest.php b/lib/php/workflow/tests/Dumper/AbstractDumperTest.php index f6fad5a2f..699e07511 100644 --- a/lib/php/workflow/tests/Dumper/AbstractDumperTest.php +++ b/lib/php/workflow/tests/Dumper/AbstractDumperTest.php @@ -41,7 +41,7 @@ protected function createWorkflowState(string $workflowId): WorkflowState $stateRepository = $this->createMock(StateRepositoryInterface::class); $stateRepository ->expects($this->exactly(count($jobMap))) - ->method('getJobState') + ->method('getLastJobState') ->will($this->returnValueMap($jobMap)); return new WorkflowState($stateRepository, 'foo', null, $workflowId); diff --git a/lib/php/workflow/tests/OrchestratorTest.php b/lib/php/workflow/tests/OrchestratorTest.php index c99e417b9..2ea6cb5b2 100644 --- a/lib/php/workflow/tests/OrchestratorTest.php +++ b/lib/php/workflow/tests/OrchestratorTest.php @@ -92,7 +92,7 @@ public function testEndToEndEchoerComplexWorkflow(): void 'outro' => JobState::STATUS_SUCCESS, ], $stateRepository, $workflowState->getId()); - $contentBisState = $stateRepository->getJobState($workflowState->getId(), 'content_bis'); + $contentBisState = $stateRepository->getJobStates($workflowState->getId(), 'content_bis'); $this->assertEquals([ 'foo' => 'bar', 'duration' => $contentBisState->getSteps()['first']->getDuration(), diff --git a/lib/php/workflow/tests/State/StateRepositoryTest.php b/lib/php/workflow/tests/State/StateRepositoryTest.php index 283678fca..197eed6d4 100644 --- a/lib/php/workflow/tests/State/StateRepositoryTest.php +++ b/lib/php/workflow/tests/State/StateRepositoryTest.php @@ -34,79 +34,80 @@ public function testStateAreCorrectlyPersistedForSuccessWorkflow(StateRepository $this->assertEquals([ ['persistWorkflowState', $workflowId, WorkflowState::STATUS_STARTED], - ['getJobState', $workflowId, 'intro'], + ['getLastJobState', $workflowId, 'intro'], ['acquireJobLock', $workflowId, 'intro'], + ['createJobState', $workflowId, 'intro'], ['persistJobState', $workflowId, 'intro', JobState::STATUS_TRIGGERED], ['releaseJobLock', $workflowId, 'intro'], ['getWorkflowState', $workflowId], ['acquireJobLock', $workflowId, 'intro'], - ['getJobState', $workflowId, 'intro'], + ['getLastJobState', $workflowId, 'intro'], ['persistJobState', $workflowId, 'intro', JobState::STATUS_RUNNING], ['releaseJobLock', $workflowId, 'intro'], ['persistJobState', $workflowId, 'intro', JobState::STATUS_SUCCESS], - ['getJobState', $workflowId, 'intro'], - ['getJobState', $workflowId, 'never-called'], + ['getLastJobState', $workflowId, 'intro'], + ['getLastJobState', $workflowId, 'never-called'], ['acquireJobLock', $workflowId, 'never-called'], ['persistJobState', $workflowId, 'never-called', JobState::STATUS_TRIGGERED], ['releaseJobLock', $workflowId, 'never-called'], ['getWorkflowState', $workflowId], ['acquireJobLock', $workflowId, 'never-called'], - ['getJobState', $workflowId, 'never-called'], + ['getLastJobState', $workflowId, 'never-called'], ['persistJobState', $workflowId, 'never-called', JobState::STATUS_SKIPPED], ['releaseJobLock', $workflowId, 'never-called'], - ['getJobState', $workflowId, 'intro'], - ['getJobState', $workflowId, 'never-called'], - ['getJobState', $workflowId, 'content'], + ['getLastJobState', $workflowId, 'intro'], + ['getLastJobState', $workflowId, 'never-called'], + ['getLastJobState', $workflowId, 'content'], ['acquireJobLock', $workflowId, 'content'], ['persistJobState', $workflowId, 'content', JobState::STATUS_TRIGGERED], ['releaseJobLock', $workflowId, 'content'], ['getWorkflowState', $workflowId], ['acquireJobLock', $workflowId, 'content'], - ['getJobState', $workflowId, 'content'], + ['getLastJobState', $workflowId, 'content'], ['persistJobState', $workflowId, 'content', JobState::STATUS_RUNNING], ['releaseJobLock', $workflowId, 'content'], ['persistJobState', $workflowId, 'content', JobState::STATUS_SUCCESS], - ['getJobState', $workflowId, 'intro'], - ['getJobState', $workflowId, 'never-called'], - ['getJobState', $workflowId, 'content'], - ['getJobState', $workflowId, 'content_bis'], + ['getLastJobState', $workflowId, 'intro'], + ['getLastJobState', $workflowId, 'never-called'], + ['getLastJobState', $workflowId, 'content'], + ['getLastJobState', $workflowId, 'content_bis'], ['acquireJobLock', $workflowId, 'content_bis'], ['persistJobState', $workflowId, 'content_bis', JobState::STATUS_TRIGGERED], ['releaseJobLock', $workflowId, 'content_bis'], ['getWorkflowState', $workflowId], ['acquireJobLock', $workflowId, 'content_bis'], - ['getJobState', $workflowId, 'content_bis'], + ['getLastJobState', $workflowId, 'content_bis'], ['persistJobState', $workflowId, 'content_bis', JobState::STATUS_RUNNING], ['releaseJobLock', $workflowId, 'content_bis'], ['persistJobState', $workflowId, 'content_bis', JobState::STATUS_SUCCESS], - ['getJobState', $workflowId, 'intro'], - ['getJobState', $workflowId, 'never-called'], - ['getJobState', $workflowId, 'content'], - ['getJobState', $workflowId, 'content_bis'], - ['getJobState', $workflowId, 'outro'], + ['getLastJobState', $workflowId, 'intro'], + ['getLastJobState', $workflowId, 'never-called'], + ['getLastJobState', $workflowId, 'content'], + ['getLastJobState', $workflowId, 'content_bis'], + ['getLastJobState', $workflowId, 'outro'], ['acquireJobLock', $workflowId, 'outro'], ['persistJobState', $workflowId, 'outro', JobState::STATUS_TRIGGERED], ['releaseJobLock', $workflowId, 'outro'], ['getWorkflowState', $workflowId], ['acquireJobLock', $workflowId, 'outro'], - ['getJobState', $workflowId, 'outro'], + ['getLastJobState', $workflowId, 'outro'], ['persistJobState', $workflowId, 'outro', JobState::STATUS_RUNNING], ['releaseJobLock', $workflowId, 'outro'], ['persistJobState', $workflowId, 'outro', JobState::STATUS_SUCCESS], - ['getJobState', $workflowId, 'intro'], - ['getJobState', $workflowId, 'never-called'], - ['getJobState', $workflowId, 'content'], - ['getJobState', $workflowId, 'content_bis'], - ['getJobState', $workflowId, 'outro'], + ['getLastJobState', $workflowId, 'intro'], + ['getLastJobState', $workflowId, 'never-called'], + ['getLastJobState', $workflowId, 'content'], + ['getLastJobState', $workflowId, 'content_bis'], + ['getLastJobState', $workflowId, 'outro'], ['persistWorkflowState', $workflowId, WorkflowState::STATUS_SUCCESS], ], $testStateRepositoryDecorator->getLogs()); @@ -132,46 +133,43 @@ public function testStateAreCorrectlyPersistedForFailJob(StateRepositoryInterfac $this->assertEquals([ ['persistWorkflowState', $workflowId, WorkflowState::STATUS_STARTED], - ['getJobState', $workflowId, 'intro'], - ['acquireJobLock', $workflowId, 'intro'], + ['getLastJobState', $workflowId, 'intro'], + ['createJobState', $workflowId, 'intro'], ['persistJobState', $workflowId, 'intro', JobState::STATUS_TRIGGERED], - ['releaseJobLock', $workflowId, 'intro'], ['getWorkflowState', $workflowId], - ['acquireJobLock', $workflowId, 'intro'], - ['getJobState', $workflowId, 'intro'], + ['acquireJobLock', $workflowId, 'intro-0'], + ['getJobState', $workflowId, 'intro-0'], ['persistJobState', $workflowId, 'intro', JobState::STATUS_RUNNING], - ['releaseJobLock', $workflowId, 'intro'], + ['releaseJobLock', $workflowId, 'intro-0'], ['persistJobState', $workflowId, 'intro', JobState::STATUS_SUCCESS], - ['getJobState', $workflowId, 'intro'], - ['getJobState', $workflowId, 'never-called'], + ['getLastJobState', $workflowId, 'intro'], + ['getLastJobState', $workflowId, 'never-called'], - ['acquireJobLock', $workflowId, 'never-called'], + ['createJobState', $workflowId, 'never-called'], ['persistJobState', $workflowId, 'never-called', JobState::STATUS_TRIGGERED], - ['releaseJobLock', $workflowId, 'never-called'], ['getWorkflowState', $workflowId], - ['acquireJobLock', $workflowId, 'never-called'], - ['getJobState', $workflowId, 'never-called'], + ['acquireJobLock', $workflowId, 'never-called-0'], + ['getJobState', $workflowId, 'never-called-0'], // 16 ['persistJobState', $workflowId, 'never-called', JobState::STATUS_SKIPPED], - ['releaseJobLock', $workflowId, 'never-called'], + ['releaseJobLock', $workflowId, 'never-called-0'], - ['getJobState', $workflowId, 'intro'], - ['getJobState', $workflowId, 'never-called'], - ['getJobState', $workflowId, 'content'], + ['getLastJobState', $workflowId, 'intro'], + ['getLastJobState', $workflowId, 'never-called'], + ['getLastJobState', $workflowId, 'content'], - ['acquireJobLock', $workflowId, 'content'], + ['createJobState', $workflowId, 'content'], ['persistJobState', $workflowId, 'content', JobState::STATUS_TRIGGERED], - ['releaseJobLock', $workflowId, 'content'], ['getWorkflowState', $workflowId], - ['acquireJobLock', $workflowId, 'content'], - ['getJobState', $workflowId, 'content'], + ['acquireJobLock', $workflowId, 'content-0'], + ['getJobState', $workflowId, 'content-0'], ['persistJobState', $workflowId, 'content', JobState::STATUS_RUNNING], - ['releaseJobLock', $workflowId, 'content'], + ['releaseJobLock', $workflowId, 'content-0'], ['persistJobState', $workflowId, 'content', JobState::STATUS_FAILURE], - ['getJobState', $workflowId, 'intro'], - ['getJobState', $workflowId, 'never-called'], - ['getJobState', $workflowId, 'content'], + ['getLastJobState', $workflowId, 'intro'], + ['getLastJobState', $workflowId, 'never-called'], + ['getLastJobState', $workflowId, 'content'], ['persistWorkflowState', $workflowId, WorkflowState::STATUS_FAILURE], ], $testStateRepositoryDecorator->getLogs()); diff --git a/lib/php/workflow/tests/State/TestStateStateRepository.php b/lib/php/workflow/tests/State/TestStateStateRepository.php index 2f5535395..89101b97c 100644 --- a/lib/php/workflow/tests/State/TestStateStateRepository.php +++ b/lib/php/workflow/tests/State/TestStateStateRepository.php @@ -31,11 +31,32 @@ public function persistWorkflowState(WorkflowState $state): void $this->inner->persistWorkflowState($state); } - public function getJobState(string $workflowId, string $jobId): ?JobState + public function getJobState(string $workflowId, string $jobStateId): ?JobState + { + $this->logs[] = ['getJobState', $workflowId, $jobStateId]; + + return $this->inner->getJobState($workflowId, $jobStateId); + } + + public function getLastJobState(string $workflowId, string $jobId): ?JobState + { + $this->logs[] = ['getLastJobState', $workflowId, $jobId]; + + return $this->inner->getLastJobState($workflowId, $jobId); + } + + public function createJobState(string $workflowId, string $jobId): JobState + { + $this->logs[] = ['createJobState', $workflowId, $jobId]; + + return $this->inner->createJobState($workflowId, $jobId); + } + + public function getJobStates(string $workflowId, string $jobId): array { $this->logs[] = ['getJobState', $workflowId, $jobId]; - return $this->inner->getJobState($workflowId, $jobId); + return $this->inner->getJobStates($workflowId, $jobId); } public function persistJobState(JobState $state): void @@ -45,33 +66,33 @@ public function persistJobState(JobState $state): void $this->inner->persistJobState($state); } - public function removeJobState(string $workflowId, string $jobId): void + public function removeJobState(string $workflowId, string $jobStateId): void { - $this->logs[] = ['removeJobState', $workflowId, $jobId]; - - $this->inner->removeJobState($workflowId, $jobId); - } + $this->logs[] = ['removeJobState', $workflowId, $jobStateId]; - public function resetJobState(string $workflowId, string $jobId): void - { - $this->removeJobState($workflowId, $jobId); + $this->inner->removeJobState($workflowId, $jobStateId); } - public function acquireJobLock(string $workflowId, string $jobId): void + public function acquireJobLock(string $workflowId, string $jobStateId): void { - $this->logs[] = ['acquireJobLock', $workflowId, $jobId]; + $this->logs[] = ['acquireJobLock', $workflowId, $jobStateId]; if ($this->inner instanceof LockAwareStateRepositoryInterface) { - $this->inner->acquireJobLock($workflowId, $jobId); + $this->inner->acquireJobLock($workflowId, $jobStateId); } } - public function releaseJobLock(string $workflowId, string $jobId): void + public function resetJobState(string $workflowId, string $jobId): void + { + $this->removeJobState($workflowId, $jobId); + } + + public function releaseJobLock(string $workflowId, string $jobStateId): void { - $this->logs[] = ['releaseJobLock', $workflowId, $jobId]; + $this->logs[] = ['releaseJobLock', $workflowId, $jobStateId]; if ($this->inner instanceof LockAwareStateRepositoryInterface) { - $this->inner->releaseJobLock($workflowId, $jobId); + $this->inner->releaseJobLock($workflowId, $jobStateId); } }