Skip to content

Commit

Permalink
ENGCOM-3931: GraphQl-93: Implement support for variables in query #259
Browse files Browse the repository at this point in the history
  • Loading branch information
naydav authored Jan 23, 2019
2 parents 2f30dde + c5c936c commit 95150ba
Show file tree
Hide file tree
Showing 23 changed files with 513 additions and 313 deletions.
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,81 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\GraphQl;

use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\TestCase\GraphQlAbstract;
use Magento\Catalog\Api\ProductRepositoryInterface;

class VariablesSupportQueryTest extends GraphQlAbstract
{
/**
* @var ProductRepositoryInterface
*/
private $productRepository;

protected function setUp()
{
$this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class);
}

/**
* @magentoApiDataFixture Magento/Catalog/_files/products_list.php
*/
public function testQueryObjectVariablesSupport()
{
$productSku = 'simple-249';
$minPrice = 153;

$query
= <<<'QUERY'
query GetProductsQuery($pageSize: Int, $filterInput: ProductFilterInput, $priceSort: SortEnum) {
products(
pageSize: $pageSize
filter: $filterInput
sort: {price: $priceSort}
) {
items {
sku
price {
minimalPrice {
amount {
value
currency
}
}
}
}
}
}
QUERY;

$variables = [
'pageSize' => 1,
'priceSort' => 'ASC',
'filterInput' => [
'min_price' => [
'gt' => 150,
],
],
];

$response = $this->graphQlQuery($query, $variables);
/** @var \Magento\Catalog\Model\Product $product */
$product = $this->productRepository->get($productSku, false, null, true);

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']
);
}
}
23 changes: 10 additions & 13 deletions lib/internal/Magento/Framework/GraphQl/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -67,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]);
}
Expand All @@ -81,18 +76,20 @@ public function getConfigElement(string $configElementName) : ConfigElementInter
}

/**
* Return all type names declared in a GraphQL schema's configuration.
*
* @return string[]
* @inheritdoc
*/
public function getDeclaredTypeNames() : array
public function getDeclaredTypes() : 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;
}
}
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Framework\GraphQl\Config\Element;

/**
* Fields object factory
*/
class FieldsFactory
{
/**
* @var ArgumentFactory
*/
private $argumentFactory;

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

/**
* @param ArgumentFactory $argumentFactory
* @param FieldFactory $fieldFactory
*/
public function __construct(
ArgumentFactory $argumentFactory,
FieldFactory $fieldFactory
) {
$this->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;
}
}
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;
}
}
Loading

0 comments on commit 95150ba

Please sign in to comment.