From d6c15d6210203483ff2f9ae35edf184803a82288 Mon Sep 17 00:00:00 2001 From: austris argalis Date: Sun, 6 May 2018 20:13:45 +0300 Subject: [PATCH 1/3] Return full canonical product URL within product object --- .../Model/Resolver/Product/CanonicalUrl.php | 67 ++++++++++++++ .../Test/Integration/Model/ProductsTest.php | 73 +++++++++++++++ .../Resolver/Product/CanonicalUrlTest.php | 92 +++++++++++++++++++ .../CatalogGraphQl/etc/schema.graphqls | 1 + 4 files changed, 233 insertions(+) create mode 100644 app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php create mode 100644 app/code/Magento/CatalogGraphQl/Test/Integration/Model/ProductsTest.php create mode 100644 app/code/Magento/CatalogGraphQl/Test/Unit/Model/Resolver/Product/CanonicalUrlTest.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php new file mode 100644 index 000000000000..071a5ec2fab8 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php @@ -0,0 +1,67 @@ +valueFactory = $valueFactory; + } + + /** + * Fetches the data from persistence models and format it according to the GraphQL schema. + * + * @param \Magento\Framework\GraphQl\Config\Element\Field $field + * @param $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @throws \Exception + * @return Value + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ): Value { + if (!isset($value['model'])) { + $result = function () { + return null; + }; + return $this->valueFactory->create($result); + } + + /* @var $product Product */ + $product = $value['model']; + $url = $product->getUrlModel()->getUrl($product, ['_ignore_category' => true]); + $result = function () use ($url) { + return $url; + }; + + return $this->valueFactory->create($result); + } +} diff --git a/app/code/Magento/CatalogGraphQl/Test/Integration/Model/ProductsTest.php b/app/code/Magento/CatalogGraphQl/Test/Integration/Model/ProductsTest.php new file mode 100644 index 000000000000..29c9f33d7471 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Test/Integration/Model/ProductsTest.php @@ -0,0 +1,73 @@ +objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->graphql = $this->objectManager->get(\Magento\GraphQl\Controller\GraphQl::class); + $this->jsonSerializer = $this->objectManager->get(SerializerInterface::class); + } + + /** + * @magentoAppArea graphql + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_with_category.php + * @magentoDbIsolation disabled + * @magentoConfigFixture default_store catalog/seo/product_url_suffix .html + */ + public function testResponseContainsCanonicalURLs(): void + { + $fixtureSku = 'p002'; + $query = << $query]; + $request = $this->objectManager->get(HttpRequest::class); + $request->setPathInfo('/graphql'); + $request->setContent(json_encode($postData)); + $headers = $this->objectManager->create(\Zend\Http\Headers::class) + ->addHeaders(['Content-Type' => 'application/json']); + $request->setHeaders($headers); + + $response = $this->graphql->dispatch($request); + $output = $this->jsonSerializer->unserialize($response->getContent()); + $canonical_url = $output['data']['products']['items'][0]['canonical_url']; + + $this->assertArrayNotHasKey('errors', $output, 'Response has errors'); + $this->assertEquals('http://localhost/index.php/' . $fixtureSku . '.html', $canonical_url); + } +} diff --git a/app/code/Magento/CatalogGraphQl/Test/Unit/Model/Resolver/Product/CanonicalUrlTest.php b/app/code/Magento/CatalogGraphQl/Test/Unit/Model/Resolver/Product/CanonicalUrlTest.php new file mode 100644 index 000000000000..ae01c67eb522 --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Test/Unit/Model/Resolver/Product/CanonicalUrlTest.php @@ -0,0 +1,92 @@ +getMockBuilder(\Magento\Framework\GraphQl\Config\Element\Field::class) + ->disableOriginalConstructor() + ->getMock(); + $mockInfo = $this->getMockBuilder(\Magento\Framework\GraphQl\Schema\Type\ResolveInfo::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->mockValueFactory->method('create')->with( + $this->callback( + function ($param) { + return $param() === null; + } + ) + ); + + $this->subject->resolve($mockField, '', $mockInfo, [], []); + } + + protected function setUp() + { + parent::setUp(); + $this->objectManager = new ObjectManager($this); + $this->mockStoreManager = $this->getMockBuilder(StoreManagerInterface::class)->getMock(); + $this->mockValueFactory = $this->getMockBuilder(ValueFactory::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->mockValueFactory->method('create')->willReturn( + $this->objectManager->getObject( + Value::class, + ['callback' => function () { + return ''; + }] + ) + ); + + $mockProductUrlPathGenerator = $this->getMockBuilder(ProductUrlPathGenerator::class) + ->disableOriginalConstructor() + ->getMock(); + $mockProductUrlPathGenerator->method('getUrlPathWithSuffix')->willReturn('product_url.html'); + + $this->subject = $this->objectManager->getObject( + CanonicalUrl::class, + [ + 'valueFactory' => $this->mockValueFactory, + 'storeManager' => $this->mockStoreManager, + 'productUrlPathGenerator' => $mockProductUrlPathGenerator + ] + ); + } +} diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index ca1ff7865431..e56f1dc3ff1a 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -279,6 +279,7 @@ interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\ gift_message_available: String @doc(description: "Indicates whether a gift message is available") manufacturer: Int @doc(description: "A number representing the product's manufacturer") categories: [CategoryInterface] @doc(description: "The categories assigned to a product") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category") + canonical_url: String @doc(description: "Canonical URL") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\CanonicalUrl") } interface PhysicalProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\ProductInterfaceTypeResolverComposite") @doc(description: "PhysicalProductInterface contains attributes specific to tangible products") { From 94fb609769e406c0a8b22a21431cecb7b076b5fe Mon Sep 17 00:00:00 2001 From: austris argalis Date: Mon, 14 May 2018 19:47:11 +0300 Subject: [PATCH 2/3] Correct docblocks, add api-functional test --- .../Model/Resolver/Product/CanonicalUrl.php | 13 +--- .../Test/Integration/Model/ProductsTest.php | 73 ------------------- .../GraphQl/Catalog/ProductViewTest.php | 5 ++ 3 files changed, 9 insertions(+), 82 deletions(-) delete mode 100644 app/code/Magento/CatalogGraphQl/Test/Integration/Model/ProductsTest.php diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php index 071a5ec2fab8..d2675848c2d2 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/CanonicalUrl.php @@ -14,6 +14,9 @@ use Magento\Framework\GraphQl\Query\ResolverInterface; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +/** + * Resolve data for product canonical URL + */ class CanonicalUrl implements ResolverInterface { /** @@ -31,15 +34,7 @@ public function __construct( } /** - * Fetches the data from persistence models and format it according to the GraphQL schema. - * - * @param \Magento\Framework\GraphQl\Config\Element\Field $field - * @param $context - * @param ResolveInfo $info - * @param array|null $value - * @param array|null $args - * @throws \Exception - * @return Value + * {@inheritdoc} */ public function resolve( Field $field, diff --git a/app/code/Magento/CatalogGraphQl/Test/Integration/Model/ProductsTest.php b/app/code/Magento/CatalogGraphQl/Test/Integration/Model/ProductsTest.php deleted file mode 100644 index 29c9f33d7471..000000000000 --- a/app/code/Magento/CatalogGraphQl/Test/Integration/Model/ProductsTest.php +++ /dev/null @@ -1,73 +0,0 @@ -objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->graphql = $this->objectManager->get(\Magento\GraphQl\Controller\GraphQl::class); - $this->jsonSerializer = $this->objectManager->get(SerializerInterface::class); - } - - /** - * @magentoAppArea graphql - * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_with_category.php - * @magentoDbIsolation disabled - * @magentoConfigFixture default_store catalog/seo/product_url_suffix .html - */ - public function testResponseContainsCanonicalURLs(): void - { - $fixtureSku = 'p002'; - $query = << $query]; - $request = $this->objectManager->get(HttpRequest::class); - $request->setPathInfo('/graphql'); - $request->setContent(json_encode($postData)); - $headers = $this->objectManager->create(\Zend\Http\Headers::class) - ->addHeaders(['Content-Type' => 'application/json']); - $request->setHeaders($headers); - - $response = $this->graphql->dispatch($request); - $output = $this->jsonSerializer->unserialize($response->getContent()); - $canonical_url = $output['data']['products']['items'][0]['canonical_url']; - - $this->assertArrayNotHasKey('errors', $output, 'Response has errors'); - $this->assertEquals('http://localhost/index.php/' . $fixtureSku . '.html', $canonical_url); - } -} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php index a4c7f41df0e9..54d363ea6c1e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php @@ -227,6 +227,7 @@ public function testQueryAllFieldsSimpleProduct() updated_at url_key url_path + canonical_url websites { id name code sort_order default_group_id is_default } ... on PhysicalProductInterface { weight @@ -272,6 +273,10 @@ public function testQueryAllFieldsSimpleProduct() 'Filter category', $responseObject->getData('products/items/0/categories/2/name') ); + self::assertEquals( + $product->getUrlModel()->getUrl($product, ['_ignore_category' => true, '_nosid' => true]), + $responseObject->getData('products/items/0/canonical_url') + ); } /** From 6c04a04434fc7fc90dadd0fd1f687508d33cb48d Mon Sep 17 00:00:00 2001 From: austris argalis Date: Sun, 20 May 2018 20:30:22 +0300 Subject: [PATCH 3/3] Correct failing test, use urlModel no more --- .../testsuite/Magento/GraphQl/Catalog/ProductViewTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php index 54d363ea6c1e..aaf22c8b373f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/ProductViewTest.php @@ -273,8 +273,9 @@ public function testQueryAllFieldsSimpleProduct() 'Filter category', $responseObject->getData('products/items/0/categories/2/name') ); + $storeManager = ObjectManager::getInstance()->get(\Magento\Store\Model\StoreManagerInterface::class); self::assertEquals( - $product->getUrlModel()->getUrl($product, ['_ignore_category' => true, '_nosid' => true]), + $storeManager->getStore()->getBaseUrl() . 'simple-product.html', $responseObject->getData('products/items/0/canonical_url') ); }