diff --git a/.github/workflows/integration-tests-callable.yaml b/.github/workflows/integration-tests-callable.yaml index acf4a329a..5664d3d56 100644 --- a/.github/workflows/integration-tests-callable.yaml +++ b/.github/workflows/integration-tests-callable.yaml @@ -30,7 +30,7 @@ jobs: coverage: none - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v4 with: path: ${{ env.COMPOSER_CACHE_DIR }} key: "${{ env.PROJECT_EDITION }}-${{ env.version }}-${{ github.sha }}" diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 4ba28536a..6b793736c 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -11,6 +11,7 @@ $configFactory = new InternalConfigFactory(); $configFactory->withRules([ 'declare_strict_types' => false, + 'phpdoc_no_empty_return' => false, ]); return $configFactory diff --git a/COPYRIGHT b/COPYRIGHT index 4e034771b..536d34977 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,4 +1,4 @@ -Copyright (C) 1999-2024 Ibexa AS (formerly eZ Systems AS). All rights reserved. +Copyright (C) 1999-2025 Ibexa AS (formerly eZ Systems AS). All rights reserved. This source code is available separately under the following licenses: diff --git a/LICENSE b/LICENSE index eb7622430..a31960f7c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 1999-2024 Ibexa AS (formerly eZ Systems AS). All rights reserved. +Copyright (C) 1999-2025 Ibexa AS (formerly eZ Systems AS). All rights reserved. This source code is available separately under the following licenses: diff --git a/README.md b/README.md index 2b0306602..56b6d22c7 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Source. See [the official documentation](https://doc.ibexa.co/) or [REST API Reference](https://ezsystems.github.io/ezplatform-rest-reference/) to learn more. ## COPYRIGHT -Copyright (C) 1999-2024 Ibexa AS (formerly eZ Systems AS). All rights reserved. +Copyright (C) 1999-2025 Ibexa AS (formerly eZ Systems AS). All rights reserved. ## LICENSE This source code is available separately under the following licenses: diff --git a/composer.json b/composer.json index 0f33fde9c..54fff7aa0 100644 --- a/composer.json +++ b/composer.json @@ -28,28 +28,28 @@ "ext-libxml": "*", "ext-simplexml": "*", "ext-xmlwriter": "*", - "api-platform/core": "dev-downgraded-deps", + "api-platform/core": "^3.0", "hautelook/templated-uri-bundle": "^3.4", "ibexa/core": "~5.0.x-dev", + "ibexa/polyfill-php82": "^1.0", "lexik/jwt-authentication-bundle": "^2.8", - "symfony/config": "^5.3", - "symfony/dependency-injection": "^5.3", - "symfony/event-dispatcher": "^5.3", - "symfony/expression-language": "^5.3", - "symfony/form": "^5.3", - "symfony/http-foundation": "^5.3", - "symfony/http-kernel": "^5.3", - "symfony/routing": "^5.3", - "symfony/security-bundle": "^5.3", - "symfony/security-csrf": "^5.3", - "symfony/yaml": "^5.3", + "symfony/config": "^6.4", + "symfony/dependency-injection": "^6.4", + "symfony/event-dispatcher": "^6.4", + "symfony/expression-language": "^6.4", + "symfony/form": "^6.4", + "symfony/http-foundation": "^6.4", + "symfony/http-kernel": "^6.4", + "symfony/routing": "^6.4", + "symfony/security-bundle": "^6.4", + "symfony/security-csrf": "^6.4", + "symfony/yaml": "^6.4", "webmozart/assert": "^1.11" }, "require-dev": { - "ibexa/ci-scripts": "^0.2@dev", "ibexa/code-style": "~2.0.0", "ibexa/doctrine-schema": "~5.0.x-dev", - "ibexa/test-core": "^0.1.x-dev", + "ibexa/test-core": "~5.0.x-dev", "justinrainbow/json-schema": "^5.2", "matthiasnoback/symfony-dependency-injection-test": "^4.1", "nyholm/psr7": "^1.1", @@ -58,8 +58,8 @@ "phpstan/phpstan-symfony": "^1.3", "phpstan/phpstan-webmozart-assert": "^1.2", "phpunit/phpunit": "^9.6", - "symfony/browser-kit": "^5.3", - "symfony/http-client": "^5.3" + "symfony/browser-kit": "^6.4", + "symfony/http-client": "^6.4" }, "config": { "allow-plugins": { diff --git a/phpstan-baseline-7.4.neon b/phpstan-baseline-7.4.neon deleted file mode 100644 index 1b506f523..000000000 --- a/phpstan-baseline-7.4.neon +++ /dev/null @@ -1,46 +0,0 @@ -parameters: - ignoreErrors: - - - message: "#^Parameter \\#1 \\$input of function array_slice expects array, iterable\\ given\\.$#" - count: 1 - path: src/lib/Server/Controller/Content.php - - - - message: "#^Parameter \\#2 \\$str of function explode expects string, string\\|null given\\.$#" - count: 1 - path: src/lib/Server/Controller/Content.php - - - - message: "#^Parameter \\#2 \\.\\.\\.\\$args of function array_merge expects array, iterable\\ given\\.$#" - count: 1 - path: src/lib/Server/Controller/ContentType.php - - - - message: "#^Parameter \\#1 \\$arr1 of function array_merge expects array, iterable\\ given\\.$#" - count: 1 - path: src/lib/Server/Controller/Role.php - - - - message: "#^Parameter \\#1 \\$input of function array_slice expects array, iterable\\ given\\.$#" - count: 1 - path: src/lib/Server/Controller/Role.php - - - - message: "#^Parameter \\#2 \\$error_type of function trigger_error expects int, string given\\.$#" - count: 4 - path: src/lib/Server/Controller/User.php - - - - message: "#^Parameter \\#1 \\$message of method Psr\\\\Log\\\\LoggerInterface\\:\\:error\\(\\) expects string, Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface\\|null given\\.$#" - count: 1 - path: src/lib/Server/Security/RestAuthenticator.php - - - - message: "#^Parameter \\#1 \\$str of function base64_encode expects string, string\\|false given\\.$#" - count: 1 - path: tests/bundle/Functional/BinaryContentTest.php - - - - message: "#^Parameter \\#2 \\$newvalue of function ini_set expects string, int given\\.$#" - count: 2 - path: tests/lib/Output/Generator/FieldTypeHashGeneratorBaseTest.php diff --git a/phpstan-baseline-8.0-specific.neon b/phpstan-baseline-8.0-specific.neon deleted file mode 100644 index ca11aa1d2..000000000 --- a/phpstan-baseline-8.0-specific.neon +++ /dev/null @@ -1,6 +0,0 @@ -parameters: - ignoreErrors: - - - message: "#^Parameter \\#2 \\$value of function ini_set expects string, int given\\.$#" - count: 2 - path: tests/lib/Output/Generator/FieldTypeHashGeneratorBaseTest.php diff --git a/phpstan-baseline-8.0.neon b/phpstan-baseline-8.0.neon deleted file mode 100644 index 39205d060..000000000 --- a/phpstan-baseline-8.0.neon +++ /dev/null @@ -1,61 +0,0 @@ -parameters: - ignoreErrors: - - - message: "#^Access to an undefined property DOMNode\\:\\:\\$data\\.$#" - count: 2 - path: src/lib/Input/Handler/Xml.php - - - - message: "#^Access to an undefined property DOMNode\\:\\:\\$tagName\\.$#" - count: 2 - path: src/lib/Input/Handler/Xml.php - - - - message: "#^Access to an undefined property DOMNode\\:\\:\\$wholeText\\.$#" - count: 2 - path: src/lib/Input/Handler/Xml.php - - - - message: "#^Call to an undefined method DOMNode\\:\\:getAttribute\\(\\)\\.$#" - count: 1 - path: src/lib/Input/Handler/Xml.php - - - - message: "#^Call to an undefined method DOMNode\\:\\:hasAttribute\\(\\)\\.$#" - count: 1 - path: src/lib/Input/Handler/Xml.php - - - - message: "#^Parameter \\#1 \\$domElement of method Ibexa\\\\Rest\\\\Input\\\\Handler\\\\Xml\\:\\:parseFieldTypeHash\\(\\) expects DOMElement, DOMNode given\\.$#" - count: 1 - path: src/lib/Input/Handler/Xml.php - - - - message: "#^Parameter \\#1 \\$array of function array_slice expects array, iterable\\ given\\.$#" - count: 1 - path: src/lib/Server/Controller/Content.php - - - - message: "#^Parameter \\#2 \\$string of function explode expects string, string\\|null given\\.$#" - count: 1 - path: src/lib/Server/Controller/Content.php - - - - message: "#^Parameter \\#2 \\.\\.\\.\\$arrays of function array_merge expects array, iterable\\ given\\.$#" - count: 1 - path: src/lib/Server/Controller/ContentType.php - - - - message: "#^Parameter \\#1 \\$array of function array_slice expects array, iterable\\ given\\.$#" - count: 1 - path: src/lib/Server/Controller/Role.php - - - - message: "#^Parameter \\#1 \\.\\.\\.\\$arrays of function array_merge expects array, iterable\\ given\\.$#" - count: 1 - path: src/lib/Server/Controller/Role.php - - - - message: "#^Parameter \\#1 \\$string of function base64_encode expects string, string\\|false given\\.$#" - count: 1 - path: tests/bundle/Functional/BinaryContentTest.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 9cf96fd4b..8fc6d01b5 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -25,56 +25,16 @@ parameters: count: 1 path: src/bundle/CorsOptions/RestProvider.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Rest\\\\DependencyInjection\\\\Compiler\\\\FieldTypeProcessorPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/DependencyInjection/Compiler/FieldTypeProcessorPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Rest\\\\DependencyInjection\\\\Compiler\\\\InputHandlerPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/DependencyInjection/Compiler/InputHandlerPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Rest\\\\DependencyInjection\\\\Compiler\\\\InputParserPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/DependencyInjection/Compiler/InputParserPass.php - - message: "#^Argument of an invalid type array\\|bool\\|float\\|int\\|string\\|null supplied for foreach, only iterables are supported\\.$#" count: 1 path: src/bundle/DependencyInjection/Compiler/OutputVisitorPass.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Rest\\\\DependencyInjection\\\\Compiler\\\\OutputVisitorPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/DependencyInjection/Compiler/OutputVisitorPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Rest\\\\DependencyInjection\\\\Compiler\\\\ValueObjectVisitorPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/DependencyInjection/Compiler/ValueObjectVisitorPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Rest\\\\DependencyInjection\\\\Configuration\\:\\:addRestRootResourcesSection\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/DependencyInjection/Configuration.php - - message: "#^Method Ibexa\\\\Bundle\\\\Rest\\\\DependencyInjection\\\\Configuration\\:\\:addRestRootResourcesSection\\(\\) has parameter \\$rootNode with no type specified\\.$#" count: 1 path: src/bundle/DependencyInjection/Configuration.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Rest\\\\DependencyInjection\\\\IbexaRestExtension\\:\\:load\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/DependencyInjection/IbexaRestExtension.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\Rest\\\\DependencyInjection\\\\IbexaRestExtension\\:\\:prepend\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/DependencyInjection/IbexaRestExtension.php - - message: "#^Method Ibexa\\\\Bundle\\\\Rest\\\\DependencyInjection\\\\IbexaRestExtension\\:\\:prependRouterConfiguration\\(\\) has no return type specified\\.$#" count: 1 @@ -90,11 +50,6 @@ parameters: count: 1 path: src/bundle/EventListener/UserCheckRequestListener.php - - - message: "#^Method Ibexa\\\\Bundle\\\\Rest\\\\RequestParser\\\\Router\\:\\:generate\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" - count: 1 - path: src/bundle/RequestParser/Router.php - - message: "#^Call to method mapCollection\\(\\) on an unknown class Ibexa\\\\Bundle\\\\Rest\\\\Routing\\\\RouteCollectionMapperMapper\\.$#" count: 1 @@ -360,11 +315,6 @@ parameters: count: 1 path: src/contracts/Output/Generator.php - - - message: "#^Method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\ValueObjectVisitor\\:\\:setRequestParser\\(\\) has no return type specified\\.$#" - count: 1 - path: src/contracts/Output/ValueObjectVisitor.php - - message: "#^Method Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\ValueObjectVisitor\\:\\:setRouter\\(\\) has no return type specified\\.$#" count: 1 @@ -440,16 +390,6 @@ parameters: count: 1 path: src/contracts/Output/Visitor.php - - - message: "#^Offset 0 does not exist on array\\\\|string\\|null\\.$#" - count: 1 - path: src/contracts/Output/Visitor.php - - - - message: "#^Parameter \\#1 \\$key of method Symfony\\\\Component\\\\HttpFoundation\\\\ResponseHeaderBag\\:\\:remove\\(\\) expects string, int\\|string given\\.$#" - count: 1 - path: src/contracts/Output/Visitor.php - - message: "#^Property Ibexa\\\\Contracts\\\\Rest\\\\Output\\\\Visitor\\:\\:\\$statusCode \\(int\\) does not accept null\\.$#" count: 1 @@ -520,11 +460,6 @@ parameters: count: 1 path: src/lib/FieldTypeProcessorRegistry.php - - - message: "#^Method Ibexa\\\\Rest\\\\Input\\\\BaseParser\\:\\:setRequestParser\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/Input/BaseParser.php - - message: "#^Method Ibexa\\\\Rest\\\\Input\\\\Dispatcher\\:\\:addHandler\\(\\) has no return type specified\\.$#" count: 1 @@ -560,6 +495,31 @@ parameters: count: 1 path: src/lib/Input/Handler/Json.php + - + message: "#^Access to an undefined property DOMNode\\:\\:\\$data\\.$#" + count: 2 + path: src/lib/Input/Handler/Xml.php + + - + message: "#^Access to an undefined property DOMNode\\:\\:\\$tagName\\.$#" + count: 2 + path: src/lib/Input/Handler/Xml.php + + - + message: "#^Access to an undefined property DOMNode\\:\\:\\$wholeText\\.$#" + count: 2 + path: src/lib/Input/Handler/Xml.php + + - + message: "#^Call to an undefined method DOMNode\\:\\:getAttribute\\(\\)\\.$#" + count: 1 + path: src/lib/Input/Handler/Xml.php + + - + message: "#^Call to an undefined method DOMNode\\:\\:hasAttribute\\(\\)\\.$#" + count: 1 + path: src/lib/Input/Handler/Xml.php + - message: "#^Method Ibexa\\\\Rest\\\\Input\\\\Handler\\\\Xml\\:\\:convert\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -590,6 +550,11 @@ parameters: count: 1 path: src/lib/Input/Handler/Xml.php + - + message: "#^Parameter \\#1 \\$domElement of method Ibexa\\\\Rest\\\\Input\\\\Handler\\\\Xml\\:\\:parseFieldTypeHash\\(\\) expects DOMElement, DOMNode given\\.$#" + count: 1 + path: src/lib/Input/Handler/Xml.php + - message: "#^Property Ibexa\\\\Rest\\\\Input\\\\Handler\\\\Xml\\:\\:\\$fieldTypeHashElements has no type specified\\.$#" count: 1 @@ -945,76 +910,6 @@ parameters: count: 1 path: src/lib/Output/ValueObjectVisitor/ContentObjectStates.php - - - message: "#^Method Ibexa\\\\Rest\\\\RequestParser\\:\\:generate\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/RequestParser.php - - - - message: "#^Method Ibexa\\\\Rest\\\\RequestParser\\:\\:parse\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/RequestParser.php - - - - message: "#^Property Ibexa\\\\Rest\\\\RequestParser\\\\EzPublish\\:\\:\\$map type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/RequestParser/EzPublish.php - - - - message: "#^Method Ibexa\\\\Rest\\\\RequestParser\\\\Pattern\\:\\:__construct\\(\\) has parameter \\$map with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/RequestParser/Pattern.php - - - - message: "#^Method Ibexa\\\\Rest\\\\RequestParser\\\\Pattern\\:\\:addPattern\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/RequestParser/Pattern.php - - - - message: "#^Method Ibexa\\\\Rest\\\\RequestParser\\\\Pattern\\:\\:generate\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/RequestParser/Pattern.php - - - - message: "#^Method Ibexa\\\\Rest\\\\RequestParser\\\\Pattern\\:\\:parse\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/RequestParser/Pattern.php - - - - message: "#^PHPDoc tag @param references unknown parameter\\: \\$type$#" - count: 1 - path: src/lib/RequestParser/Pattern.php - - - - message: "#^Property Ibexa\\\\Rest\\\\RequestParser\\\\Pattern\\:\\:\\$compileCache type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/RequestParser/Pattern.php - - - - message: "#^Property Ibexa\\\\Rest\\\\RequestParser\\\\Pattern\\:\\:\\$map type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/RequestParser/Pattern.php - - - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Controller\\:\\:setInputDispatcher\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/Server/Controller.php - - - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Controller\\:\\:setRepository\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/Server/Controller.php - - - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Controller\\:\\:setRequestParser\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/Server/Controller.php - - - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Controller\\:\\:setRouter\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/Server/Controller.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Controller\\\\BinaryContent\\:\\:getImageVariation\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Variation\\\\Values\\\\Variation but returns Ibexa\\\\Rest\\\\Server\\\\Values\\\\CachedValue\\.$#" count: 1 @@ -1045,26 +940,6 @@ parameters: count: 1 path: src/lib/Server/Controller/Bookmark.php - - - message: "#^Cannot call method get\\(\\) on Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\|null\\.$#" - count: 2 - path: src/lib/Server/Controller/Content.php - - - - message: "#^Cannot call method getCurrentRequest\\(\\) on object\\|null\\.$#" - count: 1 - path: src/lib/Server/Controller/Content.php - - - - message: "#^Cannot call method handle\\(\\) on object\\|null\\.$#" - count: 1 - path: src/lib/Server/Controller/Content.php - - - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Controller\\\\Content\\:\\:createView\\(\\) should return Ibexa\\\\Rest\\\\Server\\\\Values\\\\RestExecutedView but returns Symfony\\\\Component\\\\HttpFoundation\\\\Response\\.$#" - count: 1 - path: src/lib/Server/Controller/Content.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Controller\\\\Content\\:\\:loadContent\\(\\) should return Ibexa\\\\Rest\\\\Server\\\\Values\\\\RestContent but returns Ibexa\\\\Rest\\\\Server\\\\Values\\\\CachedValue\\.$#" count: 1 @@ -1085,31 +960,11 @@ parameters: count: 1 path: src/lib/Server/Controller/Content.php - - - message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: src/lib/Server/Controller/Content.php - - message: "#^Parameter \\#1 \\$parentLocationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:newLocationCreateStruct\\(\\) expects int, string given\\.$#" count: 1 path: src/lib/Server/Controller/Content.php - - - message: "#^Parameter \\#1 \\$versions of class Ibexa\\\\Rest\\\\Server\\\\Values\\\\VersionList constructor expects array\\, iterable\\ given\\.$#" - count: 1 - path: src/lib/Server/Controller/Content.php - - - - message: "#^Parameter \\#3 \\$relations of class Ibexa\\\\Rest\\\\Server\\\\Values\\\\Version constructor expects array\\, iterable\\ given\\.$#" - count: 4 - path: src/lib/Server/Controller/Content.php - - - - message: "#^Parameter \\#5 \\$relations of class Ibexa\\\\Rest\\\\Server\\\\Values\\\\RestContent constructor expects array\\\\|null, iterable\\\\|null given\\.$#" - count: 2 - path: src/lib/Server/Controller/Content.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Controller\\\\ContentType\\:\\:addContentTypeDraftFieldDefinition\\(\\) has parameter \\$contentTypeId with no type specified\\.$#" count: 1 @@ -1262,7 +1117,7 @@ parameters: - message: "#^Parameter \\#1 \\$contentTypeGroupId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:loadContentTypeGroup\\(\\) expects int, string given\\.$#" - count: 1 + count: 2 path: src/lib/Server/Controller/ContentType.php - @@ -1281,17 +1136,17 @@ parameters: path: src/lib/Server/Controller/ContentType.php - - message: "#^Parameter \\#1 \\$href of method Ibexa\\\\Rest\\\\RequestParser\\:\\:parseHref\\(\\) expects string, string\\|null given\\.$#" + message: "#^Parameter \\#1 \\$identifier of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:loadContentTypeByIdentifier\\(\\) expects string, string\\|null given\\.$#" count: 1 path: src/lib/Server/Controller/ContentType.php - - message: "#^Parameter \\#1 \\$identifier of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:loadContentTypeByIdentifier\\(\\) expects string, string\\|null given\\.$#" + message: "#^Parameter \\#1 \\$remoteId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:loadContentTypeByRemoteId\\(\\) expects string, string\\|null given\\.$#" count: 1 path: src/lib/Server/Controller/ContentType.php - - message: "#^Parameter \\#1 \\$remoteId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:loadContentTypeByRemoteId\\(\\) expects string, string\\|null given\\.$#" + message: "#^Parameter \\#1 \\$uri of method Ibexa\\\\Contracts\\\\Rest\\\\UriParser\\\\UriParserInterface\\:\\:getAttributeFromUri\\(\\) expects string, string\\|null given\\.$#" count: 1 path: src/lib/Server/Controller/ContentType.php @@ -1315,6 +1170,11 @@ parameters: count: 1 path: src/lib/Server/Controller/ContentType.php + - + message: "#^Parameter \\#2 \\.\\.\\.\\$arrays of function array_merge expects array, iterable\\ given\\.$#" + count: 1 + path: src/lib/Server/Controller/ContentType.php + - message: "#^Unreachable statement \\- code above always terminates\\.$#" count: 1 @@ -1336,22 +1196,17 @@ parameters: path: src/lib/Server/Controller/Location.php - - message: "#^Parameter \\#1 \\$href of method Ibexa\\\\Rest\\\\RequestParser\\:\\:parseHref\\(\\) expects string, string\\|null given\\.$#" - count: 3 - path: src/lib/Server/Controller/Location.php - - - - message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, string given\\.$#" + message: "#^Parameter \\#1 \\$remoteId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocationByRemoteId\\(\\) expects string, string\\|null given\\.$#" count: 1 path: src/lib/Server/Controller/Location.php - - message: "#^Parameter \\#1 \\$remoteId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocationByRemoteId\\(\\) expects string, string\\|null given\\.$#" - count: 1 + message: "#^Parameter \\#1 \\$uri of method Ibexa\\\\Contracts\\\\Rest\\\\UriParser\\\\UriParserInterface\\:\\:getAttributeFromUri\\(\\) expects string, string\\|null given\\.$#" + count: 3 path: src/lib/Server/Controller/Location.php - - message: "#^Parameter \\#1 \\$url of method Ibexa\\\\Rest\\\\RequestParser\\:\\:parse\\(\\) expects string, string\\|null given\\.$#" + message: "#^Parameter \\#1 \\$uri of method Ibexa\\\\Contracts\\\\Rest\\\\UriParser\\\\UriParserInterface\\:\\:matchUri\\(\\) expects string, string\\|null given\\.$#" count: 1 path: src/lib/Server/Controller/Location.php @@ -1550,6 +1405,11 @@ parameters: count: 1 path: src/lib/Server/Controller/Role.php + - + message: "#^Parameter \\#1 \\$array of function array_slice expects array, iterable\\ given\\.$#" + count: 1 + path: src/lib/Server/Controller/Role.php + - message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, string given\\.$#" count: 4 @@ -1575,6 +1435,11 @@ parameters: count: 3 path: src/lib/Server/Controller/Role.php + - + message: "#^Parameter \\#1 \\.\\.\\.\\$arrays of function array_merge expects array, iterable\\ given\\.$#" + count: 1 + path: src/lib/Server/Controller/Role.php + - message: "#^Parameter \\#2 \\$policy of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\RoleService\\:\\:updatePolicyByRoleDraft\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyDraft, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy given\\.$#" count: 2 @@ -1656,12 +1521,12 @@ parameters: path: src/lib/Server/Controller/Trash.php - - message: "#^Parameter \\#1 \\$href of method Ibexa\\\\Rest\\\\RequestParser\\:\\:parseHref\\(\\) expects string, string\\|null given\\.$#" + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, string given\\.$#" count: 1 path: src/lib/Server/Controller/Trash.php - - message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, string given\\.$#" + message: "#^Parameter \\#1 \\$uri of method Ibexa\\\\Contracts\\\\Rest\\\\UriParser\\\\UriParserInterface\\:\\:getAttributeFromUri\\(\\) expects string, string\\|null given\\.$#" count: 1 path: src/lib/Server/Controller/Trash.php @@ -1735,16 +1600,6 @@ parameters: count: 1 path: src/lib/Server/Controller/User.php - - - message: "#^Parameter \\#1 \\$href of method Ibexa\\\\Rest\\\\RequestParser\\:\\:parseHref\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: src/lib/Server/Controller/User.php - - - - message: "#^Parameter \\#1 \\$id of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\UserService\\:\\:loadUserGroup\\(\\) expects int, string given\\.$#" - count: 1 - path: src/lib/Server/Controller/User.php - - message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" count: 14 @@ -1760,21 +1615,6 @@ parameters: count: 1 path: src/lib/Server/Controller/User.php - - - message: "#^Parameter \\#1 \\$versions of class Ibexa\\\\Rest\\\\Server\\\\Values\\\\VersionList constructor expects array\\, iterable\\ given\\.$#" - count: 1 - path: src/lib/Server/Controller/User.php - - - - message: "#^Parameter \\#5 \\$relations of class Ibexa\\\\Rest\\\\Server\\\\Values\\\\RestUser constructor expects array\\, iterable\\ given\\.$#" - count: 5 - path: src/lib/Server/Controller/User.php - - - - message: "#^Parameter \\#5 \\$relations of class Ibexa\\\\Rest\\\\Server\\\\Values\\\\RestUserGroup constructor expects array\\, iterable\\ given\\.$#" - count: 10 - path: src/lib/Server/Controller/User.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\Aggregation\\\\AbstractRangeAggregationParser\\:\\:dispatchRanges\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" count: 1 @@ -1950,6 +1790,11 @@ parameters: count: 1 path: src/lib/Server/Input/Parser/ContentCreate.php + - + message: "#^Parameter \\#1 \\$contentTypeId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:loadContentType\\(\\) expects int, string given\\.$#" + count: 1 + path: src/lib/Server/Input/Parser/ContentCreate.php + - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\ContentTypeCreate\\:\\:parse\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" count: 1 @@ -2007,11 +1852,6 @@ parameters: - message: "#^Possibly invalid array key type \\(array\\\\|string\\)\\.$#" - count: 2 - path: src/lib/Server/Input/Parser/Criterion.php - - - - message: "#^Property Ibexa\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\Criterion\\:\\:\\$criterionIdMap type has no value type specified in iterable type array\\.$#" count: 1 path: src/lib/Server/Input/Parser/Criterion.php @@ -2235,6 +2075,11 @@ parameters: count: 1 path: src/lib/Server/Input/Parser/FieldDefinitionUpdate.php + - + message: "#^Parameter \\#1 \\$contentTypeId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:loadContentTypeDraft\\(\\) expects int, string given\\.$#" + count: 1 + path: src/lib/Server/Input/Parser/FieldDefinitionUpdate.php + - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\Limitation\\\\PathStringRouteBasedLimitationParser\\:\\:parseIdFromHref\\(\\) has parameter \\$limitationValue with no type specified\\.$#" count: 1 @@ -2325,16 +2170,6 @@ parameters: count: 1 path: src/lib/Server/Input/Parser/Query.php - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\:\\:\\$filter \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\|null\\.$#" - count: 1 - path: src/lib/Server/Input/Parser/Query.php - - - - message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\:\\:\\$query \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\|null\\.$#" - count: 1 - path: src/lib/Server/Input/Parser/Query.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\RelationCreate\\:\\:parse\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" count: 1 @@ -2420,23 +2255,28 @@ parameters: count: 1 path: src/lib/Server/Input/Parser/UserCreate.php + - + message: "#^Parameter \\#1 \\$contentTypeId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:loadContentType\\(\\) expects int, string given\\.$#" + count: 1 + path: src/lib/Server/Input/Parser/UserCreate.php + - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\UserGroupCreate\\:\\:parse\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" count: 1 path: src/lib/Server/Input/Parser/UserGroupCreate.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\UserGroupUpdate\\:\\:parse\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + message: "#^Parameter \\#1 \\$contentTypeId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:loadContentType\\(\\) expects int, string given\\.$#" count: 1 - path: src/lib/Server/Input/Parser/UserGroupUpdate.php + path: src/lib/Server/Input/Parser/UserGroupCreate.php - - message: "#^Parameter \\#1 \\$contentInfoId of method Ibexa\\\\Rest\\\\Input\\\\FieldTypeParser\\:\\:parseFieldValue\\(\\) expects string, int given\\.$#" + message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\UserGroupUpdate\\:\\:parse\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" count: 1 path: src/lib/Server/Input/Parser/UserGroupUpdate.php - - message: "#^Parameter \\#1 \\$fieldDefIdentifier of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentStruct\\:\\:setField\\(\\) expects string, int\\|string given\\.$#" + message: "#^Parameter \\#1 \\$contentInfoId of method Ibexa\\\\Rest\\\\Input\\\\FieldTypeParser\\:\\:parseFieldValue\\(\\) expects string, int given\\.$#" count: 1 path: src/lib/Server/Input/Parser/UserGroupUpdate.php @@ -2450,11 +2290,6 @@ parameters: count: 1 path: src/lib/Server/Input/Parser/UserUpdate.php - - - message: "#^Parameter \\#1 \\$fieldDefIdentifier of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentStruct\\:\\:setField\\(\\) expects string, int\\|string given\\.$#" - count: 1 - path: src/lib/Server/Input/Parser/UserUpdate.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\VersionUpdate\\:\\:parse\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" count: 1 @@ -2535,11 +2370,6 @@ parameters: count: 1 path: src/lib/Server/Output/ValueObjectVisitor/ContentFieldValidationException.php - - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\ContentFieldValidationException\\:\\:visit\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/ContentFieldValidationException.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\ContentList\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 @@ -2820,16 +2650,6 @@ parameters: count: 1 path: src/lib/Server/Output/ValueObjectVisitor/DeletedUserSession.php - - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\Exception\\:\\:visit\\(\\) has no return type specified\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/Exception.php - - - - message: "#^Property Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\Exception\\:\\:\\$httpStatusCodes type has no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/Exception.php - - message: "#^Property Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\Exception\\:\\:\\$translator \\(Symfony\\\\Contracts\\\\Translation\\\\TranslatorInterface\\) does not accept Symfony\\\\Contracts\\\\Translation\\\\TranslatorInterface\\|null\\.$#" count: 1 @@ -2865,11 +2685,6 @@ parameters: count: 1 path: src/lib/Server/Output/ValueObjectVisitor/Location.php - - - message: "#^Parameter \\#5 \\$relations of class Ibexa\\\\Rest\\\\Server\\\\Values\\\\RestContent constructor expects array\\\\|null, iterable\\ given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/Location.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\LocationList\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 @@ -3025,11 +2840,6 @@ parameters: count: 1 path: src/lib/Server/Output/ValueObjectVisitor/RestExecutedView.php - - - message: "#^Parameter \\#5 \\$relations of class Ibexa\\\\Rest\\\\Server\\\\Values\\\\RestContent constructor expects array\\\\|null, iterable\\ given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/RestExecutedView.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\RestFieldDefinition\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 @@ -3225,21 +3035,11 @@ parameters: count: 1 path: src/lib/Server/Output/ValueObjectVisitor/User.php - - - message: "#^Parameter \\#5 \\$relations of class Ibexa\\\\Rest\\\\Server\\\\Values\\\\RestUser constructor expects array\\, iterable\\ given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/User.php - - message: "#^Parameter \\#4 \\$mainLocation of class Ibexa\\\\Rest\\\\Server\\\\Values\\\\RestUserGroup constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null given\\.$#" count: 1 path: src/lib/Server/Output/ValueObjectVisitor/UserGroup.php - - - message: "#^Parameter \\#5 \\$relations of class Ibexa\\\\Rest\\\\Server\\\\Values\\\\RestUserGroup constructor expects array\\, iterable\\ given\\.$#" - count: 1 - path: src/lib/Server/Output/ValueObjectVisitor/UserGroup.php - - message: "#^Method Ibexa\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\UserGroupList\\:\\:visit\\(\\) has no return type specified\\.$#" count: 1 @@ -3580,6 +3380,11 @@ parameters: count: 1 path: tests/bundle/EventListener/EventListenerTest.php + - + message: "#^Parameter \\#1 \\$string of function base64_encode expects string, string\\|false given\\.$#" + count: 1 + path: tests/bundle/Functional/BinaryContentTest.php + - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Rest\\\\Functional\\\\ContentTest\\:\\:assertTranslationDoesNotExist\\(\\) has parameter \\$versionItem with no value type specified in iterable type array\\.$#" count: 1 @@ -5485,16 +5290,6 @@ parameters: count: 1 path: tests/bundle/Functional/ViewTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Rest\\\\RequestParser\\\\RouterTest\\:\\:getRouterMock\\(\\) should return PHPUnit\\\\Framework\\\\MockObject\\\\MockObject&Symfony\\\\Component\\\\Routing\\\\RouterInterface but returns Symfony\\\\Component\\\\Routing\\\\RouterInterface\\.$#" - count: 1 - path: tests/bundle/RequestParser/RouterTest.php - - - - message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Rest\\\\RequestParser\\\\RouterTest\\:\\:\\$routePrefix has no type specified\\.$#" - count: 1 - path: tests/bundle/RequestParser/RouterTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Rest\\\\Routing\\\\OptionsLoader\\\\MapperTest\\:\\:testGetOptionsRouteName\\(\\) has no return type specified\\.$#" count: 1 @@ -5680,11 +5475,6 @@ parameters: count: 1 path: tests/lib/FieldTypeProcessor/DateProcessorTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\FieldTypeProcessor\\\\ImageProcessorTest\\:\\:getRouterMock\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/FieldTypeProcessor/ImageProcessorTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\FieldTypeProcessor\\\\ImageProcessorTest\\:\\:getVariations\\(\\) has no return type specified\\.$#" count: 1 @@ -5695,16 +5485,6 @@ parameters: count: 1 path: tests/lib/FieldTypeProcessor/ImageProcessorTest.php - - - message: "#^Property Ibexa\\\\Tests\\\\Rest\\\\FieldTypeProcessor\\\\ImageProcessorTest\\:\\:\\$requestParser \\(Ibexa\\\\Rest\\\\RequestParser\\) does not accept PHPUnit\\\\Framework\\\\MockObject\\\\MockObject&Symfony\\\\Component\\\\Routing\\\\RouterInterface\\.$#" - count: 1 - path: tests/lib/FieldTypeProcessor/ImageProcessorTest.php - - - - message: "#^Property Ibexa\\\\Tests\\\\Rest\\\\FieldTypeProcessor\\\\ImageProcessorTest\\:\\:\\$requestParser \\(Ibexa\\\\Rest\\\\RequestParser\\) in isset\\(\\) is not nullable\\.$#" - count: 1 - path: tests/lib/FieldTypeProcessor/ImageProcessorTest.php - - message: "#^Variable \\$expectedVariations might not be defined\\.$#" count: 1 @@ -6640,11 +6420,6 @@ parameters: count: 1 path: tests/lib/Output/ValueObjectVisitorBaseTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\ValueObjectVisitorBaseTest\\:\\:getRequestParser\\(\\) should return Ibexa\\\\Rest\\\\RequestParser&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Rest\\\\RequestParser\\.$#" - count: 1 - path: tests/lib/Output/ValueObjectVisitorBaseTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Output\\\\ValueObjectVisitorBaseTest\\:\\:getVisitor\\(\\) has no return type specified\\.$#" count: 1 @@ -6670,11 +6445,6 @@ parameters: count: 1 path: tests/lib/Output/ValueObjectVisitorBaseTest.php - - - message: "#^Property Ibexa\\\\Tests\\\\Rest\\\\Output\\\\ValueObjectVisitorBaseTest\\:\\:\\$requestParser \\(Ibexa\\\\Rest\\\\RequestParser\\) in isset\\(\\) is not nullable\\.$#" - count: 1 - path: tests/lib/Output/ValueObjectVisitorBaseTest.php - - message: "#^Property Ibexa\\\\Tests\\\\Rest\\\\Output\\\\ValueObjectVisitorBaseTest\\:\\:\\$routerMock \\(PHPUnit\\\\Framework\\\\MockObject\\\\MockObject&Symfony\\\\Component\\\\Routing\\\\RouterInterface\\) does not accept null\\.$#" count: 1 @@ -6790,11 +6560,6 @@ parameters: count: 1 path: tests/lib/Output/VisitorTest.php - - - message: "#^Call to method setRequestParser\\(\\) on an unknown class Ibexa\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\Base\\.$#" - count: 1 - path: tests/lib/Server/Input/Parser/BaseTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\BaseTest\\:\\:getParseHrefExpectationsMap\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -6805,11 +6570,6 @@ parameters: count: 1 path: tests/lib/Server/Input/Parser/BaseTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\BaseTest\\:\\:internalGetParser\\(\\) has invalid return type Ibexa\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\Base\\.$#" - count: 1 - path: tests/lib/Server/Input/Parser/BaseTest.php - - message: "#^Property Ibexa\\\\Tests\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\BaseTest\\:\\:\\$parserTools \\(Ibexa\\\\Rest\\\\Input\\\\ParserTools\\) in isset\\(\\) is not nullable\\.$#" count: 1 @@ -6820,11 +6580,6 @@ parameters: count: 1 path: tests/lib/Server/Input/Parser/BaseTest.php - - - message: "#^Property Ibexa\\\\Tests\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\BaseTest\\:\\:\\$requestParserMock \\(Ibexa\\\\Rest\\\\RequestParser&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" - count: 1 - path: tests/lib/Server/Input/Parser/BaseTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\ContentCreateTest\\:\\:getParseHrefExpectationsMap\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -7355,16 +7110,6 @@ parameters: count: 6 path: tests/lib/Server/Input/Parser/QueryParserTest.php - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\QueryParserTest\\:\\:internalGetParser\\(\\) has invalid return type Ibexa\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\Base\\.$#" - count: 1 - path: tests/lib/Server/Input/Parser/QueryParserTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\QueryParserTest\\:\\:internalGetParser\\(\\) should return Ibexa\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\Base but returns Ibexa\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\ContentQuery\\.$#" - count: 1 - path: tests/lib/Server/Input/Parser/QueryParserTest.php - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\Server\\\\Input\\\\Parser\\\\QueryParserTest\\:\\:testDispatchMoreThanOneFilter\\(\\) has no return type specified\\.$#" count: 1 @@ -9679,83 +9424,3 @@ parameters: message: "#^Property Ibexa\\\\Tests\\\\Rest\\\\Server\\\\Output\\\\ValueObjectVisitor\\\\VersionTest\\:\\:\\$fieldTypeSerializerMock has no type specified\\.$#" count: 1 path: tests/lib/Server/Output/ValueObjectVisitor/VersionTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\UrlHandler\\\\PatternTest\\:\\:getParseValues\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: tests/lib/UrlHandler/PatternTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\UrlHandler\\\\PatternTest\\:\\:testGenerateMissingValue\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/UrlHandler/PatternTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\UrlHandler\\\\PatternTest\\:\\:testGenerateSuperfluousValue\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/UrlHandler/PatternTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\UrlHandler\\\\PatternTest\\:\\:testGenerateUnknownUrlType\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/UrlHandler/PatternTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\UrlHandler\\\\PatternTest\\:\\:testGenerateUrl\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/UrlHandler/PatternTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\UrlHandler\\\\PatternTest\\:\\:testGenerateUrl\\(\\) has parameter \\$type with no type specified\\.$#" - count: 1 - path: tests/lib/UrlHandler/PatternTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\UrlHandler\\\\PatternTest\\:\\:testGenerateUrl\\(\\) has parameter \\$url with no type specified\\.$#" - count: 1 - path: tests/lib/UrlHandler/PatternTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\UrlHandler\\\\PatternTest\\:\\:testGenerateUrl\\(\\) has parameter \\$values with no type specified\\.$#" - count: 1 - path: tests/lib/UrlHandler/PatternTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\UrlHandler\\\\PatternTest\\:\\:testParseInvalidPattern\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/UrlHandler/PatternTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\UrlHandler\\\\PatternTest\\:\\:testParseUnknownUrlType\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/UrlHandler/PatternTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\UrlHandler\\\\PatternTest\\:\\:testParseUrl\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/UrlHandler/PatternTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\UrlHandler\\\\PatternTest\\:\\:testParseUrl\\(\\) has parameter \\$type with no type specified\\.$#" - count: 1 - path: tests/lib/UrlHandler/PatternTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\UrlHandler\\\\PatternTest\\:\\:testParseUrl\\(\\) has parameter \\$url with no type specified\\.$#" - count: 1 - path: tests/lib/UrlHandler/PatternTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\UrlHandler\\\\PatternTest\\:\\:testParseUrl\\(\\) has parameter \\$values with no type specified\\.$#" - count: 1 - path: tests/lib/UrlHandler/PatternTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\UrlHandler\\\\PatternTest\\:\\:testPatternDoesNotMatch\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/UrlHandler/PatternTest.php - - - - message: "#^Method Ibexa\\\\Tests\\\\Rest\\\\UrlHandler\\\\PatternTest\\:\\:testPatternDoesNotMatchTrailing\\(\\) has no return type specified\\.$#" - count: 1 - path: tests/lib/UrlHandler/PatternTest.php diff --git a/phpstan-baseline.neon.php b/phpstan-baseline.neon.php deleted file mode 100644 index a0283663a..000000000 --- a/phpstan-baseline.neon.php +++ /dev/null @@ -1,23 +0,0 @@ - 80000 && PHP_VERSION_ID < 80100) { - $includes[] = __DIR__ . '/phpstan-baseline-8.0-specific.neon'; -} - -$config = []; -$config['includes'] = $includes; - -return $config; diff --git a/phpstan.neon b/phpstan.neon index f94799316..103be348b 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,7 +2,6 @@ includes: - vendor/phpstan/phpstan-phpunit/extension.neon - vendor/phpstan/phpstan-symfony/extension.neon - vendor/phpstan/phpstan-webmozart-assert/extension.neon - - phpstan-baseline.neon.php - phpstan-baseline.neon parameters: diff --git a/src/bundle/CorsOptions/RestProvider.php b/src/bundle/CorsOptions/RestProvider.php index 8e14ef886..ef4ab32ff 100644 --- a/src/bundle/CorsOptions/RestProvider.php +++ b/src/bundle/CorsOptions/RestProvider.php @@ -52,7 +52,7 @@ protected function getAllowedMethods($uri) { try { $route = $this->requestMatcher->matchRequest( - Request::create($uri, 'OPTIONS') + Request::create($uri, Request::METHOD_OPTIONS) ); if (isset($route['allowedMethods'])) { return explode(',', $route['allowedMethods']); diff --git a/src/bundle/DependencyInjection/Configuration.php b/src/bundle/DependencyInjection/Configuration.php index 7bcffbd89..8f6c9b68b 100644 --- a/src/bundle/DependencyInjection/Configuration.php +++ b/src/bundle/DependencyInjection/Configuration.php @@ -12,16 +12,25 @@ class Configuration extends SiteAccessConfiguration { - public function getConfigTreeBuilder() + public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder(IbexaRestExtension::EXTENSION_NAME); - $this->addRestRootResourcesSection($treeBuilder->getRootNode()); + $rootNode = $treeBuilder->getRootNode(); + $rootNode + ->children() + ->booleanNode('strict_mode') + ->defaultValue('%kernel.debug%') + ->info('Throw exceptions for missing normalizers.') + ->end() + ->end(); + + $this->addRestRootResourcesSection($rootNode); return $treeBuilder; } - public function addRestRootResourcesSection($rootNode) + private function addRestRootResourcesSection($rootNode): void { $systemNode = $this->generateScopeBaseNode($rootNode); $systemNode diff --git a/src/bundle/DependencyInjection/IbexaRestExtension.php b/src/bundle/DependencyInjection/IbexaRestExtension.php index dcd076ac3..a2940ae59 100644 --- a/src/bundle/DependencyInjection/IbexaRestExtension.php +++ b/src/bundle/DependencyInjection/IbexaRestExtension.php @@ -17,7 +17,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; use Symfony\Component\DependencyInjection\Loader; -use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension; use Symfony\Component\Yaml\Yaml; /** @@ -25,7 +25,7 @@ * * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html} */ -class IbexaRestExtension extends Extension implements PrependExtensionInterface +class IbexaRestExtension extends ConfigurableExtension implements PrependExtensionInterface { public const EXTENSION_NAME = 'ibexa_rest'; @@ -35,14 +35,13 @@ public function getAlias(): string } /** - * {@inheritdoc} + * @param array $mergedConfig */ - public function load(array $configs, ContainerBuilder $container) + protected function loadInternal(array $mergedConfig, ContainerBuilder $container): void { $this->configureApiPlatformAutotagging($container); - $configuration = $this->getConfiguration($configs, $container); - $config = $this->processConfiguration($configuration, $configs); + $container->setParameter('ibexa.rest.strict_mode', $mergedConfig['strict_mode']); $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('services.yml'); @@ -53,7 +52,7 @@ public function load(array $configs, ContainerBuilder $container) $loader->load('default_settings.yml'); $processor = new ConfigurationProcessor($container, 'ibexa.site_access.config'); - $processor->mapConfigArray('rest_root_resources', $config); + $processor->mapConfigArray('rest_root_resources', $mergedConfig); } public function prepend(ContainerBuilder $container) diff --git a/src/bundle/EventListener/UserCheckRequestListener.php b/src/bundle/EventListener/UserCheckRequestListener.php index 867b8de02..43f980d98 100644 --- a/src/bundle/EventListener/UserCheckRequestListener.php +++ b/src/bundle/EventListener/UserCheckRequestListener.php @@ -58,7 +58,7 @@ public function checkUser(RequestEvent $event): void } $user = $this->security->getUser(); - if ($user === null || $expectedUserIdentifier !== $user->getUsername()) { + if ($user === null || $expectedUserIdentifier !== $user->getUserIdentifier()) { throw new UnexpectedUserException('Expectation failed. User changed.', Response::HTTP_UNAUTHORIZED); } } diff --git a/src/bundle/RequestParser/Router.php b/src/bundle/RequestParser/Router.php deleted file mode 100644 index 61abfe03a..000000000 --- a/src/bundle/RequestParser/Router.php +++ /dev/null @@ -1,55 +0,0 @@ -router = $router; - $this->uriParser = $uriParser; - } - - /** - * @return array matched route configuration and parameters - * - * @throws \Ibexa\Contracts\Rest\Exceptions\InvalidArgumentException If no match was found - */ - public function parse($url): array - { - return $this->uriParser->matchUri($url); - } - - public function generate($type, array $values = []) - { - return $this->router->generate($type, $values); - } - - /** - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If $attribute wasn't found in the match - */ - public function parseHref($href, $attribute) - { - return $this->uriParser->getAttributeFromUri($href, $attribute); - } -} diff --git a/src/bundle/Resources/config/api_platform.yml b/src/bundle/Resources/config/api_platform.yml index cccb71de2..b0cacbb68 100644 --- a/src/bundle/Resources/config/api_platform.yml +++ b/src/bundle/Resources/config/api_platform.yml @@ -3,17 +3,17 @@ services: parent: api_platform.action.entrypoint arguments: index_0: '@Ibexa\Bundle\Rest\ApiPlatform\ClassNameResourceNameCollectionFactory' - index_1: '@ibexa.api_platform.state_provider.documentation.content_negotiation' + #index_1: '@ibexa.api_platform.state_provider.documentation.content_negotiation' - ibexa.api_platform.state_provider.documentation.content_negotiation: - parent: api_platform.state_provider.documentation.content_negotiation - arguments: - index_0: '@ibexa.api_platform.swagger_ui.documentation.provider' + #ibexa.api_platform.state_provider.documentation.content_negotiation: + # parent: api_platform.state_provider.documentation.content_negotiation + # arguments: + # index_0: '@ibexa.api_platform.swagger_ui.documentation.provider' - ibexa.api_platform.swagger_ui.documentation.provider: - parent: api_platform.swagger_ui.documentation.provider - arguments: - index_1: '@ibexa.api_platform.ibexa_openapi.factory' + #ibexa.api_platform.swagger_ui.documentation.provider: + # parent: api_platform.swagger_ui.documentation.provider + # arguments: + # index_1: '@ibexa.api_platform.ibexa_openapi.factory' ibexa.api_platform.ibexa_openapi.factory: class: 'Ibexa\Bundle\Rest\ApiPlatform\OpenApiFactory' diff --git a/src/bundle/Resources/config/input_parsers.yml b/src/bundle/Resources/config/input_parsers.yml index 8ab8a8743..a30256609 100644 --- a/src/bundle/Resources/config/input_parsers.yml +++ b/src/bundle/Resources/config/input_parsers.yml @@ -6,7 +6,7 @@ services: Ibexa\Rest\Server\Common\Parser: abstract: true calls: - - [ setRequestParser, [ '@Ibexa\Bundle\Rest\RequestParser\Router' ] ] + - [ setUriParser, [ '@Ibexa\Contracts\Rest\UriParser\UriParserInterface' ] ] Ibexa\Rest\Input\Parser\ContentObjectStates: parent: Ibexa\Rest\Server\Common\Parser @@ -859,6 +859,14 @@ services: tags: - { name: ibexa.rest.input.parser, mediaType: application/vnd.ibexa.api.internal.criterion.Image } + + Ibexa\Rest\Server\Input\Parser\Criterion\IsBookmarked: + parent: Ibexa\Rest\Server\Common\Parser + arguments: + $parserTools: '@Ibexa\Rest\Input\ParserTools' + tags: + - { name: ibexa.rest.input.parser, mediaType: application/vnd.ibexa.api.internal.criterion.IsBookmarked } + Ibexa\Rest\Server\Input\Parser\MoveLocation: parent: Ibexa\Rest\Server\Common\Parser arguments: diff --git a/src/bundle/Resources/config/routing.yml b/src/bundle/Resources/config/routing.yml index 6696d376e..2ae25090c 100644 --- a/src/bundle/Resources/config/routing.yml +++ b/src/bundle/Resources/config/routing.yml @@ -1,10 +1,8 @@ # Root resource - ibexa.rest.load_root_resource: path: / - defaults: - _controller: Ibexa\Rest\Server\Controller\Root:loadRootResource + controller: Ibexa\Rest\Server\Controller\Root::loadRootResource methods: [GET] @@ -23,53 +21,46 @@ ibexa.api_platform.documentation: # Sections - ibexa.rest.list_sections: path: /content/sections - defaults: - _controller: Ibexa\Rest\Server\Controller\Section\SectionListController:listSections + controller: Ibexa\Rest\Server\Controller\Section\SectionListController::listSections methods: [GET] ibexa.rest.create_section: path: /content/sections - defaults: - _controller: Ibexa\Rest\Server\Controller\Section\SectionCreateController:createSection + controller: Ibexa\Rest\Server\Controller\Section\SectionCreateController::createSection methods: [POST] ibexa.rest.load_section: path: /content/sections/{sectionId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Section\SectionLoadByIdController:loadSection + controller: Ibexa\Rest\Server\Controller\Section\SectionLoadByIdController::loadSection methods: [GET] requirements: sectionId: \d+ ibexa.rest.update_section: path: /content/sections/{sectionId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Section\SectionUpdateController:updateSection + controller: Ibexa\Rest\Server\Controller\Section\SectionUpdateController::updateSection methods: [PATCH] requirements: sectionId: \d+ ibexa.rest.delete_section: path: /content/sections/{sectionId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Section\SectionDeleteController:deleteSection + controller: Ibexa\Rest\Server\Controller\Section\SectionDeleteController::deleteSection methods: [DELETE] requirements: sectionId: \d+ ibexa.rest.refresh_session: path: /user/sessions/{sessionId}/refresh + controller: Ibexa\Rest\Server\Controller\SessionController:refreshSessionAction defaults: - _controller: Ibexa\Rest\Server\Controller\Session\SessionRefreshController:refreshSessionAction csrf_protection: false methods: [POST] # Content - ibexa.rest.content.copy: path: /content/objects/{contentId} controller: Ibexa\Rest\Server\Controller\Content\ContentCopyController::copy @@ -111,74 +102,64 @@ ibexa.rest.content.publish_version: ibexa.rest.redirect_content: path: /content/objects - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentRedirectController:redirectContent + controller: Ibexa\Rest\Server\Controller\Content\ContentRedirectController::redirectContent methods: [GET] ibexa.rest.create_content: path: /content/objects - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentCreateController:createContent + controller: Ibexa\Rest\Server\Controller\Content\ContentCreateController::createContent methods: [POST] ibexa.rest.update_content_metadata: path: /content/objects/{contentId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentMetadataUpdateController:updateContentMetadata + controller: Ibexa\Rest\Server\Controller\Content\ContentMetadataUpdateController::updateContentMetadata methods: [PATCH] requirements: contentId: \d+ ibexa.rest.load_content: path: /content/objects/{contentId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentLoadByIdController:loadContent + controller: Ibexa\Rest\Server\Controller\Content\ContentLoadByIdController::loadContent methods: [GET] requirements: contentId: \d+ ibexa.rest.delete_content: path: /content/objects/{contentId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentDeleteController:deleteContent + controller: Ibexa\Rest\Server\Controller\Content\ContentDeleteController::deleteContent methods: [DELETE] requirements: contentId: \d+ ibexa.rest.copy_content: path: /content/objects/{contentId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentCopyController:copyContent + controller: Ibexa\Rest\Server\Controller\Content\ContentCopyController::copyContent methods: [COPY] requirements: contentId: \d+ ibexa.rest.delete_content_translation: path: /content/objects/{contentId}/translations/{languageCode} - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentTranslationDeleteController:deleteContentTranslation + controller: Ibexa\Rest\Server\Controller\Content\ContentTranslationDeleteController::deleteContentTranslation methods: [DELETE] ibexa.rest.redirect_current_version_relations: path: /content/objects/{contentId}/relations - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentCurrentVersionRelationsRedirectController:redirectCurrentVersionRelations + controller: Ibexa\Rest\Server\Controller\Content\ContentCurrentVersionRelationsRedirectController::redirectCurrentVersionRelations methods: [GET] requirements: contentId: \d+ ibexa.rest.load_content_versions: path: /content/objects/{contentId}/versions - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentVersionsListController:loadContentVersions + controller: Ibexa\Rest\Server\Controller\Content\ContentVersionsListController::loadContentVersions methods: [GET] requirements: contentId: \d+ ibexa.rest.load_version_relations: path: /content/objects/{contentId}/versions/{versionNumber}/relations - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentVersionsRelationsListController:loadVersionRelations + controller: Ibexa\Rest\Server\Controller\Content\ContentVersionsRelationsListController::loadVersionRelations methods: [GET] requirements: contentId: \d+ @@ -186,8 +167,7 @@ ibexa.rest.load_version_relations: ibexa.rest.create_relation: path: /content/objects/{contentId}/versions/{versionNumber}/relations - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentRelationCreateController:createRelation + controller: Ibexa\Rest\Server\Controller\Content\ContentRelationCreateController::createRelation methods: [POST] requirements: contentId: \d+ @@ -195,8 +175,7 @@ ibexa.rest.create_relation: ibexa.rest.load_version_relation: path: /content/objects/{contentId}/versions/{versionNumber}/relations/{relationId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentVersionRelationLoadByIdController:loadVersionRelation + controller: Ibexa\Rest\Server\Controller\Content\ContentVersionRelationLoadByIdController::loadVersionRelation methods: [GET] requirements: contentId: \d+ @@ -205,8 +184,7 @@ ibexa.rest.load_version_relation: ibexa.rest.remove_relation: path: /content/objects/{contentId}/versions/{versionNumber}/relations/{relationId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentVersionRelationDeleteController:removeRelation + controller: Ibexa\Rest\Server\Controller\Content\ContentVersionRelationDeleteController::removeRelation methods: [DELETE] requirements: contentId: \d+ @@ -215,8 +193,7 @@ ibexa.rest.remove_relation: ibexa.rest.load_content_in_version: path: /content/objects/{contentId}/versions/{versionNumber} - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentInVersionLoadController:loadContentInVersion + controller: Ibexa\Rest\Server\Controller\Content\ContentInVersionLoadController::loadContentInVersion methods: [GET] requirements: contentId: \d+ @@ -224,8 +201,7 @@ ibexa.rest.load_content_in_version: ibexa.rest.update_version: path: /content/objects/{contentId}/versions/{versionNumber} - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentVersionUpdateController:updateVersion + controller: Ibexa\Rest\Server\Controller\Content\ContentVersionUpdateController::updateVersion methods: [PATCH] requirements: contentId: \d+ @@ -233,8 +209,7 @@ ibexa.rest.update_version: ibexa.rest.delete_content_version: path: /content/objects/{contentId}/versions/{versionNumber} - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentVersionDeleteController:deleteContentVersion + controller: Ibexa\Rest\Server\Controller\Content\ContentVersionDeleteController::deleteContentVersion methods: [DELETE] requirements: contentId: \d+ @@ -242,8 +217,7 @@ ibexa.rest.delete_content_version: ibexa.rest.delete_translation_from_draft: path: /content/objects/{contentId}/versions/{versionNumber}/translations/{languageCode} - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentDraftTranslationDeleteController:deleteTranslationFromDraft + controller: Ibexa\Rest\Server\Controller\Content\ContentDraftTranslationDeleteController::deleteTranslationFromDraft methods: [DELETE] requirements: contentId: \d+ @@ -251,8 +225,7 @@ ibexa.rest.delete_translation_from_draft: ibexa.rest.create_draft_from_version: path: /content/objects/{contentId}/versions/{versionNumber} - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentDraftCreateFromVersionController:createDraftFromVersion + controller: Ibexa\Rest\Server\Controller\Content\ContentDraftCreateFromVersionController::createDraftFromVersion methods: [COPY] requirements: contentId: \d+ @@ -260,8 +233,7 @@ ibexa.rest.create_draft_from_version: ibexa.rest.publish_version: path: /content/objects/{contentId}/versions/{versionNumber} - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentVersionPublishController:publishVersion + controller: Ibexa\Rest\Server\Controller\Content\ContentVersionPublishController::publishVersion methods: [PUBLISH] requirements: contentId: \d+ @@ -269,32 +241,28 @@ ibexa.rest.publish_version: ibexa.rest.redirect_current_version: path: /content/objects/{contentId}/currentversion - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentCurrentVersionRedirectController:redirectCurrentVersion + controller: Ibexa\Rest\Server\Controller\Content\ContentCurrentVersionRedirectController::redirectCurrentVersion methods: [GET] requirements: contentId: \d+ ibexa.rest.create_draft_from_current_version: path: /content/objects/{contentId}/currentversion - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentDraftCreateFromCurrentVersionController:createDraftFromCurrentVersion + controller: Ibexa\Rest\Server\Controller\Content\ContentDraftCreateFromCurrentVersionController::createDraftFromCurrentVersion methods: [COPY] requirements: contentId: \d+ ibexa.rest.hide_content: path: /content/objects/{contentId}/hide - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentHideController:hideContent + controller: Ibexa\Rest\Server\Controller\Content\ContentHideController::hideContent methods: [POST] requirements: contentId: \d+ ibexa.rest.reveal_content: path: /content/objects/{contentId}/reveal - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\ContentRevealController:revealContent + controller: Ibexa\Rest\Server\Controller\Content\ContentRevealController::revealContent methods: [POST] requirements: contentId: \d+ @@ -303,104 +271,83 @@ ibexa.rest.reveal_content: ibexa.rest.binary_content.get_image_variation: path: /content/binary/images/{imageId}/variations/{variationIdentifier} - defaults: - _controller: Ibexa\Rest\Server\Controller\BinaryContent:getImageVariation + controller: Ibexa\Rest\Server\Controller\BinaryContent::getImageVariation methods: [GET] requirements: imageId: \d+-\d+(-\d+)? # Views -# Deprecated in favour of /views from Platform 1.0 -ibexa.rest.create_content_view: - path: /content/views - defaults: - _controller: Ibexa\Rest\Server\Controller\Content\Content:createView - methods: [POST] - -# Views, Platform 1.0 ibexa.rest.views.create: path: /views - defaults: - _controller: Ibexa\Rest\Server\Controller\Views:createView + controller: Ibexa\Rest\Server\Controller\Views::createView methods: [POST] ibexa.rest.views.list: path: /views - defaults: - _controller: Ibexa\Rest\Server\Controller\Views:listView + controller: Ibexa\Rest\Server\Controller\Views::listView methods: [GET] ibexa.rest.views.load: path : /views/{viewId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Views:getView + controller: Ibexa\Rest\Server\Controller\Views::getView methods: [GET] ibexa.rest.views.load.results: path: /views/{viewId}/results - defaults: - _controller: Ibexa\Rest\Server\Controller\Views:loadViewResults + controller: Ibexa\Rest\Server\Controller\Views::loadViewResults methods: [GET] -# Object states +# Object states ibexa.rest.load_object_state_groups: path: /content/objectstategroups - defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateGroupListController:loadObjectStateGroups + controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateGroupListController::loadObjectStateGroups methods: [GET] ibexa.rest.create_object_state_group: path: /content/objectstategroups - defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateGroupCreateController:createObjectStateGroup + controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateGroupCreateController::createObjectStateGroup methods: [POST] ibexa.rest.load_object_state_group: path: /content/objectstategroups/{objectStateGroupId} - defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateGroupLoadByIdController:loadObjectStateGroup + controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateGroupLoadByIdController::loadObjectStateGroup methods: [GET] requirements: objectStateGroupId: \d+ ibexa.rest.update_object_state_group: path: /content/objectstategroups/{objectStateGroupId} - defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateGroupUpdateController:updateObjectStateGroup + controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateGroupUpdateController::updateObjectStateGroup methods: [PATCH] requirements: objectStateGroupId: \d+ ibexa.rest.delete_object_state_group: path: /content/objectstategroups/{objectStateGroupId} - defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateGroupDeleteController:deleteObjectStateGroup + controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateGroupDeleteController::deleteObjectStateGroup methods: [DELETE] requirements: objectStateGroupId: \d+ ibexa.rest.load_object_states: path: /content/objectstategroups/{objectStateGroupId}/objectstates - defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateListController:loadObjectStates + controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateListController::loadObjectStates methods: [GET] requirements: objectStateGroupId: \d+ ibexa.rest.create_object_state: path: /content/objectstategroups/{objectStateGroupId}/objectstates - defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateCreateController:createObjectState + controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateCreateController::createObjectState methods: [POST] requirements: objectStateGroupId: \d+ ibexa.rest.load_object_state: path: /content/objectstategroups/{objectStateGroupId}/objectstates/{objectStateId} - defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateLoadByIdController:loadObjectState + controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateLoadByIdController::loadObjectState methods: [GET] requirements: objectStateGroupId: \d+ @@ -408,8 +355,7 @@ ibexa.rest.load_object_state: ibexa.rest.update_object_state: path: /content/objectstategroups/{objectStateGroupId}/objectstates/{objectStateId} - defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateUpdateController:updateObjectState + controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateUpdateController::updateObjectState methods: [PATCH] requirements: objectStateGroupId: \d+ @@ -417,8 +363,7 @@ ibexa.rest.update_object_state: ibexa.rest.delete_object_state: path: /content/objectstategroups/{objectStateGroupId}/objectstates/{objectStateId} - defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateGroupDeleteController:deleteObjectState + controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStateGroupDeleteController::deleteObjectState methods: [DELETE] requirements: objectStateGroupId: \d+ @@ -426,16 +371,14 @@ ibexa.rest.delete_object_state: ibexa.rest.get_object_states_for_content: path: /content/objects/{contentId}/objectstates - defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStatesForContentListController:getObjectStatesForContent + controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStatesForContentListController::getObjectStatesForContent methods: [GET] requirements: contentId: \d+ ibexa.rest.set_object_states_for_content: path: /content/objects/{contentId}/objectstates - defaults: - _controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStatesForContentUpdateController:setObjectStatesForContent + controller: Ibexa\Rest\Server\Controller\ObjectState\ObjectStatesForContentUpdateController::setObjectStatesForContent methods: [PATCH] requirements: contentId: \d+ @@ -497,86 +440,74 @@ ibexa.rest.location.move: ibexa.rest.redirect_location: path: /content/locations - defaults: - _controller: Ibexa\Rest\Server\Controller\Location\LocationRedirectController:redirectLocation + controller: Ibexa\Rest\Server\Controller\Location\LocationRedirectController::redirectLocation methods: [GET] ibexa.rest.load_location: path: /content/locations/{locationPath} - defaults: - _controller: Ibexa\Rest\Server\Controller\Location\LocationLoadByPathController:loadLocation + controller: Ibexa\Rest\Server\Controller\Location\LocationLoadByPathController::loadLocation methods: [GET] requirements: locationPath: "[0-9/]+" ibexa.rest.update_location: path: /content/locations/{locationPath} - defaults: - _controller: Ibexa\Rest\Server\Controller\Location\LocationUpdateController:updateLocation + controller: Ibexa\Rest\Server\Controller\Location\LocationUpdateController::updateLocation methods: [PATCH] requirements: locationPath: "[0-9/]+" ibexa.rest.delete_subtree: path: /content/locations/{locationPath} - defaults: - _controller: Ibexa\Rest\Server\Controller\Location\LocationSubtreeDeleteController:deleteSubtree + controller: Ibexa\Rest\Server\Controller\Location\LocationSubtreeDeleteController::deleteSubtree methods: [DELETE] requirements: locationPath: "[0-9/]+" ibexa.rest.copy_subtree: path: /content/locations/{locationPath} - defaults: - _controller: Ibexa\Rest\Server\Controller\Location\LocationSubtreeCopyController:copySubtree + controller: Ibexa\Rest\Server\Controller\Location\LocationSubtreeCopyController::copySubtree methods: [COPY] requirements: locationPath: "[0-9/]+" ibexa.rest.move_subtree: path: /content/locations/{locationPath} - defaults: - _controller: Ibexa\Rest\Server\Controller\Location\LocationSubtreeMoveController:moveSubtree + controller: Ibexa\Rest\Server\Controller\Location\LocationSubtreeMoveController::moveSubtree methods: [MOVE] requirements: locationPath: "[0-9/]+" ibexa.rest.swap_location: path: /content/locations/{locationPath} - defaults: - _controller: Ibexa\Rest\Server\Controller\Location\LocationSwapController:swapLocation + controller: Ibexa\Rest\Server\Controller\Location\LocationSwapController::swapLocation methods: [SWAP] requirements: locationPath: "[0-9/]+" ibexa.rest.load_location_children: path: /content/locations/{locationPath}/children - defaults: - _controller: Ibexa\Rest\Server\Controller\Location\LocationChildrenListController:loadLocationChildren + controller: Ibexa\Rest\Server\Controller\Location\LocationChildrenListController::loadLocationChildren methods: [GET] requirements: locationPath: "[0-9/]+" ibexa.rest.load_locations_for_content: path: /content/objects/{contentId}/locations - defaults: - _controller: Ibexa\Rest\Server\Controller\Location\LocationForContentListController:loadLocationsForContent + controller: Ibexa\Rest\Server\Controller\Location\LocationForContentListController::loadLocationsForContent methods: [GET] requirements: contentId: \d+ ibexa.rest.create_location: path: /content/objects/{contentId}/locations - defaults: - _controller: Ibexa\Rest\Server\Controller\Location\LocationCreateController:createLocation + controller: Ibexa\Rest\Server\Controller\Location\LocationCreateController::createLocation methods: [POST] requirements: contentId: \d+ - # Content types - ibexa.rest.content_type.publish_draft: path: /content/types/{contentTypeId}/draft controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftPublishController::publishContentTypeDraft @@ -599,52 +530,45 @@ ibexa.rest.content_type.copy: ibexa.rest.load_content_type_group_list: path: /content/typegroups - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeGroupsListController:loadContentTypeGroupList + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeGroupsListController::loadContentTypeGroupList methods: [GET] ibexa.rest.create_content_type_group: path: /content/typegroups - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeGroupCreateController:createContentTypeGroup + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeGroupCreateController::createContentTypeGroup methods: [POST] ibexa.rest.load_content_type_group: path: /content/typegroups/{contentTypeGroupId} - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeGroupLoadByIdController:loadContentTypeGroup + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeGroupLoadByIdController::loadContentTypeGroup methods: [GET] requirements: contentTypeGroupId: \d+ ibexa.rest.update_content_type_group: path: /content/typegroups/{contentTypeGroupId} - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeGroupUpdateController:updateContentTypeGroup + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeGroupUpdateController::updateContentTypeGroup methods: [PATCH] requirements: contentTypeGroupId: \d+ ibexa.rest.delete_content_type_group: path: /content/typegroups/{contentTypeGroupId} - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeGroupDeleteController:deleteContentTypeGroup + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeGroupDeleteController::deleteContentTypeGroup methods: [DELETE] requirements: contentTypeGroupId: \d+ ibexa.rest.list_content_types_for_group: path: /content/typegroups/{contentTypeGroupId}/types - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeListForGroupController:listContentTypesForGroup + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeListForGroupController::listContentTypesForGroup methods: [GET] requirements: contentTypeGroupId: \d+ ibexa.rest.create_content_type: path: /content/typegroups/{contentTypeGroupId}/types - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeCreateController:createContentType + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeCreateController::createContentType methods: [POST] requirements: contentTypeGroupId: \d+ @@ -652,62 +576,54 @@ ibexa.rest.create_content_type: ibexa.rest.list_content_types: # @todo: Handle all GET parameters path: /content/types - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeListController:listContentTypes + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeListController::listContentTypes methods: [GET] ibexa.rest.copy_content_type: path: /content/types/{contentTypeId} - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeCopyController:copyContentType + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeCopyController::copyContentType methods: [COPY] requirements: contentTypeId: \d+ ibexa.rest.load_content_type: path: /content/types/{contentTypeId} - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeLoadByIdController:loadContentType + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeLoadByIdController::loadContentType methods: [GET] requirements: contentTypeId: \d+ ibexa.rest.create_content_type_draft: path: /content/types/{contentTypeId} - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftCreateController:createContentTypeDraft + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftCreateController::createContentTypeDraft methods: [POST] requirements: contentTypeId: \d+ ibexa.rest.delete_content_type: path: /content/types/{contentTypeId} - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDeleteController:deleteContentType + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDeleteController::deleteContentType methods: [DELETE] requirements: contentTypeId: \d+ ibexa.rest.delete_content_type_draft: path: /content/types/{contentTypeId}/draft - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftDeleteController:deleteContentTypeDraft + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftDeleteController::deleteContentTypeDraft methods: [DELETE] requirements: contentTypeId: \d+ ibexa.rest.load_content_type_field_definition_list: path: /content/types/{contentTypeId}/fieldDefinitions - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeFieldDefinitionListController:loadContentTypeFieldDefinitionList + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeFieldDefinitionListController::loadContentTypeFieldDefinitionList methods: [GET] requirements: contentTypeId: \d+ ibexa.rest.load_content_type_field_definition: path: /content/types/{contentTypeId}/fieldDefinitions/{fieldDefinitionId} - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeFieldDefinitionLoadByIdController:loadContentTypeFieldDefinition + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeFieldDefinitionLoadByIdController::loadContentTypeFieldDefinition methods: [GET] requirements: contentTypeId: \d+ @@ -723,48 +639,42 @@ ibexa.rest.load_content_type_field_definition_by_identifier: ibexa.rest.load_content_type_draft: path: /content/types/{contentTypeId}/draft - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftLoadController:loadContentTypeDraft + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftLoadController::loadContentTypeDraft methods: [GET] requirements: contentTypeId: \d+ ibexa.rest.update_content_type_draft: path: /content/types/{contentTypeId}/draft - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftUpdateController:updateContentTypeDraft + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftUpdateController::updateContentTypeDraft methods: [PATCH] requirements: contentTypeId: \d+ ibexa.rest.publish_content_type_draft: path: /content/types/{contentTypeId}/draft - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftPublishController:publishContentTypeDraft + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftPublishController::publishContentTypeDraft methods: [PUBLISH] requirements: contentTypeId: \d+ ibexa.rest.load_content_type_draft_field_definition_list: path: /content/types/{contentTypeId}/draft/fieldDefinitions - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftFieldDefinitionListController:loadContentTypeDraftFieldDefinitionList + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftFieldDefinitionListController::loadContentTypeDraftFieldDefinitionList methods: [GET] requirements: contentTypeId: \d+ ibexa.rest.add_content_type_draft_field_definition: path: /content/types/{contentTypeId}/draft/fieldDefinitions - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftFeildDefinitionAddController:addContentTypeDraftFieldDefinition + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftFeildDefinitionAddController::addContentTypeDraftFieldDefinition methods: [POST] requirements: contentTypeId: \d+ ibexa.rest.load_content_type_draft_field_definition: path: /content/types/{contentTypeId}/draft/fieldDefinitions/{fieldDefinitionId} - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftFieldDefinitionLoadByIdController:loadContentTypeDraftFieldDefinition + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftFieldDefinitionLoadByIdController::loadContentTypeDraftFieldDefinition methods: [GET] requirements: contentTypeId: \d+ @@ -772,8 +682,7 @@ ibexa.rest.load_content_type_draft_field_definition: ibexa.rest.update_content_type_draft_field_definition: path: /content/types/{contentTypeId}/draft/fieldDefinitions/{fieldDefinitionId} - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftFieldDefinitionUpdateController:updateContentTypeDraftFieldDefinition + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftFieldDefinitionUpdateController::updateContentTypeDraftFieldDefinition methods: [PATCH] requirements: contentTypeId: \d+ @@ -781,8 +690,7 @@ ibexa.rest.update_content_type_draft_field_definition: ibexa.rest.remove_content_type_draft_field_definition: path: /content/types/{contentTypeId}/draft/fieldDefinitions/{fieldDefinitionId} - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftFieldDefinitionDeleteController:removeContentTypeDraftFieldDefinition + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeDraftFieldDefinitionDeleteController::removeContentTypeDraftFieldDefinition methods: [DELETE] requirements: contentTypeId: \d+ @@ -790,8 +698,7 @@ ibexa.rest.remove_content_type_draft_field_definition: ibexa.rest.load_groups_of_content_type: path: /content/types/{contentTypeId}/groups - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeGroupListController:loadGroupsOfContentType + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeGroupListController::loadGroupsOfContentType methods: [GET] requirements: contentTypeId: \d+ @@ -799,25 +706,21 @@ ibexa.rest.load_groups_of_content_type: ibexa.rest.link_content_type_to_group: # Handle GET parameter group in controller. Most likely already done path: /content/types/{contentTypeId}/groups - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeLinkToGroupController:linkContentTypeToGroup + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeLinkToGroupController::linkContentTypeToGroup methods: [POST] requirements: contentTypeId: \d+ ibexa.rest.unlink_content_type_from_group: path: /content/types/{contentTypeId}/groups/{contentTypeGroupId} - defaults: - _controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeUnlinkFromGroupController:unlinkContentTypeFromGroup + controller: Ibexa\Rest\Server\Controller\ContentType\ContentTypeUnlinkFromGroupController::unlinkContentTypeFromGroup methods: [DELETE] requirements: contentTypeId: \d+ contentTypeGroupId: \d+ - # Trash - ibexa.rest.trash.restore_trash_item: path: /content/trash/{trashItemId} controller: Ibexa\Rest\Server\Controller\Trash\TrashItemRestoreController::restoreItem @@ -830,86 +733,70 @@ ibexa.rest.trash.restore_trash_item: ibexa.rest.load_trash_items: path: /content/trash - defaults: - _controller: Ibexa\Rest\Server\Controller\Trash\TrashItemListController:loadTrashItems + controller: Ibexa\Rest\Server\Controller\Trash\TrashItemListController::loadTrashItems methods: [GET] ibexa.rest.empty_trash: path: /content/trash - defaults: - _controller: Ibexa\Rest\Server\Controller\Trash\TrashEmptyController:emptyTrash + controller: Ibexa\Rest\Server\Controller\Trash\TrashEmptyController::emptyTrash methods: [DELETE] ibexa.rest.load_trash_item: path: /content/trash/{trashItemId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Trash\TrashItemLoadByIdController:loadTrashItem + controller: Ibexa\Rest\Server\Controller\Trash\TrashItemLoadByIdController::loadTrashItem methods: [GET] requirements: trashItemId: \d+ ibexa.rest.delete_trash_item: path: /content/trash/{trashItemId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Trash\TrashItemDeleteController:deleteTrashItem + controller: Ibexa\Rest\Server\Controller\Trash\TrashItemDeleteController::deleteTrashItem methods: [DELETE] requirements: trashItemId: \d+ ibexa.rest.restore_trash_item: path: /content/trash/{trashItemId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Trash\TrashItemRestoreController:restoreTrashItem + controller: Ibexa\Rest\Server\Controller\Trash\TrashItemRestoreController::restoreTrashItem methods: [MOVE] requirements: trashItemId: \d+ - # URL wildcards - ibexa.rest.list_url_wildcards: path: /content/urlwildcards - defaults: - _controller: Ibexa\Rest\Server\Controller\URLWildcard\URLWildcardListController:listURLWildcards + controller: Ibexa\Rest\Server\Controller\URLWildcard\URLWildcardListController::listURLWildcards methods: [GET] ibexa.rest.create_url_wildcard: path: /content/urlwildcards - defaults: - _controller: Ibexa\Rest\Server\Controller\URLWildcard\URLWildcardCreateController:createURLWildcard + controller: Ibexa\Rest\Server\Controller\URLWildcard\URLWildcardCreateController::createURLWildcard methods: [POST] ibexa.rest.load_url_wildcard: path: /content/urlwildcards/{urlWildcardId} - defaults: - _controller: Ibexa\Rest\Server\Controller\URLWildcard\URLWildcardLoadByIdController:loadURLWildcard + controller: Ibexa\Rest\Server\Controller\URLWildcard\URLWildcardLoadByIdController::loadURLWildcard methods: [GET] requirements: urlWildcardId: \d+ ibexa.rest.delete_url_wildcard: path: /content/urlwildcards/{urlWildcardId} - defaults: - _controller: Ibexa\Rest\Server\Controller\URLWildcard\URLWildcardDeleteController:deleteURLWildcard + controller: Ibexa\Rest\Server\Controller\URLWildcard\URLWildcardDeleteController::deleteURLWildcard methods: [DELETE] requirements: urlWildcardId: \d+ - # User policies - ibexa.rest.list_policies_for_user: path: /user/policies - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\Role:listPoliciesForUser + controller: Ibexa\Rest\Server\Controller\Role\Role::listPoliciesForUser methods: [GET] - # Roles - ibexa.rest.role.publish: path: /user/roles/{roleId}/draft controller: Ibexa\Rest\Server\Controller\Role\RoleDraftPublishController::publishRoleDraft @@ -922,109 +809,95 @@ ibexa.rest.role.publish: ibexa.rest.list_roles: path: /user/roles - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RoleListController:listRoles + controller: Ibexa\Rest\Server\Controller\Role\RoleListController::listRoles methods: [GET] ibexa.rest.create_role: path: /user/roles - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RoleCreateController:createRole + controller: Ibexa\Rest\Server\Controller\Role\RoleCreateController::createRole methods: [POST] requirements: ibexa.rest.create_role_draft: path: /user/roles/{roleId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RoleDraftCreateController:createRoleDraft + controller: Ibexa\Rest\Server\Controller\Role\RoleDraftCreateController::createRoleDraft methods: [POST] requirements: roleId: \d+ ibexa.rest.load_role: path: /user/roles/{roleId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RoleLoadByIdController:loadRole + controller: Ibexa\Rest\Server\Controller\Role\RoleLoadByIdController::loadRole methods: [GET] requirements: roleId: \d+ ibexa.rest.load_role_draft: path: /user/roles/{roleId}/draft - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RoleDraftLoadByRoleIdController:loadRoleDraft + controller: Ibexa\Rest\Server\Controller\Role\RoleDraftLoadByRoleIdController::loadRoleDraft methods: [GET] requirements: roleId: \d+ ibexa.rest.update_role: path: /user/roles/{roleId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RoleUpdateController:updateRole + controller: Ibexa\Rest\Server\Controller\Role\RoleUpdateController::updateRole methods: [PATCH] requirements: roleId: \d+ ibexa.rest.update_role_draft: path: /user/roles/{roleId}/draft - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RoleDraftUpdateController:updateRoleDraft + controller: Ibexa\Rest\Server\Controller\Role\RoleDraftUpdateController::updateRoleDraft methods: [PATCH] requirements: roleId: \d+ ibexa.rest.publish_role_draft: path: /user/roles/{roleId}/draft - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RoleDraftPublishController:publishRoleDraft + controller: Ibexa\Rest\Server\Controller\Role\RoleDraftPublishController::publishRoleDraft methods: [PUBLISH] requirements: roleId: \d+ ibexa.rest.delete_role: path: /user/roles/{roleId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RoleDeleteController:deleteRole + controller: Ibexa\Rest\Server\Controller\Role\RoleDeleteController::deleteRole methods: [DELETE] requirements: roleId: \d+ ibexa.rest.delete_role_draft: path: /user/roles/{roleId}/draft - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RoleDraftDeleteController:deleteRoleDraft + controller: Ibexa\Rest\Server\Controller\Role\RoleDraftDeleteController::deleteRoleDraft methods: [DELETE] requirements: roleId: \d+ ibexa.rest.load_policies: path: /user/roles/{roleId}/policies - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RolePolicyListController:loadPolicies + controller: Ibexa\Rest\Server\Controller\Role\RolePolicyListController::loadPolicies methods: [GET] requirements: roleId: \d+ ibexa.rest.add_policy: path: /user/roles/{roleId}/policies - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RolePolicyCreateController:addPolicy + controller: Ibexa\Rest\Server\Controller\Role\RolePolicyCreateController::addPolicy methods: [POST] requirements: roleId: \d+ ibexa.rest.delete_policies: path: /user/roles/{roleId}/policies - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RolePolicyDeleteAllFromRoleController:deletePolicies + controller: Ibexa\Rest\Server\Controller\Role\RolePolicyDeleteAllFromRoleController::deletePolicies methods: [DELETE] requirements: roleId: \d+ ibexa.rest.load_policy: path: /user/roles/{roleId}/policies/{policyId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RolePolicyLoadByIdController:loadPolicy + controller: Ibexa\Rest\Server\Controller\Role\RolePolicyLoadByIdController::loadPolicy methods: [GET] requirements: roleId: \d+ @@ -1032,8 +905,7 @@ ibexa.rest.load_policy: ibexa.rest.update_policy: path: /user/roles/{roleId}/policies/{policyId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RolePolicyUpdateController:updatePolicy + controller: Ibexa\Rest\Server\Controller\Role\RolePolicyUpdateController::updatePolicy methods: [PATCH] requirements: roleId: \d+ @@ -1041,14 +913,12 @@ ibexa.rest.update_policy: ibexa.rest.delete_policy: path: /user/roles/{roleId}/policies/{policyId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RolePolicyDeleteController:deletePolicy + controller: Ibexa\Rest\Server\Controller\Role\RolePolicyDeleteController::deletePolicy methods: [DELETE] requirements: roleId: \d+ policyId: \d+ - # Users ibexa.rest.user_group.move: @@ -1063,20 +933,17 @@ ibexa.rest.user_group.move: ibexa.rest.verify_users: path: /user/users - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserVerifyController:verifyUsers + controller: Ibexa\Rest\Server\Controller\User\UserVerifyController::verifyUsers methods: [HEAD] ibexa.rest.load_users: path: /user/users - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserListController:loadUsers + controller: Ibexa\Rest\Server\Controller\User\UserListController::loadUsers methods: [GET] ibexa.rest.load_user: path: /user/users/{userId} - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserLoadByIdController:loadUser + controller: Ibexa\Rest\Server\Controller\User\UserLoadByIdController::loadUser methods: [GET] requirements: userId: \d+ @@ -1088,40 +955,35 @@ ibexa.rest.current_user: ibexa.rest.update_user: path: /user/users/{userId} - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserUpdateController:updateUser + controller: Ibexa\Rest\Server\Controller\User\UserUpdateController::updateUser methods: [PATCH] requirements: userId: \d+ ibexa.rest.delete_user: path: /user/users/{userId} - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserDeleteController:deleteUser + controller: Ibexa\Rest\Server\Controller\User\UserDeleteController::deleteUser methods: [DELETE] requirements: userId: \d+ ibexa.rest.load_user_groups_of_user: path: /user/users/{userId}/groups - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserGroupsOfUserListController:loadUserGroupsOfUser + controller: Ibexa\Rest\Server\Controller\User\UserGroupsOfUserListController::loadUserGroupsOfUser methods: [GET] requirements: userId: \d+ ibexa.rest.assign_user_to_user_group: path: /user/users/{userId}/groups - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserAssignToUserGroupController:assignUserToUserGroup + controller: Ibexa\Rest\Server\Controller\User\UserAssignToUserGroupController::assignUserToUserGroup methods: [POST] requirements: userId: \d+ ibexa.rest.unassign_user_from_user_group: path: /user/users/{userId}/groups/{groupPath} - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserUnassignFromUserGroupController:unassignUserFromUserGroup + controller: Ibexa\Rest\Server\Controller\User\UserUnassignFromUserGroupController::unassignUserFromUserGroup methods: [DELETE] requirements: userId: \d+ @@ -1129,32 +991,28 @@ ibexa.rest.unassign_user_from_user_group: ibexa.rest.load_user_drafts: path: /user/users/{userId}/drafts - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserDraftListController:loadUserDrafts + controller: Ibexa\Rest\Server\Controller\User\UserDraftListController::loadUserDrafts methods: [GET] requirements: userId: \d+ ibexa.rest.load_role_assignments_for_user: path: /user/users/{userId}/roles - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RoleAssignmentForUserListController:loadRoleAssignmentsForUser + controller: Ibexa\Rest\Server\Controller\Role\RoleAssignmentForUserListController::loadRoleAssignmentsForUser methods: [GET] requirements: userId: \d+ ibexa.rest.assign_role_to_user: path: /user/users/{userId}/roles - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RoleAssignToUserController:assignRoleToUser + controller: Ibexa\Rest\Server\Controller\Role\RoleAssignToUserController::assignRoleToUser methods: [POST] requirements: userId: \d+ ibexa.rest.load_role_assignment_for_user: path: /user/users/{userId}/roles/{roleId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RoleAssignmentForUserLoadByIdController:loadRoleAssignmentForUser + controller: Ibexa\Rest\Server\Controller\Role\RoleAssignmentForUserLoadByIdController::loadRoleAssignmentForUser methods: [GET] requirements: userId: \d+ @@ -1162,8 +1020,7 @@ ibexa.rest.load_role_assignment_for_user: ibexa.rest.unassign_role_from_user: path: /user/users/{userId}/roles/{roleId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RoleUnassignFromUserController:unassignRoleFromUser + controller: Ibexa\Rest\Server\Controller\Role\RoleUnassignFromUserController::unassignRoleFromUser methods: [DELETE] requirements: userId: \d+ @@ -1171,106 +1028,92 @@ ibexa.rest.unassign_role_from_user: ibexa.rest.load_user_groups: path: /user/groups - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserGroupListController:loadUserGroups + controller: Ibexa\Rest\Server\Controller\User\UserGroupListController::loadUserGroups methods: [GET] ibexa.rest.load_root_user_group: path: /user/groups/root - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserGroupOfRootLoadController:loadRootUserGroup + controller: Ibexa\Rest\Server\Controller\User\UserGroupOfRootLoadController::loadRootUserGroup methods: [GET] ibexa.rest.create_root_user_group: path: /user/groups/subgroups - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserGroupCreateController:createUserGroup + controller: Ibexa\Rest\Server\Controller\User\UserGroupCreateController::createUserGroup methods: [POST] ibexa.rest.load_user_group: path: /user/groups/{groupPath} - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserGroupLoadByPathController:loadUserGroup + controller: Ibexa\Rest\Server\Controller\User\UserGroupLoadByPathController::loadUserGroup methods: [GET] requirements: groupPath: "[0-9/]+" ibexa.rest.update_user_group: path: /user/groups/{groupPath} - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserGroupUpdateController:updateUserGroup + controller: Ibexa\Rest\Server\Controller\User\UserGroupUpdateController::updateUserGroup methods: [PATCH] requirements: groupPath: "[0-9/]+" ibexa.rest.delete_user_group: path: /user/groups/{groupPath} - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserGroupDeleteController:deleteUserGroup + controller: Ibexa\Rest\Server\Controller\User\UserGroupDeleteController::deleteUserGroup methods: [DELETE] requirements: groupPath: "[0-9/]+" ibexa.rest.move_user_group: path: /user/groups/{groupPath} - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserGroupMoveController:moveUserGroup + controller: Ibexa\Rest\Server\Controller\User\UserGroupMoveController::moveUserGroup methods: [MOVE] requirements: groupPath: "[0-9/]+" ibexa.rest.load_sub_user_groups: path: /user/groups/{groupPath}/subgroups - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserSubGroupListController:loadSubUserGroups + controller: Ibexa\Rest\Server\Controller\User\UserSubGroupListController::loadSubUserGroups methods: [GET] requirements: groupPath: "[0-9/]+" ibexa.rest.create_user_group: path: /user/groups/{groupPath}/subgroups - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserGroupCreateController:createUserGroup + controller: Ibexa\Rest\Server\Controller\User\UserGroupCreateController::createUserGroup methods: [POST] requirements: groupPath: "[0-9/]+" ibexa.rest.load_users_from_group: path: /user/groups/{groupPath}/users - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserGroupUsersListController:loadUsersFromGroup + controller: Ibexa\Rest\Server\Controller\User\UserGroupUsersListController::loadUsersFromGroup methods: [GET] requirements: groupPath: "[0-9/]+" ibexa.rest.create_user: path: /user/groups/{groupPath}/users - defaults: - _controller: Ibexa\Rest\Server\Controller\User\UserCreateController:createUser + controller: Ibexa\Rest\Server\Controller\User\UserCreateController::createUser methods: [POST] requirements: groupPath: "[0-9/]+" ibexa.rest.load_role_assignments_for_user_group: path: /user/groups/{groupPath}/roles - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RoleAssignmentForUserGroupListController:loadRoleAssignmentsForUserGroup + controller: Ibexa\Rest\Server\Controller\Role\RoleAssignmentForUserGroupListController::loadRoleAssignmentsForUserGroup methods: [GET] requirements: groupPath: "[0-9/]+" ibexa.rest.assign_role_to_user_group: path: /user/groups/{groupPath}/roles - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RoleAssignToUserGroupController:assignRoleToUserGroup + controller: Ibexa\Rest\Server\Controller\Role\RoleAssignToUserGroupController::assignRoleToUserGroup methods: [POST] requirements: groupPath: "[0-9/]+" ibexa.rest.load_role_assignment_for_user_group: path: /user/groups/{groupPath}/roles/{roleId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RoleAssignmentForUserGroupLoadByIdController:loadRoleAssignmentForUserGroup + controller: Ibexa\Rest\Server\Controller\Role\RoleAssignmentForUserGroupLoadByIdController::loadRoleAssignmentForUserGroup methods: [GET] requirements: groupPath: "[0-9/]+" @@ -1278,8 +1121,7 @@ ibexa.rest.load_role_assignment_for_user_group: ibexa.rest.unassign_role_from_user_group: path: /user/groups/{groupPath}/roles/{roleId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Role\RoleUnassignFromUserGroupController:unassignRoleFromUserGroup + controller: Ibexa\Rest\Server\Controller\Role\RoleUnassignFromUserGroupController::unassignRoleFromUserGroup methods: [DELETE] requirements: groupPath: "[0-9/]+" @@ -1287,8 +1129,8 @@ ibexa.rest.unassign_role_from_user_group: ibexa.rest.create_session: path: /user/sessions + controller: Ibexa\Rest\Server\Controller\SessionController::createSessionAction defaults: - _controller: Ibexa\Rest\Server\Controller\Session\SessionCreateController:createSessionAction csrf_protection: false methods: [POST] @@ -1301,89 +1143,77 @@ ibexa.rest.check_session: ibexa.rest.delete_session: path: /user/sessions/{sessionId} + controller: Ibexa\Rest\Server\Controller\SessionController::deleteSessionAction defaults: - _controller: Ibexa\Rest\Server\Controller\Session\SessionDeleteController:deleteSessionAction csrf_protection: false methods: [DELETE] # URL aliases - ibexa.rest.list_global_url_aliases: path: /content/urlaliases - defaults: - _controller: Ibexa\Rest\Server\Controller\URLAlias\URLAliasListGlobalController:listGlobalURLAliases + controller: Ibexa\Rest\Server\Controller\URLAlias\URLAliasListGlobalController::listGlobalURLAliases methods: [GET] ibexa.rest.list_location_url_aliases: path: /content/locations/{locationPath}/urlaliases - defaults: - _controller: Ibexa\Rest\Server\Controller\URLAlias\URLAliasListLocationController:listLocationURLAliases + controller: Ibexa\Rest\Server\Controller\URLAlias\URLAliasListLocationController::listLocationURLAliases methods: [GET] requirements: locationPath: "[0-9/]+" ibexa.rest.create_url_alias: path: /content/urlaliases - defaults: - _controller: Ibexa\Rest\Server\Controller\URLAlias\URLAliasCreateController:createURLAlias + controller: Ibexa\Rest\Server\Controller\URLAlias\URLAliasCreateController::createURLAlias methods: [POST] ibexa.rest.load_url_alias: path: /content/urlaliases/{urlAliasId} - defaults: - _controller: Ibexa\Rest\Server\Controller\URLAlias\URLAliasLoadByIdController:loadURLAlias + controller: Ibexa\Rest\Server\Controller\URLAlias\URLAliasLoadByIdController::loadURLAlias methods: [GET] ibexa.rest.delete_url_alias: path: /content/urlaliases/{urlAliasId} - defaults: - _controller: Ibexa\Rest\Server\Controller\URLAlias\URLAliasDeleteController:deleteURLAlias + controller: Ibexa\Rest\Server\Controller\URLAlias\URLAliasDeleteController::deleteURLAlias methods: [DELETE] - # Services - ibexa.rest.load_country_list: path: /services/countries - defaults: - _controller: Ibexa\Rest\Server\Controller\Services:loadCountryList + controller: Ibexa\Rest\Server\Controller\Services::loadCountryList methods: [GET] # Bookmark ibexa.rest.create_bookmark: path: /bookmark/{locationId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Bookmark\BookmarkCreateController:createBookmark + controller: Ibexa\Rest\Server\Controller\Bookmark\BookmarkCreateController::createBookmark methods: [POST] requirements: locationId: "[0-9]+" ibexa.rest.delete_bookmark: path: /bookmark/{locationId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Bookmark\BookmarkDeleteController:deleteBookmark + controller: Ibexa\Rest\Server\Controller\Bookmark\BookmarkDeleteController::deleteBookmark methods: [DELETE] requirements: locationId: "[0-9]+" ibexa.rest.is_bookmarked: path: /bookmark/{locationId} - defaults: - _controller: Ibexa\Rest\Server\Controller\Bookmark\BookmarkIsBookmarkedController:isBookmarked + controller: Ibexa\Rest\Server\Controller\Bookmark\BookmarkIsBookmarkedController::isBookmarked methods: [GET, HEAD] requirements: locationId: "[0-9]+" ibexa.rest.load_bookmarks: path: /bookmark - defaults: - _controller: Ibexa\Rest\Server\Controller\Bookmark\BookmarkListController:loadBookmarks + controller: Ibexa\Rest\Server\Controller\Bookmark\BookmarkListController::loadBookmarks methods: [GET] # JWT + ibexa.rest.create_token: path: /user/token/jwt controller: Ibexa\Rest\Server\Controller\JWT::createToken diff --git a/src/bundle/Resources/config/services.yml b/src/bundle/Resources/config/services.yml index aa7f47c2c..68fe2ad6b 100644 --- a/src/bundle/Resources/config/services.yml +++ b/src/bundle/Resources/config/services.yml @@ -1,9 +1,9 @@ parameters: ibexa.rest.output.visitor.json.regexps: - - '(^application/vnd\.ibexa\.api\.[A-Za-z]+\+json$)' + - '(^application/vnd\.ibexa\.api(\.[A-Za-z]+)+\+json$)' - '(^application/json$)' ibexa.rest.output.visitor.xml.regexps: - - '(^application/vnd\.ibexa\.api\.[A-Za-z]+\+xml$)' + - '(^application/vnd\.ibexa\.api(\.[A-Za-z]+)+\+xml$)' - '(^application/xml$)' - '(^.*/.*$)' ibexa.rest.path_prefix.pattern: !php/const \Ibexa\Bundle\Rest\UriParser\UriParser::DEFAULT_REST_PREFIX_PATTERN @@ -42,11 +42,6 @@ services: - '@ibexa.api.service.field_type' - '@Ibexa\Rest\FieldTypeProcessorRegistry' - Ibexa\Bundle\Rest\RequestParser\Router: - arguments: - $router: '@router' - $uriParser: '@Ibexa\Contracts\Rest\UriParser\UriParserInterface' - Ibexa\Contracts\Rest\UriParser\UriParserInterface: '@Ibexa\Bundle\Rest\UriParser\UriParser' Ibexa\Bundle\Rest\UriParser\UriParser: @@ -74,10 +69,9 @@ services: Ibexa\Rest\Server\Controller: autowire: true calls: - - [ setContainer, ["@service_container"] ] - [ setInputDispatcher, ['@Ibexa\Rest\Input\Dispatcher'] ] - [ setRouter, ["@router"] ] - - [ setRequestParser, ['@Ibexa\Bundle\Rest\RequestParser\Router'] ] + - [ setUriParser, ['@Ibexa\Contracts\Rest\UriParser\UriParserInterface'] ] - [ setRepository, ['@ibexa.api.repository'] ] Ibexa\Rest\Server\Controller\Root: @@ -138,6 +132,7 @@ services: resource: '../../../lib/Server/Controller/ObjectState' autowire: true autoconfigure: true + - '@Ibexa\Contracts\Core\Repository\ContentService\RelationListFacadeInterface' tags: - controller.service_arguments @@ -344,6 +339,7 @@ services: arguments: $normalizer: '@ibexa.rest.serializer' $logger: '@logger' + $strictMode: '%ibexa.rest.strict_mode%' tags: - { name: monolog.logger, channel: ibexa.rest } @@ -357,6 +353,7 @@ services: arguments: $normalizer: '@ibexa.rest.serializer' $logger: '@logger' + $strictMode: '%ibexa.rest.strict_mode%' tags: - { name: monolog.logger, channel: ibexa.rest } @@ -400,3 +397,13 @@ services: Ibexa\Contracts\Rest\Input\MediaTypeParser: ~ Ibexa\Contracts\Rest\Input\MediaTypeParserInterface: '@Ibexa\Contracts\Rest\Input\MediaTypeParser' + + Ibexa\Contracts\Rest\Input\Parser\Query\Criterion\BaseCriterionProcessor: + abstract: true + arguments: + $parsingDispatcher: '@Ibexa\Contracts\Rest\Input\ParsingDispatcher' + + Ibexa\Contracts\Rest\Input\Parser\Query\SortClause\BaseSortClauseProcessor: + abstract: true + arguments: + $parsingDispatcher: '@Ibexa\Contracts\Rest\Input\ParsingDispatcher' diff --git a/src/bundle/Resources/config/value_object_visitors.yml b/src/bundle/Resources/config/value_object_visitors.yml index a954a3af6..802bc84de 100644 --- a/src/bundle/Resources/config/value_object_visitors.yml +++ b/src/bundle/Resources/config/value_object_visitors.yml @@ -1,47 +1,7 @@ -parameters: - - # Errors - - # Section - - # URLWildcard - - # URLAlias - - # Content - - # User group - - # User - - # ContentType - - # Object state - - # View - - # Trash - - # Location - - # Roles - - # Relation - - # Field Definition - - # Content type Group - - # HTTP - - # Cache - - # Services - services: Ibexa\Contracts\Rest\Output\ValueObjectVisitor: calls: - - [ setRequestParser, [ '@Ibexa\Bundle\Rest\RequestParser\Router' ] ] + - [ setUriParser, [ '@Ibexa\Contracts\Rest\UriParser\UriParserInterface' ] ] - [ setRouter, [ "@router" ] ] - [ setTemplateRouter, [ '@Ibexa\Bundle\Core\Routing\DefaultRouter'] ] @@ -261,7 +221,7 @@ services: parent: Ibexa\Contracts\Rest\Output\ValueObjectVisitor class: Ibexa\Rest\Server\Output\ValueObjectVisitor\UserGroup arguments: - $contentService: '@ibexa.api.service.content' + $relationListFacade: '@Ibexa\Contracts\Core\Repository\ContentService\RelationListFacadeInterface' tags: - { name: ibexa.rest.output.value_object.visitor, type: Ibexa\Contracts\Core\Repository\Values\User\UserGroup } @@ -312,7 +272,7 @@ services: parent: Ibexa\Contracts\Rest\Output\ValueObjectVisitor class: Ibexa\Rest\Server\Output\ValueObjectVisitor\User arguments: - $contentService: '@ibexa.api.service.content' + $relationListFacade: '@Ibexa\Contracts\Core\Repository\ContentService\RelationListFacadeInterface' tags: - { name: ibexa.rest.output.value_object.visitor, type: Ibexa\Contracts\Core\Repository\Values\User\User } @@ -517,7 +477,7 @@ services: - { name: ibexa.rest.output.value_object.visitor, type: Ibexa\Contracts\Core\Repository\Values\Content\Location } arguments: $locationService: '@ibexa.api.service.location' - $contentService: '@ibexa.api.service.content' + $relationListFacade: '@Ibexa\Contracts\Core\Repository\ContentService\RelationListFacadeInterface' Ibexa\Rest\Server\Output\ValueObjectVisitor\LocationList: parent: Ibexa\Contracts\Rest\Output\ValueObjectVisitor @@ -542,7 +502,9 @@ services: Ibexa\Rest\Server\Output\ValueObjectVisitor\RestExecutedView: parent: Ibexa\Contracts\Rest\Output\ValueObjectVisitor class: Ibexa\Rest\Server\Output\ValueObjectVisitor\RestExecutedView - arguments: [ '@ibexa.api.service.location', '@ibexa.api.service.content' ] + arguments: + - '@ibexa.api.service.location' + - '@Ibexa\Contracts\Core\Repository\ContentService\RelationListFacadeInterface' tags: - { name: ibexa.rest.output.value_object.visitor, type: Ibexa\Rest\Server\Values\RestExecutedView } diff --git a/src/bundle/Routing/OptionsLoader.php b/src/bundle/Routing/OptionsLoader.php index 5fc77444c..bacfbdb0b 100644 --- a/src/bundle/Routing/OptionsLoader.php +++ b/src/bundle/Routing/OptionsLoader.php @@ -30,12 +30,12 @@ public function __construct(RouteCollectionMapper $mapper) * * @return \Symfony\Component\Routing\RouteCollection */ - public function load($resource, $type = null) + public function load(mixed $resource, $type = null) { return $this->routeCollectionMapper->mapCollection($this->import($resource)); } - public function supports($resource, $type = null) + public function supports($resource, $type = null): bool { return $type === 'rest_options'; } diff --git a/src/contracts/Exceptions/NotFoundException.php b/src/contracts/Exceptions/NotFoundException.php index 33a76d759..d0fbc5e8d 100644 --- a/src/contracts/Exceptions/NotFoundException.php +++ b/src/contracts/Exceptions/NotFoundException.php @@ -10,10 +10,10 @@ use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException as APINotFoundException; /** - * Implementation of the {@link \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException} - * interface. + * REST API equivalent of PHP API's NotFoundException. * - * @see \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * Implementation of the {@see \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException} + * interface. */ class NotFoundException extends APINotFoundException { diff --git a/src/contracts/Input/Parser/Query/Criterion/BaseCriterionProcessor.php b/src/contracts/Input/Parser/Query/Criterion/BaseCriterionProcessor.php new file mode 100644 index 000000000..37f18237e --- /dev/null +++ b/src/contracts/Input/Parser/Query/Criterion/BaseCriterionProcessor.php @@ -0,0 +1,73 @@ + 'LogicalAnd', + 'OR' => 'LogicalOr', + 'NOT' => 'LogicalNot', + ]; + + private ParsingDispatcher $parsingDispatcher; + + public function __construct(ParsingDispatcher $parsingDispatcher) + { + $this->parsingDispatcher = $parsingDispatcher; + } + + final public function processCriteria(array $criteriaData): iterable + { + if (empty($criteriaData)) { + yield from []; + } + + foreach ($criteriaData as $criterionName => $criterionData) { + $mediaType = $this->getCriterionMediaType($criterionName); + + try { + yield $this->parsingDispatcher->parse([$criterionName => $criterionData], $mediaType); + } catch (Exceptions\Parser $e) { + throw new Exceptions\Parser($this->getParserInvalidCriterionMessage($criterionName), 0, $e); + } + } + } + + private function getCriterionMediaType(string $criterionName): string + { + if (self::CRITERION_SUFFIX === substr($criterionName, -strlen(self::CRITERION_SUFFIX))) { + $criterionName = substr($criterionName, 0, -strlen(self::CRITERION_SUFFIX)); + } + + if (isset(self::LOGICAL_OPERATOR_CRITERION_MAP[$criterionName])) { + $criterionName = self::LOGICAL_OPERATOR_CRITERION_MAP[$criterionName]; + } + + $mediaTypePrefix = $this->getMediaTypePrefix(); + if ('.' !== substr($mediaTypePrefix, strlen($mediaTypePrefix) - 1)) { + $mediaTypePrefix .= '.'; + } + + return $mediaTypePrefix . $criterionName; + } + + abstract protected function getMediaTypePrefix(): string; + + abstract protected function getParserInvalidCriterionMessage(string $criterionName): string; +} diff --git a/src/contracts/Input/Parser/Query/Criterion/CriterionProcessorInterface.php b/src/contracts/Input/Parser/Query/Criterion/CriterionProcessorInterface.php new file mode 100644 index 000000000..e468ff198 --- /dev/null +++ b/src/contracts/Input/Parser/Query/Criterion/CriterionProcessorInterface.php @@ -0,0 +1,24 @@ +> $criteriaData + * + * @return iterable + */ + public function processCriteria(array $criteriaData): iterable; +} diff --git a/src/contracts/Input/Parser/Query/SortClause/BaseSortClauseProcessor.php b/src/contracts/Input/Parser/Query/SortClause/BaseSortClauseProcessor.php new file mode 100644 index 000000000..4405b1396 --- /dev/null +++ b/src/contracts/Input/Parser/Query/SortClause/BaseSortClauseProcessor.php @@ -0,0 +1,58 @@ +parsingDispatcher = $parsingDispatcher; + } + + final public function processSortClauses(array $sortClauseData): iterable + { + if (empty($sortClauseData)) { + yield from []; + } + + foreach ($sortClauseData as $sortClauseName => $direction) { + $mediaType = $this->getSortClauseMediaType($sortClauseName); + + try { + yield $this->parsingDispatcher->parse([$sortClauseName => $direction], $mediaType); + } catch (Exceptions\Parser $e) { + throw new Exceptions\Parser($this->getParserInvalidSortClauseMessage($sortClauseName), 0, $e); + } + } + } + + abstract protected function getMediaTypePrefix(): string; + + abstract protected function getParserInvalidSortClauseMessage(string $sortClauseName): string; + + private function getSortClauseMediaType(string $sortClauseName): string + { + $mediaTypePrefix = $this->getMediaTypePrefix(); + if ('.' !== substr($mediaTypePrefix, strlen($mediaTypePrefix) - 1)) { + $mediaTypePrefix .= '.'; + } + + return $mediaTypePrefix . $sortClauseName; + } +} diff --git a/src/contracts/Input/Parser/Query/SortClause/SortClauseProcessorInterface.php b/src/contracts/Input/Parser/Query/SortClause/SortClauseProcessorInterface.php new file mode 100644 index 000000000..ac8ef713b --- /dev/null +++ b/src/contracts/Input/Parser/Query/SortClause/SortClauseProcessorInterface.php @@ -0,0 +1,24 @@ + $sortClauseData + * + * @return iterable + */ + public function processSortClauses(array $sortClauseData): iterable; +} diff --git a/src/contracts/Output/Exceptions/AbstractExceptionVisitor.php b/src/contracts/Output/Exceptions/AbstractExceptionVisitor.php new file mode 100644 index 000000000..30a273f41 --- /dev/null +++ b/src/contracts/Output/Exceptions/AbstractExceptionVisitor.php @@ -0,0 +1,161 @@ + + */ + protected static $httpStatusCodes = [ + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Time-out', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested range not satisfiable', + 417 => 'Expectation Failed', + 418 => "I'm a teapot", + 421 => 'There are too many connections from your internet address', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 425 => 'Unordered Collection', + 426 => 'Upgrade Required', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Time-out', + 505 => 'HTTP Version not supported', + 506 => 'Variant Also Negotiates', + 507 => 'Insufficient Storage', + 509 => 'Bandwidth Limit Exceeded', + 510 => 'Not Extended', + ]; + + /** + * Returns HTTP status code. + * + * @return int + */ + protected function getStatus() + { + return 500; + } + + /** + * @param \Exception $data + * + * @return void + */ + public function visit(Visitor $visitor, Generator $generator, $data) + { + $generator->startObjectElement('ErrorMessage'); + + $visitor->setHeader('Content-Type', $generator->getMediaType('ErrorMessage')); + + $statusCode = $this->generateErrorCode($generator, $visitor, $data); + + $errorMessage = $this->getErrorMessage($data, $statusCode); + $generator->valueElement('errorMessage', $errorMessage); + + $errorDescription = $this->getErrorDescription($data, $statusCode); + $generator->valueElement('errorDescription', $errorDescription); + + if ($this->canDisplayExceptionTrace()) { + $generator->valueElement('trace', $data->getTraceAsString()); + $generator->valueElement('file', $data->getFile()); + $generator->valueElement('line', $data->getLine()); + } + + $previous = $data->getPrevious(); + if ($previous !== null && $this->canDisplayPreviousException()) { + $generator->startObjectElement('Previous', 'ErrorMessage'); + $visitor->visitValueObject($previous); + $generator->endObjectElement('Previous'); + } + + $generator->endObjectElement('ErrorMessage'); + } + + protected function generateErrorCode(Generator $generator, Visitor $visitor, \Exception $e): int + { + $statusCode = $this->getStatus(); + $visitor->setStatus($statusCode); + + $generator->valueElement('errorCode', $statusCode); + + return $statusCode; + } + + protected function getErrorMessage(\Exception $data, int $statusCode): string + { + return static::$httpStatusCodes[$statusCode] ?? static::$httpStatusCodes[500]; + } + + protected function getErrorDescription(\Exception $data, int $statusCode): string + { + $translator = $this->getTranslator(); + if ($statusCode < 500 || $this->canDisplayExceptionMessage()) { + $errorDescription = $data instanceof Translatable && $translator + ? /** @Ignore */ + $translator->trans($data->getMessageTemplate(), $data->getParameters(), 'ibexa_repository_exceptions') + : $data->getMessage(); + } else { + // Do not leak any file paths and sensitive data on production environments + $errorDescription = $translator + ? /** @Desc("An error has occurred. Please try again later or contact your Administrator.") */ + $translator->trans('non_verbose_error', [], 'ibexa_repository_exceptions') + : 'An error has occurred. Please try again later or contact your Administrator.'; + } + + return $errorDescription; + } + + protected function getTranslator(): ?TranslatorInterface + { + return null; + } + + protected function canDisplayExceptionTrace(): bool + { + return false; + } + + protected function canDisplayPreviousException(): bool + { + return false; + } + + protected function canDisplayExceptionMessage(): bool + { + return false; + } +} diff --git a/src/contracts/Output/Generator.php b/src/contracts/Output/Generator.php index 798ceacb7..165da2176 100644 --- a/src/contracts/Output/Generator.php +++ b/src/contracts/Output/Generator.php @@ -320,21 +320,6 @@ protected function checkEndAttribute($data) */ abstract public function getMediaType($name); - /** - * Generates a media type from $name and $type. - * - * @deprecated 6.13.5 please start to use generateMediaTypeWithVendor() - * - * @param string $name - * @param string $type - * - * @return string - */ - protected function generateMediaType($name, $type) - { - return "application/vnd.ibexa.api.{$name}+{$type}"; - } - /** * Generates a media type from $name, $type and $vendor. * diff --git a/src/contracts/Output/ValueObjectVisitor.php b/src/contracts/Output/ValueObjectVisitor.php index 3f25ba9cb..e53a9fd1f 100644 --- a/src/contracts/Output/ValueObjectVisitor.php +++ b/src/contracts/Output/ValueObjectVisitor.php @@ -9,7 +9,7 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Location; use Ibexa\Contracts\Core\Repository\Values\User\Limitation; -use Ibexa\Rest\RequestParser; +use Ibexa\Contracts\Rest\UriParser\UriParserInterface; use Symfony\Component\Routing\RouterInterface; /** @@ -17,12 +17,7 @@ */ abstract class ValueObjectVisitor { - /** - * URL handler for URL generation. - * - * @var \Ibexa\Rest\RequestParser - */ - protected $requestParser; + protected UriParserInterface $uriParser; /** * @var \Symfony\Component\Routing\RouterInterface @@ -56,9 +51,9 @@ public function setTemplateRouter(RouterInterface $templateRouter) $this->templateRouter = $templateRouter; } - public function setRequestParser(RequestParser $requestParser) + public function setUriParser(UriParserInterface $uriParser): void { - $this->requestParser = $requestParser; + $this->uriParser = $uriParser; } /** @@ -172,8 +167,6 @@ protected function serializeSortField($sortField) return 'PRIORITY'; case Location::SORT_FIELD_NAME: return 'NAME'; - case Location::SORT_FIELD_MODIFIED_SUBNODE: - return 'MODIFIED_SUBNODE'; case Location::SORT_FIELD_NODE_ID: return 'NODE_ID'; case Location::SORT_FIELD_CONTENTOBJECT_ID: diff --git a/src/contracts/Output/Visitor.php b/src/contracts/Output/Visitor.php index dd0124dd3..a71e7536d 100644 --- a/src/contracts/Output/Visitor.php +++ b/src/contracts/Output/Visitor.php @@ -50,7 +50,7 @@ public function __construct(Generator $generator, ValueObjectVisitorDispatcher $ { $this->generator = $generator; $this->valueObjectVisitorDispatcher = $valueObjectVisitorDispatcher; - $this->response = new Response('', 200); + $this->response = new Response('', Response::HTTP_OK); } /** @@ -114,7 +114,7 @@ public function visit($data) $response->setContent($this->generator->isEmpty() ? null : $this->generator->endDocument($data)); // reset the inner response - $this->response = new Response(null, 200); + $this->response = new Response(null, Response::HTTP_OK); $this->statusCode = null; return $response; diff --git a/src/lib/FieldTypeProcessor/ImageProcessor.php b/src/lib/FieldTypeProcessor/ImageProcessor.php index e209c7dc7..cd5a6acd0 100644 --- a/src/lib/FieldTypeProcessor/ImageProcessor.php +++ b/src/lib/FieldTypeProcessor/ImageProcessor.php @@ -64,7 +64,7 @@ public function postProcessValueHash($outgoingValueHash) return $outgoingValueHash; } - $outgoingValueHash['path'] = '/' . $outgoingValueHash['path']; + $outgoingValueHash['path'] = '/' . $outgoingValueHash['inputUri']; foreach ($this->variations as $variationIdentifier) { $outgoingValueHash['variations'][$variationIdentifier] = [ 'href' => $this->router->generate( diff --git a/src/lib/Input/BaseParser.php b/src/lib/Input/BaseParser.php index 9db5f87b6..0f5621bae 100644 --- a/src/lib/Input/BaseParser.php +++ b/src/lib/Input/BaseParser.php @@ -8,19 +8,14 @@ namespace Ibexa\Rest\Input; use Ibexa\Contracts\Rest\Input\Parser; -use Ibexa\Rest\RequestParser; +use Ibexa\Contracts\Rest\UriParser\UriParserInterface; abstract class BaseParser extends Parser { - /** - * URL handler. - * - * @var \Ibexa\Rest\RequestParser - */ - protected $requestParser; + protected UriParserInterface $uriParser; - public function setRequestParser(RequestParser $requestParser) + public function setUriParser(UriParserInterface $uriParser): void { - $this->requestParser = $requestParser; + $this->uriParser = $uriParser; } } diff --git a/src/lib/Input/Parser/ContentObjectStates.php b/src/lib/Input/Parser/ContentObjectStates.php index 60e74bf2e..88ac7f5c7 100644 --- a/src/lib/Input/Parser/ContentObjectStates.php +++ b/src/lib/Input/Parser/ContentObjectStates.php @@ -43,10 +43,10 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) $states[] = new RestObjectState( new ObjectState( [ - 'id' => $this->requestParser->parseHref($rawStateData['_href'], 'objectStateId'), + 'id' => $this->uriParser->getAttributeFromUri($rawStateData['_href'], 'objectStateId'), ] ), - $this->requestParser->parseHref($rawStateData['_href'], 'objectStateGroupId') + $this->uriParser->getAttributeFromUri($rawStateData['_href'], 'objectStateGroupId') ); } diff --git a/src/lib/Input/ParserTools.php b/src/lib/Input/ParserTools.php index fee1b08ef..805d8a939 100644 --- a/src/lib/Input/ParserTools.php +++ b/src/lib/Input/ParserTools.php @@ -163,8 +163,6 @@ public function parseDefaultSortField($defaultSortFieldString) return Values\Content\Location::SORT_FIELD_PRIORITY; case 'NAME': return Values\Content\Location::SORT_FIELD_NAME; - case 'MODIFIED_SUBNODE': - return Values\Content\Location::SORT_FIELD_MODIFIED_SUBNODE; case 'NODE_ID': return Values\Content\Location::SORT_FIELD_NODE_ID; case 'CONTENTOBJECT_ID': diff --git a/src/lib/Output/FieldTypeSerializer.php b/src/lib/Output/FieldTypeSerializer.php index 235ed7571..22bd17791 100644 --- a/src/lib/Output/FieldTypeSerializer.php +++ b/src/lib/Output/FieldTypeSerializer.php @@ -10,7 +10,6 @@ use Ibexa\Contracts\Core\Repository\FieldType; use Ibexa\Contracts\Core\Repository\FieldTypeService; use Ibexa\Contracts\Core\Repository\Values\Content\Field; -use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; use Ibexa\Contracts\Rest\Output\Generator; use Ibexa\Rest\FieldTypeProcessorRegistry; @@ -41,17 +40,6 @@ public function __construct(FieldTypeService $fieldTypeService, FieldTypeProcess $this->fieldTypeProcessorRegistry = $fieldTypeProcessorRegistry; } - /** - * @deprecated 4.6.0 The "FieldTypeSerializer::serializeFieldValue()" method is deprecated, will be removed in 5.0. - * Use "FieldTypeSerializer::serializeContentFieldValue()" instead. - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - */ - public function serializeFieldValue(Generator $generator, ContentType $contentType, Field $field): void - { - $this->serializeContentFieldValue($generator, $field); - } - /** * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ diff --git a/src/lib/Output/Generator/Json/FieldTypeHashGenerator.php b/src/lib/Output/Generator/Json/FieldTypeHashGenerator.php index c0755e50f..15303337b 100644 --- a/src/lib/Output/Generator/Json/FieldTypeHashGenerator.php +++ b/src/lib/Output/Generator/Json/FieldTypeHashGenerator.php @@ -20,12 +20,16 @@ class FieldTypeHashGenerator implements LoggerAwareInterface private NormalizerInterface $normalizer; + private bool $strictMode; + public function __construct( NormalizerInterface $normalizer, - ?LoggerInterface $logger = null + ?LoggerInterface $logger = null, + bool $strictMode = false ) { $this->normalizer = $normalizer; $this->logger = $logger ?? new NullLogger(); + $this->strictMode = $strictMode; } /** @@ -153,6 +157,9 @@ private function generateObjectValue($parent, object $value) try { $value = $this->normalizer->normalize($value, 'json', ['parent' => $parent]); } catch (ExceptionInterface $e) { + if ($this->strictMode) { + throw $e; + } $message = sprintf( 'Unable to normalize value for type "%s". %s. ' . 'Ensure that a normalizer is registered with tag: "%s".', diff --git a/src/lib/Output/Generator/Xml/FieldTypeHashGenerator.php b/src/lib/Output/Generator/Xml/FieldTypeHashGenerator.php index 6d7ec0875..6c3715e23 100644 --- a/src/lib/Output/Generator/Xml/FieldTypeHashGenerator.php +++ b/src/lib/Output/Generator/Xml/FieldTypeHashGenerator.php @@ -20,12 +20,16 @@ class FieldTypeHashGenerator implements LoggerAwareInterface private NormalizerInterface $normalizer; + private bool $strictMode; + public function __construct( NormalizerInterface $normalizer, - ?LoggerInterface $logger = null + ?LoggerInterface $logger = null, + bool $strictMode = false ) { $this->normalizer = $normalizer; $this->logger = $logger ?? new NullLogger(); + $this->strictMode = $strictMode; } /** @@ -244,6 +248,9 @@ private function generateObjectValue(object $value, \XmlWriter $writer, ?string try { $value = $this->normalizer->normalize($value, 'xml'); } catch (ExceptionInterface $e) { + if ($this->strictMode) { + throw $e; + } $message = sprintf( 'Unable to normalize value for type "%s". %s. ' . 'Ensure that a normalizer is registered with tag: "%s".', diff --git a/src/lib/RequestParser.php b/src/lib/RequestParser.php deleted file mode 100644 index 050e53142..000000000 --- a/src/lib/RequestParser.php +++ /dev/null @@ -1,43 +0,0 @@ - '/', - 'locations' => '/content/locations', - 'locationByRemote' => '/content/locations?remoteId={location}', - 'locationById' => '/content/locations?id={location}', - 'locationChildren' => '/content/locations{&location}/children', - 'locationUrlAliases' => '/content/locations{&location}/urlaliases', - 'location' => '/content/locations{&location}', - 'getImageVariation' => '/content/binary/images/{imageId}/variations/{variationIdentifier}', - 'objects' => '/content/objects', - 'objectByRemote' => '/content/objects?remoteId={object}', - 'object' => '/content/objects/{object}', - 'objectByLangCode' => '/content/objects/{object}/{lang_code}', - 'objectLocations' => '/content/objects/{object}/locations', - 'objectObjectStates' => '/content/objects/{object}/objectstates', - 'objectVersions' => '/content/objects/{object}/versions', - 'objectVersion' => '/content/objects/{object}/versions/{version}', - 'objectVersionRelations' => '/content/objects/{object}/versions/{version}/relations', - 'objectVersionRelation' => '/content/objects/{object}/versions/{version}/relations/{relation}', - 'objectCurrentVersion' => '/content/objects/{object}/currentversion', - 'objectrelations' => '/content/objects/{object}/relations', - 'objectrelation' => '/content/objects/{object}/relations/{relation}', - 'objectstategroups' => '/content/objectstategroups', - 'objectstategroup' => '/content/objectstategroups/{objectstategroup}', - 'objectstates' => '/content/objectstategroups/{objectstategroup}/objectstates', - 'objectstate' => '/content/objectstategroups/{objectstategroup}/objectstates/{objectstate}', - 'sections' => '/content/sections', - 'section' => '/content/sections/{section}', - 'sectionByIdentifier' => '/content/sections?identifier={section}', - 'trashItems' => '/content/trash', - 'trash' => '/content/trash/{trash}', - 'typegroups' => '/content/typegroups', - 'typegroupByIdentifier' => '/content/typegroups?identifier={&typegroup}', - 'typegroup' => '/content/typegroups/{typegroup}', - 'grouptypes' => '/content/typegroups/{typegroup}/types', - 'types' => '/content/types', - 'typeByIdentifier' => '/content/types?identifier={type}', - 'typeByRemoteId' => '/content/types?remoteId={type}', - 'type' => '/content/types/{type}', - 'typeFieldDefinitions' => '/content/types/{type}/fieldDefinitions', - 'typeFieldDefinition' => '/content/types/{type}/fieldDefinitions/{fieldDefinition}', - 'typeDraft' => '/content/types/{type}/draft', - 'typeFieldDefinitionsDraft' => '/content/types/{type}/draft/fieldDefinitions', - 'typeFieldDefinitionDraft' => '/content/types/{type}/draft/fieldDefinitions/{fieldDefinition}', - 'groupsOfType' => '/content/types/{type}/groups', - 'typeGroupAssign' => '/content/types/{type}/groups?group={&group}', - 'groupOfType' => '/content/types/{type}/groups/{group}', - 'urlWildcards' => '/content/urlwildcards', - 'urlWildcard' => '/content/urlwildcards/{urlwildcard}', - 'urlAliases' => '/content/urlaliases', - 'urlAlias' => '/content/urlaliases/{urlalias}', - 'views' => '/content/views', - 'view' => '/content/views/{view}', - 'viewResults' => '/content/views/{view}/results', - 'groups' => '/user/groups', - 'group' => '/user/groups{&group}', - 'groupRoleAssignments' => '/user/groups{&group}/roles', - 'groupRoleAssignment' => '/user/groups{&group}/roles/{role}', - 'groupSubgroups' => '/user/groups{&group}/subgroups', - 'groupUsers' => '/user/groups{&group}/users', - 'rootUserGroup' => '/user/groups/root', - 'rootUserGroupSubGroups' => '/user/groups/subgroups', - 'roles' => '/user/roles', - 'role' => '/user/roles/{role}', - 'roleByIdentifier' => '/user/roles?identifier={role}', - 'policies' => '/user/roles/{role}/policies', - 'policy' => '/user/roles/{role}/policies/{policy}', - 'users' => '/user/users', - 'user' => '/user/users/{user}', - 'userDrafts' => '/user/users/{user}/drafts', - 'userGroups' => '/user/users/{user}/groups', - 'userGroupAssign' => '/user/users/{user}/groups?group={&group}', - 'userGroup' => '/user/users/{user}/groups{&group}', - 'userRoleAssignments' => '/user/users/{user}/roles', - 'userRoleAssignment' => '/user/users/{user}/roles/{role}', - 'userPolicies' => '/user/policies?userId={user}', - 'userSession' => '/user/sessions/{sessionId}', - ]; -} diff --git a/src/lib/RequestParser/Pattern.php b/src/lib/RequestParser/Pattern.php deleted file mode 100644 index 3c0e5508c..000000000 --- a/src/lib/RequestParser/Pattern.php +++ /dev/null @@ -1,192 +0,0 @@ - $pattern) { - $this->addPattern($type, $pattern); - } - } - - /** - * Adds a pattern for a type. - * - * @param string $type - * @param string $pattern - */ - public function addPattern($type, $pattern) - { - $this->map[$type] = $pattern; - unset($this->compileCache[$type]); - } - - /** - * Parse URL and return the IDs contained in the URL. - * - * @param string $type - * @param string $url - * - * @return array - */ - public function parse($url) - { - foreach ($this->map as $pattern) { - $pattern = $this->compile($pattern); - if (preg_match($pattern, $url, $match)) { - // remove numeric keys - foreach ($match as $key => $value) { - if (is_numeric($key)) { - unset($match[$key]); - } - } - - return $match; - } - } - - throw new Exceptions\InvalidArgumentException("URL '$url' did not match any route."); - } - - /** - * Compiles a given pattern to a PCRE regular expression. - * - * @param string $pattern - * - * @return string - */ - protected function compile($pattern) - { - if (isset($this->compileCache[$pattern])) { - return $this->compileCache[$pattern]; - } - - $pcre = '(^'; - - do { - switch (true) { - case preg_match('(^[^{]+)', $pattern, $match): - $pattern = substr($pattern, strlen($match[0])); - $pcre .= preg_quote($match[0]); - break; - - case preg_match('(^' . self::STANDARD_VARIABLE_REGEX . ')', $pattern, $match): - $pattern = substr($pattern, strlen($match[0])); - $pcre .= '(?P<' . $match[1] . '>[^/]+)'; - break; - - case preg_match('(^' . self::SLASHES_VARIABLE_REGEX . ')', $pattern, $match): - $pattern = substr($pattern, strlen($match[0])); - $pcre .= '(?P<' . $match[1] . '>.+)'; - break; - - default: - throw new Exceptions\InvalidArgumentException("Invalid pattern part: '$pattern'."); - } - } while ($pattern); - - $pcre .= '$)S'; - - $this->compileCache[$pattern] = $pcre; - - return $pcre; - } - - /** - * Generate a URL of the given type from the specified values. - * - * @param string $type - * @param array $values - * - * @return string - */ - public function generate($type, array $values = []) - { - if (!isset($this->map[$type])) { - throw new Exceptions\InvalidArgumentException("No URL for type '$type' available."); - } - - $url = $this->map[$type]; - preg_match_all( - '(' . self::STANDARD_VARIABLE_REGEX . '|' . self::SLASHES_VARIABLE_REGEX . ')', - $url, - $matches, - PREG_SET_ORDER - ); - foreach ($matches as $matchSet) { - $variableName = empty($matchSet[1]) ? $matchSet[2] : $matchSet[1]; - if (!isset($values[$variableName])) { - throw new Exceptions\InvalidArgumentException("No value provided for '{$variableName}'."); - } - - $url = str_replace($matchSet[0], $values[$variableName], $url); - unset($values[$variableName]); - } - - if (count($values)) { - throw new Exceptions\InvalidArgumentException("Unused values in values array: '" . implode("', '", array_keys($values)) . "'."); - } - - return $url; - } - - public function parseHref($href, $attribute) - { - $parsingResult = $this->parse($href); - - if (!isset($parsingResult[$attribute])) { - throw new Exceptions\InvalidArgumentException("No attribute '$attribute' in route matched from $href\n" . print_r($parsingResult, true)); - } - - return $parsingResult[$attribute]; - } -} diff --git a/src/lib/Security/Authenticator/RestAuthenticator.php b/src/lib/Security/Authenticator/RestAuthenticator.php index 6aa1deccb..d9285ff4f 100644 --- a/src/lib/Security/Authenticator/RestAuthenticator.php +++ b/src/lib/Security/Authenticator/RestAuthenticator.php @@ -45,14 +45,13 @@ public function supports(Request $request): ?bool public function authenticate(Request $request): Passport { $existingUserToken = $this->fetchExistingToken($request); - if ($this->canUserFromSessionBeAuthenticated($existingUserToken)) { + if (null !== $existingUserToken && $this->canUserFromSessionBeAuthenticated($existingUserToken)) { $existingUser = $existingUserToken->getUser(); return $this->createAuthorizationPassport( - /** @phpstan-ignore-next-line */ $existingUser->getUserIdentifier(), - /** @phpstan-ignore-next-line */ - $existingUser->getPassword() + // @todo not sure how to refactor this + '' ); } @@ -101,7 +100,7 @@ private function fetchExistingToken(Request $request): ?TokenInterface $previousToken = $this->tokenStorage->getToken(); if ( $previousToken === null || - $previousToken->getUsername() !== $request->attributes->get('username') + $previousToken->getUserIdentifier() !== $request->attributes->get('username') ) { return null; } @@ -112,17 +111,11 @@ private function fetchExistingToken(Request $request): ?TokenInterface } /** - * @phpstan-assert-if-true !null $existingUserToken + * @phpstan-assert-if-true !null $existingUserToken->getUser() */ private function canUserFromSessionBeAuthenticated(?TokenInterface $existingUserToken): bool { - if ($existingUserToken === null) { - return false; - } - - $user = $existingUserToken->getUser(); - - return !($user === null || $user->getPassword() === null); + return $existingUserToken?->getUser() !== null; } private function createAuthorizationPassport(string $login, string $password): Passport diff --git a/src/lib/Server/Controller.php b/src/lib/Server/Controller.php index c1171409f..73d7a4d97 100644 --- a/src/lib/Server/Controller.php +++ b/src/lib/Server/Controller.php @@ -8,67 +8,47 @@ namespace Ibexa\Rest\Server; use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Rest\UriParser\UriParserInterface; use Ibexa\Rest\Input\Dispatcher as InputDispatcher; -use Ibexa\Rest\RequestParser; -use Symfony\Component\DependencyInjection\ContainerAwareInterface; -use Symfony\Component\DependencyInjection\ContainerAwareTrait; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\RouterInterface; -abstract class Controller implements ContainerAwareInterface +abstract class Controller { - use ContainerAwareTrait; + protected InputDispatcher $inputDispatcher; - /** - * @var \Ibexa\Rest\Input\Dispatcher - */ - protected $inputDispatcher; + protected RouterInterface $router; - /** - * @var \Symfony\Component\Routing\RouterInterface - */ - protected $router; + protected UriParserInterface $uriParser; - /** - * @var \Ibexa\Rest\RequestParser - */ - protected $requestParser; + protected Repository $repository; - /** - * Repository. - * - * @var \Ibexa\Contracts\Core\Repository\Repository - */ - protected $repository; - - public function setInputDispatcher(InputDispatcher $inputDispatcher) + public function setInputDispatcher(InputDispatcher $inputDispatcher): void { $this->inputDispatcher = $inputDispatcher; } - public function setRouter(RouterInterface $router) + public function setRouter(RouterInterface $router): void { $this->router = $router; } - public function setRepository(Repository $repository) + public function setRepository(Repository $repository): void { $this->repository = $repository; } - public function setRequestParser(RequestParser $requestParser) + public function setUriParser(UriParserInterface $uriParser): void { - $this->requestParser = $requestParser; + $this->uriParser = $uriParser; } /** * Extracts the requested media type from $request. * * @todo refactor, maybe to a REST Request with an accepts('content-type') method - * - * @return string */ - protected function getMediaType(Request $request) + protected function getMediaType(Request $request): string { foreach ($request->getAcceptableContentTypes() as $mimeType) { if (preg_match('(^([a-z0-9-/.]+)\+.*$)', strtolower($mimeType), $matches)) { diff --git a/src/lib/Server/Controller/Section/SectionCreateController.php b/src/lib/Server/Controller/Section/SectionCreateController.php index 1f2aa66fa..2ae04da23 100644 --- a/src/lib/Server/Controller/Section/SectionCreateController.php +++ b/src/lib/Server/Controller/Section/SectionCreateController.php @@ -9,7 +9,6 @@ use ApiPlatform\Metadata\Post; use ApiPlatform\OpenApi\Factory\OpenApiFactory; -use ApiPlatform\OpenApi\Model; use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; use Ibexa\Contracts\Core\Repository\SectionService; use Ibexa\Rest\Message; @@ -22,34 +21,33 @@ #[Post( uriTemplate: '/content/sections', name: 'Create new Section', - extraProperties: [OpenApiFactory::OVERRIDE_OPENAPI_RESPONSES => false], - openapi: new Model\Operation( - summary: 'Creates a new Section.', - tags: [ + openapiContext: [ + 'summary' => 'Creates a new Section.', + 'tags' => [ 'Section', ], - parameters: [ - new Model\Parameter( - name: 'Accept', - in: 'header', - required: true, - description: 'If set, the new Section is returned in XML or JSON format.', - schema: [ + 'parameters' => [ + [ + 'name' => 'Accept', + 'in' => 'header', + 'required' => true, + 'description' => 'If set, the new Section is returned in XML or JSON format.', + 'schema' => [ 'type' => 'string', ], - ), - new Model\Parameter( - name: 'Content-Type', - in: 'header', - required: true, - description: 'The Section input schema encoded in XML or JSON format.', - schema: [ + ], + [ + 'name' => 'Content-Type', + 'in' => 'header', + 'required' => true, + 'description' => 'The Section input schema encoded in XML or JSON format.', + 'schema' => [ 'type' => 'string', ], - ), + ], ], - requestBody: new Model\RequestBody( - content: new \ArrayObject([ + 'requestBody' => [ + 'content' => [ 'application/vnd.ibexa.api.SectionInput+xml' => [ 'schema' => [ '$ref' => '#/components/schemas/SectionInput', @@ -62,9 +60,9 @@ ], 'x-ibexa-example-file' => '@IbexaRestBundle/Resources/api_platform/examples/content/sections/POST/SectionInput.json.example', ], - ]), - ), - responses: [ + ], + ], + 'responses' => [ Response::HTTP_CREATED => [ 'content' => [ 'application/vnd.ibexa.api.Section+xml' => [ @@ -82,7 +80,7 @@ ], ], ], - ), + ], )] class SectionCreateController extends RestController { diff --git a/src/lib/Server/Controller/Section/SectionDeleteController.php b/src/lib/Server/Controller/Section/SectionDeleteController.php index 8336ea130..6c76f8049 100644 --- a/src/lib/Server/Controller/Section/SectionDeleteController.php +++ b/src/lib/Server/Controller/Section/SectionDeleteController.php @@ -8,7 +8,6 @@ namespace Ibexa\Rest\Server\Controller\Section; use ApiPlatform\Metadata\Delete; -use ApiPlatform\OpenApi\Model; use Ibexa\Contracts\Core\Repository\SectionService; use Ibexa\Rest\Server\Controller as RestController; use Ibexa\Rest\Server\Values\NoContent; @@ -17,22 +16,22 @@ #[Delete( uriTemplate: '/content/sections/{sectionId}', name: 'Delete Section', - openapi: new Model\Operation( - summary: 'The given Section is deleted.', - tags: [ + openapiContext: [ + 'summary' => 'The given Section is deleted.', + 'tags' => [ 'Section', ], - parameters: [ - new Model\Parameter( - name: 'sectionId', - in: 'path', - required: true, - schema: [ + 'parameters' => [ + [ + 'name' => 'sectionId', + 'in' => 'path', + 'required' => true, + 'schema' => [ 'type' => 'string', ], - ), + ], ], - responses: [ + 'responses' => [ Response::HTTP_NO_CONTENT => [ 'description' => 'No Content - given Section is deleted.', ], @@ -43,7 +42,7 @@ 'description' => 'Error - the Section does not exist.', ], ], - ), + ], )] class SectionDeleteController extends RestController { diff --git a/src/lib/Server/Controller/Trash/TrashItemRestoreController.php b/src/lib/Server/Controller/Trash/TrashItemRestoreController.php index e7f33c073..14340882a 100644 --- a/src/lib/Server/Controller/Trash/TrashItemRestoreController.php +++ b/src/lib/Server/Controller/Trash/TrashItemRestoreController.php @@ -49,7 +49,7 @@ public function restoreTrashItem($trashItemId, Request $request) if ($request->headers->has('Destination')) { $locationPathParts = explode( '/', - $this->requestParser->parseHref($request->headers->get('Destination'), 'locationPath') + $this->uriParser->getAttributeFromUri($request->headers->get('Destination'), 'locationPath') ); try { diff --git a/src/lib/Server/Controller/User/UserBaseController.php b/src/lib/Server/Controller/User/UserBaseController.php index 6120f6f71..0b9395522 100644 --- a/src/lib/Server/Controller/User/UserBaseController.php +++ b/src/lib/Server/Controller/User/UserBaseController.php @@ -39,12 +39,7 @@ class UserBaseController extends RestController protected SectionService $sectionService; - /** - * Repository. - * - * @var \Ibexa\Contracts\Core\Repository\Repository - */ - protected $repository; + protected Repository $repository; protected PermissionResolver $permissionResolver; diff --git a/src/lib/Server/Input/Parser/ContentCreate.php b/src/lib/Server/Input/Parser/ContentCreate.php index 06d178aa5..12f4e8328 100644 --- a/src/lib/Server/Input/Parser/ContentCreate.php +++ b/src/lib/Server/Input/Parser/ContentCreate.php @@ -109,7 +109,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) } $contentType = $this->contentTypeService->loadContentType( - $this->requestParser->parseHref($data['ContentType']['_href'], 'contentTypeId') + $this->uriParser->getAttributeFromUri($data['ContentType']['_href'], 'contentTypeId') ); $contentCreateStruct = $this->contentService->newContentCreateStruct($contentType, $data['mainLanguageCode']); @@ -119,7 +119,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) throw new Exceptions\Parser("Missing '_href' attribute for the Section element in ContentCreate."); } - $contentCreateStruct->sectionId = $this->requestParser->parseHref($data['Section']['_href'], 'sectionId'); + $contentCreateStruct->sectionId = $this->uriParser->getAttributeFromUri($data['Section']['_href'], 'sectionId'); } if (array_key_exists('alwaysAvailable', $data)) { @@ -139,7 +139,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) throw new Exceptions\Parser("Missing '_href' attribute for the User element in ContentCreate."); } - $contentCreateStruct->ownerId = $this->requestParser->parseHref($data['User']['_href'], 'userId'); + $contentCreateStruct->ownerId = $this->uriParser->getAttributeFromUri($data['User']['_href'], 'userId'); } if (!array_key_exists('fields', $data) || !is_array($data['fields']) || !is_array($data['fields']['field'])) { diff --git a/src/lib/Server/Input/Parser/ContentTypeCreate.php b/src/lib/Server/Input/Parser/ContentTypeCreate.php index df4f3f9b7..ca227b352 100644 --- a/src/lib/Server/Input/Parser/ContentTypeCreate.php +++ b/src/lib/Server/Input/Parser/ContentTypeCreate.php @@ -146,7 +146,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) throw new Exceptions\Parser("Missing '_href' attribute for the User element in ContentTypeCreate."); } - $contentTypeCreateStruct->creatorId = $this->requestParser->parseHref($data['User']['_href'], 'userId'); + $contentTypeCreateStruct->creatorId = $this->uriParser->getAttributeFromUri($data['User']['_href'], 'userId'); } if (!array_key_exists('FieldDefinitions', $data)) { diff --git a/src/lib/Server/Input/Parser/ContentTypeGroupInput.php b/src/lib/Server/Input/Parser/ContentTypeGroupInput.php index 570d86b50..3a2c43783 100644 --- a/src/lib/Server/Input/Parser/ContentTypeGroupInput.php +++ b/src/lib/Server/Input/Parser/ContentTypeGroupInput.php @@ -76,7 +76,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) throw new Exceptions\Parser("Missing '_href' attribute for the User element in ContentTypeGroupInput."); } - $contentTypeGroupCreateStruct->creatorId = $this->requestParser->parseHref($data['User']['_href'], 'userId'); + $contentTypeGroupCreateStruct->creatorId = $this->uriParser->getAttributeFromUri($data['User']['_href'], 'userId'); } return $contentTypeGroupCreateStruct; diff --git a/src/lib/Server/Input/Parser/ContentTypeUpdate.php b/src/lib/Server/Input/Parser/ContentTypeUpdate.php index f10e9771a..249c1a507 100644 --- a/src/lib/Server/Input/Parser/ContentTypeUpdate.php +++ b/src/lib/Server/Input/Parser/ContentTypeUpdate.php @@ -118,7 +118,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) throw new Exceptions\Parser("Missing '_href' attribute for the User element in ContentTypeUpdate."); } - $contentTypeUpdateStruct->modifierId = $this->requestParser->parseHref($data['User']['_href'], 'userId'); + $contentTypeUpdateStruct->modifierId = $this->uriParser->getAttributeFromUri($data['User']['_href'], 'userId'); } return $contentTypeUpdateStruct; diff --git a/src/lib/Server/Input/Parser/ContentUpdate.php b/src/lib/Server/Input/Parser/ContentUpdate.php index 9c4184a44..fc71d3088 100644 --- a/src/lib/Server/Input/Parser/ContentUpdate.php +++ b/src/lib/Server/Input/Parser/ContentUpdate.php @@ -37,7 +37,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) if (array_key_exists('Section', $data) && is_array($data['Section']) && isset($data['Section']['_href'])) { try { - $parsedData['sectionId'] = $this->requestParser->parseHref($data['Section']['_href'], 'sectionId'); + $parsedData['sectionId'] = $this->uriParser->getAttributeFromUri($data['Section']['_href'], 'sectionId'); } catch (Exceptions\InvalidArgumentException $e) { throw new Exceptions\Parser('Invalid format for the
reference in .'); } @@ -45,7 +45,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) if (array_key_exists('Owner', $data) && is_array($data['Owner']) && isset($data['Owner']['_href'])) { try { - $parsedData['ownerId'] = $this->requestParser->parseHref($data['Owner']['_href'], 'userId'); + $parsedData['ownerId'] = $this->uriParser->getAttributeFromUri($data['Owner']['_href'], 'userId'); } catch (Exceptions\InvalidArgumentException $e) { throw new Exceptions\Parser('Invalid format for the reference in .'); } @@ -57,7 +57,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) if (array_key_exists('MainLocation', $data)) { try { - $mainLocationIdParts = explode('/', $this->requestParser->parseHref($data['MainLocation']['_href'], 'locationPath')); + $mainLocationIdParts = explode('/', $this->uriParser->getAttributeFromUri($data['MainLocation']['_href'], 'locationPath')); $parsedData['mainLocationId'] = array_pop($mainLocationIdParts); } catch (Exceptions\InvalidArgumentException $e) { throw new Exceptions\Parser('Invalid format for the reference in .'); diff --git a/src/lib/Server/Input/Parser/Criterion.php b/src/lib/Server/Input/Parser/Criterion.php index eb8fbe264..279426707 100644 --- a/src/lib/Server/Input/Parser/Criterion.php +++ b/src/lib/Server/Input/Parser/Criterion.php @@ -18,7 +18,7 @@ abstract class Criterion extends BaseParser { /** - * @var array + * @var string[] */ protected static $criterionIdMap = [ 'AND' => 'LogicalAnd', diff --git a/src/lib/Server/Input/Parser/Criterion/IsBookmarked.php b/src/lib/Server/Input/Parser/Criterion/IsBookmarked.php new file mode 100644 index 000000000..d64e30769 --- /dev/null +++ b/src/lib/Server/Input/Parser/Criterion/IsBookmarked.php @@ -0,0 +1,38 @@ +parserTools = $parserTools; + } + + public function parse(array $data, ParsingDispatcher $parsingDispatcher): IsBookmarkedCriterion + { + if (!array_key_exists(self::IS_BOOKMARKED_CRITERION, $data)) { + throw new Parser('Invalid format'); + } + + return new IsBookmarkedCriterion( + $this->parserTools->parseBooleanValue($data[self::IS_BOOKMARKED_CRITERION]) + ); + } +} diff --git a/src/lib/Server/Input/Parser/Criterion/UserMetadata.php b/src/lib/Server/Input/Parser/Criterion/UserMetadata.php index 0c336eb21..63a503528 100644 --- a/src/lib/Server/Input/Parser/Criterion/UserMetadata.php +++ b/src/lib/Server/Input/Parser/Criterion/UserMetadata.php @@ -18,16 +18,11 @@ class UserMetadata extends BaseParser { /** - * Parses input structure to a Criterion object. - * - * @param array $data - * @param \Ibexa\Contracts\Rest\Input\ParsingDispatcher $parsingDispatcher + * @phpstan-param array{UserMetadataCriterion: array{Target: string, Value: int|string|array}} $data * * @throws \Ibexa\Contracts\Rest\Exceptions\Parser - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\UserMetadata */ - public function parse(array $data, ParsingDispatcher $parsingDispatcher) + public function parse(array $data, ParsingDispatcher $parsingDispatcher): UserMetadataCriterion { if (!isset($data['UserMetadataCriterion'])) { throw new Exceptions\Parser('Invalid format'); @@ -49,7 +44,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) $value = is_array($data['UserMetadataCriterion']['Value']) ? $data['UserMetadataCriterion']['Value'] - : explode(',', $data['UserMetadataCriterion']['Value']); + : explode(',', (string)$data['UserMetadataCriterion']['Value']); return new UserMetadataCriterion($target, null, $value); } diff --git a/src/lib/Server/Input/Parser/FieldDefinitionUpdate.php b/src/lib/Server/Input/Parser/FieldDefinitionUpdate.php index 3bd974e94..f77362b9c 100644 --- a/src/lib/Server/Input/Parser/FieldDefinitionUpdate.php +++ b/src/lib/Server/Input/Parser/FieldDefinitionUpdate.php @@ -161,8 +161,8 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) */ protected function getFieldDefinition(array $data) { - $contentTypeId = $this->requestParser->parseHref($data['__url'], 'contentTypeId'); - $fieldDefinitionId = $this->requestParser->parseHref($data['__url'], 'fieldDefinitionId'); + $contentTypeId = $this->uriParser->getAttributeFromUri($data['__url'], 'contentTypeId'); + $fieldDefinitionId = $this->uriParser->getAttributeFromUri($data['__url'], 'fieldDefinitionId'); $contentTypeDraft = $this->contentTypeService->loadContentTypeDraft($contentTypeId); foreach ($contentTypeDraft->getFieldDefinitions() as $fieldDefinition) { diff --git a/src/lib/Server/Input/Parser/Limitation/RouteBasedLimitationParser.php b/src/lib/Server/Input/Parser/Limitation/RouteBasedLimitationParser.php index c74c156a2..b502c5207 100644 --- a/src/lib/Server/Input/Parser/Limitation/RouteBasedLimitationParser.php +++ b/src/lib/Server/Input/Parser/Limitation/RouteBasedLimitationParser.php @@ -96,7 +96,7 @@ protected function buildLimitation() */ protected function parseIdFromHref($limitationValue) { - return $this->requestParser->parseHref( + return $this->uriParser->getAttributeFromUri( $limitationValue['_href'], $this->limitationRouteParameterName ); diff --git a/src/lib/Server/Input/Parser/LocationCreate.php b/src/lib/Server/Input/Parser/LocationCreate.php index d403d26dd..7c0ab7bff 100644 --- a/src/lib/Server/Input/Parser/LocationCreate.php +++ b/src/lib/Server/Input/Parser/LocationCreate.php @@ -62,7 +62,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) throw new Exceptions\Parser("Missing '_href' attribute for the ParentLocation element in LocationCreate."); } - $locationHrefParts = explode('/', $this->requestParser->parseHref($data['ParentLocation']['_href'], 'locationPath')); + $locationHrefParts = explode('/', $this->uriParser->getAttributeFromUri($data['ParentLocation']['_href'], 'locationPath')); $locationCreateStruct = $this->locationService->newLocationCreateStruct( array_pop($locationHrefParts) diff --git a/src/lib/Server/Input/Parser/Query.php b/src/lib/Server/Input/Parser/Query.php index 6b1440841..1ff0d23e7 100644 --- a/src/lib/Server/Input/Parser/Query.php +++ b/src/lib/Server/Input/Parser/Query.php @@ -8,6 +8,7 @@ namespace Ibexa\Rest\Server\Input\Parser; use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion as CriterionValue; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface; use Ibexa\Contracts\Rest\Input\ParsingDispatcher; use Ibexa\Rest\Server\Input\Parser\Criterion as CriterionParser; @@ -89,11 +90,8 @@ abstract protected function buildQuery(); /** * @param array $criteriaArray - * @param \Ibexa\Contracts\Rest\Input\ParsingDispatcher $parsingDispatcher - * - * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion|null A criterion, or a LogicalAnd with a set of Criterion, or null if an empty array was given */ - private function processCriteriaArray(array $criteriaArray, ParsingDispatcher $parsingDispatcher) + private function processCriteriaArray(array $criteriaArray, ParsingDispatcher $parsingDispatcher): ?CriterionInterface { if (count($criteriaArray) === 0) { return null; diff --git a/src/lib/Server/Input/Parser/RelationCreate.php b/src/lib/Server/Input/Parser/RelationCreate.php index 7462a5d37..19b5e25cb 100644 --- a/src/lib/Server/Input/Parser/RelationCreate.php +++ b/src/lib/Server/Input/Parser/RelationCreate.php @@ -34,6 +34,6 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) throw new Exceptions\Parser("Missing '_href' attribute for the Destination element in RelationCreate."); } - return $this->requestParser->parseHref($data['Destination']['_href'], 'contentId'); + return $this->uriParser->getAttributeFromUri($data['Destination']['_href'], 'contentId'); } } diff --git a/src/lib/Server/Input/Parser/RoleAssignInput.php b/src/lib/Server/Input/Parser/RoleAssignInput.php index 1b1cae0dd..4fb0a20c6 100644 --- a/src/lib/Server/Input/Parser/RoleAssignInput.php +++ b/src/lib/Server/Input/Parser/RoleAssignInput.php @@ -54,7 +54,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) } try { - $roleId = $this->requestParser->parseHref($data['Role']['_href'], 'roleId'); + $roleId = $this->uriParser->getAttributeFromUri($data['Role']['_href'], 'roleId'); } catch (Exceptions\InvalidArgumentException $e) { throw new Exceptions\Parser('Invalid format for reference in .'); } diff --git a/src/lib/Server/Input/Parser/UserCreate.php b/src/lib/Server/Input/Parser/UserCreate.php index 496476620..430c36804 100644 --- a/src/lib/Server/Input/Parser/UserCreate.php +++ b/src/lib/Server/Input/Parser/UserCreate.php @@ -81,7 +81,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) } $contentType = $this->contentTypeService->loadContentType( - $this->requestParser->parseHref($data['ContentType']['_href'], 'contentTypeId') + $this->uriParser->getAttributeFromUri($data['ContentType']['_href'], 'contentTypeId') ); } @@ -114,7 +114,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) throw new Exceptions\Parser("Missing '_href' attribute for the Section element in UserCreate."); } - $userCreateStruct->sectionId = $this->requestParser->parseHref($data['Section']['_href'], 'sectionId'); + $userCreateStruct->sectionId = $this->uriParser->getAttributeFromUri($data['Section']['_href'], 'sectionId'); } if (array_key_exists('remoteId', $data)) { diff --git a/src/lib/Server/Input/Parser/UserGroupCreate.php b/src/lib/Server/Input/Parser/UserGroupCreate.php index 38a6b5ef3..ba689b285 100644 --- a/src/lib/Server/Input/Parser/UserGroupCreate.php +++ b/src/lib/Server/Input/Parser/UserGroupCreate.php @@ -71,7 +71,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) } $contentType = $this->contentTypeService->loadContentType( - $this->requestParser->parseHref($data['ContentType']['_href'], 'contentTypeId') + $this->uriParser->getAttributeFromUri($data['ContentType']['_href'], 'contentTypeId') ); } @@ -86,7 +86,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) throw new Exceptions\Parser("Missing '_href' attribute for the Section element in UserGroupCreate."); } - $userGroupCreateStruct->sectionId = $this->requestParser->parseHref($data['Section']['_href'], 'sectionId'); + $userGroupCreateStruct->sectionId = $this->uriParser->getAttributeFromUri($data['Section']['_href'], 'sectionId'); } if (array_key_exists('remoteId', $data)) { diff --git a/src/lib/Server/Input/Parser/UserGroupUpdate.php b/src/lib/Server/Input/Parser/UserGroupUpdate.php index edc4d059b..b8e00a23d 100644 --- a/src/lib/Server/Input/Parser/UserGroupUpdate.php +++ b/src/lib/Server/Input/Parser/UserGroupUpdate.php @@ -86,7 +86,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) throw new Exceptions\Parser("Missing '_href' attribute for the Section element in UserGroupUpdate."); } - $parsedData['sectionId'] = $this->requestParser->parseHref($data['Section']['_href'], 'sectionId'); + $parsedData['sectionId'] = $this->uriParser->getAttributeFromUri($data['Section']['_href'], 'sectionId'); } if (array_key_exists('remoteId', $data)) { @@ -94,7 +94,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) } if (array_key_exists('fields', $data)) { - $groupLocationParts = explode('/', $this->requestParser->parseHref($data['__url'], 'groupPath')); + $groupLocationParts = explode('/', $this->uriParser->getAttributeFromUri($data['__url'], 'groupPath')); $groupLocation = $this->locationService->loadLocation(array_pop($groupLocationParts)); diff --git a/src/lib/Server/Input/Parser/UserUpdate.php b/src/lib/Server/Input/Parser/UserUpdate.php index b412c3d04..d7adf6f39 100644 --- a/src/lib/Server/Input/Parser/UserUpdate.php +++ b/src/lib/Server/Input/Parser/UserUpdate.php @@ -100,7 +100,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) throw new Exceptions\Parser("Missing '_href' attribute for the Section element in UserUpdate."); } - $parsedData['sectionId'] = $this->requestParser->parseHref($data['Section']['_href'], 'sectionId'); + $parsedData['sectionId'] = $this->uriParser->getAttributeFromUri($data['Section']['_href'], 'sectionId'); } if (array_key_exists('remoteId', $data)) { @@ -108,7 +108,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) } if (array_key_exists('fields', $data)) { - $userId = $this->requestParser->parseHref($data['__url'], 'userId'); + $userId = $this->uriParser->getAttributeFromUri($data['__url'], 'userId'); if (!is_array($data['fields']) || !array_key_exists('field', $data['fields']) || !is_array($data['fields']['field'])) { throw new Exceptions\Parser("Invalid 'fields' element for UserUpdate."); diff --git a/src/lib/Server/Input/Parser/VersionUpdate.php b/src/lib/Server/Input/Parser/VersionUpdate.php index 6cb94c28d..53362bc75 100644 --- a/src/lib/Server/Input/Parser/VersionUpdate.php +++ b/src/lib/Server/Input/Parser/VersionUpdate.php @@ -70,7 +70,7 @@ public function parse(array $data, ParsingDispatcher $parsingDispatcher) throw new Exceptions\Parser("Invalid 'fields' element for VersionUpdate."); } - $contentId = $this->requestParser->parseHref($data['__url'], 'contentId'); + $contentId = $this->uriParser->getAttributeFromUri($data['__url'], 'contentId'); foreach ($data['fields']['field'] as $fieldData) { if (!array_key_exists('fieldDefinitionIdentifier', $fieldData)) { diff --git a/src/lib/Server/Output/ValueObjectVisitor/Exception.php b/src/lib/Server/Output/ValueObjectVisitor/Exception.php index 2240e1f6a..9be8b7962 100644 --- a/src/lib/Server/Output/ValueObjectVisitor/Exception.php +++ b/src/lib/Server/Output/ValueObjectVisitor/Exception.php @@ -7,18 +7,13 @@ namespace Ibexa\Rest\Server\Output\ValueObjectVisitor; -use Ibexa\Contracts\Rest\Output\Generator; -use Ibexa\Contracts\Rest\Output\ValueObjectVisitor; -use Ibexa\Contracts\Rest\Output\Visitor; -use Ibexa\Core\Base\Translatable; -use JMS\TranslationBundle\Annotation\Desc; -use JMS\TranslationBundle\Annotation\Ignore; +use Ibexa\Contracts\Rest\Output\Exceptions\AbstractExceptionVisitor; use Symfony\Contracts\Translation\TranslatorInterface; /** * Exception value object visitor. */ -class Exception extends ValueObjectVisitor +class Exception extends AbstractExceptionVisitor { /** * Is debug mode enabled? @@ -27,49 +22,6 @@ class Exception extends ValueObjectVisitor */ protected $debug = false; - /** - * Mapping of HTTP status codes to their respective error messages. - * - * @var array - */ - protected static $httpStatusCodes = [ - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Time-out', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Long', - 415 => 'Unsupported Media Type', - 416 => 'Requested range not satisfiable', - 417 => 'Expectation Failed', - 418 => "I'm a teapot", - 421 => 'There are too many connections from your internet address', - 422 => 'Unprocessable Entity', - 423 => 'Locked', - 424 => 'Failed Dependency', - 425 => 'Unordered Collection', - 426 => 'Upgrade Required', - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Time-out', - 505 => 'HTTP Version not supported', - 506 => 'Variant Also Negotiates', - 507 => 'Insufficient Storage', - 509 => 'Bandwidth Limit Exceeded', - 510 => 'Not Extended', - ]; - /** @var \Symfony\Contracts\Translation\TranslatorInterface */ protected $translator; @@ -85,71 +37,23 @@ public function __construct($debug = false, ?TranslatorInterface $translator = n $this->translator = $translator; } - /** - * Returns HTTP status code. - * - * @return int - */ - protected function getStatus() + protected function getTranslator(): ?TranslatorInterface { - return 500; + return $this->translator; } - /** - * Visit struct returned by controllers. - * - * @param \Ibexa\Contracts\Rest\Output\Visitor $visitor - * @param \Ibexa\Contracts\Rest\Output\Generator $generator - * @param \Exception $data - */ - public function visit(Visitor $visitor, Generator $generator, $data) + protected function canDisplayExceptionMessage(): bool { - $generator->startObjectElement('ErrorMessage'); - - $visitor->setHeader('Content-Type', $generator->getMediaType('ErrorMessage')); - - $statusCode = $this->generateErrorCode($generator, $visitor, $data); - - $generator->valueElement( - 'errorMessage', - static::$httpStatusCodes[$statusCode] ?? static::$httpStatusCodes[500] - ); - - if ($this->debug || $statusCode < 500) { - $errorDescription = $data instanceof Translatable && $this->translator - ? /** @Ignore */ $this->translator->trans($data->getMessageTemplate(), $data->getParameters(), 'ibexa_repository_exceptions') - : $data->getMessage(); - } else { - // Do not leak any file paths and sensitive data on production environments - $errorDescription = $this->translator - ? /** @Desc("An error has occurred. Please try again later or contact your Administrator.") */ $this->translator->trans('non_verbose_error', [], 'ibexa_repository_exceptions') - : 'An error has occurred. Please try again later or contact your Administrator.'; - } - - $generator->valueElement('errorDescription', $errorDescription); - - if ($this->debug) { - $generator->valueElement('trace', $data->getTraceAsString()); - $generator->valueElement('file', $data->getFile()); - $generator->valueElement('line', $data->getLine()); - } - - if ($previous = $data->getPrevious()) { - $generator->startObjectElement('Previous', 'ErrorMessage'); - $visitor->visitValueObject($previous); - $generator->endObjectElement('Previous'); - } - - $generator->endObjectElement('ErrorMessage'); + return $this->debug; } - protected function generateErrorCode(Generator $generator, Visitor $visitor, \Exception $e): int + protected function canDisplayExceptionTrace(): bool { - $statusCode = $this->getStatus(); - $visitor->setStatus($statusCode); - - $generator->valueElement('errorCode', $statusCode); + return $this->debug; + } - return $statusCode; + protected function canDisplayPreviousException(): bool + { + return true; } } diff --git a/src/lib/Server/Output/ValueObjectVisitor/Location.php b/src/lib/Server/Output/ValueObjectVisitor/Location.php index a34836eb6..283de9616 100644 --- a/src/lib/Server/Output/ValueObjectVisitor/Location.php +++ b/src/lib/Server/Output/ValueObjectVisitor/Location.php @@ -7,7 +7,7 @@ namespace Ibexa\Rest\Server\Output\ValueObjectVisitor; -use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\ContentService\RelationListFacadeInterface; use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; use Ibexa\Contracts\Core\Repository\LocationService; use Ibexa\Contracts\Core\Repository\Values\Content; @@ -21,18 +21,10 @@ */ class Location extends ValueObjectVisitor { - /** @var \Ibexa\Contracts\Core\Repository\LocationService */ - private $locationService; - - /** @var \Ibexa\Contracts\Core\Repository\ContentService */ - private $contentService; - public function __construct( - LocationService $locationService, - ContentService $contentService + private readonly LocationService $locationService, + private readonly RelationListFacadeInterface $relationListFacade ) { - $this->locationService = $locationService; - $this->contentService = $contentService; } /** @@ -172,7 +164,7 @@ protected function visitLocationAttributes( $mainLocation, $content, $content->getContentType(), - $this->contentService->loadRelations($content->getVersionInfo()) + iterator_to_array($this->relationListFacade->getRelations($content->getVersionInfo())) ) ); diff --git a/src/lib/Server/Output/ValueObjectVisitor/RestExecutedView.php b/src/lib/Server/Output/ValueObjectVisitor/RestExecutedView.php index bcc72d7cc..fab0f5267 100644 --- a/src/lib/Server/Output/ValueObjectVisitor/RestExecutedView.php +++ b/src/lib/Server/Output/ValueObjectVisitor/RestExecutedView.php @@ -7,7 +7,7 @@ namespace Ibexa\Rest\Server\Output\ValueObjectVisitor; -use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\ContentService\RelationListFacadeInterface; use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; use Ibexa\Contracts\Core\Repository\LocationService; @@ -23,30 +23,10 @@ */ class RestExecutedView extends ValueObjectVisitor { - /** - * Location service. - * - * @var \Ibexa\Contracts\Core\Repository\LocationService - */ - protected $locationService; - - /** - * Content service. - * - * @var \Ibexa\Contracts\Core\Repository\ContentService - */ - protected $contentService; - - /** - * @param \Ibexa\Contracts\Core\Repository\LocationService $locationService - * @param \Ibexa\Contracts\Core\Repository\ContentService $contentService - */ public function __construct( - LocationService $locationService, - ContentService $contentService + private readonly LocationService $locationService, + private readonly RelationListFacadeInterface $relationListFacade ) { - $this->locationService = $locationService; - $this->contentService = $contentService; } /** @@ -128,7 +108,11 @@ public function visit(Visitor $visitor, Generator $generator, $data) $mainLocation, $searchHit->valueObject, $searchHit->valueObject->getContentType(), - $this->contentService->loadRelations($searchHit->valueObject->getVersionInfo()) + iterator_to_array( + $this->relationListFacade->getRelations( + $searchHit->valueObject->getVersionInfo() + ) + ) ); } elseif ($searchHit->valueObject instanceof ApiValues\Location) { $valueObject = $searchHit->valueObject; diff --git a/src/lib/Server/Output/ValueObjectVisitor/User.php b/src/lib/Server/Output/ValueObjectVisitor/User.php index a2a091205..9a8235061 100644 --- a/src/lib/Server/Output/ValueObjectVisitor/User.php +++ b/src/lib/Server/Output/ValueObjectVisitor/User.php @@ -8,7 +8,7 @@ namespace Ibexa\Rest\Server\Output\ValueObjectVisitor; -use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\ContentService\RelationListFacadeInterface; use Ibexa\Contracts\Rest\Output\Generator; use Ibexa\Contracts\Rest\Output\ValueObjectVisitor; use Ibexa\Contracts\Rest\Output\Visitor; @@ -17,12 +17,8 @@ final class User extends ValueObjectVisitor implements DelegateValueObjectVisitor { - /** @var \Ibexa\Contracts\Core\Repository\ContentService */ - private $contentService; - - public function __construct(ContentService $contentService) + public function __construct(private readonly RelationListFacadeInterface $relationListFacade) { - $this->contentService = $contentService; } /** @@ -36,8 +32,10 @@ public function visit(Visitor $visitor, Generator $generator, $data): void $data->getContentType(), $data->contentInfo, $data->contentInfo->getMainLocation(), - $this->contentService->loadRelations( - $data->getVersionInfo() + iterator_to_array( + $this->relationListFacade->getRelations( + $data->getVersionInfo() + ) ) ) ); diff --git a/src/lib/Server/Output/ValueObjectVisitor/UserGroup.php b/src/lib/Server/Output/ValueObjectVisitor/UserGroup.php index cc20c0316..08c786115 100644 --- a/src/lib/Server/Output/ValueObjectVisitor/UserGroup.php +++ b/src/lib/Server/Output/ValueObjectVisitor/UserGroup.php @@ -8,7 +8,7 @@ namespace Ibexa\Rest\Server\Output\ValueObjectVisitor; -use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\ContentService\RelationListFacadeInterface; use Ibexa\Contracts\Rest\Output\Generator; use Ibexa\Contracts\Rest\Output\ValueObjectVisitor; use Ibexa\Contracts\Rest\Output\Visitor; @@ -17,12 +17,9 @@ final class UserGroup extends ValueObjectVisitor implements DelegateValueObjectVisitor { - /** @var \Ibexa\Contracts\Core\Repository\ContentService */ - private $contentService; - - public function __construct(ContentService $contentService) - { - $this->contentService = $contentService; + public function __construct( + private readonly RelationListFacadeInterface $relationListFacade + ) { } /** @@ -36,7 +33,7 @@ public function visit(Visitor $visitor, Generator $generator, $data): void $data->getContentType(), $data->contentInfo, $data->contentInfo->getMainLocation(), - $this->contentService->loadRelations($data->getVersionInfo()) + iterator_to_array($this->relationListFacade->getRelations($data->getVersionInfo())) ) ); } diff --git a/src/lib/Server/Security/JWTUser.php b/src/lib/Server/Security/JWTUser.php deleted file mode 100644 index 39f5bcf30..000000000 --- a/src/lib/Server/Security/JWTUser.php +++ /dev/null @@ -1,56 +0,0 @@ -wrappedUser = $wrappedUser; - $this->userIdentifier = $userIdentifier; - } - - public function getPassword(): ?string - { - return $this->wrappedUser->getPassword(); - } - - public function eraseCredentials(): void - { - $this->wrappedUser->eraseCredentials(); - } - - public function getRoles(): array - { - return $this->wrappedUser->getRoles(); - } - - public function getSalt(): ?string - { - return $this->wrappedUser->getSalt(); - } - - public function getUsername(): string - { - return $this->userIdentifier ?? $this->wrappedUser->getUsername(); - } - - public function getWrappedUser(): UserInterface - { - return $this->wrappedUser; - } -} diff --git a/tests/bundle/EventListener/ResponseListenerTest.php b/tests/bundle/EventListener/ResponseListenerTest.php index ab1c2f805..eabad7978 100644 --- a/tests/bundle/EventListener/ResponseListenerTest.php +++ b/tests/bundle/EventListener/ResponseListenerTest.php @@ -43,7 +43,7 @@ public function setUp(): void { $this->eventValue = new stdClass(); $this->exceptionEventValue = new Exception(); - $this->response = new Response('BODY', 406, ['foo' => 'bar']); + $this->response = new Response('BODY', Response::HTTP_NOT_ACCEPTABLE, ['foo' => 'bar']); } public function provideExpectedSubscribedEventTypes(): array diff --git a/tests/bundle/Functional/ContentTest.php b/tests/bundle/Functional/ContentTest.php index b2c3c046a..170b60217 100644 --- a/tests/bundle/Functional/ContentTest.php +++ b/tests/bundle/Functional/ContentTest.php @@ -374,7 +374,7 @@ public function testLoadVersionRelations($restContentVersionHref) /** * @depends testCreateDraftFromVersion - * Covers POST /content/objects//versions//relations/ + * Covers POST /content/objects//versions//relations * * @return string created relation HREF (/content/objects//versions//relations/ */ diff --git a/tests/bundle/Functional/HttpOptionsTest.php b/tests/bundle/Functional/HttpOptionsTest.php index 9f8e417f4..accdc6e7b 100644 --- a/tests/bundle/Functional/HttpOptionsTest.php +++ b/tests/bundle/Functional/HttpOptionsTest.php @@ -71,7 +71,6 @@ public function providerForTestHttpOptions(): array ['/content/objects/1/currentversion', ['POST'], 'CreateDraftFromCurrentVersionInput+json'], ['/content/objects/1/currentversion', ['GET', 'COPY']], ['/content/binary/images/1-2-3/variations/123', ['GET']], - ['/content/views', ['POST']], ['/views', ['POST', 'GET']], ['/views/1', ['GET']], ['/views/1/results', ['GET']], diff --git a/tests/bundle/Functional/RelationTest.php b/tests/bundle/Functional/RelationTest.php index bcd1dd4cd..81f7e09ab 100644 --- a/tests/bundle/Functional/RelationTest.php +++ b/tests/bundle/Functional/RelationTest.php @@ -60,8 +60,14 @@ public function testRelation() $testContent = $this->createContent($xml); $relations = $testContent['CurrentVersion']['Version']['Relations']['Relation']; - self::assertEquals('LINK', $relations[0]['RelationType']); - self::assertEquals('EMBED', $relations[1]['RelationType']); - self::assertEquals('ATTRIBUTE', $relations[2]['RelationType']); + $expectedRelationTypes = [ + 'LINK', + 'EMBED', + 'ATTRIBUTE', + ]; + + $actualTypes = array_column($relations, 'RelationType'); + + self::assertEqualsCanonicalizing($expectedRelationTypes, $actualTypes); } } diff --git a/tests/bundle/Functional/RoleTest.php b/tests/bundle/Functional/RoleTest.php index 251df418e..f75ec0fef 100644 --- a/tests/bundle/Functional/RoleTest.php +++ b/tests/bundle/Functional/RoleTest.php @@ -14,48 +14,6 @@ class RoleTest extends RESTFunctionalTestCase /** * Covers POST /user/roles. * - * BC compatible mode, will return a role - * - * @return string The created role href - */ - public function testCreateRole() - { - $xml = <<< XML - - - testCreateRole - eng-GB - - testCreateRole - - - testCreateRole description - - -XML; - $request = $this->createHttpRequest( - 'POST', - '/api/ibexa/v2/user/roles', - 'RoleInput+xml', - 'Role+json', - $xml - ); - $response = $this->sendHttpRequest($request); - - self::assertHttpResponseCodeEquals($response, 201); - self::assertHttpResponseHasHeader($response, 'Location'); - - $href = $response->getHeader('Location')[0]; - $this->addCreatedElement($href); - - return $href; - } - - /** - * Covers POST /user/roles. - * - * BC incompatible mode, will return a role draft - * * @return string The created role draft href */ public function testCreateRoleWithDraft() @@ -75,7 +33,7 @@ public function testCreateRoleWithDraft() XML; $request = $this->createHttpRequest( 'POST', - '/api/ibexa/v2/user/roles?publish=false', + '/api/ibexa/v2/user/roles', 'RoleInput+xml', 'RoleDraft+json', $xml @@ -104,7 +62,7 @@ public function testListRoles() } /** - * @depends testCreateRole + * @depends testPublishRoleDraft * Covers GET /user/roles/{roleId} */ public function testLoadRole($roleHref) @@ -117,7 +75,7 @@ public function testLoadRole($roleHref) } /** - * @depends testCreateRole + * @depends testPublishRoleDraft * Covers POST /user/roles/{roleId} * * @return string The created role draft href @@ -169,7 +127,7 @@ public function testLoadRoleDraft($roleDraftHref) } /** - * @depends testCreateRole + * @depends testPublishRoleDraft * Covers PATCH /user/roles/{roleId} */ public function testUpdateRole($roleHref) @@ -229,7 +187,7 @@ public function testUpdateRoleDraft($roleDraftHref) /** * Covers POST /user/roles/{roleId}/policies. * - * @depends testCreateRole + * @depends testPublishRoleDraft * * @return string The created policy href */ @@ -326,7 +284,7 @@ public function testLoadPolicy($policyHref) /** * Covers GET /user/roles/{roleId}/policies. * - * @depends testCreateRole + * @depends testPublishRoleDraft */ public function testLoadPolicies($roleHref) { @@ -398,7 +356,7 @@ public function testUpdatePolicyByRoleDraft($policyHref) } /** - * @depends testCreateRole + * @depends testPublishRoleDraft * Covers POST /user/users/{userId}/roles * * @return string assigned role href @@ -509,7 +467,7 @@ public function testUnassignRoleFromUser($roleAssignmentHref) } /** - * @depends testCreateRole + * @depends testPublishRoleDraft * Covers POST /user/groups/{groupId}/roles * * @return string role assignment href @@ -641,7 +599,7 @@ public function testRemovePolicyByRoleDraft($policyHref) /** * Covers DELETE /user/roles/{roleId}/policies. * - * @depends testCreateRole + * @depends testPublishRoleDraft */ public function testDeletePolicies($roleHref) { @@ -655,7 +613,7 @@ public function testDeletePolicies($roleHref) /** * Covers DELETE /user/roles/{roleId}. * - * @depends testCreateRole + * @depends testPublishRoleDraft */ public function testDeleteRole($roleHref) { @@ -683,6 +641,11 @@ public function testPublishRoleDraft($roleDraftHref) 'Location', '/api/ibexa/v2/user/roles/' . preg_replace('/.*roles\/(\d+).*/', '$1', $roleDraftHref) ); + + $href = $response->getHeader('Location')[0]; + $this->addCreatedElement($href); + + return $href; } /** @@ -749,6 +712,10 @@ private function createAndPublishRole($identifier) self::assertHttpResponseHasHeader($response, 'Location'); $href = $response->getHeader('Location')[0]; + $this->sendHttpRequest( + $this->createHttpRequest('PUBLISH', $href . '/draft') + ); + $this->addCreatedElement($href); return $href; diff --git a/tests/bundle/Functional/SearchView/Criterion/IsBookmarkedTest.php b/tests/bundle/Functional/SearchView/Criterion/IsBookmarkedTest.php new file mode 100644 index 000000000..4ab833b55 --- /dev/null +++ b/tests/bundle/Functional/SearchView/Criterion/IsBookmarkedTest.php @@ -0,0 +1,55 @@ +addMediaFolderToBookmarks(); + } + + /** + * @phpstan-return iterable< + * string, + * array{ + * string, + * string, + * int, + * }, + * > + */ + public function getCriteriaPayloads(): iterable + { + yield 'Bookmarked locations' => [ + 'json', + $this->buildJsonCriterionQuery('"IsBookmarkedCriterion": true'), + 1, + ]; + + yield 'Not bookmarked locations' => [ + 'json', + $this->buildJsonCriterionQuery('"IsBookmarkedCriterion": false'), + 15, // <- This can differ between DXP versions. + ]; + } + + private function addMediaFolderToBookmarks(): void + { + $request = $this->createHttpRequest( + 'POST', + '/api/ibexa/v2/bookmark/43' + ); + + $this->sendHttpRequest($request); + } +} diff --git a/tests/bundle/RequestParser/RouterTest.php b/tests/bundle/RequestParser/RouterTest.php deleted file mode 100644 index dcbdeecac..000000000 --- a/tests/bundle/RequestParser/RouterTest.php +++ /dev/null @@ -1,168 +0,0 @@ - 'ibexa.rest.test_route', - '_controller' => '', - ]; - - $this->getRouterInvocationMockerForMatchingUri($uri) - ->willReturn($expectedMatchResult) - ; - - self::assertEquals( - $expectedMatchResult, - $this->getRequestParser()->parse($uri) - ); - } - - public function testParseNoMatch(): void - { - $this->expectException(InvalidArgumentException::class); - $exceptionMessage = 'No route matched \'/api/test/v1/nomatch\''; - $this->expectExceptionMessage($exceptionMessage); - - $uri = self::$routePrefix . '/nomatch'; - - $this->getRouterInvocationMockerForMatchingUri($uri) - ->willThrowException(new ResourceNotFoundException($exceptionMessage)) - ; - - $this->getRequestParser()->parse($uri); - } - - public function testParseNoPrefix(): void - { - $this->expectException(InvalidArgumentException::class); - $exceptionMessage = 'No route matched \'/no/prefix\''; - $this->expectExceptionMessage($exceptionMessage); - - $uri = '/no/prefix'; - - // invalid prefix should cause internal url matcher not to be called - $this->getRouterInvocationMockerForMatchingUri($uri, self::never()); - - $this->getRequestParser()->parse($uri); - } - - public function testParseHref(): void - { - $href = '/api/test/v1/content/objects/1'; - - $expectedMatchResult = [ - '_route' => 'ibexa.rest.test_parse_href', - 'contentId' => 1, - ]; - - $this->getRouterInvocationMockerForMatchingUri($href) - ->willReturn($expectedMatchResult) - ; - - self::assertEquals(1, $this->getRequestParser()->parseHref($href, 'contentId')); - } - - public function testParseHrefAttributeNotFound(): void - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage( - 'No attribute \'badAttribute\' in route matched from /api/test/v1/content/no-attribute' - ); - - $href = '/api/test/v1/content/no-attribute'; - - $matchResult = [ - '_route' => 'ibexa.rest.test_parse_href_attribute_not_found', - ]; - - $this->getRouterInvocationMockerForMatchingUri($href) - ->willReturn($matchResult) - ; - - self::assertEquals(1, $this->getRequestParser()->parseHref($href, 'badAttribute')); - } - - public function testGenerate(): void - { - $routeName = 'ibexa.rest.test_generate'; - $arguments = ['arg1' => 1]; - - $expectedResult = self::$routePrefix . '/generate/' . $arguments['arg1']; - $this->getRouterMock() - ->expects(self::once()) - ->method('generate') - ->with($routeName, $arguments) - ->willReturn($expectedResult) - ; - - self::assertEquals( - $expectedResult, - $this->getRequestParser()->generate($routeName, $arguments) - ); - } - - private function getRequestParser(): RequestParser - { - $routerMock = $this->getRouterMock(); - - return new RouterRequestParser( - $routerMock, - new UriParser($routerMock) - ); - } - - /** - * @return \Symfony\Component\Routing\RouterInterface&\PHPUnit\Framework\MockObject\MockObject - */ - private function getRouterMock(): RouterInterface - { - if (!isset($this->router)) { - $this->router = $this->createMock(RouterInterface::class); - - $this->router - ->method('getContext') - ->willReturn(new RequestContext()) - ; - } - - return $this->router; - } - - private function getRouterInvocationMockerForMatchingUri( - string $uri, - ?InvokedCountMatcher $invokedCount = null - ): InvocationMocker { - return $this->getRouterMock() - ->expects($invokedCount ?? self::once()) - ->method('match') - ->with($uri) - ; - } -} diff --git a/tests/integration/UriParser/UriParserTest.php b/tests/integration/UriParser/UriParserTest.php index 87dc95eb0..a68ea7ed5 100644 --- a/tests/integration/UriParser/UriParserTest.php +++ b/tests/integration/UriParser/UriParserTest.php @@ -160,7 +160,7 @@ public static function getDataForTestMatchUri(): iterable 'PATCH', [ '_route' => 'ibexa.rest.update_object_state', - '_controller' => 'Ibexa\Rest\Server\Controller\ObjectState:updateObjectState', + '_controller' => 'Ibexa\Rest\Server\Controller\ObjectState::updateObjectState', 'objectStateGroupId' => '123', 'objectStateId' => '456', ], diff --git a/tests/lib/FieldTypeProcessor/ImageProcessorTest.php b/tests/lib/FieldTypeProcessor/ImageProcessorTest.php index 35f680a83..41128121e 100644 --- a/tests/lib/FieldTypeProcessor/ImageProcessorTest.php +++ b/tests/lib/FieldTypeProcessor/ImageProcessorTest.php @@ -8,12 +8,12 @@ namespace Ibexa\Tests\Rest\FieldTypeProcessor; use Ibexa\Rest\FieldTypeProcessor\ImageProcessor; +use PHPUnit\Framework\MockObject\MockObject; use Symfony\Component\Routing\RouterInterface; class ImageProcessorTest extends BinaryInputProcessorTest { - /** @var \Ibexa\Rest\RequestParser */ - protected $requestParser; + protected RouterInterface&MockObject $router; /** * @covers \Ibexa\Rest\FieldTypeProcessor\ImageProcessor::postProcessValueHash @@ -23,7 +23,7 @@ public function testPostProcessValueHash() $processor = $this->getProcessor(); $inputHash = [ - 'path' => 'var/some_site/223-1-eng-US/Cool-File.jpg', + 'inputUri' => 'var/some_site/223-1-eng-US/Cool-File.jpg', 'imageId' => '223-12345', ]; @@ -46,6 +46,7 @@ public function testPostProcessValueHash() self::assertEquals( [ + 'inputUri' => 'var/some_site/223-1-eng-US/Cool-File.jpg', 'path' => '/var/some_site/223-1-eng-US/Cool-File.jpg', 'imageId' => '223-12345', 'variations' => $expectedVariations, @@ -68,16 +69,13 @@ protected function getProcessor() ); } - /** - * @returns \Symfony\Component\Routing\RouterInterface|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getRouterMock() + protected function getRouterMock(): RouterInterface&MockObject { - if (!isset($this->requestParser)) { - $this->requestParser = $this->createMock(RouterInterface::class); + if (!isset($this->router)) { + $this->router = $this->createMock(RouterInterface::class); } - return $this->requestParser; + return $this->router; } protected function getVariations() diff --git a/tests/lib/Output/FieldTypeSerializerTest.php b/tests/lib/Output/FieldTypeSerializerTest.php index ef9ff532f..df18f89b1 100644 --- a/tests/lib/Output/FieldTypeSerializerTest.php +++ b/tests/lib/Output/FieldTypeSerializerTest.php @@ -34,34 +34,6 @@ class FieldTypeSerializerTest extends TestCase protected $generatorMock; - /** - * @dataProvider provideDataWithFieldValueToSerialize - * - * @param mixed $hashValue - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - */ - public function testSerializeFieldValue( - APIFieldType $fieldType, - $hashValue - ): void { - $serializer = $this->getFieldTypeSerializer(); - - $this->mockFieldTypeServiceGetFieldType( - 'myFancyFieldType', - $fieldType - ); - - $serializer->serializeFieldValue( - $this->mockGeneratorGenerateFieldTypeHash( - 'fieldValue', - $hashValue - ), - $this->getContentTypeMock(), - $this->createFieldMock('myFancyFieldType', 'my-field-value') - ); - } - /** * @dataProvider provideDataWithFieldValueToSerialize * @@ -107,51 +79,6 @@ public function provideDataWithFieldValueToSerialize(): iterable ]; } - public function testSerializeFieldValueWithProcessor(): void - { - $serializer = $this->getFieldTypeSerializer(); - - $processorMock = $this->getFieldTypeProcessorMock(); - $this->getFieldTypeProcessorRegistryMock() - ->expects(self::once()) - ->method('hasProcessor') - ->with('myFancyFieldType') - ->willReturn(true); - $this->getFieldTypeProcessorRegistryMock() - ->expects(self::once()) - ->method('getProcessor') - ->with('myFancyFieldType') - ->willReturn($processorMock); - $processorMock->expects(self::once()) - ->method('postProcessValueHash') - ->with(self::equalTo([23, 42])) - ->willReturn(['post-processed']); - - $fieldTypeMock = $this->getFieldTypeMock(); - - $this->mockFieldTypeServiceGetFieldType( - 'myFancyFieldType', - $fieldTypeMock - ); - - $fieldTypeMock->expects(self::once()) - ->method('getFieldTypeIdentifier') - ->willReturn('myFancyFieldType'); - $fieldTypeMock->expects(self::once()) - ->method('toHash') - ->with(self::equalTo('my-field-value')) - ->willReturn([23, 42]); - - $serializer->serializeFieldValue( - $this->mockGeneratorGenerateFieldTypeHash( - 'fieldValue', - ['post-processed'] - ), - $this->getContentTypeMock(), - $this->createFieldMock('myFancyFieldType', 'my-field-value') - ); - } - public function testSerializeFieldDefaultValue(): void { $serializer = $this->getFieldTypeSerializer(); diff --git a/tests/lib/Output/ValueObjectVisitorBaseTest.php b/tests/lib/Output/ValueObjectVisitorBaseTest.php index 51ac42ecc..d4bddbf05 100644 --- a/tests/lib/Output/ValueObjectVisitorBaseTest.php +++ b/tests/lib/Output/ValueObjectVisitorBaseTest.php @@ -8,10 +8,11 @@ namespace Ibexa\Tests\Rest\Output; use Ibexa\Contracts\Rest\Output\Visitor; +use Ibexa\Contracts\Rest\UriParser\UriParserInterface; use Ibexa\Rest\Output\Generator; -use Ibexa\Rest\RequestParser; use Ibexa\Tests\Rest\AssertXmlTagTrait; use Ibexa\Tests\Rest\Server; +use PHPUnit\Framework\MockObject\MockObject; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; @@ -34,11 +35,6 @@ abstract class ValueObjectVisitorBaseTest extends Server\BaseTest */ protected $generator; - /** - * @var \Ibexa\Rest\RequestParser - */ - protected $requestParser; - /** * @var \Symfony\Component\Routing\RouterInterface|\PHPUnit\Framework\MockObject\MockObject */ @@ -55,6 +51,8 @@ abstract class ValueObjectVisitorBaseTest extends Server\BaseTest /** @var int */ private $templatedRouterCallIndex = 0; + private UriParserInterface&MockObject $uriParser; + /** * Gets the visitor mock. * @@ -133,23 +131,20 @@ protected function assertXPath(\DOMNode $domNode, $xpathExpression) protected function getVisitor() { $visitor = $this->internalGetVisitor(); - $visitor->setRequestParser($this->getRequestParser()); + $visitor->setUriParser($this->getUriParser()); $visitor->setRouter($this->getRouterMock()); $visitor->setTemplateRouter($this->getTemplatedRouterMock()); return $visitor; } - /** - * @return \Ibexa\Rest\RequestParser|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getRequestParser() + protected function getUriParser(): UriParserInterface&MockObject { - if (!isset($this->requestParser)) { - $this->requestParser = $this->createMock(RequestParser::class); + if (!isset($this->uriParser)) { + $this->uriParser = $this->createMock(UriParserInterface::class); } - return $this->requestParser; + return $this->uriParser; } /** diff --git a/tests/lib/Output/VisitorTest.php b/tests/lib/Output/VisitorTest.php index 7b58cb385..ceb7ce984 100644 --- a/tests/lib/Output/VisitorTest.php +++ b/tests/lib/Output/VisitorTest.php @@ -46,7 +46,7 @@ public function testVisitDocument() ->getMock(); self::assertEquals( - new Response('Hello world!', 200, []), + new Response('Hello world!', Response::HTTP_OK, []), $visitor->visit($data) ); } @@ -76,7 +76,7 @@ public function testVisitEmptyDocument() ->getMock(); self::assertEquals( - new Response(null, 200, []), + new Response(null, Response::HTTP_OK, []), $visitor->visit($data) ); } @@ -108,7 +108,7 @@ public function testSetHeaders() self::assertEquals( new Response( null, - 200, + Response::HTTP_OK, [ 'Content-Type' => 'text/xml', ] @@ -133,9 +133,10 @@ public function testSetFilteredHeaders() self::assertEquals( new Response( null, - 200, + Response::HTTP_OK, [ 'Content-Type' => 'text/xml', + 'Accept-Patch' => [0 => ''], ] ), $visitor->visit($data) @@ -153,7 +154,7 @@ public function testSetHeadersNoOverwrite() self::assertEquals( new Response( null, - 200, + Response::HTTP_OK, [ 'Content-Type' => 'text/xml', ] @@ -176,7 +177,7 @@ public function testSetHeaderResetAfterVisit() self::assertEquals( new Response( null, - 200, + Response::HTTP_OK, [] ), $result @@ -193,7 +194,7 @@ public function testSetStatusCode() self::assertEquals( new Response( null, - 201 + Response::HTTP_CREATED ), $visitor->visit($data) ); @@ -211,7 +212,7 @@ public function testSetStatusCodeNoOverride() self::assertEquals( new Response( null, - 201 + Response::HTTP_CREATED ), $visitor->visit($data) ); diff --git a/tests/lib/Server/Input/Parser/BaseTest.php b/tests/lib/Server/Input/Parser/BaseTest.php index 5d2b20951..0272f5428 100644 --- a/tests/lib/Server/Input/Parser/BaseTest.php +++ b/tests/lib/Server/Input/Parser/BaseTest.php @@ -8,9 +8,10 @@ namespace Ibexa\Tests\Rest\Server\Input\Parser; use Ibexa\Contracts\Rest\Input\ParsingDispatcher; +use Ibexa\Contracts\Rest\UriParser\UriParserInterface; use Ibexa\Rest\Input; -use Ibexa\Rest\RequestParser; use Ibexa\Tests\Rest\Server\BaseTest as ParentBaseTest; +use PHPUnit\Framework\MockObject\MockObject; /** * Base test for input parsers. @@ -22,10 +23,7 @@ abstract class BaseTest extends ParentBaseTest */ protected $parsingDispatcherMock; - /** - * @var \Ibexa\Rest\RequestParser|\PHPUnit\Framework\MockObject\MockObject - */ - protected $requestParserMock; + protected UriParserInterface&MockObject $uriParserMock; /** * @var \Ibexa\Rest\Input\ParserTools @@ -59,14 +57,9 @@ public function getParseHrefExpectationsMap() return []; } - /** - * Get the Request parser. - * - * @return \Ibexa\Rest\RequestParser|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getRequestParserMock() + protected function getUriParserMock(): UriParserInterface&MockObject { - if (!isset($this->requestParserMock)) { + if (!isset($this->uriParserMock)) { $that = &$this; $callback = static function ($href, $attribute) use ($that) { @@ -75,7 +68,7 @@ protected function getRequestParserMock() if ($map[2] instanceof \Exception) { throw $map[2]; } else { - return $map[2]; + return (string)$map[2]; } } } @@ -83,15 +76,15 @@ protected function getRequestParserMock() return null; }; - $this->requestParserMock = $this->createMock(RequestParser::class); + $this->uriParserMock = $this->createMock(UriParserInterface::class); - $this->requestParserMock + $this->uriParserMock ->expects(self::any()) - ->method('parseHref') + ->method('getAttributeFromUri') ->willReturnCallback($callback); } - return $this->requestParserMock; + return $this->uriParserMock; } /** @@ -111,7 +104,7 @@ protected function getParserTools() protected function getParser() { $parser = $this->internalGetParser(); - $parser->setRequestParser($this->getRequestParserMock()); + $parser->setUriParser($this->getUriParserMock()); return $parser; } @@ -119,7 +112,7 @@ protected function getParser() /** * Must return the tested parser object. * - * @return \Ibexa\Rest\Server\Input\Parser\Base + * @return \Ibexa\Rest\Input\BaseParser */ abstract protected function internalGetParser(); } diff --git a/tests/lib/Server/Input/Parser/RelationCreateTest.php b/tests/lib/Server/Input/Parser/RelationCreateTest.php index 980dfe598..d9089e4ab 100644 --- a/tests/lib/Server/Input/Parser/RelationCreateTest.php +++ b/tests/lib/Server/Input/Parser/RelationCreateTest.php @@ -69,7 +69,7 @@ public function testParseExceptionOnMissingDestinationHref() protected function internalGetParser() { $parser = new RelationCreate(); - $parser->setRequestParser($this->getRequestParserMock()); + $parser->setUriParser($this->getUriParserMock()); return $parser; } diff --git a/tests/lib/Server/Input/Parser/URLWildcardCreateTest.php b/tests/lib/Server/Input/Parser/URLWildcardCreateTest.php index 5f5b6fc95..d0caa50a3 100644 --- a/tests/lib/Server/Input/Parser/URLWildcardCreateTest.php +++ b/tests/lib/Server/Input/Parser/URLWildcardCreateTest.php @@ -93,7 +93,7 @@ public function testParseExceptionOnMissingForward() protected function internalGetParser() { $parser = new URLWildcardCreate($this->getParserTools()); - $parser->setRequestParser($this->getRequestParserMock()); + $parser->setUriParser($this->getUriParserMock()); return $parser; } diff --git a/tests/lib/Server/Output/ValueObjectVisitor/ExceptionTest.php b/tests/lib/Server/Output/ValueObjectVisitor/ExceptionTest.php index 8aefc8f0e..b8968cbf6 100644 --- a/tests/lib/Server/Output/ValueObjectVisitor/ExceptionTest.php +++ b/tests/lib/Server/Output/ValueObjectVisitor/ExceptionTest.php @@ -46,7 +46,7 @@ public function testVisitNonVerbose(): string ->willReturn(self::NON_VERBOSE_ERROR_DESCRIPTION); $visitor = $this->internalGetNonDebugVisitor(); - $visitor->setRequestParser($this->getRequestParser()); + $visitor->setUriParser($this->getUriParser()); $visitor->setRouter($this->getRouterMock()); $visitor->setTemplateRouter($this->getTemplatedRouterMock()); diff --git a/tests/lib/Server/Output/ValueObjectVisitor/LocationTest.php b/tests/lib/Server/Output/ValueObjectVisitor/LocationTest.php index fdb75e0c1..aa6631937 100644 --- a/tests/lib/Server/Output/ValueObjectVisitor/LocationTest.php +++ b/tests/lib/Server/Output/ValueObjectVisitor/LocationTest.php @@ -18,6 +18,7 @@ use Ibexa\Core\Repository\Values\ContentType\ContentType; use Ibexa\Rest\Server\Output\ValueObjectVisitor; use Ibexa\Tests\Rest\Output\ValueObjectVisitorBaseTest; +use PHPUnit\Framework\MockObject\MockObject; final class LocationTest extends ValueObjectVisitorBaseTest { @@ -27,16 +28,14 @@ final class LocationTest extends ValueObjectVisitorBaseTest private const LOCATION_ID = 55; - /** @var \Ibexa\Contracts\Core\Repository\LocationService&\PHPUnit\Framework\MockObject\MockObject */ - private LocationService $locationServiceMock; + private LocationService&MockObject $locationServiceMock; - /** @var \Ibexa\Contracts\Core\Repository\ContentService&\PHPUnit\Framework\MockObject\MockObject */ - private ContentService $contentServiceMock; + private ContentService\RelationListFacadeInterface&MockObject $relationListFacade; protected function setUp(): void { $this->locationServiceMock = $this->createMock(LocationService::class); - $this->contentServiceMock = $this->createMock(ContentService::class); + $this->relationListFacade = $this->createMock(ContentService\RelationListFacadeInterface::class); parent::setUp(); } @@ -76,10 +75,12 @@ public function testVisitLocationAttributesResolvesMainLocation( $this->mockLoadLocation($location); - $this->contentServiceMock->expects(self::once()) - ->method('loadRelations') + $this->relationListFacade->expects(self::once()) + ->method('getRelations') ->with($versionInfo) - ->willReturn([]); + ->willReturnCallback( + static fn () => yield + ); $visitor->visit( $this->getVisitorMock(), @@ -139,6 +140,9 @@ public function getDataForTestVisitLocationAttributesResolvesMainLocation(): ite protected function internalGetVisitor(): ValueObjectVisitor\Location { - return new ValueObjectVisitor\Location($this->locationServiceMock, $this->contentServiceMock); + return new ValueObjectVisitor\Location( + $this->locationServiceMock, + $this->relationListFacade + ); } } diff --git a/tests/lib/Server/Output/ValueObjectVisitor/RestExecutedViewTest.php b/tests/lib/Server/Output/ValueObjectVisitor/RestExecutedViewTest.php index 537833bbb..e38bf0392 100644 --- a/tests/lib/Server/Output/ValueObjectVisitor/RestExecutedViewTest.php +++ b/tests/lib/Server/Output/ValueObjectVisitor/RestExecutedViewTest.php @@ -19,6 +19,7 @@ use Ibexa\Rest\Server\Output\ValueObjectVisitor; use Ibexa\Rest\Server\Values\RestExecutedView; use Ibexa\Tests\Rest\Output\ValueObjectVisitorBaseTest; +use PHPUnit\Framework\MockObject\MockObject; class RestExecutedViewTest extends ValueObjectVisitorBaseTest { @@ -40,6 +41,7 @@ public function testVisit() [ 'identifier' => 'test_view', 'searchResults' => new SearchResult([ + 'totalCount' => null, 'searchHits' => [ $this->buildContentSearchHit(), $this->buildLocationSearchHit(), @@ -115,7 +117,7 @@ protected function internalGetVisitor() { return new ValueObjectVisitor\RestExecutedView( $this->getLocationServiceMock(), - $this->getContentServiceMock(), + $this->getRelationListFacadeMock() ); } @@ -127,15 +129,14 @@ public function getLocationServiceMock() return $this->createMock(LocationService::class); } - /** - * @return \Ibexa\Contracts\Core\Repository\ContentService|\PHPUnit\Framework\MockObject\MockObject - */ - public function getContentServiceMock() + private function getRelationListFacadeMock(): ContentService\RelationListFacadeInterface&MockObject { - $contentService = $this->createMock(ContentService::class); - $contentService->method('loadRelations')->willReturn([]); + $relationListFacade = $this->createMock(ContentService\RelationListFacadeInterface::class); + $relationListFacade->method('getRelations')->willReturnCallback( + static fn () => yield + ); - return $contentService; + return $relationListFacade; } /** diff --git a/tests/lib/Server/Output/ValueObjectVisitor/RestUserTest.php b/tests/lib/Server/Output/ValueObjectVisitor/RestUserTest.php index 5f844592a..0da732830 100644 --- a/tests/lib/Server/Output/ValueObjectVisitor/RestUserTest.php +++ b/tests/lib/Server/Output/ValueObjectVisitor/RestUserTest.php @@ -73,7 +73,10 @@ public function testVisitWithoutEmbeddedVersion(): DOMDocument protected function getBasicRestUser(): RestUser { return new RestUser( - new Values\User\User(), + new Values\User\User([ + 'login' => 'rest_user', + 'email' => 'rest_user@ibexa.co', + ]), $this->getMockForAbstractClass(ContentType::class), $this->getContentInfoStub(), new Values\Content\Location( diff --git a/tests/lib/UrlHandler/PatternTest.php b/tests/lib/UrlHandler/PatternTest.php deleted file mode 100644 index 25ae73f2c..000000000 --- a/tests/lib/UrlHandler/PatternTest.php +++ /dev/null @@ -1,215 +0,0 @@ -expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("URL '/foo' did not match any route."); - - $urlHandler = new Pattern(); - $urlHandler->parse('/foo'); - } - - /** - * Tests parsing invalid pattern. - */ - public function testParseInvalidPattern() - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Invalid pattern part: '{broken'."); - - $urlHandler = new Pattern( - [ - 'invalid' => '/foo/{broken', - ] - ); - $urlHandler->parse('/foo'); - } - - /** - * Tests parsing when pattern does not match. - */ - public function testPatternDoesNotMatch() - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("URL '/bar' did not match any route."); - - $urlHandler = new Pattern( - [ - 'pattern' => '/foo/{foo}', - ] - ); - $urlHandler->parse('/bar'); - } - - /** - * Test parsing when pattern does not match the end of the URL. - */ - public function testPatternDoesNotMatchTrailing() - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("URL '/foo/23/bar' did not match any route."); - - $urlHandler = new Pattern( - [ - 'pattern' => '/foo/{foo}', - ] - ); - $urlHandler->parse('/foo/23/bar'); - } - - /** - * Data provider. - * - * @return array - */ - public static function getParseValues() - { - return [ - [ - 'section', - '/content/section/42', - [ - 'section' => '42', - ], - ], - [ - 'objectversion', - '/content/object/42/23', - [ - 'object' => '42', - 'version' => '23', - ], - ], - [ - 'location', - '/content/locations/23/42/100', - [ - 'location' => '23/42/100', - ], - ], - [ - 'locationChildren', - '/content/locations/23/42/100/children', - [ - 'location' => '23/42/100', - ], - ], - ]; - } - - /** - * Test parsing URL. - * - * @dataProvider getParseValues - */ - public function testParseUrl($type, $url, $values) - { - $urlHandler = $this->getWorkingUrlHandler(); - - self::assertSame( - $values, - $urlHandler->parse($url) - ); - } - - /** - * Test generating unknown URL type. - */ - public function testGenerateUnknownUrlType() - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("No URL for type 'unknown' available."); - - $urlHandler = new Pattern(); - $urlHandler->generate('unknown', []); - } - - /** - * Test generating URL with missing value. - */ - public function testGenerateMissingValue() - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("No value provided for 'unknown'."); - - $urlHandler = new Pattern( - [ - 'pattern' => '/foo/{unknown}', - ] - ); - $urlHandler->generate('pattern', []); - } - - /** - * Test generating URL with extra value. - */ - public function testGenerateSuperfluousValue() - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage("Unused values in values array: 'bar'."); - - $urlHandler = new Pattern( - [ - 'pattern' => '/foo/{foo}', - ] - ); - $urlHandler->generate( - 'pattern', - [ - 'foo' => 23, - 'bar' => 42, - ] - ); - } - - /** - * Data provider. - * - * @dataProvider getParseValues - */ - public function testGenerateUrl($type, $url, $values) - { - $urlHandler = $this->getWorkingUrlHandler(); - - self::assertSame( - $url, - $urlHandler->generate($type, $values) - ); - } - - /** - * Returns the URL handler. - * - * @return \Ibexa\Rest\RequestParser\Pattern - */ - protected function getWorkingUrlHandler() - { - return new Pattern( - [ - 'section' => '/content/section/{section}', - 'objectversion' => '/content/object/{object}/{version}', - 'locationChildren' => '/content/locations/{&location}/children', - 'location' => '/content/locations/{&location}', - ] - ); - } -}