diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ece6d89..e5cd1d1 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -10,31 +10,6 @@ parameters: count: 1 path: src/bundle/Command/GeneratePlatformSchemaCommand.php - - - message: "#^Method Ibexa\\\\Bundle\\\\GraphQL\\\\Command\\\\GeneratePlatformSchemaCommand\\:\\:configure\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/Command/GeneratePlatformSchemaCommand.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\GraphQL\\\\DependencyInjection\\\\Compiler\\\\FieldInputHandlersPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/DependencyInjection/Compiler/FieldInputHandlersPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\GraphQL\\\\DependencyInjection\\\\Compiler\\\\RichTextInputConvertersPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/DependencyInjection/Compiler/RichTextInputConvertersPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\GraphQL\\\\DependencyInjection\\\\Compiler\\\\SchemaDomainIteratorsPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/DependencyInjection/Compiler/SchemaDomainIteratorsPass.php - - - - message: "#^Method Ibexa\\\\Bundle\\\\GraphQL\\\\DependencyInjection\\\\Compiler\\\\SchemaWorkersPass\\:\\:process\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/DependencyInjection/Compiler/SchemaWorkersPass.php - - message: "#^Method Ibexa\\\\Bundle\\\\GraphQL\\\\DependencyInjection\\\\GraphQL\\\\SchemaProvider\\:\\:getSchemaConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -100,11 +75,6 @@ parameters: count: 1 path: src/bundle/DependencyInjection/IbexaGraphQLExtension.php - - - message: "#^Method Ibexa\\\\Bundle\\\\GraphQL\\\\DependencyInjection\\\\IbexaGraphQLExtension\\:\\:prepend\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/DependencyInjection/IbexaGraphQLExtension.php - - message: "#^Parameter \\#1 \\$configDir of method Ibexa\\\\Bundle\\\\GraphQL\\\\DependencyInjection\\\\IbexaGraphQLExtension\\:\\:getGraphQLConfig\\(\\) expects string, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" count: 1 @@ -115,11 +85,6 @@ parameters: count: 1 path: src/bundle/DependencyInjection/IbexaGraphQLExtension.php - - - message: "#^Method Ibexa\\\\Bundle\\\\GraphQL\\\\IbexaGraphQLBundle\\:\\:build\\(\\) has no return type specified\\.$#" - count: 1 - path: src/bundle/IbexaGraphQLBundle.php - - message: "#^Method Ibexa\\\\Contracts\\\\GraphQL\\\\Mutation\\\\InputHandler\\\\FieldType\\\\RichText\\\\RichTextInputConverter\\:\\:convertToXml\\(\\) has parameter \\$text with no type specified\\.$#" count: 1 @@ -540,56 +505,11 @@ parameters: count: 1 path: src/lib/Relay/NodeResolver.php - - - message: "#^Access to protected property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Connection\\:\\:\\$edges\\.$#" - count: 1 - path: src/lib/Relay/PageAwareConnection.php - - - - message: "#^Access to protected property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Connection\\:\\:\\$pageInfo\\.$#" - count: 1 - path: src/lib/Relay/PageAwareConnection.php - - - - message: "#^Access to protected property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Connection\\:\\:\\$totalCount\\.$#" - count: 1 - path: src/lib/Relay/PageAwareConnection.php - - - - message: "#^Method Ibexa\\\\GraphQL\\\\Relay\\\\PageAwareConnection\\:\\:__construct\\(\\) has parameter \\$edges with no value type specified in iterable type array\\.$#" - count: 1 - path: src/lib/Relay/PageAwareConnection.php - - - - message: "#^Parameter \\#2 \\$pageInfo of class Ibexa\\\\GraphQL\\\\Relay\\\\PageAwareConnection constructor expects Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\PageInfoInterface, Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\PageInfoInterface\\|null given\\.$#" - count: 1 - path: src/lib/Relay/PageAwareConnection.php - - - - message: "#^Property Ibexa\\\\GraphQL\\\\Relay\\\\PageAwareConnection\\:\\:\\$totalCount \\(int\\) does not accept GraphQL\\\\Executor\\\\Promise\\\\Promise\\|int\\|null\\.$#" - count: 1 - path: src/lib/Relay/PageAwareConnection.php - - - - message: "#^Access to an undefined property Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\ConnectionInterface\\:\\:\\$sliceSize\\.$#" - count: 1 - path: src/lib/Relay/SearchResolver.php - - message: "#^Method Ibexa\\\\GraphQL\\\\Relay\\\\SearchResolver\\:\\:searchContent\\(\\) has parameter \\$args with no type specified\\.$#" count: 1 path: src/lib/Relay/SearchResolver.php - - - message: "#^Method Ibexa\\\\GraphQL\\\\Relay\\\\SearchResolver\\:\\:searchContent\\(\\) should return Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Connection but returns Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\ConnectionInterface\\.$#" - count: 1 - path: src/lib/Relay/SearchResolver.php - - - - message: "#^Method Ibexa\\\\GraphQL\\\\Relay\\\\SearchResolver\\:\\:searchContent\\(\\) should return Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Connection but returns null\\.$#" - count: 1 - path: src/lib/Relay/SearchResolver.php - - message: "#^Method Ibexa\\\\GraphQL\\\\Resolver\\\\ContentResolver\\:\\:findContentByType\\(\\) has no return type specified\\.$#" count: 1 @@ -980,16 +900,6 @@ parameters: count: 1 path: src/lib/Resolver/ItemResolver.php - - - message: "#^Method Ibexa\\\\GraphQL\\\\Resolver\\\\ItemResolver\\:\\:resolveItemsOfTypeAsConnection\\(\\) should return Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Connection but returns GraphQL\\\\Executor\\\\Promise\\\\Promise\\|Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Connection\\.$#" - count: 1 - path: src/lib/Resolver/ItemResolver.php - - - - message: "#^Property Ibexa\\\\GraphQL\\\\Resolver\\\\ItemResolver\\:\\:\\$contentTypeLoader is never read, only written\\.$#" - count: 1 - path: src/lib/Resolver/ItemResolver.php - - message: "#^Parameter \\#1 \\$location of method Ibexa\\\\GraphQL\\\\Resolver\\\\LocationGuesser\\\\ObjectStorageLocationList\\:\\:addLocation\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject given\\.$#" count: 1 @@ -1065,16 +975,6 @@ parameters: count: 1 path: src/lib/Resolver/LocationResolver.php - - - message: "#^PHPDoc tag @return with type Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Connection is incompatible with native type Ibexa\\\\GraphQL\\\\Relay\\\\PageAwareConnection\\.$#" - count: 1 - path: src/lib/Resolver/LocationResolver.php - - - - message: "#^Parameter \\#1 \\$connection of static method Ibexa\\\\GraphQL\\\\Relay\\\\PageAwareConnection\\:\\:fromConnection\\(\\) expects Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Connection, GraphQL\\\\Executor\\\\Promise\\\\Promise\\|Overblog\\\\GraphQLBundle\\\\Relay\\\\Connection\\\\Output\\\\Connection given\\.$#" - count: 1 - path: src/lib/Resolver/LocationResolver.php - - message: "#^Method Ibexa\\\\GraphQL\\\\Resolver\\\\ObjectStateGroupResolver\\:\\:resolveObjectStateGroups\\(\\) should return array\\ but returns iterable\\\\.$#" count: 1 diff --git a/src/lib/Relay/DomainConnectionBuilder.php b/src/lib/Relay/DomainConnectionBuilder.php deleted file mode 100644 index 46b509c..0000000 --- a/src/lib/Relay/DomainConnectionBuilder.php +++ /dev/null @@ -1,15 +0,0 @@ -> */ + public iterable $edges = []; - /** @var \Overblog\GraphQLBundle\Relay\Connection\PageInfoInterface */ - public $pageInfo; + public PageInfoInterface $pageInfo; /** @var int */ - public $totalCount; + public int $totalCount; /** @var Page[] */ - public $pages; + public array $pages; - public function __construct(array $edges, PageInfoInterface $pageInfo) + /** + * @param iterable<\Overblog\GraphQLBundle\Relay\Connection\EdgeInterface> $edges + */ + public function __construct(iterable $edges, PageInfoInterface $pageInfo) { $this->edges = $edges; $this->pageInfo = $pageInfo; } - public static function fromConnection(Connection $connection, Argument $args): PageAwareConnection + /** + * @param \GraphQL\Executor\Promise\Promise|\Overblog\GraphQLBundle\Relay\Connection\ConnectionInterface $connection + * + * @return \Ibexa\GraphQL\Relay\PageAwareConnection + */ + public static function fromConnection(ConnectionInterface|Promise $connection, Argument $args): PageAwareConnection { - $return = new self($connection->edges, $connection->pageInfo); - $return->totalCount = $connection->totalCount; + $connection = self::resolvePromise( + $connection, + fn($resolved) => $resolved instanceof ConnectionInterface, + 'Resolved result is not a ConnectionInterface' + ); + + $return = new self($connection->getEdges(), $connection->getPageInfo()); + + $totalCount = self::resolvePromise( + $connection->getTotalCount(), + fn($resolved) => is_int($resolved) || is_null($resolved), + 'Resolved result is not an int or null' + ); + + $return->totalCount = (int)$totalCount; $return->pages = []; @@ -49,4 +76,18 @@ public static function fromConnection(Connection $connection, Argument $args): P return $return; } + + private static function resolvePromise(mixed $value, callable $validator, string $errorMessage): mixed + { + if ($value instanceof Promise) { + $promiseAdapter = new SyncPromiseAdapter(); + $resolvedValue = $promiseAdapter->wait($value); + + if (!$validator($resolvedValue)) { + throw new \UnexpectedValueException($errorMessage); + } + return $resolvedValue; + } + return $value; + } } diff --git a/src/lib/Relay/SearchResolver.php b/src/lib/Relay/SearchResolver.php index 5f49c41..3116e07 100644 --- a/src/lib/Relay/SearchResolver.php +++ b/src/lib/Relay/SearchResolver.php @@ -11,6 +11,7 @@ use Ibexa\Contracts\Core\Repository\Values\Content\Query; use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; use Overblog\GraphQLBundle\Relay\Connection\ConnectionBuilder; +use Overblog\GraphQLBundle\Relay\Connection\ConnectionInterface; class SearchResolver { @@ -27,11 +28,11 @@ public function __construct(SearchService $searchService) /** * @param $args * - * @return \Overblog\GraphQLBundle\Relay\Connection\Output\Connection + * @return \Overblog\GraphQLBundle\Relay\Connection\ConnectionInterface<\Ibexa\Contracts\Core\Repository\Values\ValueObject> * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ - public function searchContent($args) + public function searchContent($args): ConnectionInterface { $queryArg = $args['query']; @@ -48,10 +49,9 @@ public function searchContent($args) } } - if (count($criteria) === 0) { - return null; + if (count($criteria) !== 0) { + $query->filter = count($criteria) > 1 ? new Query\Criterion\LogicalAnd($criteria) : $criteria[0]; } - $query->filter = count($criteria) > 1 ? new Query\Criterion\LogicalAnd($criteria) : $criteria[0]; $searchResult = $this->searchService->findContentInfo($query); $contentItems = array_map( @@ -62,7 +62,8 @@ static function (SearchHit $hit) { ); $connectionBuilder = new ConnectionBuilder(); - $connection = $connectionBuilder->connectionFromArraySlice( + + return $connectionBuilder->connectionFromArraySlice( $contentItems, $args, [ @@ -70,8 +71,5 @@ static function (SearchHit $hit) { 'arrayLength' => $searchResult->totalCount, ] ); - $connection->sliceSize = count($contentItems); - - return $connection; } } diff --git a/src/lib/Resolver/ItemResolver.php b/src/lib/Resolver/ItemResolver.php index e24c694..679c5d5 100644 --- a/src/lib/Resolver/ItemResolver.php +++ b/src/lib/Resolver/ItemResolver.php @@ -8,6 +8,7 @@ namespace Ibexa\GraphQL\Resolver; use GraphQL\Error\UserError; +use GraphQL\Executor\Promise\Promise; use Ibexa\Contracts\Core\Repository\Values\Content\Content; use Ibexa\Contracts\Core\Repository\Values\Content\Query; use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; @@ -37,9 +38,6 @@ final class ItemResolver /** @var \Ibexa\GraphQL\DataLoader\ContentLoader */ private $contentLoader; - /** @var \Ibexa\GraphQL\DataLoader\ContentTypeLoader */ - private $contentTypeLoader; - /** @var \Ibexa\GraphQL\DataLoader\LocationLoader */ private $locationLoader; @@ -50,14 +48,12 @@ public function __construct( TypeResolver $typeResolver, QueryMapper $queryMapper, ContentLoader $contentLoader, - ContentTypeLoader $contentTypeLoader, LocationLoader $locationLoader, ItemFactory $currentSiteItemFactory ) { $this->typeResolver = $typeResolver; $this->queryMapper = $queryMapper; $this->contentLoader = $contentLoader; - $this->contentTypeLoader = $contentTypeLoader; $this->locationLoader = $locationLoader; $this->itemFactory = $currentSiteItemFactory; } @@ -99,7 +95,7 @@ public function resolveItem($args): Item ); } elseif (isset($args['contentId'])) { $item = $this->itemFactory->fromContent( - $content = $this->contentLoader->findSingle(new Query\Criterion\ContentId($args['contentId'])) + $this->contentLoader->findSingle(new Query\Criterion\ContentId($args['contentId'])) ); } elseif (isset($args['remoteId'])) { $item = $this->itemFactory->fromContent( @@ -129,7 +125,10 @@ public function resolveItemFieldValue(Item $item, $fieldDefinitionIdentifier, $a return Field::fromField($item->getContent()->getField($fieldDefinitionIdentifier, $args['language'] ?? null)); } - public function resolveItemsOfTypeAsConnection(string $contentTypeIdentifier, $args): Connection + /** + * @return \GraphQL\Executor\Promise\Promise|\Overblog\GraphQLBundle\Relay\Connection\Output\Connection<\Ibexa\GraphQL\Value\Item> + */ + public function resolveItemsOfTypeAsConnection(string $contentTypeIdentifier, $args): Connection|Promise { $query = $args['query'] ?: []; $query['ContentTypeIdentifier'] = $contentTypeIdentifier; diff --git a/src/lib/Resolver/LocationResolver.php b/src/lib/Resolver/LocationResolver.php index bcaad39..5e41e7e 100644 --- a/src/lib/Resolver/LocationResolver.php +++ b/src/lib/Resolver/LocationResolver.php @@ -86,7 +86,7 @@ public function resolveLocationById($locationId) /** * @param int $locationId * - * @return \Overblog\GraphQLBundle\Relay\Connection\Output\Connection + * @return \Ibexa\GraphQL\Relay\PageAwareConnection<\Ibexa\Contracts\Core\Repository\Values\Content\Location> */ public function resolveLocationChildren($locationId, Argument $args): PageAwareConnection {