From 75daae663a66e70ec6cd2dccecdd21816f6e0148 Mon Sep 17 00:00:00 2001 From: Vincent Chalamon <407859+vincentchalamon@users.noreply.github.com> Date: Fri, 4 Aug 2023 18:25:36 +0200 Subject: [PATCH] chore: enable all tests to expose failing ones (+ improve admin and client) --- api/composer.json | 2 +- api/composer.lock | 535 +++++++++--------- api/config/packages/api_platform.yaml | 2 - .../DataFixtures/Story/DefaultBookStory.php | 2 +- api/src/Entity/Book.php | 6 +- api/tests/Api/Admin/BookTest.php | 41 +- api/tests/Api/Admin/ReviewTest.php | 10 +- api/tests/Api/BookTest.php | 16 + api/tests/Api/BookmarkTest.php | 1 - api/tests/Api/ReviewTest.php | 32 +- pwa/components/admin/book/BookInput.tsx | 43 +- pwa/components/admin/book/ConditionInput.tsx | 9 +- pwa/components/admin/book/List.tsx | 3 +- pwa/components/book/Filters.tsx | 26 +- pwa/components/book/Item.tsx | 25 +- pwa/components/book/List.tsx | 40 +- pwa/components/book/Show.tsx | 26 +- pwa/components/bookmark/List.tsx | 6 +- pwa/components/common/Error.tsx | 2 +- pwa/components/common/Pagination.tsx | 2 +- pwa/components/review/Form.tsx | 8 +- pwa/components/review/Item.tsx | 8 +- pwa/components/review/List.tsx | 5 +- pwa/pages/books/index.tsx | 17 +- pwa/utils/book.ts | 104 ++-- 25 files changed, 495 insertions(+), 476 deletions(-) diff --git a/api/composer.json b/api/composer.json index 6946b8ee2..245808a63 100644 --- a/api/composer.json +++ b/api/composer.json @@ -12,7 +12,7 @@ "ext-ctype": "*", "ext-iconv": "*", "ext-xml": "*", - "api-platform/core": "dev-demo", + "api-platform/core": "dev-fix/issues/5662", "doctrine/doctrine-bundle": "^2.7", "doctrine/doctrine-migrations-bundle": "^3.2", "doctrine/orm": "^2.12", diff --git a/api/composer.lock b/api/composer.lock index cac0e877d..f790e88af 100644 --- a/api/composer.lock +++ b/api/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "556e2634ee5ee54f81424ff814ccaa8f", + "content-hash": "95597546d29879426282d52cf97d5681", "packages": [ { "name": "api-platform/core", - "version": "dev-demo", + "version": "dev-fix/issues/5662", "source": { "type": "git", "url": "https://github.com/vincentchalamon/core.git", - "reference": "2b27c9c704edce96d52acbca0ce6dfc85554ea05" + "reference": "7690b1da32e885aec51a7cdbd135e797905cca45" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vincentchalamon/core/zipball/2b27c9c704edce96d52acbca0ce6dfc85554ea05", - "reference": "2b27c9c704edce96d52acbca0ce6dfc85554ea05", + "url": "https://api.github.com/repos/vincentchalamon/core/zipball/7690b1da32e885aec51a7cdbd135e797905cca45", + "reference": "7690b1da32e885aec51a7cdbd135e797905cca45", "shasum": "" }, "require": { @@ -170,7 +170,7 @@ "Swagger" ], "support": { - "source": "https://github.com/vincentchalamon/core/tree/demo" + "source": "https://github.com/vincentchalamon/core/tree/fix/issues/5662" }, "funding": [ { @@ -178,7 +178,7 @@ "url": "https://tidelift.com/funding/github/packagist/api-platform/core" } ], - "time": "2023-07-20T14:37:44+00:00" + "time": "2023-08-04T16:20:47+00:00" }, { "name": "brick/math", @@ -330,16 +330,16 @@ }, { "name": "doctrine/collections", - "version": "2.1.2", + "version": "2.1.3", "source": { "type": "git", "url": "https://github.com/doctrine/collections.git", - "reference": "db8cda536a034337f7dd63febecc713d4957f9ee" + "reference": "3023e150f90a38843856147b58190aa8b46cc155" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/db8cda536a034337f7dd63febecc713d4957f9ee", - "reference": "db8cda536a034337f7dd63febecc713d4957f9ee", + "url": "https://api.github.com/repos/doctrine/collections/zipball/3023e150f90a38843856147b58190aa8b46cc155", + "reference": "3023e150f90a38843856147b58190aa8b46cc155", "shasum": "" }, "require": { @@ -352,7 +352,7 @@ "phpstan/phpstan": "^1.8", "phpstan/phpstan-phpunit": "^1.0", "phpunit/phpunit": "^9.5", - "vimeo/psalm": "^4.22" + "vimeo/psalm": "^5.11" }, "type": "library", "autoload": { @@ -396,7 +396,7 @@ ], "support": { "issues": "https://github.com/doctrine/collections/issues", - "source": "https://github.com/doctrine/collections/tree/2.1.2" + "source": "https://github.com/doctrine/collections/tree/2.1.3" }, "funding": [ { @@ -412,7 +412,7 @@ "type": "tidelift" } ], - "time": "2022-12-27T23:41:38+00:00" + "time": "2023-07-06T15:15:36+00:00" }, { "name": "doctrine/common", @@ -507,16 +507,16 @@ }, { "name": "doctrine/dbal", - "version": "3.6.4", + "version": "3.6.5", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "19f0dec95edd6a3c3c5ff1d188ea94c6b7fc903f" + "reference": "96d5a70fd91efdcec81fc46316efc5bf3da17ddf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/19f0dec95edd6a3c3c5ff1d188ea94c6b7fc903f", - "reference": "19f0dec95edd6a3c3c5ff1d188ea94c6b7fc903f", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/96d5a70fd91efdcec81fc46316efc5bf3da17ddf", + "reference": "96d5a70fd91efdcec81fc46316efc5bf3da17ddf", "shasum": "" }, "require": { @@ -531,10 +531,10 @@ "require-dev": { "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", - "jetbrains/phpstorm-stubs": "2022.3", - "phpstan/phpstan": "1.10.14", + "jetbrains/phpstorm-stubs": "2023.1", + "phpstan/phpstan": "1.10.21", "phpstan/phpstan-strict-rules": "^1.5", - "phpunit/phpunit": "9.6.7", + "phpunit/phpunit": "9.6.9", "psalm/plugin-phpunit": "0.18.4", "squizlabs/php_codesniffer": "3.7.2", "symfony/cache": "^5.4|^6.0", @@ -599,7 +599,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.6.4" + "source": "https://github.com/doctrine/dbal/tree/3.6.5" }, "funding": [ { @@ -615,7 +615,7 @@ "type": "tidelift" } ], - "time": "2023-06-15T07:40:12+00:00" + "time": "2023-07-17T09:15:50+00:00" }, { "name": "doctrine/deprecations", @@ -1299,16 +1299,16 @@ }, { "name": "doctrine/orm", - "version": "2.15.3", + "version": "2.16.0", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "4c3bd208018c26498e5f682aaad45fa00ea307d5" + "reference": "495cd06b9a630f9c38a21ceb249ed008edbe8414" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/4c3bd208018c26498e5f682aaad45fa00ea307d5", - "reference": "4c3bd208018c26498e5f682aaad45fa00ea307d5", + "url": "https://api.github.com/repos/doctrine/orm/zipball/495cd06b9a630f9c38a21ceb249ed008edbe8414", + "reference": "495cd06b9a630f9c38a21ceb249ed008edbe8414", "shasum": "" }, "require": { @@ -1337,14 +1337,14 @@ "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.10.18", + "phpstan/phpstan": "~1.4.10 || 1.10.25", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", "psr/log": "^1 || ^2 || ^3", "squizlabs/php_codesniffer": "3.7.2", "symfony/cache": "^4.4 || ^5.4 || ^6.0", "symfony/var-exporter": "^4.4 || ^5.4 || ^6.2", "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0", - "vimeo/psalm": "4.30.0 || 5.12.0" + "vimeo/psalm": "4.30.0 || 5.13.1" }, "suggest": { "ext-dom": "Provides support for XSD validation for XML mapping files", @@ -1394,9 +1394,9 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/2.15.3" + "source": "https://github.com/doctrine/orm/tree/2.16.0" }, - "time": "2023-06-22T12:36:06+00:00" + "time": "2023-08-01T12:07:04+00:00" }, { "name": "doctrine/persistence", @@ -1918,16 +1918,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.22.1", + "version": "1.23.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0" + "reference": "846ae76eef31c6d7790fac9bc399ecee45160b26" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/65c39594fbd8c67abfc68bb323f86447bab79cc0", - "reference": "65c39594fbd8c67abfc68bb323f86447bab79cc0", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/846ae76eef31c6d7790fac9bc399ecee45160b26", + "reference": "846ae76eef31c6d7790fac9bc399ecee45160b26", "shasum": "" }, "require": { @@ -1959,9 +1959,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.22.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.23.1" }, - "time": "2023-06-29T20:46:06+00:00" + "time": "2023-08-03T16:32:59+00:00" }, { "name": "psr/cache", @@ -2450,16 +2450,16 @@ }, { "name": "symfony/cache", - "version": "v6.3.1", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "52cff7608ef6e38376ac11bd1fbb0a220107f066" + "reference": "d176b97600860df13e851538c2df2ad88e9e5ca9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/52cff7608ef6e38376ac11bd1fbb0a220107f066", - "reference": "52cff7608ef6e38376ac11bd1fbb0a220107f066", + "url": "https://api.github.com/repos/symfony/cache/zipball/d176b97600860df13e851538c2df2ad88e9e5ca9", + "reference": "d176b97600860df13e851538c2df2ad88e9e5ca9", "shasum": "" }, "require": { @@ -2526,7 +2526,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v6.3.1" + "source": "https://github.com/symfony/cache/tree/v6.3.2" }, "funding": [ { @@ -2542,7 +2542,7 @@ "type": "tidelift" } ], - "time": "2023-06-24T11:51:27+00:00" + "time": "2023-07-27T16:19:14+00:00" }, { "name": "symfony/cache-contracts", @@ -2695,16 +2695,16 @@ }, { "name": "symfony/config", - "version": "v6.3.0", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "a5e00dec161b08c946a2c16eed02adbeedf827ae" + "reference": "b47ca238b03e7b0d7880ffd1cf06e8d637ca1467" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/a5e00dec161b08c946a2c16eed02adbeedf827ae", - "reference": "a5e00dec161b08c946a2c16eed02adbeedf827ae", + "url": "https://api.github.com/repos/symfony/config/zipball/b47ca238b03e7b0d7880ffd1cf06e8d637ca1467", + "reference": "b47ca238b03e7b0d7880ffd1cf06e8d637ca1467", "shasum": "" }, "require": { @@ -2750,7 +2750,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v6.3.0" + "source": "https://github.com/symfony/config/tree/v6.3.2" }, "funding": [ { @@ -2766,20 +2766,20 @@ "type": "tidelift" } ], - "time": "2023-04-25T10:46:17+00:00" + "time": "2023-07-19T20:22:16+00:00" }, { "name": "symfony/console", - "version": "v6.3.0", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7" + "reference": "aa5d64ad3f63f2e48964fc81ee45cb318a723898" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7", - "reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7", + "url": "https://api.github.com/repos/symfony/console/zipball/aa5d64ad3f63f2e48964fc81ee45cb318a723898", + "reference": "aa5d64ad3f63f2e48964fc81ee45cb318a723898", "shasum": "" }, "require": { @@ -2840,7 +2840,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.3.0" + "source": "https://github.com/symfony/console/tree/v6.3.2" }, "funding": [ { @@ -2856,20 +2856,20 @@ "type": "tidelift" } ], - "time": "2023-05-29T12:49:39+00:00" + "time": "2023-07-19T20:17:28+00:00" }, { "name": "symfony/dependency-injection", - "version": "v6.3.1", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "7abf242af21f196b65f20ab00ff251fdf3889b8d" + "reference": "474cfbc46aba85a1ca11a27db684480d0db64ba7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/7abf242af21f196b65f20ab00ff251fdf3889b8d", - "reference": "7abf242af21f196b65f20ab00ff251fdf3889b8d", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/474cfbc46aba85a1ca11a27db684480d0db64ba7", + "reference": "474cfbc46aba85a1ca11a27db684480d0db64ba7", "shasum": "" }, "require": { @@ -2921,7 +2921,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v6.3.1" + "source": "https://github.com/symfony/dependency-injection/tree/v6.3.2" }, "funding": [ { @@ -2937,7 +2937,7 @@ "type": "tidelift" } ], - "time": "2023-06-24T11:51:27+00:00" + "time": "2023-07-19T20:17:28+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3008,16 +3008,16 @@ }, { "name": "symfony/doctrine-bridge", - "version": "v6.3.1", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-bridge.git", - "reference": "594263c7d2677022a16e4f39d20070463ba03888" + "reference": "61c7d16fbb61ffe3a08d1b965355d6b1006c3594" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/594263c7d2677022a16e4f39d20070463ba03888", - "reference": "594263c7d2677022a16e4f39d20070463ba03888", + "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/61c7d16fbb61ffe3a08d1b965355d6b1006c3594", + "reference": "61c7d16fbb61ffe3a08d1b965355d6b1006c3594", "shasum": "" }, "require": { @@ -3098,7 +3098,7 @@ "description": "Provides integration for Doctrine with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/doctrine-bridge/tree/v6.3.1" + "source": "https://github.com/symfony/doctrine-bridge/tree/v6.3.2" }, "funding": [ { @@ -3114,7 +3114,7 @@ "type": "tidelift" } ], - "time": "2023-06-18T20:33:34+00:00" + "time": "2023-07-20T14:51:28+00:00" }, { "name": "symfony/dotenv", @@ -3192,16 +3192,16 @@ }, { "name": "symfony/error-handler", - "version": "v6.3.0", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "99d2d814a6351461af350ead4d963bd67451236f" + "reference": "85fd65ed295c4078367c784e8a5a6cee30348b7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/99d2d814a6351461af350ead4d963bd67451236f", - "reference": "99d2d814a6351461af350ead4d963bd67451236f", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/85fd65ed295c4078367c784e8a5a6cee30348b7a", + "reference": "85fd65ed295c4078367c784e8a5a6cee30348b7a", "shasum": "" }, "require": { @@ -3246,7 +3246,7 @@ "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.0" + "source": "https://github.com/symfony/error-handler/tree/v6.3.2" }, "funding": [ { @@ -3262,20 +3262,20 @@ "type": "tidelift" } ], - "time": "2023-05-10T12:03:13+00:00" + "time": "2023-07-16T17:05:46+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v6.3.0", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa" + "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa", - "reference": "3af8ac1a3f98f6dbc55e10ae59c9e44bfc38dfaa", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/adb01fe097a4ee930db9258a3cc906b5beb5cf2e", + "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e", "shasum": "" }, "require": { @@ -3326,7 +3326,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/v6.3.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.3.2" }, "funding": [ { @@ -3342,7 +3342,7 @@ "type": "tidelift" } ], - "time": "2023-04-21T14:41:17+00:00" + "time": "2023-07-06T06:56:43+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -3549,16 +3549,16 @@ }, { "name": "symfony/finder", - "version": "v6.3.0", + "version": "v6.3.3", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2" + "reference": "9915db259f67d21eefee768c1abcf1cc61b1fc9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/d9b01ba073c44cef617c7907ce2419f8d00d75e2", - "reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2", + "url": "https://api.github.com/repos/symfony/finder/zipball/9915db259f67d21eefee768c1abcf1cc61b1fc9e", + "reference": "9915db259f67d21eefee768c1abcf1cc61b1fc9e", "shasum": "" }, "require": { @@ -3593,7 +3593,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.3.0" + "source": "https://github.com/symfony/finder/tree/v6.3.3" }, "funding": [ { @@ -3609,20 +3609,20 @@ "type": "tidelift" } ], - "time": "2023-04-02T01:25:41+00:00" + "time": "2023-07-31T08:31:44+00:00" }, { "name": "symfony/flex", - "version": "v2.3.1", + "version": "v2.3.3", "source": { "type": "git", "url": "https://github.com/symfony/flex.git", - "reference": "3c9c3424efdafe33e0e3cfb5e87e50b34711fedf" + "reference": "9c402af768c6c9f8126a9ffa192ecf7c16581e35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/flex/zipball/3c9c3424efdafe33e0e3cfb5e87e50b34711fedf", - "reference": "3c9c3424efdafe33e0e3cfb5e87e50b34711fedf", + "url": "https://api.github.com/repos/symfony/flex/zipball/9c402af768c6c9f8126a9ffa192ecf7c16581e35", + "reference": "9c402af768c6c9f8126a9ffa192ecf7c16581e35", "shasum": "" }, "require": { @@ -3658,7 +3658,7 @@ "description": "Composer plugin for Symfony", "support": { "issues": "https://github.com/symfony/flex/issues", - "source": "https://github.com/symfony/flex/tree/v2.3.1" + "source": "https://github.com/symfony/flex/tree/v2.3.3" }, "funding": [ { @@ -3674,20 +3674,20 @@ "type": "tidelift" } ], - "time": "2023-05-27T07:38:25+00:00" + "time": "2023-08-04T09:02:35+00:00" }, { "name": "symfony/framework-bundle", - "version": "v6.3.1", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "42b0707efba17ca7c6af7373cb1b1a1b4f24f772" + "reference": "930fe7ee25a928b9b3627d717873ddd171430a82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/42b0707efba17ca7c6af7373cb1b1a1b4f24f772", - "reference": "42b0707efba17ca7c6af7373cb1b1a1b4f24f772", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/930fe7ee25a928b9b3627d717873ddd171430a82", + "reference": "930fe7ee25a928b9b3627d717873ddd171430a82", "shasum": "" }, "require": { @@ -3802,7 +3802,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v6.3.1" + "source": "https://github.com/symfony/framework-bundle/tree/v6.3.2" }, "funding": [ { @@ -3818,20 +3818,20 @@ "type": "tidelift" } ], - "time": "2023-06-24T09:59:31+00:00" + "time": "2023-07-26T17:39:03+00:00" }, { "name": "symfony/http-client", - "version": "v6.3.1", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "1c828a06aef2f5eeba42026dfc532d4fc5406123" + "reference": "15f9f4bad62bfcbe48b5dedd866f04a08fc7ff00" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/1c828a06aef2f5eeba42026dfc532d4fc5406123", - "reference": "1c828a06aef2f5eeba42026dfc532d4fc5406123", + "url": "https://api.github.com/repos/symfony/http-client/zipball/15f9f4bad62bfcbe48b5dedd866f04a08fc7ff00", + "reference": "15f9f4bad62bfcbe48b5dedd866f04a08fc7ff00", "shasum": "" }, "require": { @@ -3894,7 +3894,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v6.3.1" + "source": "https://github.com/symfony/http-client/tree/v6.3.2" }, "funding": [ { @@ -3910,7 +3910,7 @@ "type": "tidelift" } ], - "time": "2023-06-24T11:51:27+00:00" + "time": "2023-07-05T08:41:27+00:00" }, { "name": "symfony/http-client-contracts", @@ -3992,16 +3992,16 @@ }, { "name": "symfony/http-foundation", - "version": "v6.3.1", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "e0ad0d153e1c20069250986cd9e9dd1ccebb0d66" + "reference": "43ed99d30f5f466ffa00bdac3f5f7aa9cd7617c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e0ad0d153e1c20069250986cd9e9dd1ccebb0d66", - "reference": "e0ad0d153e1c20069250986cd9e9dd1ccebb0d66", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/43ed99d30f5f466ffa00bdac3f5f7aa9cd7617c3", + "reference": "43ed99d30f5f466ffa00bdac3f5f7aa9cd7617c3", "shasum": "" }, "require": { @@ -4049,7 +4049,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.3.1" + "source": "https://github.com/symfony/http-foundation/tree/v6.3.2" }, "funding": [ { @@ -4065,20 +4065,20 @@ "type": "tidelift" } ], - "time": "2023-06-24T11:51:27+00:00" + "time": "2023-07-23T21:58:39+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.3.1", + "version": "v6.3.3", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "161e16fd2e35fb4881a43bc8b383dfd5be4ac374" + "reference": "d3b567f0addf695e10b0c6d57564a9bea2e058ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/161e16fd2e35fb4881a43bc8b383dfd5be4ac374", - "reference": "161e16fd2e35fb4881a43bc8b383dfd5be4ac374", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/d3b567f0addf695e10b0c6d57564a9bea2e058ee", + "reference": "d3b567f0addf695e10b0c6d57564a9bea2e058ee", "shasum": "" }, "require": { @@ -4162,7 +4162,7 @@ "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.3.1" + "source": "https://github.com/symfony/http-kernel/tree/v6.3.3" }, "funding": [ { @@ -4178,7 +4178,7 @@ "type": "tidelift" } ], - "time": "2023-06-26T06:07:32+00:00" + "time": "2023-07-31T10:33:00+00:00" }, { "name": "symfony/mercure", @@ -4739,16 +4739,16 @@ }, { "name": "symfony/property-access", - "version": "v6.3.0", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/property-access.git", - "reference": "db9358571ce63f09c439c2fee6c12e5b090b69ac" + "reference": "2dc4f9da444b8f8ff592e95d570caad67924f1d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/db9358571ce63f09c439c2fee6c12e5b090b69ac", - "reference": "db9358571ce63f09c439c2fee6c12e5b090b69ac", + "url": "https://api.github.com/repos/symfony/property-access/zipball/2dc4f9da444b8f8ff592e95d570caad67924f1d0", + "reference": "2dc4f9da444b8f8ff592e95d570caad67924f1d0", "shasum": "" }, "require": { @@ -4796,7 +4796,7 @@ "reflection" ], "support": { - "source": "https://github.com/symfony/property-access/tree/v6.3.0" + "source": "https://github.com/symfony/property-access/tree/v6.3.2" }, "funding": [ { @@ -4812,7 +4812,7 @@ "type": "tidelift" } ], - "time": "2023-05-19T08:06:44+00:00" + "time": "2023-07-13T15:26:11+00:00" }, { "name": "symfony/property-info", @@ -4899,20 +4899,21 @@ }, { "name": "symfony/routing", - "version": "v6.3.1", + "version": "v6.3.3", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "d37ad1779c38b8eb71996d17dc13030dcb7f9cf5" + "reference": "e7243039ab663822ff134fbc46099b5fdfa16f6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/d37ad1779c38b8eb71996d17dc13030dcb7f9cf5", - "reference": "d37ad1779c38b8eb71996d17dc13030dcb7f9cf5", + "url": "https://api.github.com/repos/symfony/routing/zipball/e7243039ab663822ff134fbc46099b5fdfa16f6a", + "reference": "e7243039ab663822ff134fbc46099b5fdfa16f6a", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "doctrine/annotations": "<1.12", @@ -4961,7 +4962,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v6.3.1" + "source": "https://github.com/symfony/routing/tree/v6.3.3" }, "funding": [ { @@ -4977,20 +4978,20 @@ "type": "tidelift" } ], - "time": "2023-06-05T15:30:22+00:00" + "time": "2023-07-31T07:08:24+00:00" }, { "name": "symfony/runtime", - "version": "v6.3.1", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/runtime.git", - "reference": "8e83b5d8e0ace903e1a91dedfe08a84ed2a54b0d" + "reference": "d5c09493647a0c1a16e6c8da308098e840d1164f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/runtime/zipball/8e83b5d8e0ace903e1a91dedfe08a84ed2a54b0d", - "reference": "8e83b5d8e0ace903e1a91dedfe08a84ed2a54b0d", + "url": "https://api.github.com/repos/symfony/runtime/zipball/d5c09493647a0c1a16e6c8da308098e840d1164f", + "reference": "d5c09493647a0c1a16e6c8da308098e840d1164f", "shasum": "" }, "require": { @@ -5040,7 +5041,7 @@ "runtime" ], "support": { - "source": "https://github.com/symfony/runtime/tree/v6.3.1" + "source": "https://github.com/symfony/runtime/tree/v6.3.2" }, "funding": [ { @@ -5056,20 +5057,20 @@ "type": "tidelift" } ], - "time": "2023-06-21T12:08:28+00:00" + "time": "2023-07-16T17:05:46+00:00" }, { "name": "symfony/security-bundle", - "version": "v6.3.1", + "version": "v6.3.3", "source": { "type": "git", "url": "https://github.com/symfony/security-bundle.git", - "reference": "f4fe79d7ebafd406e1a6f646839bfbbed641d8b2" + "reference": "b33382ca3034ee691dd0d882f214ae9e037f4427" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-bundle/zipball/f4fe79d7ebafd406e1a6f646839bfbbed641d8b2", - "reference": "f4fe79d7ebafd406e1a6f646839bfbbed641d8b2", + "url": "https://api.github.com/repos/symfony/security-bundle/zipball/b33382ca3034ee691dd0d882f214ae9e037f4427", + "reference": "b33382ca3034ee691dd0d882f214ae9e037f4427", "shasum": "" }, "require": { @@ -5079,6 +5080,7 @@ "symfony/clock": "^6.3", "symfony/config": "^6.1", "symfony/dependency-injection": "^6.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/event-dispatcher": "^5.4|^6.0", "symfony/http-foundation": "^6.2", "symfony/http-kernel": "^6.2", @@ -5149,7 +5151,7 @@ "description": "Provides a tight integration of the Security component into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-bundle/tree/v6.3.1" + "source": "https://github.com/symfony/security-bundle/tree/v6.3.3" }, "funding": [ { @@ -5165,24 +5167,25 @@ "type": "tidelift" } ], - "time": "2023-06-21T12:08:28+00:00" + "time": "2023-07-31T07:08:24+00:00" }, { "name": "symfony/security-core", - "version": "v6.3.0", + "version": "v6.3.3", "source": { "type": "git", "url": "https://github.com/symfony/security-core.git", - "reference": "9cb74232e978be1440d2bb7daf91eb40a9363890" + "reference": "b86ce012cc9a62a15ed43af5037eebc3e6de4d7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-core/zipball/9cb74232e978be1440d2bb7daf91eb40a9363890", - "reference": "9cb74232e978be1440d2bb7daf91eb40a9363890", + "url": "https://api.github.com/repos/symfony/security-core/zipball/b86ce012cc9a62a15ed43af5037eebc3e6de4d7f", + "reference": "b86ce012cc9a62a15ed43af5037eebc3e6de4d7f", "shasum": "" }, "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/event-dispatcher-contracts": "^2.5|^3", "symfony/password-hasher": "^5.4|^6.0", "symfony/service-contracts": "^2.5|^3" @@ -5233,7 +5236,7 @@ "description": "Symfony Security Component - Core Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-core/tree/v6.3.0" + "source": "https://github.com/symfony/security-core/tree/v6.3.3" }, "funding": [ { @@ -5249,20 +5252,20 @@ "type": "tidelift" } ], - "time": "2023-04-28T15:57:00+00:00" + "time": "2023-07-31T07:08:24+00:00" }, { "name": "symfony/security-csrf", - "version": "v6.3.0", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/security-csrf.git", - "reference": "1f505c9060bde692eb37718c78a91d95d9abeeec" + "reference": "63d7b098c448cbddb46ea5eda33b68c1ece6eb5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-csrf/zipball/1f505c9060bde692eb37718c78a91d95d9abeeec", - "reference": "1f505c9060bde692eb37718c78a91d95d9abeeec", + "url": "https://api.github.com/repos/symfony/security-csrf/zipball/63d7b098c448cbddb46ea5eda33b68c1ece6eb5b", + "reference": "63d7b098c448cbddb46ea5eda33b68c1ece6eb5b", "shasum": "" }, "require": { @@ -5301,7 +5304,7 @@ "description": "Symfony Security Component - CSRF Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-csrf/tree/v6.3.0" + "source": "https://github.com/symfony/security-csrf/tree/v6.3.2" }, "funding": [ { @@ -5317,20 +5320,20 @@ "type": "tidelift" } ], - "time": "2023-04-21T14:41:17+00:00" + "time": "2023-07-05T08:41:27+00:00" }, { "name": "symfony/security-http", - "version": "v6.3.1", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/security-http.git", - "reference": "36d2bdd09c33f63014dc65f164a77ff099d256c6" + "reference": "04d6b868786a56c1fadc52b003fe5a4f9ab3f3d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-http/zipball/36d2bdd09c33f63014dc65f164a77ff099d256c6", - "reference": "36d2bdd09c33f63014dc65f164a77ff099d256c6", + "url": "https://api.github.com/repos/symfony/security-http/zipball/04d6b868786a56c1fadc52b003fe5a4f9ab3f3d0", + "reference": "04d6b868786a56c1fadc52b003fe5a4f9ab3f3d0", "shasum": "" }, "require": { @@ -5388,7 +5391,7 @@ "description": "Symfony Security Component - HTTP Integration", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-http/tree/v6.3.1" + "source": "https://github.com/symfony/security-http/tree/v6.3.2" }, "funding": [ { @@ -5404,24 +5407,25 @@ "type": "tidelift" } ], - "time": "2023-06-18T15:50:12+00:00" + "time": "2023-07-13T14:29:38+00:00" }, { "name": "symfony/serializer", - "version": "v6.3.1", + "version": "v6.3.3", "source": { "type": "git", "url": "https://github.com/symfony/serializer.git", - "reference": "1d238ee3180bc047f8ab713bfb73848d553f4407" + "reference": "33deb86d212893042d7758d452aa39d19ca0efe3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/1d238ee3180bc047f8ab713bfb73848d553f4407", - "reference": "1d238ee3180bc047f8ab713bfb73848d553f4407", + "url": "https://api.github.com/repos/symfony/serializer/zipball/33deb86d212893042d7758d452aa39d19ca0efe3", + "reference": "33deb86d212893042d7758d452aa39d19ca0efe3", "shasum": "" }, "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "~1.8" }, "conflict": { @@ -5430,7 +5434,7 @@ "phpdocumentor/type-resolver": "<1.4.0", "symfony/dependency-injection": "<5.4", "symfony/property-access": "<5.4", - "symfony/property-info": "<5.4", + "symfony/property-info": "<5.4.24|>=6,<6.2.11", "symfony/uid": "<5.4", "symfony/yaml": "<5.4" }, @@ -5448,7 +5452,7 @@ "symfony/http-kernel": "^5.4|^6.0", "symfony/mime": "^5.4|^6.0", "symfony/property-access": "^5.4|^6.0", - "symfony/property-info": "^5.4|^6.0", + "symfony/property-info": "^5.4.24|^6.2.11", "symfony/uid": "^5.4|^6.0", "symfony/validator": "^5.4|^6.0", "symfony/var-dumper": "^5.4|^6.0", @@ -5481,7 +5485,7 @@ "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/serializer/tree/v6.3.1" + "source": "https://github.com/symfony/serializer/tree/v6.3.3" }, "funding": [ { @@ -5497,7 +5501,7 @@ "type": "tidelift" } ], - "time": "2023-06-21T19:54:33+00:00" + "time": "2023-07-31T07:08:24+00:00" }, { "name": "symfony/service-contracts", @@ -5645,16 +5649,16 @@ }, { "name": "symfony/string", - "version": "v6.3.0", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f" + "reference": "53d1a83225002635bca3482fcbf963001313fb68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f2e190ee75ff0f5eced645ec0be5c66fac81f51f", - "reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f", + "url": "https://api.github.com/repos/symfony/string/zipball/53d1a83225002635bca3482fcbf963001313fb68", + "reference": "53d1a83225002635bca3482fcbf963001313fb68", "shasum": "" }, "require": { @@ -5711,7 +5715,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.3.0" + "source": "https://github.com/symfony/string/tree/v6.3.2" }, "funding": [ { @@ -5727,7 +5731,7 @@ "type": "tidelift" } ], - "time": "2023-03-21T21:06:29+00:00" + "time": "2023-07-05T08:41:27+00:00" }, { "name": "symfony/translation-contracts", @@ -5809,16 +5813,16 @@ }, { "name": "symfony/twig-bridge", - "version": "v6.3.0", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/twig-bridge.git", - "reference": "67a33c71062d7d931fe9a8cb7be79cca986a6c09" + "reference": "6f8435db76a2d79917489a19a82679276c1b4e32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/67a33c71062d7d931fe9a8cb7be79cca986a6c09", - "reference": "67a33c71062d7d931fe9a8cb7be79cca986a6c09", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/6f8435db76a2d79917489a19a82679276c1b4e32", + "reference": "6f8435db76a2d79917489a19a82679276c1b4e32", "shasum": "" }, "require": { @@ -5897,7 +5901,7 @@ "description": "Provides integration for Twig with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bridge/tree/v6.3.0" + "source": "https://github.com/symfony/twig-bridge/tree/v6.3.2" }, "funding": [ { @@ -5913,7 +5917,7 @@ "type": "tidelift" } ], - "time": "2023-05-29T13:12:36+00:00" + "time": "2023-07-20T16:42:33+00:00" }, { "name": "symfony/twig-bundle", @@ -6076,16 +6080,16 @@ }, { "name": "symfony/validator", - "version": "v6.3.1", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "1b71f43c62ee867ab08195ba6039a1bc3e6654dc" + "reference": "b0c4ecf17d39eee1edfecc92299a03b9f5d5220b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/1b71f43c62ee867ab08195ba6039a1bc3e6654dc", - "reference": "1b71f43c62ee867ab08195ba6039a1bc3e6654dc", + "url": "https://api.github.com/repos/symfony/validator/zipball/b0c4ecf17d39eee1edfecc92299a03b9f5d5220b", + "reference": "b0c4ecf17d39eee1edfecc92299a03b9f5d5220b", "shasum": "" }, "require": { @@ -6152,7 +6156,7 @@ "description": "Provides tools to validate values", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/validator/tree/v6.3.1" + "source": "https://github.com/symfony/validator/tree/v6.3.2" }, "funding": [ { @@ -6168,24 +6172,25 @@ "type": "tidelift" } ], - "time": "2023-06-21T12:08:28+00:00" + "time": "2023-07-26T17:39:03+00:00" }, { "name": "symfony/var-dumper", - "version": "v6.3.1", + "version": "v6.3.3", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "c81268d6960ddb47af17391a27d222bd58cf0515" + "reference": "77fb4f2927f6991a9843633925d111147449ee7a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c81268d6960ddb47af17391a27d222bd58cf0515", - "reference": "c81268d6960ddb47af17391a27d222bd58cf0515", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/77fb4f2927f6991a9843633925d111147449ee7a", + "reference": "77fb4f2927f6991a9843633925d111147449ee7a", "shasum": "" }, "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { @@ -6194,6 +6199,7 @@ "require-dev": { "ext-iconv": "*", "symfony/console": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", "symfony/process": "^5.4|^6.0", "symfony/uid": "^5.4|^6.0", "twig/twig": "^2.13|^3.0.4" @@ -6234,7 +6240,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.3.1" + "source": "https://github.com/symfony/var-dumper/tree/v6.3.3" }, "funding": [ { @@ -6250,20 +6256,20 @@ "type": "tidelift" } ], - "time": "2023-06-21T12:08:28+00:00" + "time": "2023-07-31T07:08:24+00:00" }, { "name": "symfony/var-exporter", - "version": "v6.3.0", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "db5416d04269f2827d8c54331ba4cfa42620d350" + "reference": "3400949782c0cb5b3e73aa64cfd71dde000beccc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/db5416d04269f2827d8c54331ba4cfa42620d350", - "reference": "db5416d04269f2827d8c54331ba4cfa42620d350", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/3400949782c0cb5b3e73aa64cfd71dde000beccc", + "reference": "3400949782c0cb5b3e73aa64cfd71dde000beccc", "shasum": "" }, "require": { @@ -6308,7 +6314,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v6.3.0" + "source": "https://github.com/symfony/var-exporter/tree/v6.3.2" }, "funding": [ { @@ -6324,7 +6330,7 @@ "type": "tidelift" } ], - "time": "2023-04-21T08:48:44+00:00" + "time": "2023-07-26T17:39:03+00:00" }, { "name": "symfony/web-link", @@ -6411,20 +6417,21 @@ }, { "name": "symfony/yaml", - "version": "v6.3.0", + "version": "v6.3.3", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "a9a8337aa641ef2aa39c3e028f9107ec391e5927" + "reference": "e23292e8c07c85b971b44c1c4b87af52133e2add" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/a9a8337aa641ef2aa39c3e028f9107ec391e5927", - "reference": "a9a8337aa641ef2aa39c3e028f9107ec391e5927", + "url": "https://api.github.com/repos/symfony/yaml/zipball/e23292e8c07c85b971b44c1c4b87af52133e2add", + "reference": "e23292e8c07c85b971b44c1c4b87af52133e2add", "shasum": "" }, "require": { "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -6462,7 +6469,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v6.3.0" + "source": "https://github.com/symfony/yaml/tree/v6.3.3" }, "funding": [ { @@ -6478,20 +6485,20 @@ "type": "tidelift" } ], - "time": "2023-04-28T13:28:14+00:00" + "time": "2023-07-31T07:08:24+00:00" }, { "name": "twig/twig", - "version": "v3.6.1", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd" + "reference": "5cf942bbab3df42afa918caeba947f1b690af64b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd", - "reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/5cf942bbab3df42afa918caeba947f1b690af64b", + "reference": "5cf942bbab3df42afa918caeba947f1b690af64b", "shasum": "" }, "require": { @@ -6537,7 +6544,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.6.1" + "source": "https://github.com/twigphp/Twig/tree/v3.7.0" }, "funding": [ { @@ -6549,7 +6556,7 @@ "type": "tidelift" } ], - "time": "2023-06-08T12:52:13+00:00" + "time": "2023-07-26T07:16:09+00:00" }, { "name": "web-token/jwt-checker", @@ -6846,16 +6853,16 @@ }, { "name": "webonyx/graphql-php", - "version": "v15.5.1", + "version": "v15.6.0", "source": { "type": "git", "url": "https://github.com/webonyx/graphql-php.git", - "reference": "b305633164a48947e22d53b6b15fcb98613c6592" + "reference": "d950e2b542ee5c092c5d1375240b561282c06af1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/b305633164a48947e22d53b6b15fcb98613c6592", - "reference": "b305633164a48947e22d53b6b15fcb98613c6592", + "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/d950e2b542ee5c092c5d1375240b561282c06af1", + "reference": "d950e2b542ee5c092c5d1375240b561282c06af1", "shasum": "" }, "require": { @@ -6872,14 +6879,14 @@ "nyholm/psr7": "^1.5", "phpbench/phpbench": "^1.2", "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "1.10.19", + "phpstan/phpstan": "1.10.26", "phpstan/phpstan-phpunit": "1.3.13", "phpstan/phpstan-strict-rules": "1.5.1", "phpunit/phpunit": "^9.5 || ^10", "psr/http-message": "^1 || ^2", "react/http": "^1.6", "react/promise": "^2.9", - "rector/rector": "^0.17.0", + "rector/rector": "^0.17", "symfony/polyfill-php81": "^1.23", "symfony/var-exporter": "^5 || ^6", "thecodingmachine/safe": "^1.3 || ^2" @@ -6907,7 +6914,7 @@ ], "support": { "issues": "https://github.com/webonyx/graphql-php/issues", - "source": "https://github.com/webonyx/graphql-php/tree/v15.5.1" + "source": "https://github.com/webonyx/graphql-php/tree/v15.6.0" }, "funding": [ { @@ -6915,7 +6922,7 @@ "type": "open_collective" } ], - "time": "2023-06-16T12:19:23+00:00" + "time": "2023-08-04T09:43:22+00:00" }, { "name": "willdurand/negotiation", @@ -7942,16 +7949,16 @@ }, { "name": "masterminds/html5", - "version": "2.8.0", + "version": "2.8.1", "source": { "type": "git", "url": "https://github.com/Masterminds/html5-php.git", - "reference": "3c5d5a56d56f48a1ca08a0670f0f80c1dad368f3" + "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/3c5d5a56d56f48a1ca08a0670f0f80c1dad368f3", - "reference": "3c5d5a56d56f48a1ca08a0670f0f80c1dad368f3", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f47dcf3c70c584de14f21143c55d9939631bc6cf", + "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf", "shasum": "" }, "require": { @@ -8003,9 +8010,9 @@ ], "support": { "issues": "https://github.com/Masterminds/html5-php/issues", - "source": "https://github.com/Masterminds/html5-php/tree/2.8.0" + "source": "https://github.com/Masterminds/html5-php/tree/2.8.1" }, - "time": "2023-04-26T07:27:39+00:00" + "time": "2023-05-10T11:58:31+00:00" }, { "name": "netresearch/jsonmapper", @@ -8403,16 +8410,16 @@ }, { "name": "symfony/browser-kit", - "version": "v6.3.0", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "0eb7228e7c435169e65c911ba8d107d56d850049" + "reference": "ca4a988488f61ac18f8f845445eabdd36f89aa8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/0eb7228e7c435169e65c911ba8d107d56d850049", - "reference": "0eb7228e7c435169e65c911ba8d107d56d850049", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/ca4a988488f61ac18f8f845445eabdd36f89aa8d", + "reference": "ca4a988488f61ac18f8f845445eabdd36f89aa8d", "shasum": "" }, "require": { @@ -8451,7 +8458,7 @@ "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/browser-kit/tree/v6.3.0" + "source": "https://github.com/symfony/browser-kit/tree/v6.3.2" }, "funding": [ { @@ -8467,20 +8474,20 @@ "type": "tidelift" } ], - "time": "2023-04-25T10:46:17+00:00" + "time": "2023-07-06T06:56:43+00:00" }, { "name": "symfony/css-selector", - "version": "v6.3.0", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "88453e64cd86c5b60e8d2fb2c6f953bbc353ffbf" + "reference": "883d961421ab1709877c10ac99451632a3d6fa57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/88453e64cd86c5b60e8d2fb2c6f953bbc353ffbf", - "reference": "88453e64cd86c5b60e8d2fb2c6f953bbc353ffbf", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/883d961421ab1709877c10ac99451632a3d6fa57", + "reference": "883d961421ab1709877c10ac99451632a3d6fa57", "shasum": "" }, "require": { @@ -8516,7 +8523,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.3.0" + "source": "https://github.com/symfony/css-selector/tree/v6.3.2" }, "funding": [ { @@ -8532,20 +8539,20 @@ "type": "tidelift" } ], - "time": "2023-03-20T16:43:42+00:00" + "time": "2023-07-12T16:00:22+00:00" }, { "name": "symfony/debug-bundle", - "version": "v6.3.0", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/debug-bundle.git", - "reference": "02fe831f7cdd472c561116189bcc30d0759665e7" + "reference": "3f04a578e1a9f1d7da84a87b690c03123e5d8c31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug-bundle/zipball/02fe831f7cdd472c561116189bcc30d0759665e7", - "reference": "02fe831f7cdd472c561116189bcc30d0759665e7", + "url": "https://api.github.com/repos/symfony/debug-bundle/zipball/3f04a578e1a9f1d7da84a87b690c03123e5d8c31", + "reference": "3f04a578e1a9f1d7da84a87b690c03123e5d8c31", "shasum": "" }, "require": { @@ -8590,7 +8597,7 @@ "description": "Provides a tight integration of the Symfony VarDumper component and the ServerLogCommand from MonologBridge into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/debug-bundle/tree/v6.3.0" + "source": "https://github.com/symfony/debug-bundle/tree/v6.3.2" }, "funding": [ { @@ -8606,7 +8613,7 @@ "type": "tidelift" } ], - "time": "2023-05-25T12:58:06+00:00" + "time": "2023-07-13T14:29:38+00:00" }, { "name": "symfony/dom-crawler", @@ -8771,16 +8778,16 @@ }, { "name": "symfony/phpunit-bridge", - "version": "v6.3.1", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "0b0bf59b0d9bd1422145a123a67fb12af546ef0d" + "reference": "e020e1efbd1b42cb670fcd7d19a25abbddba035d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/0b0bf59b0d9bd1422145a123a67fb12af546ef0d", - "reference": "0b0bf59b0d9bd1422145a123a67fb12af546ef0d", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/e020e1efbd1b42cb670fcd7d19a25abbddba035d", + "reference": "e020e1efbd1b42cb670fcd7d19a25abbddba035d", "shasum": "" }, "require": { @@ -8832,7 +8839,7 @@ "description": "Provides utilities for PHPUnit, especially user deprecation notices management", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v6.3.1" + "source": "https://github.com/symfony/phpunit-bridge/tree/v6.3.2" }, "funding": [ { @@ -8848,20 +8855,20 @@ "type": "tidelift" } ], - "time": "2023-06-23T13:25:16+00:00" + "time": "2023-07-12T16:00:22+00:00" }, { "name": "symfony/process", - "version": "v6.3.0", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628" + "reference": "c5ce962db0d9b6e80247ca5eb9af6472bd4d7b5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/8741e3ed7fe2e91ec099e02446fb86667a0f1628", - "reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628", + "url": "https://api.github.com/repos/symfony/process/zipball/c5ce962db0d9b6e80247ca5eb9af6472bd4d7b5d", + "reference": "c5ce962db0d9b6e80247ca5eb9af6472bd4d7b5d", "shasum": "" }, "require": { @@ -8893,7 +8900,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.3.0" + "source": "https://github.com/symfony/process/tree/v6.3.2" }, "funding": [ { @@ -8909,20 +8916,20 @@ "type": "tidelift" } ], - "time": "2023-05-19T08:06:44+00:00" + "time": "2023-07-12T16:00:22+00:00" }, { "name": "symfony/web-profiler-bundle", - "version": "v6.3.1", + "version": "v6.3.2", "source": { "type": "git", "url": "https://github.com/symfony/web-profiler-bundle.git", - "reference": "4a6cf8cb093e720c7ae4d55b1af848ce7e9abd36" + "reference": "6101b5ab7857c373d237e121f9060c68b32e1373" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/4a6cf8cb093e720c7ae4d55b1af848ce7e9abd36", - "reference": "4a6cf8cb093e720c7ae4d55b1af848ce7e9abd36", + "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/6101b5ab7857c373d237e121f9060c68b32e1373", + "reference": "6101b5ab7857c373d237e121f9060c68b32e1373", "shasum": "" }, "require": { @@ -8974,7 +8981,7 @@ "dev" ], "support": { - "source": "https://github.com/symfony/web-profiler-bundle/tree/v6.3.1" + "source": "https://github.com/symfony/web-profiler-bundle/tree/v6.3.2" }, "funding": [ { @@ -8990,7 +8997,7 @@ "type": "tidelift" } ], - "time": "2023-06-24T11:51:27+00:00" + "time": "2023-07-19T20:17:28+00:00" }, { "name": "vimeo/psalm", @@ -9326,16 +9333,16 @@ }, { "name": "zenstruck/foundry", - "version": "v1.34.0", + "version": "v1.34.1", "source": { "type": "git", "url": "https://github.com/zenstruck/foundry.git", - "reference": "0b29af2f701da8a6a15c5daa4dbbe636a7720baf" + "reference": "65c916e31944fa9738eaf82063c879a0154e52fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zenstruck/foundry/zipball/0b29af2f701da8a6a15c5daa4dbbe636a7720baf", - "reference": "0b29af2f701da8a6a15c5daa4dbbe636a7720baf", + "url": "https://api.github.com/repos/zenstruck/foundry/zipball/65c916e31944fa9738eaf82063c879a0154e52fa", + "reference": "65c916e31944fa9738eaf82063c879a0154e52fa", "shasum": "" }, "require": { @@ -9403,7 +9410,7 @@ ], "support": { "issues": "https://github.com/zenstruck/foundry/issues", - "source": "https://github.com/zenstruck/foundry/tree/v1.34.0" + "source": "https://github.com/zenstruck/foundry/tree/v1.34.1" }, "funding": [ { @@ -9411,7 +9418,7 @@ "type": "github" } ], - "time": "2023-07-12T07:19:34+00:00" + "time": "2023-08-04T13:05:27+00:00" } ], "aliases": [], diff --git a/api/config/packages/api_platform.yaml b/api/config/packages/api_platform.yaml index 5fe081e2d..7552c905f 100644 --- a/api/config/packages/api_platform.yaml +++ b/api/config/packages/api_platform.yaml @@ -13,8 +13,6 @@ api_platform: stateless: true cache_headers: vary: ['Content-Type', 'Authorization', 'Origin'] - extra_properties: - standard_put: true oauth: enabled: true clientId: '%env(OIDC_SWAGGER_CLIENT_ID)%' diff --git a/api/src/DataFixtures/Story/DefaultBookStory.php b/api/src/DataFixtures/Story/DefaultBookStory.php index 501e18a08..991c5a589 100644 --- a/api/src/DataFixtures/Story/DefaultBookStory.php +++ b/api/src/DataFixtures/Story/DefaultBookStory.php @@ -24,7 +24,7 @@ public function build(): void BookFactory::createOne([ 'condition' => BookCondition::NewCondition, 'book' => 'https://openlibrary.org/books/OL6095440M.json', - 'title' => 'Fondation', + 'title' => 'Foundation', 'author' => 'Isaac Asimov', ]); diff --git a/api/src/Entity/Book.php b/api/src/Entity/Book.php index 57c2a9d17..00f796c46 100644 --- a/api/src/Entity/Book.php +++ b/api/src/Entity/Book.php @@ -5,6 +5,7 @@ namespace App\Entity; use ApiPlatform\Doctrine\Common\Filter\SearchFilterInterface; +use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; use ApiPlatform\Metadata\ApiFilter; use ApiPlatform\Metadata\ApiProperty; @@ -91,7 +92,7 @@ class Book )] #[Assert\NotBlank(allowNull: false)] #[Assert\Url(protocols: ['https'])] - #[Assert\Regex(pattern: '/^https:\/\/openlibrary.org\/books\/OL\d{8}M\.json$/')] + #[Assert\Regex(pattern: '/^https:\/\/openlibrary.org\/books\/OL\d+M\.json$/')] #[Groups(groups: ['Book:read', 'Book:read:admin', 'Bookmark:read', 'Book:write'])] #[ORM\Column(unique: true)] public ?string $book = null; @@ -99,10 +100,11 @@ class Book /** * @see https://schema.org/name */ + #[ApiFilter(OrderFilter::class)] #[ApiFilter(SearchFilter::class, strategy: 'i'.SearchFilterInterface::STRATEGY_PARTIAL)] #[ApiProperty( types: ['https://schema.org/name'], - example: 'Fondation' + example: 'Foundation' )] #[Groups(groups: ['Book:read', 'Book:read:admin', 'Bookmark:read', 'Review:read:admin'])] #[ORM\Column(type: Types::TEXT)] diff --git a/api/tests/Api/Admin/BookTest.php b/api/tests/Api/Admin/BookTest.php index ea516b7fa..a0f584360 100644 --- a/api/tests/Api/Admin/BookTest.php +++ b/api/tests/Api/Admin/BookTest.php @@ -115,6 +115,26 @@ public function getUrls(): iterable ]; } + public function testAsAdminUserICanGetACollectionOfBooksOrderedByTitle(): void + { + BookFactory::createOne(['title' => 'Foundation']); + BookFactory::createOne(['title' => 'Nemesis']); + BookFactory::createOne(['title' => 'I, Robot']); + + $token = self::getContainer()->get(OidcTokenGenerator::class)->generate([ + 'email' => UserFactory::createOneAdmin()->email, + ]); + + $response = $this->client->request('GET', '/admin/books?order[title]=asc', ['auth_bearer' => $token]); + + self::assertResponseIsSuccessful(); + self::assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + self::assertEquals('Foundation', $response->toArray()['hydra:member'][0]['title']); + self::assertEquals('I, Robot', $response->toArray()['hydra:member'][1]['title']); + self::assertEquals('Nemesis', $response->toArray()['hydra:member'][2]['title']); + self::assertMatchesJsonSchema(file_get_contents(__DIR__.'/schemas/Book/collection.json')); + } + /** * @dataProvider getAllUsers */ @@ -318,14 +338,11 @@ public function testAsNonAdminUserICannotUpdateBook(int $expectedCode, string $h $options['auth_bearer'] = $token; } - $this->client->request('PATCH', '/admin/books/'.$book->getId(), $options + [ + $this->client->request('PUT', '/admin/books/'.$book->getId(), $options + [ 'json' => [ 'book' => 'https://openlibrary.org/books/OL28346544M.json', 'condition' => BookCondition::NewCondition->value, ], - 'headers' => [ - 'Content-Type' => 'application/merge-patch+json', - ], ]); self::assertResponseStatusCodeSame($expectedCode); @@ -346,14 +363,11 @@ public function testAsAdminUserICannotUpdateAnInvalidBook(): void 'email' => UserFactory::createOneAdmin()->email, ]); - $this->client->request('PATCH', '/admin/books/invalid', [ + $this->client->request('PUT', '/admin/books/invalid', [ 'auth_bearer' => $token, 'json' => [ 'condition' => BookCondition::DamagedCondition->value, ], - 'headers' => [ - 'Content-Type' => 'application/merge-patch+json', - ], ]); self::assertResponseStatusCodeSame(Response::HTTP_NOT_FOUND); @@ -364,19 +378,15 @@ public function testAsAdminUserICannotUpdateAnInvalidBook(): void */ public function testAsAdminUserICannotUpdateABookWithInvalidData(array $data, array $violations): void { - $this->markTestIncomplete('Invalid identifier value or configuration.'); BookFactory::createOne(); $token = self::getContainer()->get(OidcTokenGenerator::class)->generate([ 'email' => UserFactory::createOneAdmin()->email, ]); - $this->client->request('PATCH', '/admin/books/invalid', [ + $this->client->request('PUT', '/admin/books/invalid', [ 'auth_bearer' => $token, 'json' => $data, - 'headers' => [ - 'Content-Type' => 'application/merge-patch+json', - ], ]); self::assertResponseStatusCodeSame(Response::HTTP_UNPROCESSABLE_ENTITY); @@ -402,14 +412,11 @@ public function testAsAdminUserICanUpdateABook(): void 'email' => UserFactory::createOneAdmin()->email, ]); - $this->client->request('PATCH', '/admin/books/'.$book->getId(), [ + $this->client->request('PUT', '/admin/books/'.$book->getId(), [ 'auth_bearer' => $token, 'json' => [ 'condition' => BookCondition::DamagedCondition->value, ], - 'headers' => [ - 'Content-Type' => 'application/merge-patch+json', - ], ]); self::assertResponseStatusCodeSame(Response::HTTP_OK); diff --git a/api/tests/Api/Admin/ReviewTest.php b/api/tests/Api/Admin/ReviewTest.php index 30ae43259..81afb8068 100644 --- a/api/tests/Api/Admin/ReviewTest.php +++ b/api/tests/Api/Admin/ReviewTest.php @@ -229,15 +229,12 @@ public function testAsAdminUserICannotUpdateAnInvalidReview(): void 'email' => UserFactory::createOneAdmin()->email, ]); - $this->client->request('PATCH', '/admin/reviews/invalid', [ + $this->client->request('PUT', '/admin/reviews/invalid', [ 'auth_bearer' => $token, 'json' => [ 'body' => 'Very good book!', 'rating' => 5, ], - 'headers' => [ - 'Content-Type' => 'application/merge-patch+json', - ], ]); self::assertResponseStatusCodeSame(Response::HTTP_NOT_FOUND); @@ -251,15 +248,12 @@ public function testAsAdminUserICanUpdateAReview(): void 'email' => UserFactory::createOneAdmin()->email, ]); - $this->client->request('PATCH', '/admin/reviews/'.$review->getId(), [ + $this->client->request('PUT', '/admin/reviews/'.$review->getId(), [ 'auth_bearer' => $token, 'json' => [ 'body' => 'Very good book!', 'rating' => 5, ], - 'headers' => [ - 'Content-Type' => 'application/merge-patch+json', - ], ]); self::assertResponseStatusCodeSame(Response::HTTP_OK); diff --git a/api/tests/Api/BookTest.php b/api/tests/Api/BookTest.php index 05e47da2f..922a7d7fe 100644 --- a/api/tests/Api/BookTest.php +++ b/api/tests/Api/BookTest.php @@ -83,6 +83,22 @@ public function getUrls(): iterable ]; } + public function testAsAdminUserICanGetACollectionOfBooksOrderedByTitle(): void + { + BookFactory::createOne(['title' => 'Foundation']); + BookFactory::createOne(['title' => 'Nemesis']); + BookFactory::createOne(['title' => 'I, Robot']); + + $response = $this->client->request('GET', '/books?order[title]=asc'); + + self::assertResponseIsSuccessful(); + self::assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); + self::assertEquals('Foundation', $response->toArray()['hydra:member'][0]['title']); + self::assertEquals('I, Robot', $response->toArray()['hydra:member'][1]['title']); + self::assertEquals('Nemesis', $response->toArray()['hydra:member'][2]['title']); + self::assertMatchesJsonSchema(file_get_contents(__DIR__.'/schemas/Book/collection.json')); + } + public function testAsAnonymousICannotGetAnInvalidBook(): void { BookFactory::createOne(); diff --git a/api/tests/Api/BookmarkTest.php b/api/tests/Api/BookmarkTest.php index 377d9a6f3..85c24361a 100644 --- a/api/tests/Api/BookmarkTest.php +++ b/api/tests/Api/BookmarkTest.php @@ -88,7 +88,6 @@ public function testAsAnonymousICannotCreateABookmark(): void public function testAsAUserICannotCreateABookmarkWithInvalidData(): void { - $this->markTestIncomplete('Identifier "id" could not be transformed.'); $token = self::getContainer()->get(OidcTokenGenerator::class)->generate([ 'email' => UserFactory::createOne()->email, ]); diff --git a/api/tests/Api/ReviewTest.php b/api/tests/Api/ReviewTest.php index 35ed57476..0feb333c2 100644 --- a/api/tests/Api/ReviewTest.php +++ b/api/tests/Api/ReviewTest.php @@ -190,24 +190,25 @@ public function getInvalidData(): iterable ], ], ]; - // yield [ - // [ - // 'book' => 'invalid book', - // 'body' => 'Very good book!', - // 'rating' => 5, - // ], - // [ - // [ - // 'propertyPath' => 'book', - // 'message' => 'This value is not a valid URL.', - // ], - // ] - // ]; + yield [ + [ + 'book' => 'invalid book', + 'body' => 'Very good book!', + 'rating' => 5, + ], + [ + [ + 'propertyPath' => 'book', + 'message' => 'This value is not a valid URL.', + ], + ] + ]; } public function testAsAUserICanAddAReviewOnABook(): void { - $book = BookFactory::createOne(); + $book = BookFactory::createOne()->disableAutoRefresh(); + ReviewFactory::createMany(5, ['book' => $book]); $user = UserFactory::createOne(); $token = self::getContainer()->get(OidcTokenGenerator::class)->generate([ @@ -234,6 +235,9 @@ public function testAsAUserICanAddAReviewOnABook(): void 'rating' => 5, ]); self::assertMatchesJsonSchema(file_get_contents(__DIR__.'/schemas/Review/item.json')); + // if I add a review on a book with reviews, it doesn't erase the existing reviews + $reviews = self::getContainer()->get(ReviewRepository::class)->findBy(['book' => $book]); + self::assertCount(6, $reviews); } public function testAsAnonymousICannotGetAnInvalidReview(): void diff --git a/pwa/components/admin/book/BookInput.tsx b/pwa/components/admin/book/BookInput.tsx index 18b82412d..3a61c16c6 100644 --- a/pwa/components/admin/book/BookInput.tsx +++ b/pwa/components/admin/book/BookInput.tsx @@ -1,4 +1,4 @@ -import { forwardRef, SyntheticEvent, useRef, useState } from "react"; +import { SyntheticEvent, useMemo, useRef, useState } from "react"; import Autocomplete from "@mui/material/Autocomplete"; import { debounce } from "@mui/material"; import { TextInput, type TextInputProps, useInput } from "react-admin"; @@ -9,14 +9,14 @@ import { Search } from "@/types/OpenLibrary/Search"; import { SearchDoc } from "@/types/OpenLibrary/SearchDoc"; interface Result { - title: string - author: string - value: string + title: string; + author: string; + value: string; } interface BookInputProps extends TextInputProps { - title?: string - author?: string + title?: string; + author?: string; } const fetchOpenLibrarySearch = async (query: string, signal?: AbortSignal | undefined): Promise> => { @@ -54,16 +54,14 @@ const fetchOpenLibrarySearch = async (query: string, signal?: AbortSignal | unde }; export const BookInput = (props: BookInputProps) => { - const { field } = useInput(props); + const { field: { ref, ...field} } = useInput(props); const title = useWatch({ name: "title" }); const author = useWatch({ name: "author" }); const controller = useRef(); const [searchQuery, setSearchQuery] = useState(""); - const initialData = !!title && !!author && !!field.value ? { - title: title, - author: author, - value: field.value, - } : undefined; + const [value, setValue] = useState( + !!title && !!author && !!field.value ? { title: title, author: author, value: field.value } : undefined + ); const { isLoading, data, isFetched } = useQuery( ["search", searchQuery], async () => { @@ -78,15 +76,22 @@ export const BookInput = (props: BookInputProps) => { enabled: !!searchQuery, } ); + const onInputChange = useMemo(() => + debounce((event: SyntheticEvent, value: string) => setSearchQuery(value), 400), + [] + ); + const onChange = (event: SyntheticEvent, value: Result | null | undefined) => { + field.onChange(value?.value); + setValue(value); + }; return option.value === value.value} - // @ts-ignore - options={!isFetched ? [initialData] : data} - onChange={(event: SyntheticEvent, value: Result | null) => field.value = value?.value ?? ""} - onInputChange={debounce((event: SyntheticEvent, value: string) => setSearchQuery(value), 400)} - getOptionLabel={(option: Result) => `${option.title} - ${option.author}`} + value={value} + options={!isFetched ? (!!value ? [value] : []) : (data ?? [])} + isOptionEqualToValue={(option, val) => option?.value === (val?.value || value?.value)} + onChange={onChange} + onInputChange={onInputChange} + getOptionLabel={(option: Result | undefined) => !!option ? `${option.title} - ${option.author}` : "No options"} style={{ width: 500 }} loading={isLoading} renderInput={(params) => ( diff --git a/pwa/components/admin/book/ConditionInput.tsx b/pwa/components/admin/book/ConditionInput.tsx index 4b60faded..90f7c0b53 100644 --- a/pwa/components/admin/book/ConditionInput.tsx +++ b/pwa/components/admin/book/ConditionInput.tsx @@ -2,10 +2,9 @@ import { SelectInput, SelectInputProps } from "react-admin"; export const ConditionInput = (props: SelectInputProps) => ( ); diff --git a/pwa/components/admin/book/List.tsx b/pwa/components/admin/book/List.tsx index 517ef37f2..ceda3cb88 100644 --- a/pwa/components/admin/book/List.tsx +++ b/pwa/components/admin/book/List.tsx @@ -20,8 +20,7 @@ import { ConditionInput } from "@/components/admin/book/ConditionInput"; const ConditionField = (props: UseRecordContextParams) => { const record = useRecordContext(props); - // todo translate condition - return !!record && !!record.condition ? {record.condition.replace("https://schema.org/", "")} : null; + return !!record && !!record.condition ? {record.condition.replace(/https:\/\/schema\.org\/(.+)Condition$/, "$1")} : null; }; ConditionField.defaultProps = { label: "Condition" }; diff --git a/pwa/components/book/Filters.tsx b/pwa/components/book/Filters.tsx index 01cc202d7..da98db165 100644 --- a/pwa/components/book/Filters.tsx +++ b/pwa/components/book/Filters.tsx @@ -1,7 +1,7 @@ import { Formik } from "formik"; import { type FunctionComponent } from "react"; import { type UseMutationResult } from "react-query"; -import { Checkbox, FormControlLabel, FormGroup, TextField, Typography } from "@mui/material"; +import { debounce, Checkbox, FormControlLabel, FormGroup, TextField, Typography } from "@mui/material"; import { type FiltersProps } from "@/utils/book"; import { type FetchError, type FetchResponse } from "@/utils/dataAccess"; @@ -9,8 +9,8 @@ import { type PagedCollection } from "@/types/collection"; import { type Book } from "@/types/Book"; interface Props { - filters: FiltersProps | undefined - mutation: UseMutationResult>> + filters: FiltersProps | undefined; + mutation: UseMutationResult>>; } export const Filters: FunctionComponent = ({ filters, mutation }) => ( @@ -51,29 +51,25 @@ export const Filters: FunctionComponent = ({ filters, mutation }) => ( }) => (
- Author } control={ { + variant="standard" className="w-full" onChange={(e) => { handleChange(e); - // todo use debounce - submitForm(); + debounce(submitForm, 1000)(); }} /> }/> - Title } control={ { + variant="standard" className="w-full" onChange={(e) => { handleChange(e); - // todo use debounce - submitForm(); + debounce(submitForm, 1000)(); }} /> }/> @@ -87,7 +83,6 @@ export const Filters: FunctionComponent = ({ filters, mutation }) => ( value="https://schema.org/NewCondition" onChange={(e) => { handleChange(e); - // todo use debounce submitForm(); }} /> @@ -98,7 +93,6 @@ export const Filters: FunctionComponent = ({ filters, mutation }) => ( value="https://schema.org/DamagedCondition" onChange={(e) => { handleChange(e); - // todo use debounce submitForm(); }} /> @@ -109,7 +103,6 @@ export const Filters: FunctionComponent = ({ filters, mutation }) => ( value="https://schema.org/RefurbishedCondition" onChange={(e) => { handleChange(e); - // todo use debounce submitForm(); }} /> @@ -120,7 +113,6 @@ export const Filters: FunctionComponent = ({ filters, mutation }) => ( value="https://schema.org/UsedCondition" onChange={(e) => { handleChange(e); - // todo use debounce submitForm(); }} /> diff --git a/pwa/components/book/Item.tsx b/pwa/components/book/Item.tsx index 24a4f45ab..b7bd1bc1a 100644 --- a/pwa/components/book/Item.tsx +++ b/pwa/components/book/Item.tsx @@ -1,36 +1,21 @@ import Image from "next/image"; import Link from "next/link"; -import { useSession } from "next-auth/react"; -import { type FunctionComponent, useEffect, useState } from "react"; +import { type FunctionComponent } from "react"; import Rating from "@mui/material/Rating"; import { type Book } from "@/types/Book"; import { getItemPath } from "@/utils/dataAccess"; -import { populateBook } from "@/utils/book"; +import { useOpenLibraryBook } from "@/utils/book"; import { Loading } from "@/components/common/Loading"; interface Props { - book: Book + book: Book; } export const Item: FunctionComponent = ({ book }) => { - const [data, setData] = useState(); - const { status } = useSession(); + const { data, isLoading } = useOpenLibraryBook(book); - useEffect(() => { - if (status === "loading") return; - - (async () => { - try { - const bookData = await populateBook(book); - setData(bookData); - } catch (error) { - console.error(error); - } - })() - }, [book, status]); - - if (!data) return ; + if (isLoading || !data) return ; return (
diff --git a/pwa/components/book/List.tsx b/pwa/components/book/List.tsx index dbf3fb4c6..288f9d6d5 100644 --- a/pwa/components/book/List.tsx +++ b/pwa/components/book/List.tsx @@ -3,6 +3,7 @@ import Head from "next/head"; import { useRouter } from "next/router"; import { useMutation } from "react-query"; import FilterListOutlinedIcon from "@mui/icons-material/FilterListOutlined"; +import { FormControl, InputLabel, MenuItem, Select } from "@mui/material"; import { Item } from "@/components/book/Item"; import { Filters } from "@/components/book/Filters"; @@ -14,15 +15,14 @@ import { type FetchError, type FetchResponse } from "@/utils/dataAccess"; import { useMercure } from "@/utils/mercure"; interface Props { - data: PagedCollection | null - hubURL: string | null - filters: FiltersProps - page: number + data: PagedCollection | null; + hubURL: string | null; + filters: FiltersProps; } const getPagePath = (page: number): string => `/books?page=${page}`; -export const List: NextPage = ({ data, hubURL, filters, page }) => { +export const List: NextPage = ({ data, hubURL, filters }) => { const collection = useMercure(data, hubURL); const router = useRouter(); @@ -52,24 +52,30 @@ export const List: NextPage = ({ data, hubURL, filters, page }) => {
{!!collection && !!collection["hydra:member"] && ( <> -

- - Sort by: - {/*todo move to filters form?*/} - - +

+
+ Sort by: + +
{collection["hydra:totalItems"]} book(s) found -

+
{collection["hydra:member"].length !== 0 && collection["hydra:member"].map((book) => ( ))}
- + ) || (

No books found.

diff --git a/pwa/components/book/Show.tsx b/pwa/components/book/Show.tsx index e39274bfb..b83f5233b 100644 --- a/pwa/components/book/Show.tsx +++ b/pwa/components/book/Show.tsx @@ -14,20 +14,20 @@ import FavoriteIcon from "@mui/icons-material/Favorite"; import { type Book } from "@/types/Book"; import { useMercure } from "@/utils/mercure"; import { List as Reviews } from "@/components/review/List"; -import { populateBook } from "@/utils/book"; +import { useOpenLibraryBook } from "@/utils/book"; import { fetch, type FetchError, type FetchResponse } from "@/utils/dataAccess"; import { type Bookmark } from "@/types/Bookmark"; import { type PagedCollection } from "@/types/collection"; import { Loading } from "@/components/common/Loading"; interface Props { - data: Book - hubURL: string | null - page: number + data: Book; + hubURL: string | null; + page: number; } interface BookmarkProps { - book: string | undefined + book: string | undefined; } const saveBookmark = async (values: BookmarkProps) => @@ -41,8 +41,8 @@ const deleteBookmark = async (id: string) => export const Show: NextPage = ({ data, hubURL, page }) => { const { data: session, status } = useSession(); - const [book, setBook] = useState(); const [bookmark, setBookmark] = useState(); + const { data: book, isLoading } = useOpenLibraryBook(data); const item = useMercure(data, hubURL); const bookmarkMutation = useMutation< @@ -60,15 +60,6 @@ export const Show: NextPage = ({ data, hubURL, page }) => { return saveBookmark(data); }); - useEffect(() => { - if (status === "loading") return; - - (async () => { - const book = await populateBook(data); - setBook(book); - })() - }, [data, status]); - useEffect(() => { if (status === "loading") return; @@ -101,7 +92,7 @@ export const Show: NextPage = ({ data, hubURL, page }) => { {item["title"]}
- {!!book && ( + {!!book && !isLoading && ( <>
@@ -120,8 +111,7 @@ export const Show: NextPage = ({ data, hubURL, page }) => {

- {/*todo translate condition*/} - Condition: {book["condition"].replace("https://schema.org/", "")} + Condition: {book["condition"].replace(/https:\/\/schema\.org\/(.+)Condition$/, "$1")} {!!book["publicationDate"] && ( | Published on {book["publicationDate"]} )} diff --git a/pwa/components/bookmark/List.tsx b/pwa/components/bookmark/List.tsx index 2224bfc51..5b4f8029b 100644 --- a/pwa/components/bookmark/List.tsx +++ b/pwa/components/bookmark/List.tsx @@ -8,9 +8,9 @@ import { type PagedCollection } from "@/types/collection"; import { useMercure } from "@/utils/mercure"; interface Props { - data: PagedCollection | null - hubURL: string | null - page: number + data: PagedCollection | null; + hubURL: string | null; + page: number; } const getPagePath = (page: number): string => `/bookmarks?page=${page}`; diff --git a/pwa/components/common/Error.tsx b/pwa/components/common/Error.tsx index 17cbba2ad..71fb7db28 100644 --- a/pwa/components/common/Error.tsx +++ b/pwa/components/common/Error.tsx @@ -1,5 +1,5 @@ interface Props { - message: string + message: string; } export const Error = ({ message }: Props) => ( diff --git a/pwa/components/common/Pagination.tsx b/pwa/components/common/Pagination.tsx index e4f33241b..b40bfb117 100644 --- a/pwa/components/common/Pagination.tsx +++ b/pwa/components/common/Pagination.tsx @@ -8,7 +8,7 @@ import { parsePage } from "@/utils/dataAccess"; interface Props { collection: PagedCollection; getPagePath: (page: number) => string; - currentPage: number; + currentPage: number | undefined; } export const Pagination = ({ collection, getPagePath, currentPage }: Props) => { diff --git a/pwa/components/review/Form.tsx b/pwa/components/review/Form.tsx index 82f275e28..557b5d4c2 100644 --- a/pwa/components/review/Form.tsx +++ b/pwa/components/review/Form.tsx @@ -9,10 +9,10 @@ import { type Book } from "@/types/Book"; import { type Review } from "@/types/Review"; interface Props { - book: Book - onSuccess?: (review: Review) => void, - review?: Review - username: string + book: Book; + onSuccess?: (review: Review) => void; + review?: Review; + username: string; } export const Form: FunctionComponent = ({ book, onSuccess, review, username }) => { diff --git a/pwa/components/review/Item.tsx b/pwa/components/review/Item.tsx index 0a168f31e..315837b8d 100644 --- a/pwa/components/review/Item.tsx +++ b/pwa/components/review/Item.tsx @@ -9,13 +9,13 @@ import { fetch, type FetchError, type FetchResponse } from "@/utils/dataAccess"; import { Form } from "@/components/review/Form"; interface Props { - review: Review - onDelete?: (review: Review) => void - onEdit?: (review: Review) => void + review: Review; + onDelete?: (review: Review) => void; + onEdit?: (review: Review) => void; } interface DeleteParams { - id: string + id: string; } const deleteReview = async (id: string) => diff --git a/pwa/components/review/List.tsx b/pwa/components/review/List.tsx index 7f4574757..ad34cd8c3 100644 --- a/pwa/components/review/List.tsx +++ b/pwa/components/review/List.tsx @@ -13,8 +13,8 @@ import { Form } from "@/components/review/Form"; import { Loading } from "@/components/common/Loading"; interface Props { - book: Book - page: number + book: Book; + page: number; } export const List: FunctionComponent = ({ book, page }) => { @@ -27,7 +27,6 @@ export const List: FunctionComponent = ({ book, page }) => { useEffect(() => { if (status === "loading") return; - // todo call is done twice (async () => { try { const response: FetchResponse> | undefined = await fetch(`${book["reviews"]}?itemsPerPage=5&page=${page}`); diff --git a/pwa/pages/books/index.tsx b/pwa/pages/books/index.tsx index a1bb737f0..b9cb64723 100644 --- a/pwa/pages/books/index.tsx +++ b/pwa/pages/books/index.tsx @@ -9,12 +9,13 @@ import { type FiltersProps, buildUriFromFilters } from "@/utils/book"; export const getServerSideProps: GetServerSideProps<{ data: PagedCollection | null, hubURL: string | null, - page: number, filters: FiltersProps, }> = async ({ query }) => { - const page = query.page ? Number(query.page) : 1; - const filters: FiltersProps = {}; + if (query.page) { + // @ts-ignore + filters.page = query.page; + } if (query.author) { // @ts-ignore filters.author = query.author; @@ -31,19 +32,23 @@ export const getServerSideProps: GetServerSideProps<{ } else if (typeof query["condition[]"] === "object") { filters.condition = query["condition[]"]; } + if (query["order[title]"]) { + // @ts-ignore + filters.order = { title: query["order[title]"] }; + } try { - const response: FetchResponse> | undefined = await fetch(buildUriFromFilters("/books", filters, page)); + const response: FetchResponse> | undefined = await fetch(buildUriFromFilters("/books", filters)); if (!response?.data) { throw new Error('Unable to retrieve data from /books.'); } - return { props: { data: response.data, hubURL: response.hubURL, filters, page } }; + return { props: { data: response.data, hubURL: response.hubURL, filters } }; } catch (error) { console.error(error); } - return { props: { data: null, hubURL: null, filters, page } }; + return { props: { data: null, hubURL: null, filters } }; }; export default List; diff --git a/pwa/utils/book.ts b/pwa/utils/book.ts index be3ec4353..e4f473bec 100644 --- a/pwa/utils/book.ts +++ b/pwa/utils/book.ts @@ -1,84 +1,96 @@ import slugify from "slugify"; +import { useQuery } from "react-query"; + import { isItem } from "@/types/item"; import { type Book } from "@/types/Book"; import { type Book as OLBook } from "@/types/OpenLibrary/Book"; import { type Work } from "@/types/OpenLibrary/Work"; +interface OrderFilter { + title: string; +} + export interface FiltersProps { author?: string | undefined; title?: string | undefined; condition?: string | string[] | undefined; + order?: OrderFilter | undefined; + page?: number | undefined; } -export const populateBook = async (book: TData): Promise => { - if (!isItem(book)) { - console.error("Object sent is not in JSON-LD format."); - - return book; +export const useOpenLibraryBook = (data: TData) => { + if (!isItem(data)) { + throw new Error("Object sent is not in JSON-LD format."); } - book["id"] = book["@id"]?.replace("/books/", ""); - book["slug"] = slugify(`${book["title"]}-${book["author"]}`, { lower: true, trim: true, remove: /[*+~.(),;'"!:@]/g }); - book["condition"] = book["condition"].substring(19, book["condition"].length-9); + data["id"] = data["@id"]?.replace("/books/", ""); + data["slug"] = slugify(`${data["title"]}-${data["author"]}`, { lower: true, trim: true, remove: /[*+~.(),;'"!:@]/g }); + data["condition"] = data["condition"].substring(19, data["condition"].length-9); - const response = await fetch(book["book"], { method: "GET" }); - const data: OLBook = await response.json(); + return useQuery(data["book"], async () => { + const response = await fetch(data["book"], { method: "GET" }); + const book: OLBook = await response.json(); - if (typeof data["publish_date"] !== "undefined") { - book["publicationDate"] = data["publish_date"]; - } + if (typeof book["publish_date"] !== "undefined") { + data["publicationDate"] = book["publish_date"]; + } - if (typeof data["covers"] !== "undefined") { - book["images"] = { - medium: `https://covers.openlibrary.org/b/id/${data["covers"][0]}-M.jpg`, - large: `https://covers.openlibrary.org/b/id/${data["covers"][0]}-L.jpg`, - }; - } + if (typeof book["covers"] !== "undefined") { + data["images"] = { + medium: `https://covers.openlibrary.org/b/id/${book["covers"][0]}-M.jpg`, + large: `https://covers.openlibrary.org/b/id/${book["covers"][0]}-L.jpg`, + }; + } - if (typeof data["description"] !== "undefined") { - book["description"] = (typeof data["description"] === "string" ? data["description"] : data["description"]["value"]).replace( /(<([^>]+)>)/ig, ''); - } + if (typeof book["description"] !== "undefined") { + data["description"] = (typeof book["description"] === "string" ? book["description"] : book["description"]["value"]).replace( /(<([^>]+)>)/ig, ''); + } - // retrieve data from work if necessary - if ((!book["description"] || !book["images"]) && typeof data["works"] !== "undefined" && data["works"].length > 0) { - const response = await fetch(`https://openlibrary.org${data["works"][0]["key"]}.json`); - const work: Work = await response.json(); + // retrieve data from work if necessary + if ((!data["description"] || !data["images"]) && typeof book["works"] !== "undefined" && book["works"].length > 0) { + const response = await fetch(`https://openlibrary.org${book["works"][0]["key"]}.json`); + const work: Work = await response.json(); - if (!book["description"] && typeof work["description"] !== "undefined") { - book["description"] = (typeof work["description"] === "string" ? work["description"] : work["description"]["value"]).replace( /(<([^>]+)>)/ig, ''); - } + if (!data["description"] && typeof work["description"] !== "undefined") { + data["description"] = (typeof work["description"] === "string" ? work["description"] : work["description"]["value"]).replace( /(<([^>]+)>)/ig, ''); + } - if (!book["images"] && typeof work["covers"] !== "undefined") { - book["images"] = { - medium: `https://covers.openlibrary.org/b/id/${work["covers"][0]}-M.jpg`, - large: `https://covers.openlibrary.org/b/id/${work["covers"][0]}-L.jpg`, - }; + if (!data["images"] && typeof work["covers"] !== "undefined") { + data["images"] = { + medium: `https://covers.openlibrary.org/b/id/${work["covers"][0]}-M.jpg`, + large: `https://covers.openlibrary.org/b/id/${work["covers"][0]}-L.jpg`, + }; + } } - } - return book; + return data; + }); }; -export const buildUriFromFilters = (uri: string, filters: FiltersProps, page: number | undefined = undefined): string => { +// @ts-ignore +const filterObject = (object: object) => Object.fromEntries(Object.entries(object).filter(([, value]) => { + return typeof value === "object" ? Object.keys(value).length > 0 : value?.length > 0; +})); + +export const buildUriFromFilters = (uri: string, filters: FiltersProps): string => { // remove empty filters - filters = Object.fromEntries(Object.entries(filters).filter(([, value]) => value?.length > 0)); + filters = filterObject(filters); const params = new URLSearchParams(); Object.keys(filters).forEach((filter: string) => { // @ts-ignore const value = filters[filter]; - if (typeof value === "string") { - params.append(filter, value); - } else if (typeof value === "object") { + if (typeof value === "string" || typeof value === "number") { + params.append(filter, value.toString()); + } else if (Array.isArray(value)) { value.forEach((v: string) => { params.append(`${filter}[]`, v); - }) + }); + } else if (typeof value === "object") { + // @ts-ignore + Object.entries(value).forEach(([k, v]) => params.append(`${filter}[${k}]`, v)); } }); - if (page) { - // @ts-ignore - params.append("page", page); - } return `${uri}${params.size === 0 ? "" : `?${params.toString()}`}`; };