diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
index 6d9f5e33dd55b..4e3a8403f3132 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/CategoryTree.php
@@ -7,6 +7,7 @@
namespace Magento\CatalogGraphQl\Model\Resolver;
+use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\ExtractDataFromCategoryTree;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
@@ -27,16 +28,26 @@ class CategoryTree implements ResolverInterface
*/
private $categoryTree;
+ /**
+ * @var ExtractDataFromCategoryTree
+ */
+ private $extractDataFromCategoryTree;
+
/**
* @param \Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\CategoryTree $categoryTree
+ * @param ExtractDataFromCategoryTree $extractDataFromCategoryTree
*/
public function __construct(
- \Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\CategoryTree $categoryTree
+ \Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\CategoryTree $categoryTree,
+ ExtractDataFromCategoryTree $extractDataFromCategoryTree
) {
$this->categoryTree = $categoryTree;
+ $this->extractDataFromCategoryTree = $extractDataFromCategoryTree;
}
/**
+ * Get category id
+ *
* @param array $args
* @return int
* @throws GraphQlInputException
@@ -62,7 +73,8 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
$rootCategoryId = $this->getCategoryId($args);
$categoriesTree = $this->categoryTree->getTree($info, $rootCategoryId);
if (!empty($categoriesTree)) {
- return current($categoriesTree);
+ $result = $this->extractDataFromCategoryTree->execute($categoriesTree);
+ return current($result);
} else {
return null;
}
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php
index 07d8996838e9f..f2634574a2d15 100644
--- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/CategoryTree.php
@@ -9,7 +9,6 @@
use GraphQL\Language\AST\FieldNode;
use Magento\CatalogGraphQl\Model\Category\DepthCalculator;
-use Magento\CatalogGraphQl\Model\Category\Hydrator;
use Magento\CatalogGraphQl\Model\Category\LevelCalculator;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
@@ -17,6 +16,7 @@
use Magento\Catalog\Model\ResourceModel\Category\Collection;
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory;
use Magento\CatalogGraphQl\Model\AttributesJoiner;
+use Magento\Catalog\Model\Category;
/**
* Category tree data provider
@@ -53,33 +53,25 @@ class CategoryTree
*/
private $metadata;
- /**
- * @var Hydrator
- */
- private $hydrator;
-
/**
* @param CollectionFactory $collectionFactory
* @param AttributesJoiner $attributesJoiner
* @param DepthCalculator $depthCalculator
* @param LevelCalculator $levelCalculator
* @param MetadataPool $metadata
- * @param Hydrator $hydrator
*/
public function __construct(
CollectionFactory $collectionFactory,
AttributesJoiner $attributesJoiner,
DepthCalculator $depthCalculator,
LevelCalculator $levelCalculator,
- MetadataPool $metadata,
- Hydrator $hydrator
+ MetadataPool $metadata
) {
$this->collectionFactory = $collectionFactory;
$this->attributesJoiner = $attributesJoiner;
$this->depthCalculator = $depthCalculator;
$this->levelCalculator = $levelCalculator;
$this->metadata = $metadata;
- $this->hydrator = $hydrator;
}
/**
@@ -87,17 +79,26 @@ public function __construct(
*
* @param ResolveInfo $resolveInfo
* @param int $rootCategoryId
- * @return array
+ * @return \Iterator
*/
- public function getTree(ResolveInfo $resolveInfo, int $rootCategoryId) : array
+ public function getTree(ResolveInfo $resolveInfo, int $rootCategoryId): \Iterator
{
$categoryQuery = $resolveInfo->fieldNodes[0];
$collection = $this->collectionFactory->create();
$this->joinAttributesRecursively($collection, $categoryQuery);
$depth = $this->depthCalculator->calculate($categoryQuery);
$level = $this->levelCalculator->calculate($rootCategoryId);
+
+ // If root category is being filter, we've to remove first slash
+ if ($rootCategoryId == Category::TREE_ROOT_ID) {
+ $regExpPathFilter = sprintf('.*%s/[/0-9]*$', $rootCategoryId);
+ } else {
+ $regExpPathFilter = sprintf('.*/%s/[/0-9]*$', $rootCategoryId);
+ }
+
//Search for desired part of category tree
- $collection->addPathFilter(sprintf('.*/%s/[/0-9]*$', $rootCategoryId));
+ $collection->addPathFilter($regExpPathFilter);
+
$collection->addFieldToFilter('level', ['gt' => $level]);
$collection->addFieldToFilter('level', ['lteq' => $level + $depth - self::DEPTH_OFFSET]);
$collection->setOrder('level');
@@ -105,35 +106,11 @@ public function getTree(ResolveInfo $resolveInfo, int $rootCategoryId) : array
$this->metadata->getMetadata(CategoryInterface::class)->getIdentifierField() . ' = ?',
$rootCategoryId
);
- return $this->processTree($collection->getIterator());
- }
-
- /**
- * Iterates through category tree
- *
- * @param \Iterator $iterator
- * @return array
- */
- private function processTree(\Iterator $iterator) : array
- {
- $tree = [];
- while ($iterator->valid()) {
- /** @var CategoryInterface $category */
- $category = $iterator->current();
- $iterator->next();
- $nextCategory = $iterator->current();
- $tree[$category->getId()] = $this->hydrator->hydrateCategory($category);
- $tree[$category->getId()]['model'] = $category;
- if ($nextCategory && (int) $nextCategory->getLevel() !== (int) $category->getLevel()) {
- $tree[$category->getId()]['children'] = $this->processTree($iterator);
- }
- }
-
- return $tree;
+ return $collection->getIterator();
}
/**
- * Joins EAV attributes recursively
+ * Join attributes recursively
*
* @param Collection $collection
* @param FieldNode $fieldNode
diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ExtractDataFromCategoryTree.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ExtractDataFromCategoryTree.php
new file mode 100644
index 0000000000000..ac8d5709c85b3
--- /dev/null
+++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Products/DataProvider/ExtractDataFromCategoryTree.php
@@ -0,0 +1,55 @@
+categoryHydrator = $categoryHydrator;
+ }
+
+ /**
+ * Extract data from category tree
+ *
+ * @param \Iterator $iterator
+ * @return array
+ */
+ public function execute(\Iterator $iterator): array
+ {
+ $tree = [];
+ while ($iterator->valid()) {
+ /** @var CategoryInterface $category */
+ $category = $iterator->current();
+ $iterator->next();
+ $nextCategory = $iterator->current();
+ $tree[$category->getId()] = $this->categoryHydrator->hydrateCategory($category);
+ $tree[$category->getId()]['model'] = $category;
+ if ($nextCategory && (int) $nextCategory->getLevel() !== (int) $category->getLevel()) {
+ $tree[$category->getId()]['children'] = $this->execute($iterator);
+ }
+ }
+
+ return $tree;
+ }
+}
diff --git a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/module.xml b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/module.xml
index be4bb9fcd7010..e6e0f6cf72100 100644
--- a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/module.xml
+++ b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/module.xml
@@ -6,5 +6,10 @@
*/
-->
-
+
+
+
+
+
+
diff --git a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/schema.graphqls
index b96cfcb03d41f..f4ad2e930ddab 100644
--- a/app/code/Magento/CatalogUrlRewriteGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CatalogUrlRewriteGraphQl/etc/schema.graphqls
@@ -4,6 +4,7 @@
interface ProductInterface {
url_key: String @doc(description: "The part of the URL that identifies the product")
url_path: String @doc(description: "The part of the URL that precedes the url_key")
+ url_rewrites: [UrlRewrite] @doc(description: "URL rewrites list") @resolver(class: "Magento\\UrlRewriteGraphQl\\Model\\Resolver\\UrlRewrite")
}
input ProductFilterInput {
diff --git a/app/code/Magento/CmsGraphQl/composer.json b/app/code/Magento/CmsGraphQl/composer.json
index eb515c91bbb38..6a2e3950f93d0 100644
--- a/app/code/Magento/CmsGraphQl/composer.json
+++ b/app/code/Magento/CmsGraphQl/composer.json
@@ -8,6 +8,10 @@
"magento/module-cms": "*",
"magento/module-widget": "*"
},
+ "suggest": {
+ "magento/module-graph-ql": "*",
+ "magento/module-store-graph-ql": "*"
+ },
"license": [
"OSL-3.0",
"AFL-3.0"
diff --git a/app/code/Magento/CmsGraphQl/etc/graphql/di.xml b/app/code/Magento/CmsGraphQl/etc/graphql/di.xml
new file mode 100644
index 0000000000000..78c1071d8e07c
--- /dev/null
+++ b/app/code/Magento/CmsGraphQl/etc/graphql/di.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+ - web/default/front
+ - web/default/cms_home_page
+ - web/default/no_route
+ - web/default/cms_no_route
+ - web/default/cms_no_cookies
+ - web/default/show_cms_breadcrumbs
+
+
+
+
diff --git a/app/code/Magento/CmsGraphQl/etc/schema.graphqls b/app/code/Magento/CmsGraphQl/etc/schema.graphqls
index 997bbf920a09e..e8abd2201b886 100644
--- a/app/code/Magento/CmsGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CmsGraphQl/etc/schema.graphqls
@@ -1,5 +1,14 @@
# Copyright © Magento, Inc. All rights reserved.
# See COPYING.txt for license details.
+type StoreConfig @doc(description: "The type contains information about a store config") {
+ front : String @doc(description: "Default Web URL")
+ cms_home_page : String @doc(description: "CMS Home Page")
+ no_route : String @doc(description: "Default No-route URL")
+ cms_no_route : String @doc(description: "CMS No Route Page")
+ cms_no_cookies : String @doc(description: "CMS No Cookies Page")
+ show_cms_breadcrumbs : Int @doc(description: "Show Breadcrumbs for CMS Pages")
+}
+
type Query {
cmsPage (
diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/Account/RevokeCustomerToken.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/Account/RevokeCustomerToken.php
new file mode 100644
index 0000000000000..7eb219a01217e
--- /dev/null
+++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/Customer/Account/RevokeCustomerToken.php
@@ -0,0 +1,67 @@
+userContext = $userContext;
+ $this->customerTokenService = $customerTokenService;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(
+ Field $field,
+ $context,
+ ResolveInfo $info,
+ array $value = null,
+ array $args = null
+ ) {
+ $customerId = (int)$this->userContext->getUserId();
+
+ if ($customerId === 0) {
+ throw new GraphQlAuthorizationException(
+ __(
+ 'Current customer does not have access to the resource "%1"',
+ [\Magento\Customer\Model\Customer::ENTITY]
+ )
+ );
+ }
+
+ return $this->customerTokenService->revokeCustomerAccessToken($customerId);
+ }
+}
diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls
index 93c741adea87b..f3e3365f1c35e 100644
--- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls
@@ -8,6 +8,7 @@ type Query {
type Mutation {
generateCustomerToken(email: String!, password: String!): CustomerToken @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\Customer\\Account\\GenerateCustomerToken") @doc(description:"Retrieve Customer token")
changeCustomerPassword(currentPassword: String!, newPassword: String!): Customer @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\Customer\\Account\\ChangePassword") @doc(description:"Changes password for logged in customer")
+ revokeCustomerToken: Boolean @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\Customer\\Account\\RevokeCustomerToken") @doc(description:"Revoke Customer token")
}
type CustomerToken {
diff --git a/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php
index 92926c12e86d3..8c2d6c36591d5 100644
--- a/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php
+++ b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php
@@ -7,10 +7,10 @@
namespace Magento\StoreGraphQl\Model\Resolver\Store;
-use Magento\Store\Api\Data\StoreConfigInterface;
+use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Store\Api\StoreConfigManagerInterface;
-use Magento\Store\Api\StoreRepositoryInterface;
-use Magento\Store\Api\StoreResolverInterface;
+use Magento\Store\Model\ScopeInterface;
+use Magento\Store\Model\StoreManagerInterface;
/**
* StoreConfig field data provider, used for GraphQL request processing.
@@ -23,39 +23,60 @@ class StoreConfigDataProvider
private $storeConfigManager;
/**
- * @var StoreResolverInterface
+ * @var StoreManagerInterface
*/
- private $storeResolver;
+ private $storeManager;
/**
- * @var StoreRepositoryInterface
+ * @var ScopeConfigInterface
*/
- private $storeRepository;
+ private $scopeConfig;
+
+ /**
+ * @var array
+ */
+ private $extendedConfigData;
/**
* @param StoreConfigManagerInterface $storeConfigManager
- * @param StoreResolverInterface $storeResolver
- * @param StoreRepositoryInterface $storeRepository
+ * @param StoreManagerInterface $storeManager
+ * @param ScopeConfigInterface $scopeConfig
+ * @param array $extendedConfigData
*/
public function __construct(
StoreConfigManagerInterface $storeConfigManager,
- StoreResolverInterface $storeResolver,
- StoreRepositoryInterface $storeRepository
+ StoreManagerInterface $storeManager,
+ ScopeConfigInterface $scopeConfig,
+ array $extendedConfigData = []
) {
$this->storeConfigManager = $storeConfigManager;
- $this->storeResolver = $storeResolver;
- $this->storeRepository = $storeRepository;
+ $this->storeManager = $storeManager;
+ $this->scopeConfig = $scopeConfig;
+ $this->extendedConfigData = $extendedConfigData;
+ }
+
+ /**
+ * Get store config data
+ *
+ * @return array
+ */
+ public function getStoreConfigData(): array
+ {
+ $storeConfigData = array_merge(
+ $this->getBaseConfigData(),
+ $this->getExtendedConfigData()
+ );
+ return $storeConfigData;
}
/**
- * Get store config for current store
+ * Get base config data
*
* @return array
*/
- public function getStoreConfig() : array
+ private function getBaseConfigData() : array
{
- $storeId = $this->storeResolver->getCurrentStoreId();
- $store = $this->storeRepository->getById($storeId);
+ $store = $this->storeManager->getStore();
$storeConfig = current($this->storeConfigManager->getStoreConfigs([$store->getCode()]));
$storeConfigData = [
@@ -78,4 +99,23 @@ public function getStoreConfig() : array
];
return $storeConfigData;
}
+
+ /**
+ * Get extended config data
+ *
+ * @return array
+ */
+ private function getExtendedConfigData()
+ {
+ $store = $this->storeManager->getStore();
+ $extendedConfigData = [];
+ foreach ($this->extendedConfigData as $key => $path) {
+ $extendedConfigData[$key] = $this->scopeConfig->getValue(
+ $path,
+ ScopeInterface::SCOPE_STORE,
+ $store->getId()
+ );
+ }
+ return $extendedConfigData;
+ }
}
diff --git a/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigResolver.php b/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigResolver.php
index 39fcd1bf2792d..9c426172de85d 100644
--- a/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigResolver.php
+++ b/app/code/Magento/StoreGraphQl/Model/Resolver/StoreConfigResolver.php
@@ -23,12 +23,12 @@ class StoreConfigResolver implements ResolverInterface
private $storeConfigDataProvider;
/**
- * @param StoreConfigDataProvider $storeConfigDataProvider
+ * @param StoreConfigDataProvider $storeConfigsDataProvider
*/
public function __construct(
- StoreConfigDataProvider $storeConfigDataProvider
+ StoreConfigDataProvider $storeConfigsDataProvider
) {
- $this->storeConfigDataProvider = $storeConfigDataProvider;
+ $this->storeConfigDataProvider = $storeConfigsDataProvider;
}
/**
@@ -41,8 +41,6 @@ public function resolve(
array $value = null,
array $args = null
) {
-
- $storeConfigData = $this->storeConfigDataProvider->getStoreConfig();
- return $storeConfigData;
+ return $this->storeConfigDataProvider->getStoreConfigData();
}
}
diff --git a/app/code/Magento/StoreGraphQl/composer.json b/app/code/Magento/StoreGraphQl/composer.json
index d03d759babd22..d53ba9fbb0023 100644
--- a/app/code/Magento/StoreGraphQl/composer.json
+++ b/app/code/Magento/StoreGraphQl/composer.json
@@ -8,8 +8,7 @@
"magento/module-store": "*"
},
"suggest": {
- "magento/module-graph-ql": "*",
- "magento/module-catalog-graph-ql": "*"
+ "magento/module-graph-ql": "*"
},
"license": [
"OSL-3.0",
diff --git a/app/code/Magento/ThemeGraphQl/README.md b/app/code/Magento/ThemeGraphQl/README.md
new file mode 100644
index 0000000000000..fed6b54fa5cf1
--- /dev/null
+++ b/app/code/Magento/ThemeGraphQl/README.md
@@ -0,0 +1,4 @@
+# ThemeGraphQlhQl
+
+**ThemeGraphQlhQl** provides type information for the GraphQl module
+to generate theme fields information endpoints.
diff --git a/app/code/Magento/ThemeGraphQl/composer.json b/app/code/Magento/ThemeGraphQl/composer.json
new file mode 100644
index 0000000000000..e3aac55aea37e
--- /dev/null
+++ b/app/code/Magento/ThemeGraphQl/composer.json
@@ -0,0 +1,24 @@
+{
+ "name": "magento/module-theme-graph-ql",
+ "description": "N/A",
+ "type": "magento2-module",
+ "require": {
+ "php": "~7.1.3||~7.2.0",
+ "magento/framework": "*"
+ },
+ "suggest": {
+ "magento/module-store-graph-ql": "*"
+ },
+ "license": [
+ "OSL-3.0",
+ "AFL-3.0"
+ ],
+ "autoload": {
+ "files": [
+ "registration.php"
+ ],
+ "psr-4": {
+ "Magento\\ThemeGraphQl\\": ""
+ }
+ }
+}
diff --git a/app/code/Magento/ThemeGraphQl/etc/module.xml b/app/code/Magento/ThemeGraphQl/etc/module.xml
new file mode 100644
index 0000000000000..0e10b776af9c0
--- /dev/null
+++ b/app/code/Magento/ThemeGraphQl/etc/module.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
diff --git a/app/code/Magento/ThemeGraphQl/etc/schema.graphqls b/app/code/Magento/ThemeGraphQl/etc/schema.graphqls
new file mode 100644
index 0000000000000..325fcc8bd9834
--- /dev/null
+++ b/app/code/Magento/ThemeGraphQl/etc/schema.graphqls
@@ -0,0 +1,19 @@
+# Copyright © Magento, Inc. All rights reserved.
+# See COPYING.txt for license details.
+type StoreConfig @doc(description: "The type contains information about a store config") {
+ head_shortcut_icon : String @doc(description: "Favicon Icon")
+ default_title : String @doc(description: "Default Page Title")
+ title_prefix : String @doc(description: "Page Title Prefix")
+ title_suffix : String @doc(description: "Page Title Suffix")
+ default_description : String @doc(description: "Default Meta Description")
+ default_keywords : String @doc(description: "Default Meta Keywords")
+ head_includes : String @doc(description: "Scripts and Style Sheets")
+ demonotice : Int @doc(description: "Display Demo Store Notice")
+ header_logo_src : String @doc(description: "Logo Image")
+ logo_width : Int @doc(description: "Logo Attribute Width")
+ logo_height : Int @doc(description: "Logo Attribute Height")
+ welcome : String @doc(description: "Welcome Text")
+ logo_alt : String @doc(description: "Logo Image Alt")
+ absolute_footer : String @doc(description: "Footer Miscellaneous HTML")
+ copyright : String @doc(description: "Copyright")
+}
diff --git a/app/code/Magento/ThemeGraphQl/registration.php b/app/code/Magento/ThemeGraphQl/registration.php
new file mode 100644
index 0000000000000..e320fbc9868ef
--- /dev/null
+++ b/app/code/Magento/ThemeGraphQl/registration.php
@@ -0,0 +1,10 @@
+urlFinder = $urlFinder;
+ $this->storeManager = $storeManager;
+ $this->customUrlLocator = $customUrlLocator;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function resolve(
+ Field $field,
+ $context,
+ ResolveInfo $info,
+ array $value = null,
+ array $args = null
+ ) {
+ if (!isset($args['url']) || empty(trim($args['url']))) {
+ throw new GraphQlInputException(__('"url" argument should be specified and not empty'));
+ }
+
+ $result = null;
+ $url = $args['url'];
+ if (substr($url, 0, 1) === '/' && $url !== '/') {
+ $url = ltrim($url, '/');
+ }
+ $customUrl = $this->customUrlLocator->locateUrl($url);
+ $url = $customUrl ?: $url;
+ $urlRewrite = $this->findCanonicalUrl($url);
+ if ($urlRewrite) {
+ $result = [
+ 'id' => $urlRewrite->getEntityId(),
+ 'canonical_url' => $urlRewrite->getTargetPath(),
+ 'type' => $this->sanitizeType($urlRewrite->getEntityType())
+ ];
+ }
+ return $result;
+ }
+
+ /**
+ * Find the canonical url passing through all redirects if any
+ *
+ * @param string $requestPath
+ * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite|null
+ */
+ private function findCanonicalUrl(string $requestPath) : ?\Magento\UrlRewrite\Service\V1\Data\UrlRewrite
+ {
+ $urlRewrite = $this->findUrlFromRequestPath($requestPath);
+ if ($urlRewrite && $urlRewrite->getRedirectType() > 0) {
+ while ($urlRewrite && $urlRewrite->getRedirectType() > 0) {
+ $urlRewrite = $this->findUrlFromRequestPath($urlRewrite->getTargetPath());
+ }
+ }
+ if (!$urlRewrite) {
+ $urlRewrite = $this->findUrlFromTargetPath($requestPath);
+ }
+
+ return $urlRewrite;
+ }
+
+ /**
+ * Find a url from a request url on the current store
+ *
+ * @param string $requestPath
+ * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite|null
+ */
+ private function findUrlFromRequestPath(string $requestPath) : ?\Magento\UrlRewrite\Service\V1\Data\UrlRewrite
+ {
+ return $this->urlFinder->findOneByData(
+ [
+ 'request_path' => $requestPath,
+ 'store_id' => $this->storeManager->getStore()->getId()
+ ]
+ );
+ }
+
+ /**
+ * Find a url from a target url on the current store
+ *
+ * @param string $targetPath
+ * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite|null
+ */
+ private function findUrlFromTargetPath(string $targetPath) : ?\Magento\UrlRewrite\Service\V1\Data\UrlRewrite
+ {
+ return $this->urlFinder->findOneByData(
+ [
+ 'target_path' => $targetPath,
+ 'store_id' => $this->storeManager->getStore()->getId()
+ ]
+ );
+ }
+
+ /**
+ * Sanitize the type to fit schema specifications
+ *
+ * @param string $type
+ * @return string
+ */
+ private function sanitizeType(string $type) : string
+ {
+ return strtoupper(str_replace('-', '_', $type));
+ }
+}
diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php
index 5bdaf36272536..0c4c78b582941 100644
--- a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php
+++ b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/UrlRewrite.php
@@ -11,12 +11,12 @@
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
-use Magento\Store\Model\StoreManagerInterface;
+use Magento\Framework\Model\AbstractModel;
use Magento\UrlRewrite\Model\UrlFinderInterface;
-use Magento\UrlRewriteGraphQl\Model\Resolver\UrlRewrite\CustomUrlLocatorInterface;
+use Magento\UrlRewrite\Service\V1\Data\UrlRewrite as UrlRewriteDTO;
/**
- * UrlRewrite field resolver, used for GraphQL request processing.
+ * Returns URL rewrites list for the specified product
*/
class UrlRewrite implements ResolverInterface
{
@@ -25,29 +25,13 @@ class UrlRewrite implements ResolverInterface
*/
private $urlFinder;
- /**
- * @var StoreManagerInterface
- */
- private $storeManager;
-
- /**
- * @var CustomUrlLocatorInterface
- */
- private $customUrlLocator;
-
/**
* @param UrlFinderInterface $urlFinder
- * @param StoreManagerInterface $storeManager
- * @param CustomUrlLocatorInterface $customUrlLocator
*/
public function __construct(
- UrlFinderInterface $urlFinder,
- StoreManagerInterface $storeManager,
- CustomUrlLocatorInterface $customUrlLocator
+ UrlFinderInterface $urlFinder
) {
$this->urlFinder = $urlFinder;
- $this->storeManager = $storeManager;
- $this->customUrlLocator = $customUrlLocator;
}
/**
@@ -59,90 +43,51 @@ public function resolve(
ResolveInfo $info,
array $value = null,
array $args = null
- ) {
- if (!isset($args['url']) || empty(trim($args['url']))) {
- throw new GraphQlInputException(__('"url" argument should be specified and not empty'));
+ ): array {
+ if (!isset($value['model'])) {
+ throw new GraphQlInputException(__('"model" value should be specified'));
}
- $result = null;
- $url = $args['url'];
- if (substr($url, 0, 1) === '/' && $url !== '/') {
- $url = ltrim($url, '/');
- }
- $customUrl = $this->customUrlLocator->locateUrl($url);
- $url = $customUrl ?: $url;
- $urlRewrite = $this->findCanonicalUrl($url);
- if ($urlRewrite) {
- $result = [
- 'id' => $urlRewrite->getEntityId(),
- 'relative_url' => $urlRewrite->getTargetPath(),
- 'type' => $this->sanitizeType($urlRewrite->getEntityType())
- ];
- }
- return $result;
- }
+ /** @var AbstractModel $entity */
+ $entity = $value['model'];
+ $entityId = $entity->getEntityId();
- /**
- * Find the canonical url passing through all redirects if any
- *
- * @param string $requestPath
- * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite|null
- */
- private function findCanonicalUrl(string $requestPath) : ?\Magento\UrlRewrite\Service\V1\Data\UrlRewrite
- {
- $urlRewrite = $this->findUrlFromRequestPath($requestPath);
- if ($urlRewrite && $urlRewrite->getRedirectType() > 0) {
- while ($urlRewrite && $urlRewrite->getRedirectType() > 0) {
- $urlRewrite = $this->findUrlFromRequestPath($urlRewrite->getTargetPath());
+ $urlRewriteCollection = $this->urlFinder->findAllByData([UrlRewriteDTO::ENTITY_ID => $entityId]);
+ $urlRewrites = [];
+
+ /** @var UrlRewriteDTO $urlRewrite */
+ foreach ($urlRewriteCollection as $urlRewrite) {
+ if ($urlRewrite->getRedirectType() !== 0) {
+ continue;
}
+
+ $urlRewrites[] = [
+ 'url' => $urlRewrite->getRequestPath(),
+ 'parameters' => $this->getUrlParameters($urlRewrite->getTargetPath())
+ ];
}
- if (!$urlRewrite) {
- $urlRewrite = $this->findUrlFromTargetPath($requestPath);
- }
-
- return $urlRewrite;
- }
- /**
- * Find a url from a request url on the current store
- *
- * @param string $requestPath
- * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite|null
- */
- private function findUrlFromRequestPath(string $requestPath) : ?\Magento\UrlRewrite\Service\V1\Data\UrlRewrite
- {
- return $this->urlFinder->findOneByData(
- [
- 'request_path' => $requestPath,
- 'store_id' => $this->storeManager->getStore()->getId()
- ]
- );
+ return $urlRewrites;
}
/**
- * Find a url from a target url on the current store
+ * Parses target path and extracts parameters
*
* @param string $targetPath
- * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite|null
+ * @return array
*/
- private function findUrlFromTargetPath(string $targetPath) : ?\Magento\UrlRewrite\Service\V1\Data\UrlRewrite
+ private function getUrlParameters(string $targetPath): array
{
- return $this->urlFinder->findOneByData(
- [
- 'target_path' => $targetPath,
- 'store_id' => $this->storeManager->getStore()->getId()
- ]
- );
- }
+ $urlParameters = [];
+ $targetPathParts = explode('/', trim($targetPath, '/'));
- /**
- * Sanitize the type to fit schema specifications
- *
- * @param string $type
- * @return string
- */
- private function sanitizeType(string $type) : string
- {
- return strtoupper(str_replace('-', '_', $type));
+ for ($i = 3; ($i < sizeof($targetPathParts) - 1); $i += 2) {
+ $urlParameters[] = [
+ 'name' => $targetPathParts[$i],
+ 'value' => $targetPathParts[$i + 1]
+ ];
+ }
+
+ return $urlParameters;
}
}
diff --git a/app/code/Magento/UrlRewriteGraphQl/etc/schema.graphqls b/app/code/Magento/UrlRewriteGraphQl/etc/schema.graphqls
index e9a39617774c0..dae695c69a33c 100644
--- a/app/code/Magento/UrlRewriteGraphQl/etc/schema.graphqls
+++ b/app/code/Magento/UrlRewriteGraphQl/etc/schema.graphqls
@@ -1,15 +1,25 @@
# Copyright © Magento, Inc. All rights reserved.
# See COPYING.txt for license details.
-type EntityUrl @doc(description: "EntityUrl is an output object containing the `id`, `relative_url`, and `type` attributes") {
+type EntityUrl @doc(description: "EntityUrl is an output object containing the `id`, `canonical_url`, and `type` attributes") {
id: Int @doc(description: "The ID assigned to the object associated with the specified url. This could be a product ID, category ID, or page ID.")
- relative_url: String @doc(description: "The internal relative URL. If the specified url is a redirect, the query returns the redirected URL, not the original.")
+ canonical_url: String @doc(description: "The internal relative URL. If the specified url is a redirect, the query returns the redirected URL, not the original.")
type: UrlRewriteEntityTypeEnum @doc(description: "One of PRODUCT, CATEGORY, or CMS_PAGE.")
}
type Query {
- urlResolver(url: String!): EntityUrl @resolver(class: "Magento\\UrlRewriteGraphQl\\Model\\Resolver\\UrlRewrite") @doc(description: "The urlResolver query returns the relative URL for a specified product, category or CMS page")
+ urlResolver(url: String!): EntityUrl @resolver(class: "Magento\\UrlRewriteGraphQl\\Model\\Resolver\\EntityUrl") @doc(description: "The urlResolver query returns the relative URL for a specified product, category or CMS page")
}
enum UrlRewriteEntityTypeEnum {
}
+
+type UrlRewrite @doc(description: "The object contains URL rewrite details") {
+ url: String @doc(description: "Request URL")
+ parameters: [HttpQueryParameter] @doc(description: "Request parameters")
+}
+
+type HttpQueryParameter @doc(description: "The object details of target path parameters") {
+ name: String @doc(description: "Parameter name")
+ value: String @doc(description: "Parameter value")
+}
diff --git a/composer.json b/composer.json
index e2a646275d98b..3f8f0a033c893 100644
--- a/composer.json
+++ b/composer.json
@@ -221,6 +221,7 @@
"magento/module-tax": "*",
"magento/module-tax-import-export": "*",
"magento/module-theme": "*",
+ "magento/module-theme-graph-ql": "*",
"magento/module-translation": "*",
"magento/module-ui": "*",
"magento/module-ups": "*",
diff --git a/composer.lock b/composer.lock
index 2550f70f0be81..1d101c8aaaf15 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "18982aa4d36bcfd22cf073dfb578efdb",
+ "content-hash": "d6640ddfd342feceaec44c406c056f57",
"packages": [
{
"name": "braintree/braintree_php",
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php
new file mode 100644
index 0000000000000..c39b32e4bfa4f
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/UrlRewritesTest.php
@@ -0,0 +1,149 @@
+graphQlQuery($query);
+
+ /** @var ProductRepositoryInterface $productRepository */
+ $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class);
+ $product = $productRepository->get('virtual-product', false, null, true);
+
+ $urlFinder = ObjectManager::getInstance()->get(UrlFinderInterface::class);
+
+ $rewritesCollection = $urlFinder->findAllByData([UrlRewriteDTO::ENTITY_ID => $product->getId()]);
+
+ /* There should be only one rewrite */
+ /** @var UrlRewriteDTO $urlRewrite */
+ $urlRewrite = current($rewritesCollection);
+
+ $this->assertArrayHasKey('url_rewrites', $response['products']['items'][0]);
+ $this->assertCount(1, $response['products']['items'][0]['url_rewrites']);
+
+ $this->assertResponseFields(
+ $response['products']['items'][0]['url_rewrites'][0],
+ [
+ "url" => $urlRewrite->getRequestPath(),
+ "parameters" => $this->getUrlParameters($urlRewrite->getTargetPath())
+ ]
+ );
+ }
+
+ /**
+ *
+ * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php
+ * @throws \Magento\Framework\Exception\NoSuchEntityException
+ */
+ public function testProductWithOneCategoryAssigned()
+ {
+ $productSku = 'simple';
+ $query
+ = <<graphQlQuery($query);
+
+ /** @var ProductRepositoryInterface $productRepository */
+ $productRepository = ObjectManager::getInstance()->get(ProductRepositoryInterface::class);
+ $product = $productRepository->get('simple', false, null, true);
+
+ $urlFinder = ObjectManager::getInstance()->get(UrlFinderInterface::class);
+
+ $rewritesCollection = $urlFinder->findAllByData([UrlRewriteDTO::ENTITY_ID => $product->getId()]);
+ $rewritesCount = count($rewritesCollection);
+
+ $this->assertArrayHasKey('url_rewrites', $response['products']['items'][0]);
+ $this->assertCount($rewritesCount, $response['products']['items'][0]['url_rewrites']);
+
+ for ($i = 0; $i < $rewritesCount; $i++) {
+ $urlRewrite = $rewritesCollection[$i];
+ $this->assertResponseFields(
+ $response['products']['items'][0]['url_rewrites'][$i],
+ [
+ "url" => $urlRewrite->getRequestPath(),
+ "parameters" => $this->getUrlParameters($urlRewrite->getTargetPath())
+ ]
+ );
+ }
+ }
+
+ /**
+ * Parses target path and extracts parameters
+ *
+ * @param string $targetPath
+ * @return array
+ */
+ private function getUrlParameters(string $targetPath): array
+ {
+ $urlParameters = [];
+ $targetPathParts = explode('/', trim($targetPath, '/'));
+
+ for ($i = 3; ($i < sizeof($targetPathParts) - 1); $i += 2) {
+ $urlParameters[] = [
+ 'name' => $targetPathParts[$i],
+ 'value' => $targetPathParts[$i + 1]
+ ];
+ }
+
+ return $urlParameters;
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogInventory/ProductOnlyXLeftInStockTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogInventory/ProductOnlyXLeftInStockTest.php
index 4e49bb63e49a9..3969c758f12db 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogInventory/ProductOnlyXLeftInStockTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogInventory/ProductOnlyXLeftInStockTest.php
@@ -16,7 +16,6 @@ class ProductOnlyXLeftInStockTest extends GraphQlAbstract
*/
public function testQueryProductOnlyXLeftInStockDisabled()
{
- $this->cleanCache();
$productSku = 'simple';
$query = <<get(\Magento\Integration\Api\CustomerTokenServiceInterface::class);
+ $customerToken = $customerTokenService->createCustomerAccessToken($userName, $password);
+
+ $headerMap = ['Authorization' => 'Bearer ' . $customerToken];
+ $response = $this->graphQlQuery($query, [], '', $headerMap);
+ $this->assertTrue($response['revokeCustomerToken']);
+ }
+
+ /**
+ * Verify guest customers
+ */
+ public function testRevokeCustomerTokenForGuestCustomer()
+ {
+ $query = <<expectException(\Exception::class);
+ $this->expectExceptionMessage(
+ 'GraphQL response contains errors: Current customer' . ' ' .
+ 'does not have access to the resource "customer"'
+ );
+ $this->graphQlQuery($query, [], '');
+ }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php
index 4657a1e763ae1..a536022e31631 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php
@@ -25,6 +25,7 @@ class StoreConfigResolverTest extends GraphQlAbstract
protected function setUp()
{
+ $this->markTestIncomplete('https://github.com/magento/graphql-ce/issues/167');
$this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
}
diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php
index 370121a1dad78..c70b1631e85cd 100644
--- a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php
@@ -31,7 +31,7 @@ protected function setUp()
}
/**
- * Tests if target_path(relative_url) is resolved for Product entity
+ * Tests if target_path(canonical_url) is resolved for Product entity
*
* @magentoApiDataFixture Magento/CatalogUrlRewrite/_files/product_with_category.php
*/
@@ -60,7 +60,7 @@ public function testProductUrlResolver()
urlResolver(url:"{$urlPath}")
{
id
- relative_url
+ canonical_url
type
}
}
@@ -68,12 +68,12 @@ public function testProductUrlResolver()
$response = $this->graphQlQuery($query);
$this->assertArrayHasKey('urlResolver', $response);
$this->assertEquals($product->getEntityId(), $response['urlResolver']['id']);
- $this->assertEquals($targetPath, $response['urlResolver']['relative_url']);
+ $this->assertEquals($targetPath, $response['urlResolver']['canonical_url']);
$this->assertEquals(strtoupper($expectedType), $response['urlResolver']['type']);
}
/**
- * Tests the use case where relative_url is provided as resolver input in the Query
+ * Tests the use case where canonical_url is provided as resolver input in the Query
*
* @magentoApiDataFixture Magento/CatalogUrlRewrite/_files/product_with_category.php
*/
@@ -104,7 +104,7 @@ public function testProductUrlWithCanonicalUrlInput()
urlResolver(url:"{$canonicalPath}")
{
id
- relative_url
+ canonical_url
type
}
}
@@ -112,7 +112,7 @@ public function testProductUrlWithCanonicalUrlInput()
$response = $this->graphQlQuery($query);
$this->assertArrayHasKey('urlResolver', $response);
$this->assertEquals($product->getEntityId(), $response['urlResolver']['id']);
- $this->assertEquals($targetPath, $response['urlResolver']['relative_url']);
+ $this->assertEquals($targetPath, $response['urlResolver']['canonical_url']);
$this->assertEquals(strtoupper($expectedType), $response['urlResolver']['type']);
}
@@ -147,7 +147,7 @@ public function testCategoryUrlResolver()
urlResolver(url:"{$urlPath2}")
{
id
- relative_url
+ canonical_url
type
}
}
@@ -155,7 +155,7 @@ public function testCategoryUrlResolver()
$response = $this->graphQlQuery($query);
$this->assertArrayHasKey('urlResolver', $response);
$this->assertEquals($categoryId, $response['urlResolver']['id']);
- $this->assertEquals($targetPath, $response['urlResolver']['relative_url']);
+ $this->assertEquals($targetPath, $response['urlResolver']['canonical_url']);
$this->assertEquals(strtoupper($expectedType), $response['urlResolver']['type']);
}
@@ -183,14 +183,14 @@ public function testCMSPageUrlResolver()
urlResolver(url:"{$requestPath}")
{
id
- relative_url
+ canonical_url
type
}
}
QUERY;
$response = $this->graphQlQuery($query);
$this->assertEquals($cmsPageId, $response['urlResolver']['id']);
- $this->assertEquals($targetPath, $response['urlResolver']['relative_url']);
+ $this->assertEquals($targetPath, $response['urlResolver']['canonical_url']);
$this->assertEquals(strtoupper(str_replace('-', '_', $expectedEntityType)), $response['urlResolver']['type']);
}
@@ -226,7 +226,7 @@ public function testProductUrlRewriteResolver()
urlResolver(url:"{$urlPath}")
{
id
- relative_url
+ canonical_url
type
}
}
@@ -234,7 +234,7 @@ public function testProductUrlRewriteResolver()
$response = $this->graphQlQuery($query);
$this->assertArrayHasKey('urlResolver', $response);
$this->assertEquals($product->getEntityId(), $response['urlResolver']['id']);
- $this->assertEquals($targetPath, $response['urlResolver']['relative_url']);
+ $this->assertEquals($targetPath, $response['urlResolver']['canonical_url']);
$this->assertEquals(strtoupper($expectedType), $response['urlResolver']['type']);
}
@@ -266,7 +266,7 @@ public function testInvalidUrlResolverInput()
urlResolver(url:"{$urlPath}")
{
id
- relative_url
+ canonical_url
type
}
}
@@ -307,7 +307,7 @@ public function testCategoryUrlWithLeadingSlash()
urlResolver(url:"/{$urlPath}")
{
id
- relative_url
+ canonical_url
type
}
}
@@ -315,7 +315,7 @@ public function testCategoryUrlWithLeadingSlash()
$response = $this->graphQlQuery($query);
$this->assertArrayHasKey('urlResolver', $response);
$this->assertEquals($categoryId, $response['urlResolver']['id']);
- $this->assertEquals($targetPath, $response['urlResolver']['relative_url']);
+ $this->assertEquals($targetPath, $response['urlResolver']['canonical_url']);
$this->assertEquals(strtoupper($expectedType), $response['urlResolver']['type']);
}
@@ -344,7 +344,7 @@ public function testResolveSlash()
urlResolver(url:"/")
{
id
- relative_url
+ canonical_url
type
}
}
@@ -352,7 +352,7 @@ public function testResolveSlash()
$response = $this->graphQlQuery($query);
$this->assertArrayHasKey('urlResolver', $response);
$this->assertEquals($homePageId, $response['urlResolver']['id']);
- $this->assertEquals($targetPath, $response['urlResolver']['relative_url']);
+ $this->assertEquals($targetPath, $response['urlResolver']['canonical_url']);
$this->assertEquals('CMS_PAGE', $response['urlResolver']['type']);
}
}