From 958a0ac69475fd8b43d878c1ad3850434a7a3538 Mon Sep 17 00:00:00 2001 From: Aquarvin Date: Tue, 20 Nov 2018 19:34:09 +0200 Subject: [PATCH 1/5] GraphQl-93: Implement support for variables in query -- Variables may be Input type -- Query now accepts variables -- added type property for Output/Input Type element -- functional test was added --- .../Magento/GraphQl/Controller/GraphQl.php | 4 +- .../TestFramework/TestCase/GraphQl/Client.php | 4 +- .../GraphQl/VariablesSupportQueryTest.php | 90 +++++++++++++++++++ .../Magento/Framework/GraphQl/Config.php | 14 ++- .../Framework/GraphQl/Config/Element/Type.php | 18 ++++ .../GraphQl/Config/Element/TypeFactory.php | 1 + .../Framework/GraphQl/Query/Fields.php | 27 +++++- .../GraphQl/Schema/SchemaGenerator.php | 42 ++++++--- .../GraphQl/Schema/Type/Input/InputMapper.php | 30 +++++++ 9 files changed, 211 insertions(+), 19 deletions(-) create mode 100644 dev/tests/api-functional/testsuite/Magento/GraphQl/VariablesSupportQueryTest.php diff --git a/app/code/Magento/GraphQl/Controller/GraphQl.php b/app/code/Magento/GraphQl/Controller/GraphQl.php index c4a0b55de9bfc..c04bb7f5775a0 100644 --- a/app/code/Magento/GraphQl/Controller/GraphQl.php +++ b/app/code/Magento/GraphQl/Controller/GraphQl.php @@ -111,10 +111,10 @@ public function dispatch(RequestInterface $request) : ResponseInterface $data = $this->jsonSerializer->unserialize($request->getContent()); $query = isset($data['query']) ? $data['query'] : ''; - + $variables = isset($data['variables']) ? $data['variables'] : null; // We have to extract queried field names to avoid instantiation of non necessary fields in webonyx schema // Temporal coupling is required for performance optimization - $this->queryFields->setQuery($query); + $this->queryFields->setQuery($query, $variables); $schema = $this->schemaGenerator->generate(); $result = $this->queryProcessor->process( diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php index 64ad44528572d..bd91d58b6b00e 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php @@ -59,8 +59,8 @@ public function postQuery(string $query, array $variables = [], string $operatio $headers = array_merge($headers, ['Accept: application/json', 'Content-Type: application/json']); $requestArray = [ 'query' => $query, - 'variables' => empty($variables) ? $variables : null, - 'operationName' => empty($operationName) ? $operationName : null + 'variables' => !empty($variables) ? $variables : null, + 'operationName' => !empty($operationName) ? $operationName : null ]; $postData = $this->json->jsonEncode($requestArray); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/VariablesSupportQueryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/VariablesSupportQueryTest.php new file mode 100644 index 0000000000000..138547ecdbc38 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/VariablesSupportQueryTest.php @@ -0,0 +1,90 @@ +objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + } + + /** + * Tests that Introspection is disabled when not in developer mode + * + * @magentoApiDataFixture Magento/Catalog/_files/product_simple_with_all_fields.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testQueryObjectVariablesSupport() + { + $productSku = 'simple'; + + $query + = <<<'QUERY' +query GetProductsQuery($page: Int, $filterInput: ProductFilterInput){ + products( + pageSize: 10 + currentPage: $page + filter: $filterInput + sort: {} + ) { + items { + name + } + } +} +QUERY; + $variables = [ + 'page' => 1, + 'filterInput' => [ + 'sku' => [ + 'like' => '%simple%' + ] + ] + ]; + + $response = $this->graphQlQuery($query, $variables); + /** @var \Magento\Catalog\Model\Product $product */ + $product = $this->productRepository->get($productSku, false, null, true); + + $this->assertArrayHasKey('products', $response); + $this->assertArrayHasKey('items', $response['products']); + $this->assertEquals(1, count($response['products']['items'])); + $this->assertArrayHasKey(0, $response['products']['items']); + $this->assertFields($product, $response['products']['items'][0]); + } + + /** + * @param ProductInterface $product + * @param array $actualResponse + */ + private function assertFields($product, $actualResponse) + { + $assertionMap = [ + ['response_field' => 'name', 'expected_value' => $product->getName()], + ]; + + $this->assertResponseFields($actualResponse, $assertionMap); + } +} diff --git a/lib/internal/Magento/Framework/GraphQl/Config.php b/lib/internal/Magento/Framework/GraphQl/Config.php index ff4c920d2cd6a..a9f5eb3aaff6f 100644 --- a/lib/internal/Magento/Framework/GraphQl/Config.php +++ b/lib/internal/Magento/Framework/GraphQl/Config.php @@ -77,18 +77,24 @@ public function getConfigElement(string $configElementName) : ConfigElementInter } /** - * Return all type names declared in a GraphQL schema's configuration. + * Return all type names declared in a GraphQL schema's configuration and their type. * - * @return string[] + * @return array $types + * name string + * type string */ public function getDeclaredTypeNames() : array { $types = []; foreach ($this->configData->get(null) as $item) { - if (isset($item['type']) && $item['type'] == 'graphql_type') { - $types[] = $item['name']; + if (isset($item['type'])) { + $types[] = [ + 'name' => $item['name'], + 'type' => $item['type'], + ]; } } + return $types; } } diff --git a/lib/internal/Magento/Framework/GraphQl/Config/Element/Type.php b/lib/internal/Magento/Framework/GraphQl/Config/Element/Type.php index 24ff439db0347..bda2c5ad5bd52 100644 --- a/lib/internal/Magento/Framework/GraphQl/Config/Element/Type.php +++ b/lib/internal/Magento/Framework/GraphQl/Config/Element/Type.php @@ -27,6 +27,11 @@ class Type implements TypeInterface */ private $interfaces; + /** + * @var string + */ + private $type; + /** * @var string */ @@ -36,17 +41,20 @@ class Type implements TypeInterface * @param string $name * @param Field[] $fields * @param string[] $interfaces + * @param string $type * @param string $description */ public function __construct( string $name, array $fields, array $interfaces, + string $type, string $description ) { $this->name = $name; $this->fields = $fields; $this->interfaces = $interfaces; + $this->type = $type; $this->description = $description; } @@ -89,4 +97,14 @@ public function getDescription() : string { return $this->description; } + + /** + * Get a type. + * + * @return string + */ + public function getType() : string + { + return $this->type; + } } diff --git a/lib/internal/Magento/Framework/GraphQl/Config/Element/TypeFactory.php b/lib/internal/Magento/Framework/GraphQl/Config/Element/TypeFactory.php index c5f3187b04841..a6a6de8475009 100644 --- a/lib/internal/Magento/Framework/GraphQl/Config/Element/TypeFactory.php +++ b/lib/internal/Magento/Framework/GraphQl/Config/Element/TypeFactory.php @@ -92,6 +92,7 @@ public function create( 'name' => $typeData['name'], 'fields' => $fields, 'interfaces' => isset($typeData['implements']) ? $typeData['implements'] : [], + 'type' => isset($typeData['type']) ? $typeData['type'] : '', 'description' => isset($typeData['description']) ? $typeData['description'] : '' ] ); diff --git a/lib/internal/Magento/Framework/GraphQl/Query/Fields.php b/lib/internal/Magento/Framework/GraphQl/Query/Fields.php index d0bc9591265eb..9c812e5259032 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/Fields.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/Fields.php @@ -24,9 +24,11 @@ class Fields * Set Query for extracting list of fields. * * @param string $query + * @param array|null $variables + * * @return void */ - public function setQuery($query) + public function setQuery($query, $variables = null) { $queryFields = []; try { @@ -48,6 +50,9 @@ public function setQuery($query) // It must be possible to query any fields during introspection query $queryFields = []; } + if (isset($variables)) { + $queryFields = array_merge($queryFields, $this->getVariables($variables)); + } $this->fieldsUsedInQuery = $queryFields; } @@ -62,4 +67,24 @@ public function getFieldsUsedInQuery() { return $this->fieldsUsedInQuery; } + + /** + * Extract and return list of all used fields in GraphQL query's variables + * + * @param array $variables + * + * @return string[] + */ + private function getVariables($variables) + { + $fields = []; + foreach ($variables as $key => $value){ + if (is_array($value)){ + $fields = array_merge($fields, $this->getVariables($value)); + } + $fields[$key] = $key; + } + + return $fields; + } } diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php b/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php index 63fef73186b12..c768d2fab5afa 100644 --- a/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php +++ b/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php @@ -8,9 +8,9 @@ namespace Magento\Framework\GraphQl\Schema; use Magento\Framework\GraphQl\ConfigInterface; -use Magento\Framework\GraphQl\Schema\SchemaGeneratorInterface; -use Magento\Framework\GraphQl\Schema\Type\Output\OutputMapper; use Magento\Framework\GraphQl\Schema; +use Magento\Framework\GraphQl\Schema\Type\Input\InputMapper; +use Magento\Framework\GraphQl\Schema\Type\Output\OutputMapper; use Magento\Framework\GraphQl\SchemaFactory; /** @@ -28,6 +28,11 @@ class SchemaGenerator implements SchemaGeneratorInterface */ private $outputMapper; + /** + * @var InputMapper + */ + private $inputMapper; + /** * @var ConfigInterface */ @@ -36,15 +41,18 @@ class SchemaGenerator implements SchemaGeneratorInterface /** * @param SchemaFactory $schemaFactory * @param OutputMapper $outputMapper + * @param InputMapper $inputMapper * @param ConfigInterface $config */ public function __construct( SchemaFactory $schemaFactory, OutputMapper $outputMapper, + InputMapper $inputMapper, ConfigInterface $config ) { $this->schemaFactory = $schemaFactory; $this->outputMapper = $outputMapper; + $this->inputMapper = $inputMapper; $this->config = $config; } @@ -60,16 +68,30 @@ public function generate() : Schema 'typeLoader' => function ($name) { return $this->outputMapper->getOutputType($name); }, - 'types' => function () { - //all types should be generated only on introspection - $typesImplementors = []; - foreach ($this->config->getDeclaredTypeNames() as $name) { - $typesImplementors [] = $this->outputMapper->getOutputType($name); - } - return $typesImplementors; - } + 'types' => $this->getTypes() ] ); return $schema; } + + /** + * @return array + * @throws \Magento\Framework\GraphQl\Exception\GraphQlInputException + */ + private function getTypes() + { + $typesImplementors = []; + foreach ($this->config->getDeclaredTypeNames() as $type) { + switch ($type['type']) { + case 'graphql_type' : + $typesImplementors [] = $this->outputMapper->getOutputType($type['name']); + break; + case 'graphql_input' : + $typesImplementors [] = $this->inputMapper->getInputType($type['name']); + break; + } + } + + return $typesImplementors; + } } diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputMapper.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputMapper.php index d806c0b3e68ab..0785ea1278045 100644 --- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputMapper.php +++ b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputMapper.php @@ -11,7 +11,10 @@ use Magento\Framework\GraphQl\Config\Element\Argument; use Magento\Framework\GraphQl\ConfigInterface; use Magento\Framework\GraphQl\Schema\Type\ScalarTypes; +use Magento\Framework\GraphQl\Schema\Type\InputTypeInterface; use Magento\Framework\GraphQl\Schema\TypeFactory; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\Phrase; class InputMapper { @@ -35,6 +38,11 @@ class InputMapper */ private $scalarTypes; + /** + * @var InputTypeInterface[] + */ + private $inputTypes; + /** * @var WrappedTypeProcessor */ @@ -101,4 +109,26 @@ public function getRepresentation(Argument $argument) : array return $calculatedArgument; } + + /** + * Get GraphQL input type object by type name. + * + * @param string $typeName + * @return InputTypeInterface + * @throws GraphQlInputException + */ + public function getInputType($typeName) + { + if (!isset($this->inputTypes[$typeName])) { + $configElement = $this->config->getConfigElement($typeName); + $this->inputTypes[$typeName] = $this->inputFactory->create($configElement); + if (!($this->inputTypes[$typeName] instanceof InputTypeInterface)) { + throw new GraphQlInputException( + new Phrase("Type '{$typeName}' was requested but is not declared in the GraphQL schema.") + ); + } + } + + return $this->inputTypes[$typeName]; + } } From 6f585f43ebba9036350aaa2dadb1f82255230313 Mon Sep 17 00:00:00 2001 From: Valerii Naida Date: Thu, 17 Jan 2019 17:20:09 -0600 Subject: [PATCH 2/5] GraphQl-93: Implement support for variables in query --- app/code/Magento/GraphQl/etc/di.xml | 16 +-- .../Framework/GraphQl/Config/Element/Enum.php | 2 +- .../GraphQl/Config/Element/Input.php | 74 ++++++++++++++ .../GraphQl/Config/Element/InputFactory.php | 97 +++++++++++++++++++ .../GraphQl/Config/Element/InterfaceType.php | 4 +- .../Framework/GraphQl/Config/Element/Type.php | 20 +--- .../GraphQl/Config/Element/TypeFactory.php | 5 +- .../Framework/GraphQl/Query/Fields.php | 10 +- .../Argument/FieldEntityAttributesPool.php | 2 +- .../GraphQl/Schema/SchemaGenerator.php | 62 ++++-------- .../Schema/Type/Input/InputFactory.php | 60 ------------ .../GraphQl/Schema/Type/Input/InputMapper.php | 70 +++---------- .../Schema/Type/Input/InputObjectType.php | 41 +++----- .../Output/ElementMapper/Formatter/Fields.php | 9 -- .../Schema/Type/Output/OutputFactory.php | 65 ------------- .../Schema/Type/Output/OutputMapper.php | 53 +++------- .../GraphQl/Schema/Type/TypeRegistry.php | 93 ++++++++++++++++++ 17 files changed, 338 insertions(+), 345 deletions(-) create mode 100644 lib/internal/Magento/Framework/GraphQl/Config/Element/Input.php create mode 100644 lib/internal/Magento/Framework/GraphQl/Config/Element/InputFactory.php delete mode 100644 lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputFactory.php delete mode 100644 lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/OutputFactory.php create mode 100644 lib/internal/Magento/Framework/GraphQl/Schema/Type/TypeRegistry.php diff --git a/app/code/Magento/GraphQl/etc/di.xml b/app/code/Magento/GraphQl/etc/di.xml index b2083ea758e56..6acb78f9c7f9e 100644 --- a/app/code/Magento/GraphQl/etc/di.xml +++ b/app/code/Magento/GraphQl/etc/di.xml @@ -27,7 +27,7 @@ Magento\Framework\GraphQl\Config\Element\InterfaceFactory Magento\Framework\GraphQl\Config\Element\TypeFactory - Magento\Framework\GraphQl\Config\Element\TypeFactory + Magento\Framework\GraphQl\Config\Element\InputFactory Magento\Framework\GraphQl\Config\Element\EnumFactory @@ -55,24 +55,16 @@ - + - + Magento\Framework\GraphQl\Schema\Type\Output\OutputTypeObject + Magento\Framework\GraphQl\Schema\Type\Input\InputObjectType Magento\Framework\GraphQl\Schema\Type\Output\OutputInterfaceObject Magento\Framework\GraphQl\Schema\Type\Enum\Enum - - - - Magento\Framework\GraphQl\Schema\Type\Input\InputObjectType - Magento\Framework\GraphQl\Schema\Type\Input\InputObjectType - Magento\Framework\GraphQl\Schema\Type\Enum\Enum - - - Magento\Framework\GraphQl\Schema\Type\Output\ElementMapper\FormatterComposite diff --git a/lib/internal/Magento/Framework/GraphQl/Config/Element/Enum.php b/lib/internal/Magento/Framework/GraphQl/Config/Element/Enum.php index b1210e986b772..994ae489af128 100644 --- a/lib/internal/Magento/Framework/GraphQl/Config/Element/Enum.php +++ b/lib/internal/Magento/Framework/GraphQl/Config/Element/Enum.php @@ -37,7 +37,7 @@ class Enum implements ConfigElementInterface public function __construct( string $name, array $values, - string $description = "" + string $description ) { $this->name = $name; $this->values = $values; diff --git a/lib/internal/Magento/Framework/GraphQl/Config/Element/Input.php b/lib/internal/Magento/Framework/GraphQl/Config/Element/Input.php new file mode 100644 index 0000000000000..8e86f701672c6 --- /dev/null +++ b/lib/internal/Magento/Framework/GraphQl/Config/Element/Input.php @@ -0,0 +1,74 @@ +name = $name; + $this->fields = $fields; + $this->description = $description; + } + + /** + * Get the type name. + * + * @return string + */ + public function getName(): string + { + return $this->name; + } + + /** + * Get a list of fields that make up the possible return or input values of a type. + * + * @return Field[] + */ + public function getFields(): array + { + return $this->fields; + } + + /** + * Get a human-readable description of the type. + * + * @return string + */ + public function getDescription(): string + { + return $this->description; + } +} diff --git a/lib/internal/Magento/Framework/GraphQl/Config/Element/InputFactory.php b/lib/internal/Magento/Framework/GraphQl/Config/Element/InputFactory.php new file mode 100644 index 0000000000000..677354f67e230 --- /dev/null +++ b/lib/internal/Magento/Framework/GraphQl/Config/Element/InputFactory.php @@ -0,0 +1,97 @@ +objectManager = $objectManager; + $this->argumentFactory = $argumentFactory; + $this->fieldFactory = $fieldFactory; + } + + /** + * Instantiate an object representing 'input' GraphQL config element. + * + * @param array $data + * @return ConfigElementInterface + */ + public function createFromConfigData(array $data): ConfigElementInterface + { + $fields = []; + $data['fields'] = isset($data['fields']) ? $data['fields'] : []; + foreach ($data['fields'] as $field) { + $arguments = []; + foreach ($field['arguments'] as $argument) { + $arguments[$argument['name']] = $this->argumentFactory->createFromConfigData($argument); + } + $fields[$field['name']] = $this->fieldFactory->createFromConfigData( + $field, + $arguments + ); + } + return $this->create( + $data, + $fields + ); + } + + /** + * Create type object based off array of configured GraphQL InputType data. + * + * Type data must contain name and the type's fields. Optional data includes description. + * + * @param array $typeData + * @param array $fields + * @return Input + */ + private function create( + array $typeData, + array $fields + ): Input { + return $this->objectManager->create( + Input::class, + [ + 'name' => $typeData['name'], + 'fields' => $fields, + 'description' => isset($typeData['description']) ? $typeData['description'] : '' + ] + ); + } +} diff --git a/lib/internal/Magento/Framework/GraphQl/Config/Element/InterfaceType.php b/lib/internal/Magento/Framework/GraphQl/Config/Element/InterfaceType.php index 320199c14a6d6..73ebd42acfb27 100644 --- a/lib/internal/Magento/Framework/GraphQl/Config/Element/InterfaceType.php +++ b/lib/internal/Magento/Framework/GraphQl/Config/Element/InterfaceType.php @@ -8,7 +8,7 @@ namespace Magento\Framework\GraphQl\Config\Element; /** - * Describes the configured data for a GraphQL interface type. + * Class representing 'interface' GraphQL config element. */ class InterfaceType implements TypeInterface { @@ -42,7 +42,7 @@ public function __construct( string $name, string $typeResolver, array $fields, - string $description = "" + string $description ) { $this->name = $name; $this->fields = $fields; diff --git a/lib/internal/Magento/Framework/GraphQl/Config/Element/Type.php b/lib/internal/Magento/Framework/GraphQl/Config/Element/Type.php index bda2c5ad5bd52..20d017cc71062 100644 --- a/lib/internal/Magento/Framework/GraphQl/Config/Element/Type.php +++ b/lib/internal/Magento/Framework/GraphQl/Config/Element/Type.php @@ -8,7 +8,7 @@ namespace Magento\Framework\GraphQl\Config\Element; /** - * Describes all the configured data of an Output or Input type in GraphQL. + * Class representing 'type' GraphQL config element. */ class Type implements TypeInterface { @@ -27,11 +27,6 @@ class Type implements TypeInterface */ private $interfaces; - /** - * @var string - */ - private $type; - /** * @var string */ @@ -41,20 +36,17 @@ class Type implements TypeInterface * @param string $name * @param Field[] $fields * @param string[] $interfaces - * @param string $type * @param string $description */ public function __construct( string $name, array $fields, array $interfaces, - string $type, string $description ) { $this->name = $name; $this->fields = $fields; $this->interfaces = $interfaces; - $this->type = $type; $this->description = $description; } @@ -97,14 +89,4 @@ public function getDescription() : string { return $this->description; } - - /** - * Get a type. - * - * @return string - */ - public function getType() : string - { - return $this->type; - } } diff --git a/lib/internal/Magento/Framework/GraphQl/Config/Element/TypeFactory.php b/lib/internal/Magento/Framework/GraphQl/Config/Element/TypeFactory.php index a6a6de8475009..f17d99be9bd65 100644 --- a/lib/internal/Magento/Framework/GraphQl/Config/Element/TypeFactory.php +++ b/lib/internal/Magento/Framework/GraphQl/Config/Element/TypeFactory.php @@ -73,10 +73,10 @@ public function createFromConfigData(array $data): ConfigElementInterface } /** - * Create type object based off array of configured GraphQL Output/InputType data. + * Create type object based off array of configured GraphQL Type data. * * Type data must contain name and the type's fields. Optional data includes 'implements' (i.e. the interfaces - * implemented by the types), and description. An InputType cannot implement an interface. + * implemented by the types), and description. * * @param array $typeData * @param array $fields @@ -92,7 +92,6 @@ public function create( 'name' => $typeData['name'], 'fields' => $fields, 'interfaces' => isset($typeData['implements']) ? $typeData['implements'] : [], - 'type' => isset($typeData['type']) ? $typeData['type'] : '', 'description' => isset($typeData['description']) ? $typeData['description'] : '' ] ); diff --git a/lib/internal/Magento/Framework/GraphQl/Query/Fields.php b/lib/internal/Magento/Framework/GraphQl/Query/Fields.php index 9c812e5259032..d0c88cb0f898e 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/Fields.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/Fields.php @@ -28,7 +28,7 @@ class Fields * * @return void */ - public function setQuery($query, $variables = null) + public function setQuery($query, array $variables = null) { $queryFields = []; try { @@ -43,6 +43,9 @@ public function setQuery($query, $variables = null) ] ] ); + if (isset($variables)) { + $queryFields = array_merge($queryFields, $this->getVariables($variables)); + } } catch (\Exception $e) { // If a syntax error is encountered do not collect fields } @@ -50,9 +53,6 @@ public function setQuery($query, $variables = null) // It must be possible to query any fields during introspection query $queryFields = []; } - if (isset($variables)) { - $queryFields = array_merge($queryFields, $this->getVariables($variables)); - } $this->fieldsUsedInQuery = $queryFields; } @@ -75,7 +75,7 @@ public function getFieldsUsedInQuery() * * @return string[] */ - private function getVariables($variables) + private function getVariables(array $variables): array { $fields = []; foreach ($variables as $key => $value){ diff --git a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/FieldEntityAttributesPool.php b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/FieldEntityAttributesPool.php index e7d14a81b9dee..bd9de206ccda1 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/FieldEntityAttributesPool.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/FieldEntityAttributesPool.php @@ -38,7 +38,7 @@ public function getEntityAttributesForEntityFromField(string $fieldName) : array if (isset($this->attributesInstances[$fieldName])) { return $this->attributesInstances[$fieldName]->getEntityAttributes(); } else { - throw new \LogicException(sprintf('There is no attrribute class assigned to field %1', $fieldName)); + throw new \LogicException(sprintf('There is no attribute class assigned to field %1', $fieldName)); } } } diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php b/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php index c768d2fab5afa..ef78bea476f49 100644 --- a/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php +++ b/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php @@ -9,8 +9,7 @@ use Magento\Framework\GraphQl\ConfigInterface; use Magento\Framework\GraphQl\Schema; -use Magento\Framework\GraphQl\Schema\Type\Input\InputMapper; -use Magento\Framework\GraphQl\Schema\Type\Output\OutputMapper; +use Magento\Framework\GraphQl\Schema\Type\TypeRegistry; use Magento\Framework\GraphQl\SchemaFactory; /** @@ -24,36 +23,28 @@ class SchemaGenerator implements SchemaGeneratorInterface private $schemaFactory; /** - * @var OutputMapper - */ - private $outputMapper; - - /** - * @var InputMapper + * @var ConfigInterface */ - private $inputMapper; + private $config; /** - * @var ConfigInterface + * @var TypeRegistry */ - private $config; + private $typeRegistry; /** * @param SchemaFactory $schemaFactory - * @param OutputMapper $outputMapper - * @param InputMapper $inputMapper * @param ConfigInterface $config + * @param TypeRegistry $typeRegistry */ public function __construct( SchemaFactory $schemaFactory, - OutputMapper $outputMapper, - InputMapper $inputMapper, - ConfigInterface $config + ConfigInterface $config, + TypeRegistry $typeRegistry ) { $this->schemaFactory = $schemaFactory; - $this->outputMapper = $outputMapper; - $this->inputMapper = $inputMapper; $this->config = $config; + $this->typeRegistry = $typeRegistry; } /** @@ -63,35 +54,20 @@ public function generate() : Schema { $schema = $this->schemaFactory->create( [ - 'query' => $this->outputMapper->getOutputType('Query'), - 'mutation' => $this->outputMapper->getOutputType('Mutation'), + 'query' => $this->typeRegistry->get('Query'), + 'mutation' => $this->typeRegistry->get('Mutation'), 'typeLoader' => function ($name) { - return $this->outputMapper->getOutputType($name); + return $this->typeRegistry->get($name); }, - 'types' => $this->getTypes() + 'types' => function () { + $typesImplementors = []; + foreach ($this->config->getDeclaredTypeNames() as $type) { + $typesImplementors [] = $this->typeRegistry->get($type['name']); + } + return $typesImplementors; + } ] ); return $schema; } - - /** - * @return array - * @throws \Magento\Framework\GraphQl\Exception\GraphQlInputException - */ - private function getTypes() - { - $typesImplementors = []; - foreach ($this->config->getDeclaredTypeNames() as $type) { - switch ($type['type']) { - case 'graphql_type' : - $typesImplementors [] = $this->outputMapper->getOutputType($type['name']); - break; - case 'graphql_input' : - $typesImplementors [] = $this->inputMapper->getInputType($type['name']); - break; - } - } - - return $typesImplementors; - } } diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputFactory.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputFactory.php deleted file mode 100644 index cbbd97cfdb8c7..0000000000000 --- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputFactory.php +++ /dev/null @@ -1,60 +0,0 @@ -objectManager = $objectManager; - $this->prototypes = $prototypes; - } - - /** - * @param ConfigElementInterface $configElement - * @return InputTypeInterface - */ - public function create(ConfigElementInterface $configElement) : InputTypeInterface - { - if (!isset($this->typeRegistry[$configElement->getName()])) { - $this->typeRegistry[$configElement->getName()] = - $this->objectManager->create( - $this->prototypes[get_class($configElement)], - [ - 'configElement' => $configElement - ] - ); - } - return $this->typeRegistry[$configElement->getName()]; - } -} diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputMapper.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputMapper.php index 0785ea1278045..95ab1635e968b 100644 --- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputMapper.php +++ b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputMapper.php @@ -9,64 +9,40 @@ use Magento\Framework\GraphQl\Config\Data\WrappedTypeProcessor; use Magento\Framework\GraphQl\Config\Element\Argument; -use Magento\Framework\GraphQl\ConfigInterface; -use Magento\Framework\GraphQl\Schema\Type\ScalarTypes; -use Magento\Framework\GraphQl\Schema\Type\InputTypeInterface; -use Magento\Framework\GraphQl\Schema\TypeFactory; use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\Framework\Phrase; +use Magento\Framework\GraphQl\Schema\Type\ScalarTypes; +use Magento\Framework\GraphQl\Schema\Type\TypeRegistry; class InputMapper { - /** - * @var InputFactory - */ - private $inputFactory; - - /** - * @var ConfigInterface - */ - private $config; - - /** - * @var TypeFactory - */ - private $typeFactory; - /** * @var ScalarTypes */ private $scalarTypes; /** - * @var InputTypeInterface[] + * @var WrappedTypeProcessor */ - private $inputTypes; + private $wrappedTypeProcessor; /** - * @var WrappedTypeProcessor + * @var TypeRegistry */ - private $wrappedTypeProcessor; + private $typeRegistry; /** - * @param InputFactory $inputFactory - * @param ConfigInterface $config - * @param TypeFactory $typeFactory * @param ScalarTypes $scalarTypes * @param WrappedTypeProcessor $wrappedTypeProcessor + * @param TypeRegistry $typeRegistry */ public function __construct( - InputFactory $inputFactory, - ConfigInterface $config, - TypeFactory $typeFactory, ScalarTypes $scalarTypes, - WrappedTypeProcessor $wrappedTypeProcessor + WrappedTypeProcessor $wrappedTypeProcessor, + TypeRegistry $typeRegistry ) { - $this->inputFactory = $inputFactory; - $this->config = $config; - $this->typeFactory = $typeFactory; $this->scalarTypes = $scalarTypes; $this->wrappedTypeProcessor = $wrappedTypeProcessor; + $this->typeRegistry = $typeRegistry; } /** @@ -74,6 +50,7 @@ public function __construct( * * @param Argument $argument * @return array + * @throws GraphQlInputException */ public function getRepresentation(Argument $argument) : array { @@ -81,8 +58,7 @@ public function getRepresentation(Argument $argument) : array if ($this->scalarTypes->isScalarType($typeName)) { $instance = $this->wrappedTypeProcessor->processScalarWrappedType($argument); } else { - $configElement = $this->config->getConfigElement($typeName); - $instance = $this->inputFactory->create($configElement); + $instance = $this->typeRegistry->get($typeName); $instance = $this->wrappedTypeProcessor->processWrappedType($argument, $instance); } @@ -109,26 +85,4 @@ public function getRepresentation(Argument $argument) : array return $calculatedArgument; } - - /** - * Get GraphQL input type object by type name. - * - * @param string $typeName - * @return InputTypeInterface - * @throws GraphQlInputException - */ - public function getInputType($typeName) - { - if (!isset($this->inputTypes[$typeName])) { - $configElement = $this->config->getConfigElement($typeName); - $this->inputTypes[$typeName] = $this->inputFactory->create($configElement); - if (!($this->inputTypes[$typeName] instanceof InputTypeInterface)) { - throw new GraphQlInputException( - new Phrase("Type '{$typeName}' was requested but is not declared in the GraphQL schema.") - ); - } - } - - return $this->inputTypes[$typeName]; - } } diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputObjectType.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputObjectType.php index ae2d07ade2ad0..fa0327f79bc66 100644 --- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputObjectType.php +++ b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputObjectType.php @@ -8,21 +8,16 @@ namespace Magento\Framework\GraphQl\Schema\Type\Input; use Magento\Framework\GraphQl\Config\Data\WrappedTypeProcessor; -use Magento\Framework\GraphQl\Config\Element\Type as TypeConfigElement; -use Magento\Framework\GraphQl\ConfigInterface; +use Magento\Framework\GraphQl\Config\Element\Input as InputConfigElement; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\GraphQl\Schema\Type\ScalarTypes; -use Magento\Framework\GraphQl\Schema\TypeFactory; +use Magento\Framework\GraphQl\Schema\Type\TypeRegistry; /** * Class InputObjectType */ class InputObjectType extends \Magento\Framework\GraphQl\Schema\Type\InputObjectType { - /** - * @var TypeFactory - */ - private $typeFactory; - /** * @var ScalarTypes */ @@ -34,36 +29,27 @@ class InputObjectType extends \Magento\Framework\GraphQl\Schema\Type\InputObject private $wrappedTypeProcessor; /** - * @var InputFactory + * @var TypeRegistry */ - private $inputFactory; + private $typeRegistry; /** - * @var ConfigInterface - */ - public $graphQlConfig; - - /** - * @param TypeConfigElement $configElement - * @param TypeFactory $typeFactory + * @param InputConfigElement $configElement * @param ScalarTypes $scalarTypes * @param WrappedTypeProcessor $wrappedTypeProcessor - * @param InputFactory $inputFactory - * @param ConfigInterface $graphQlConfig + * @param TypeRegistry $typeRegistry + * @throws GraphQlInputException */ public function __construct( - TypeConfigElement $configElement, - TypeFactory $typeFactory, + InputConfigElement $configElement, ScalarTypes $scalarTypes, WrappedTypeProcessor $wrappedTypeProcessor, - InputFactory $inputFactory, - ConfigInterface $graphQlConfig + TypeRegistry $typeRegistry ) { - $this->typeFactory = $typeFactory; $this->scalarTypes = $scalarTypes; $this->wrappedTypeProcessor = $wrappedTypeProcessor; - $this->inputFactory = $inputFactory; - $this->graphQlConfig = $graphQlConfig; + $this->typeRegistry = $typeRegistry; + $config = [ 'name' => $configElement->getName(), 'description' => $configElement->getDescription() @@ -75,8 +61,7 @@ public function __construct( if ($field->getTypeName() == $configElement->getName()) { $type = $this; } else { - $fieldConfigElement = $this->graphQlConfig->getConfigElement($field->getTypeName()); - $type = $this->inputFactory->create($fieldConfigElement); + $type = $this->typeRegistry->get($field->getTypeName()); } $type = $this->wrappedTypeProcessor->processWrappedType($field, $type); } diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/ElementMapper/Formatter/Fields.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/ElementMapper/Formatter/Fields.php index b54cd4d8ca218..9c61211f84bde 100644 --- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/ElementMapper/Formatter/Fields.php +++ b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/ElementMapper/Formatter/Fields.php @@ -16,7 +16,6 @@ use Magento\Framework\GraphQl\Schema\Type\Output\OutputMapper; use Magento\Framework\GraphQl\Schema\Type\OutputTypeInterface; use Magento\Framework\GraphQl\Schema\Type\ScalarTypes; -use Magento\Framework\GraphQl\Schema\TypeFactory; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfoFactory; @@ -40,11 +39,6 @@ class Fields implements FormatterInterface */ private $inputMapper; - /** - * @var TypeFactory - */ - private $typeFactory; - /** * @var ScalarTypes */ @@ -64,7 +58,6 @@ class Fields implements FormatterInterface * @param ObjectManagerInterface $objectManager * @param OutputMapper $outputMapper * @param InputMapper $inputMapper - * @param TypeFactory $typeFactory * @param ScalarTypes $scalarTypes * @param WrappedTypeProcessor $wrappedTypeProcessor * @param ResolveInfoFactory $resolveInfoFactory @@ -73,7 +66,6 @@ public function __construct( ObjectManagerInterface $objectManager, OutputMapper $outputMapper, InputMapper $inputMapper, - TypeFactory $typeFactory, ScalarTypes $scalarTypes, WrappedTypeProcessor $wrappedTypeProcessor, ResolveInfoFactory $resolveInfoFactory @@ -81,7 +73,6 @@ public function __construct( $this->objectManager = $objectManager; $this->outputMapper = $outputMapper; $this->inputMapper = $inputMapper; - $this->typeFactory = $typeFactory; $this->scalarTypes = $scalarTypes; $this->wrappedTypeProcessor = $wrappedTypeProcessor; $this->resolveInfoFactory = $resolveInfoFactory; diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/OutputFactory.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/OutputFactory.php deleted file mode 100644 index 81dad11774b01..0000000000000 --- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/OutputFactory.php +++ /dev/null @@ -1,65 +0,0 @@ -objectManager = $objectManager; - $this->prototypes = $prototypes; - } - - /** - * Create output type. - * - * @param ConfigElementInterface $configElement - * @return OutputTypeInterface - */ - public function create(ConfigElementInterface $configElement) : OutputTypeInterface - { - if (!isset($this->typeRegistry[$configElement->getName()])) { - $this->typeRegistry[$configElement->getName()] = - $this->objectManager->create( - $this->prototypes[get_class($configElement)], - [ - 'configElement' => $configElement - ] - ); - } - return $this->typeRegistry[$configElement->getName()]; - } -} diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/OutputMapper.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/OutputMapper.php index b7f4b8a1f60db..046eeb5b1f93d 100644 --- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/OutputMapper.php +++ b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/OutputMapper.php @@ -7,50 +7,28 @@ namespace Magento\Framework\GraphQl\Schema\Type\Output; -use Magento\Framework\GraphQl\ConfigInterface; use Magento\Framework\GraphQl\Schema\Type\OutputTypeInterface; -use Magento\Framework\GraphQl\Schema\TypeFactory; +use Magento\Framework\GraphQl\Schema\Type\TypeRegistry; use Magento\Framework\GraphQl\Exception\GraphQlInputException; use Magento\Framework\Phrase; /** - * Map type names to their output type/interface classes. + * Map type names to their output type/interface/enum classes. */ class OutputMapper { /** - * @var OutputFactory + * @var TypeRegistry */ - private $outputFactory; + private $typeRegistry; /** - * @var OutputTypeInterface[] - */ - private $outputTypes; - - /** - * @var TypeFactory - */ - private $typeFactory; - - /** - * @var ConfigInterface - */ - private $config; - - /** - * @param OutputFactory $outputFactory - * @param TypeFactory $typeFactory - * @param ConfigInterface $config + * @param TypeRegistry $typeRegistry */ public function __construct( - OutputFactory $outputFactory, - TypeFactory $typeFactory, - ConfigInterface $config + TypeRegistry $typeRegistry ) { - $this->outputFactory = $outputFactory; - $this->config = $config; - $this->typeFactory = $typeFactory; + $this->typeRegistry = $typeRegistry; } /** @@ -62,16 +40,13 @@ public function __construct( */ public function getOutputType($typeName) { - if (!isset($this->outputTypes[$typeName])) { - $configElement = $this->config->getConfigElement($typeName); - $this->outputTypes[$typeName] = $this->outputFactory->create($configElement); - if (!($this->outputTypes[$typeName] instanceof OutputTypeInterface)) { - throw new GraphQlInputException( - new Phrase("Type '{$typeName}' was requested but is not declared in the GraphQL schema.") - ); - } - } + $outputType = $this->typeRegistry->get($typeName); - return $this->outputTypes[$typeName]; + if (!$outputType instanceof OutputTypeInterface) { + throw new GraphQlInputException( + new Phrase("Type '{$typeName}' was requested but is not declared in the GraphQL schema.") + ); + } + return $outputType; } } diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/TypeRegistry.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/TypeRegistry.php new file mode 100644 index 0000000000000..6b1e20f996def --- /dev/null +++ b/lib/internal/Magento/Framework/GraphQl/Schema/Type/TypeRegistry.php @@ -0,0 +1,93 @@ +objectManager = $objectManager; + $this->config = $config; + $this->configToTypeMap = $configToTypeMap; + } + + /** + * Get GraphQL type object by type name + * + * @param string $typeName + * @return TypeInterface|InputTypeInterface|OutputTypeInterface + * @throws GraphQlInputException + */ + public function get(string $typeName): TypeInterface + { + if (!isset($this->types[$typeName])) { + $configElement = $this->config->getConfigElement($typeName); + + $configElementClass = get_class($configElement); + if (!isset($this->configToTypeMap[$configElementClass])) { + throw new GraphQlInputException( + new Phrase("Type for '{$configElementClass}' has not declared.") + ); + } + + $this->types[$typeName] = $this->objectManager->create( + $this->configToTypeMap[$configElementClass], + [ + 'configElement' => $configElement, + ] + ); + + if (!($this->types[$typeName] instanceof TypeInterface)) { + throw new GraphQlInputException( + new Phrase("Type '{$typeName}' was requested but is not declared in the GraphQL schema.") + ); + } + } + return $this->types[$typeName]; + } +} From 098524486aaad76c9af25cff83afd6134d4780e3 Mon Sep 17 00:00:00 2001 From: Valerii Naida Date: Fri, 18 Jan 2019 14:46:28 -0600 Subject: [PATCH 3/5] GraphQl-93: Implement support for variables in query -- Fixes after CR --- lib/internal/Magento/Framework/GraphQl/Config.php | 6 +++--- .../Framework/GraphQl/Config/Element/InputFactory.php | 2 +- lib/internal/Magento/Framework/GraphQl/Query/Fields.php | 6 +++--- .../Magento/Framework/GraphQl/Schema/Type/TypeRegistry.php | 5 ++++- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/internal/Magento/Framework/GraphQl/Config.php b/lib/internal/Magento/Framework/GraphQl/Config.php index b7f198656d81d..3873044007b4e 100644 --- a/lib/internal/Magento/Framework/GraphQl/Config.php +++ b/lib/internal/Magento/Framework/GraphQl/Config.php @@ -83,11 +83,11 @@ public function getConfigElement(string $configElementName) : ConfigElementInter /** * Return all type names declared in a GraphQL schema's configuration and their type. * + * Format is ['name' => 'example value', 'type' = 'example value'] + * * @return array $types - * name string - * type string */ - public function getDeclaredTypeNames() : array + public function getDeclaredTypes() : array { $types = []; foreach ($this->configData->get(null) as $item) { diff --git a/lib/internal/Magento/Framework/GraphQl/Config/Element/InputFactory.php b/lib/internal/Magento/Framework/GraphQl/Config/Element/InputFactory.php index 677354f67e230..3d6e6a56781e0 100644 --- a/lib/internal/Magento/Framework/GraphQl/Config/Element/InputFactory.php +++ b/lib/internal/Magento/Framework/GraphQl/Config/Element/InputFactory.php @@ -73,7 +73,7 @@ public function createFromConfigData(array $data): ConfigElementInterface } /** - * Create type object based off array of configured GraphQL InputType data. + * Create input type object based off array of configured GraphQL InputType data. * * Type data must contain name and the type's fields. Optional data includes description. * diff --git a/lib/internal/Magento/Framework/GraphQl/Query/Fields.php b/lib/internal/Magento/Framework/GraphQl/Query/Fields.php index d0c88cb0f898e..32f4e264f5eda 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/Fields.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/Fields.php @@ -44,7 +44,7 @@ public function setQuery($query, array $variables = null) ] ); if (isset($variables)) { - $queryFields = array_merge($queryFields, $this->getVariables($variables)); + $queryFields = array_merge($queryFields, $this->extracttVariables($variables)); } } catch (\Exception $e) { // If a syntax error is encountered do not collect fields @@ -75,12 +75,12 @@ public function getFieldsUsedInQuery() * * @return string[] */ - private function getVariables(array $variables): array + private function extracttVariables(array $variables): array { $fields = []; foreach ($variables as $key => $value){ if (is_array($value)){ - $fields = array_merge($fields, $this->getVariables($value)); + $fields = array_merge($fields, $this->extracttVariables($value)); } $fields[$key] = $key; } diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/TypeRegistry.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/TypeRegistry.php index 6b1e20f996def..634975426cf80 100644 --- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/TypeRegistry.php +++ b/lib/internal/Magento/Framework/GraphQl/Schema/Type/TypeRegistry.php @@ -71,7 +71,10 @@ public function get(string $typeName): TypeInterface $configElementClass = get_class($configElement); if (!isset($this->configToTypeMap[$configElementClass])) { throw new GraphQlInputException( - new Phrase("Type for '{$configElementClass}' has not declared.") + new Phrase( + "No mapping to Webonyx type is declared for '%1' config element type.", + [$configElementClass] + ) ); } From b3c194f9319c0e9eea089bde7ce8a2307d5c9bcd Mon Sep 17 00:00:00 2001 From: Valerii Naida Date: Tue, 22 Jan 2019 15:09:05 -0600 Subject: [PATCH 4/5] GraphQl-93: Implement support for variables in query -- Fixes after CR --- .../GraphQl/VariablesSupportQueryTest.php | 72 +++++++++---------- .../Magento/Framework/GraphQl/Config.php | 13 +--- .../Framework/GraphQl/ConfigInterface.php | 8 ++- .../GraphQl/Schema/Type/TypeRegistry.php | 1 - 4 files changed, 39 insertions(+), 55 deletions(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/VariablesSupportQueryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/VariablesSupportQueryTest.php index 138547ecdbc38..20af9984cff8d 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/VariablesSupportQueryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/VariablesSupportQueryTest.php @@ -14,11 +14,6 @@ class VariablesSupportQueryTest extends GraphQlAbstract { - /** - * @var ObjectManager - */ - private $objectManager; - /** * @var ProductRepositoryInterface */ @@ -26,65 +21,62 @@ class VariablesSupportQueryTest extends GraphQlAbstract protected function setUp() { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->productRepository = $this->objectManager->get(ProductRepositoryInterface::class); + $this->productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); } /** - * Tests that Introspection is disabled when not in developer mode - * - * @magentoApiDataFixture Magento/Catalog/_files/product_simple_with_all_fields.php - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @magentoApiDataFixture Magento/Catalog/_files/products_list.php */ public function testQueryObjectVariablesSupport() { - $productSku = 'simple'; + $productSku = 'simple-249'; + $minPrice = 153; $query = <<<'QUERY' -query GetProductsQuery($page: Int, $filterInput: ProductFilterInput){ +query GetProductsQuery($pageSize: Int, $filterInput: ProductFilterInput, $priceSort: SortEnum) { products( - pageSize: 10 - currentPage: $page + pageSize: $pageSize filter: $filterInput - sort: {} + sort: {price: $priceSort} ) { items { - name + sku + price { + minimalPrice { + amount { + value + currency + } + } + } } } } QUERY; + $variables = [ - 'page' => 1, + 'pageSize' => 1, + 'priceSort' => 'ASC', 'filterInput' => [ - 'sku' => [ - 'like' => '%simple%' - ] - ] + 'min_price' => [ + 'gt' => 150, + ], + ], ]; $response = $this->graphQlQuery($query, $variables); /** @var \Magento\Catalog\Model\Product $product */ $product = $this->productRepository->get($productSku, false, null, true); - $this->assertArrayHasKey('products', $response); - $this->assertArrayHasKey('items', $response['products']); - $this->assertEquals(1, count($response['products']['items'])); - $this->assertArrayHasKey(0, $response['products']['items']); - $this->assertFields($product, $response['products']['items'][0]); - } - - /** - * @param ProductInterface $product - * @param array $actualResponse - */ - private function assertFields($product, $actualResponse) - { - $assertionMap = [ - ['response_field' => 'name', 'expected_value' => $product->getName()], - ]; - - $this->assertResponseFields($actualResponse, $assertionMap); + self::assertArrayHasKey('products', $response); + self::assertArrayHasKey('items', $response['products']); + self::assertEquals(1, count($response['products']['items'])); + self::assertArrayHasKey(0, $response['products']['items']); + self::assertEquals($product->getSku(), $response['products']['items'][0]['sku']); + self::assertEquals( + $minPrice, + $response['products']['items'][0]['price']['minimalPrice']['amount']['value'] + ); } } diff --git a/lib/internal/Magento/Framework/GraphQl/Config.php b/lib/internal/Magento/Framework/GraphQl/Config.php index 3873044007b4e..9be4f0c97948d 100644 --- a/lib/internal/Magento/Framework/GraphQl/Config.php +++ b/lib/internal/Magento/Framework/GraphQl/Config.php @@ -48,12 +48,7 @@ public function __construct( } /** - * Get a data object with data pertaining to a GraphQL type's structural makeup. - * - * @param string $configElementName - * @return ConfigElementInterface - * @throws \LogicException - * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * @inheritdoc */ public function getConfigElement(string $configElementName) : ConfigElementInterface { @@ -81,11 +76,7 @@ public function getConfigElement(string $configElementName) : ConfigElementInter } /** - * Return all type names declared in a GraphQL schema's configuration and their type. - * - * Format is ['name' => 'example value', 'type' = 'example value'] - * - * @return array $types + * @inheritdoc */ public function getDeclaredTypes() : array { diff --git a/lib/internal/Magento/Framework/GraphQl/ConfigInterface.php b/lib/internal/Magento/Framework/GraphQl/ConfigInterface.php index c2670967f1db5..f7d6cf49e180c 100644 --- a/lib/internal/Magento/Framework/GraphQl/ConfigInterface.php +++ b/lib/internal/Magento/Framework/GraphQl/ConfigInterface.php @@ -25,9 +25,11 @@ interface ConfigInterface public function getConfigElement(string $configElementName) : ConfigElementInterface; /** - * Return all type names from a GraphQL schema's configuration. + * Return all type names declared in a GraphQL schema's configuration and their type. * - * @return string[] + * Format is ['name' => 'example value', 'type' = 'example value'] + * + * @return array $types */ - public function getDeclaredTypeNames() : array; + public function getDeclaredTypes() : array; } diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/TypeRegistry.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/TypeRegistry.php index 634975426cf80..cde8b6b3e446b 100644 --- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/TypeRegistry.php +++ b/lib/internal/Magento/Framework/GraphQl/Schema/Type/TypeRegistry.php @@ -9,7 +9,6 @@ use Magento\Framework\GraphQl\ConfigInterface; use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\Framework\GraphQl\Schema\Type\InputTypeInterface; use Magento\Framework\GraphQl\Schema\TypeInterface; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Phrase; From 2e74cc1fc69558b0b7964d361c1ff0ebe0aafa0a Mon Sep 17 00:00:00 2001 From: Valerii Naida Date: Tue, 22 Jan 2019 17:22:26 -0600 Subject: [PATCH 5/5] GraphQl-93: Implement support for variables in query -- Builds fixes --- .../GraphQl/VariablesSupportQueryTest.php | 5 +- .../Magento/Framework/GraphQl/Config.php | 2 +- .../GraphQl/Config/Element/FieldsFactory.php | 62 +++++++++++++++++++ .../GraphQl/Config/Element/InputFactory.php | 32 +++------- .../GraphQl/Config/Element/TypeFactory.php | 32 +++------- .../Framework/GraphQl/Query/Fields.php | 8 +-- .../GraphQl/Schema/SchemaGenerator.php | 4 +- .../GraphQl/Schema/Type/Input/InputMapper.php | 3 + .../Output/ElementMapper/Formatter/Fields.php | 2 +- 9 files changed, 89 insertions(+), 61 deletions(-) create mode 100644 lib/internal/Magento/Framework/GraphQl/Config/Element/FieldsFactory.php diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/VariablesSupportQueryTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/VariablesSupportQueryTest.php index 20af9984cff8d..7448b165fc234 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/VariablesSupportQueryTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/VariablesSupportQueryTest.php @@ -7,9 +7,8 @@ namespace Magento\GraphQl; -use Magento\Catalog\Api\Data\ProductInterface; +use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\TestCase\GraphQlAbstract; -use Magento\TestFramework\ObjectManager; use Magento\Catalog\Api\ProductRepositoryInterface; class VariablesSupportQueryTest extends GraphQlAbstract @@ -21,7 +20,7 @@ class VariablesSupportQueryTest extends GraphQlAbstract protected function setUp() { - $this->productRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); + $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); } /** diff --git a/lib/internal/Magento/Framework/GraphQl/Config.php b/lib/internal/Magento/Framework/GraphQl/Config.php index 9be4f0c97948d..ec22b742b1d6c 100644 --- a/lib/internal/Magento/Framework/GraphQl/Config.php +++ b/lib/internal/Magento/Framework/GraphQl/Config.php @@ -62,7 +62,7 @@ public function getConfigElement(string $configElementName) : ConfigElementInter $fieldsInQuery = $this->queryFields->getFieldsUsedInQuery(); if (isset($data['fields'])) { if (!empty($fieldsInQuery)) { - foreach ($data['fields'] as $fieldName => $fieldConfig) { + foreach (array_keys($data['fields']) as $fieldName) { if (!isset($fieldsInQuery[$fieldName])) { unset($data['fields'][$fieldName]); } diff --git a/lib/internal/Magento/Framework/GraphQl/Config/Element/FieldsFactory.php b/lib/internal/Magento/Framework/GraphQl/Config/Element/FieldsFactory.php new file mode 100644 index 0000000000000..ca6b67eac3d83 --- /dev/null +++ b/lib/internal/Magento/Framework/GraphQl/Config/Element/FieldsFactory.php @@ -0,0 +1,62 @@ +argumentFactory = $argumentFactory; + $this->fieldFactory = $fieldFactory; + } + + /** + * Create a fields object from a configured array with optional arguments. + * + * Field data must contain name and type. Other values are optional and include required, itemType, description, + * and resolver. Arguments array must be in the format of [$argumentData['name'] => $argumentData]. + * + * @param array $fieldsData + * @return Field[] + */ + public function createFromConfigData( + array $fieldsData + ) : array { + $fields = []; + foreach ($fieldsData as $fieldData) { + $arguments = []; + foreach ($fieldData['arguments'] as $argumentData) { + $arguments[$argumentData['name']] = $this->argumentFactory->createFromConfigData($argumentData); + } + $fields[$fieldData['name']] = $this->fieldFactory->createFromConfigData( + $fieldData, + $arguments + ); + } + return $fields; + } +} diff --git a/lib/internal/Magento/Framework/GraphQl/Config/Element/InputFactory.php b/lib/internal/Magento/Framework/GraphQl/Config/Element/InputFactory.php index 3d6e6a56781e0..0e7ccb831a5a4 100644 --- a/lib/internal/Magento/Framework/GraphQl/Config/Element/InputFactory.php +++ b/lib/internal/Magento/Framework/GraphQl/Config/Element/InputFactory.php @@ -22,28 +22,20 @@ class InputFactory implements ConfigElementFactoryInterface private $objectManager; /** - * @var ArgumentFactory + * @var FieldsFactory */ - private $argumentFactory; - - /** - * @var FieldFactory - */ - private $fieldFactory; + private $fieldsFactory; /** * @param ObjectManagerInterface $objectManager - * @param ArgumentFactory $argumentFactory - * @param FieldFactory $fieldFactory + * @param FieldsFactory $fieldsFactory */ public function __construct( ObjectManagerInterface $objectManager, - ArgumentFactory $argumentFactory, - FieldFactory $fieldFactory + FieldsFactory $fieldsFactory ) { $this->objectManager = $objectManager; - $this->argumentFactory = $argumentFactory; - $this->fieldFactory = $fieldFactory; + $this->fieldsFactory = $fieldsFactory; } /** @@ -54,18 +46,8 @@ public function __construct( */ public function createFromConfigData(array $data): ConfigElementInterface { - $fields = []; - $data['fields'] = isset($data['fields']) ? $data['fields'] : []; - foreach ($data['fields'] as $field) { - $arguments = []; - foreach ($field['arguments'] as $argument) { - $arguments[$argument['name']] = $this->argumentFactory->createFromConfigData($argument); - } - $fields[$field['name']] = $this->fieldFactory->createFromConfigData( - $field, - $arguments - ); - } + $fields = isset($data['fields']) ? $this->fieldsFactory->createFromConfigData($data['fields']) : []; + return $this->create( $data, $fields diff --git a/lib/internal/Magento/Framework/GraphQl/Config/Element/TypeFactory.php b/lib/internal/Magento/Framework/GraphQl/Config/Element/TypeFactory.php index f17d99be9bd65..5dd477a050890 100644 --- a/lib/internal/Magento/Framework/GraphQl/Config/Element/TypeFactory.php +++ b/lib/internal/Magento/Framework/GraphQl/Config/Element/TypeFactory.php @@ -22,28 +22,20 @@ class TypeFactory implements ConfigElementFactoryInterface private $objectManager; /** - * @var ArgumentFactory + * @var FieldsFactory */ - private $argumentFactory; - - /** - * @var FieldFactory - */ - private $fieldFactory; + private $fieldsFactory; /** * @param ObjectManagerInterface $objectManager - * @param ArgumentFactory $argumentFactory - * @param FieldFactory $fieldFactory + * @param FieldsFactory $fieldsFactory */ public function __construct( ObjectManagerInterface $objectManager, - ArgumentFactory $argumentFactory, - FieldFactory $fieldFactory + FieldsFactory $fieldsFactory ) { $this->objectManager = $objectManager; - $this->argumentFactory = $argumentFactory; - $this->fieldFactory = $fieldFactory; + $this->fieldsFactory = $fieldsFactory; } /** @@ -54,18 +46,8 @@ public function __construct( */ public function createFromConfigData(array $data): ConfigElementInterface { - $fields = []; - $data['fields'] = isset($data['fields']) ? $data['fields'] : []; - foreach ($data['fields'] as $field) { - $arguments = []; - foreach ($field['arguments'] as $argument) { - $arguments[$argument['name']] = $this->argumentFactory->createFromConfigData($argument); - } - $fields[$field['name']] = $this->fieldFactory->createFromConfigData( - $field, - $arguments - ); - } + $fields = isset($data['fields']) ? $this->fieldsFactory->createFromConfigData($data['fields']) : []; + return $this->create( $data, $fields diff --git a/lib/internal/Magento/Framework/GraphQl/Query/Fields.php b/lib/internal/Magento/Framework/GraphQl/Query/Fields.php index 32f4e264f5eda..ae1d8d68d5bc7 100644 --- a/lib/internal/Magento/Framework/GraphQl/Query/Fields.php +++ b/lib/internal/Magento/Framework/GraphQl/Query/Fields.php @@ -44,7 +44,7 @@ public function setQuery($query, array $variables = null) ] ); if (isset($variables)) { - $queryFields = array_merge($queryFields, $this->extracttVariables($variables)); + $queryFields = array_merge($queryFields, $this->extractVariables($variables)); } } catch (\Exception $e) { // If a syntax error is encountered do not collect fields @@ -75,12 +75,12 @@ public function getFieldsUsedInQuery() * * @return string[] */ - private function extracttVariables(array $variables): array + private function extractVariables(array $variables): array { $fields = []; - foreach ($variables as $key => $value){ + foreach ($variables as $key => $value) { if (is_array($value)){ - $fields = array_merge($fields, $this->extracttVariables($value)); + $fields = array_merge($fields, $this->extractVariables($value)); } $fields[$key] = $key; } diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php b/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php index ef78bea476f49..250b80defa6dd 100644 --- a/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php +++ b/lib/internal/Magento/Framework/GraphQl/Schema/SchemaGenerator.php @@ -48,7 +48,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ public function generate() : Schema { @@ -61,7 +61,7 @@ public function generate() : Schema }, 'types' => function () { $typesImplementors = []; - foreach ($this->config->getDeclaredTypeNames() as $type) { + foreach ($this->config->getDeclaredTypes() as $type) { $typesImplementors [] = $this->typeRegistry->get($type['name']); } return $typesImplementors; diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputMapper.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputMapper.php index 95ab1635e968b..d1f48dada2cbd 100644 --- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputMapper.php +++ b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Input/InputMapper.php @@ -13,6 +13,9 @@ use Magento\Framework\GraphQl\Schema\Type\ScalarTypes; use Magento\Framework\GraphQl\Schema\Type\TypeRegistry; +/** + * Prepare argument's metadata for GraphQL schema generation + */ class InputMapper { /** diff --git a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/ElementMapper/Formatter/Fields.php b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/ElementMapper/Formatter/Fields.php index 9c61211f84bde..034a5702090d9 100644 --- a/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/ElementMapper/Formatter/Fields.php +++ b/lib/internal/Magento/Framework/GraphQl/Schema/Type/Output/ElementMapper/Formatter/Fields.php @@ -79,7 +79,7 @@ public function __construct( } /** - * {@inheritDoc} + * @inheritdoc */ public function format(TypeInterface $configElement, OutputTypeInterface $outputType): array {