Skip to content
This repository has been archived by the owner on Dec 19, 2019. It is now read-only.

GraphQl-93: Implement support for variables in query #259

Merged
merged 6 commits into from
Jan 23, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/code/Magento/GraphQl/Controller/GraphQl.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
16 changes: 4 additions & 12 deletions app/code/Magento/GraphQl/etc/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<argument name="factoryMapByConfigElementType" xsi:type="array">
<item name="graphql_interface" xsi:type="object">Magento\Framework\GraphQl\Config\Element\InterfaceFactory</item>
<item name="graphql_type" xsi:type="object">Magento\Framework\GraphQl\Config\Element\TypeFactory</item>
<item name="graphql_input" xsi:type="object">Magento\Framework\GraphQl\Config\Element\TypeFactory</item>
<item name="graphql_input" xsi:type="object">Magento\Framework\GraphQl\Config\Element\InputFactory</item>
<item name="graphql_enum" xsi:type="object">Magento\Framework\GraphQl\Config\Element\EnumFactory</item>
</argument>
</arguments>
Expand Down Expand Up @@ -55,24 +55,16 @@
</argument>
</arguments>
</virtualType>
<type name="Magento\Framework\GraphQl\Schema\Type\Output\OutputFactory">
<type name="Magento\Framework\GraphQl\Schema\Type\TypeRegistry">
<arguments>
<argument name="prototypes" xsi:type="array">
<argument name="configToTypeMap" xsi:type="array">
<item name="Magento\Framework\GraphQl\Config\Element\Type" xsi:type="string">Magento\Framework\GraphQl\Schema\Type\Output\OutputTypeObject</item>
<item name="Magento\Framework\GraphQl\Config\Element\Input" xsi:type="string">Magento\Framework\GraphQl\Schema\Type\Input\InputObjectType</item>
<item name="Magento\Framework\GraphQl\Config\Element\InterfaceType" xsi:type="string">Magento\Framework\GraphQl\Schema\Type\Output\OutputInterfaceObject</item>
<item name="Magento\Framework\GraphQl\Config\Element\Enum" xsi:type="string">Magento\Framework\GraphQl\Schema\Type\Enum\Enum</item>
</argument>
</arguments>
</type>
<type name="Magento\Framework\GraphQl\Schema\Type\Input\InputFactory">
<arguments>
<argument name="prototypes" xsi:type="array">
<item name="Magento\Framework\GraphQl\Config\Element\Type" xsi:type="string">Magento\Framework\GraphQl\Schema\Type\Input\InputObjectType</item>
<item name="Magento\Framework\GraphQl\Config\Element\InterfaceType" xsi:type="string">Magento\Framework\GraphQl\Schema\Type\Input\InputObjectType</item>
<item name="Magento\Framework\GraphQl\Config\Element\Enum" xsi:type="string">Magento\Framework\GraphQl\Schema\Type\Enum\Enum</item>
</argument>
</arguments>
</type>
<type name="Magento\Framework\GraphQl\Schema\Type\Output\ElementMapper">
<arguments>
<argument name="formatter" xsi:type="object">Magento\Framework\GraphQl\Schema\Type\Output\ElementMapper\FormatterComposite</argument>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,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);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\GraphQl;

use Magento\Catalog\Api\Data\ProductInterface;
use Magento\TestFramework\TestCase\GraphQlAbstract;
use Magento\TestFramework\ObjectManager;
use Magento\Catalog\Api\ProductRepositoryInterface;

class VariablesSupportQueryTest extends GraphQlAbstract
{
/**
* @var ObjectManager
*/
private $objectManager;

/**
* @var ProductRepositoryInterface
*/
private $productRepository;

protected function setUp()
{
$this->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){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have enum used here?

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);
}
}
14 changes: 10 additions & 4 deletions lib/internal/Magento/Framework/GraphQl/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,18 +81,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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this common style for documenting array structure? Usually I see
['name' => 'example value', 'type' = 'example value']

* type string
*/
public function getDeclaredTypeNames() : array
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rename this method, to getDeclaredTypes, for example

{
$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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
74 changes: 74 additions & 0 deletions lib/internal/Magento/Framework/GraphQl/Config/Element/Input.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Framework\GraphQl\Config\Element;

/**
* Class representing 'input' GraphQL config element.
*/
class Input implements TypeInterface
{
/**
* @var string
*/
private $name;

/**
* @var Field[]
*/
private $fields;

/**
* @var string
*/
private $description;

/**
* @param string $name
* @param Field[] $fields
* @param string $description
*/
public function __construct(
string $name,
array $fields,
string $description
) {
$this->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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Framework\GraphQl\Config\Element;

use Magento\Framework\GraphQl\Config\ConfigElementFactoryInterface;
use Magento\Framework\GraphQl\Config\ConfigElementInterface;
use Magento\Framework\ObjectManagerInterface;

/**
* Factory for config elements of 'input' type.
*/
class InputFactory implements ConfigElementFactoryInterface
{
/**
* @var ObjectManagerInterface
*/
private $objectManager;

/**
* @var ArgumentFactory
*/
private $argumentFactory;

/**
* @var FieldFactory
*/
private $fieldFactory;

/**
* @param ObjectManagerInterface $objectManager
* @param ArgumentFactory $argumentFactory
* @param FieldFactory $fieldFactory
*/
public function __construct(
ObjectManagerInterface $objectManager,
ArgumentFactory $argumentFactory,
FieldFactory $fieldFactory
) {
$this->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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* 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.
*
* @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'] : ''
]
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -42,7 +42,7 @@ public function __construct(
string $name,
string $typeResolver,
array $fields,
string $description = ""
string $description
) {
$this->name = $name;
$this->fields = $fields;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Class representing 'type' GraphQL config element.
* Class representing 'type' GraphQL config elements.

*/
class Type implements TypeInterface
{
Expand Down
Loading