diff --git a/app/code/Magento/CmsGraphQl/composer.json b/app/code/Magento/CmsGraphQl/composer.json index eb515c91bbb3..6a2e3950f93d 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 000000000000..78c1071d8e07 --- /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 997bbf920a09..e8abd2201b88 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/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php b/app/code/Magento/StoreGraphQl/Model/Resolver/Store/StoreConfigDataProvider.php index 92926c12e86d..ef64750c6016 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,56 @@ 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; + } + + /** + * @return array + */ + public function getStoreConfigData(): array + { + $storeConfigData = array_merge( + $this->getBaseConfigData(), + $this->getExtendedConfigData() + ); + return $storeConfigData; } /** - * Get store config for current store - * * @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 +95,21 @@ public function getStoreConfig() : array ]; return $storeConfigData; } + + /** + * @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 39fcd1bf2792..9c426172de85 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 d03d759babd2..d53ba9fbb002 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 000000000000..fed6b54fa5cf --- /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 000000000000..3170d9c75387 --- /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\\ThemeGraphQlhQl\\": "" + } + } +} diff --git a/app/code/Magento/ThemeGraphQl/etc/graphql/di.xml b/app/code/Magento/ThemeGraphQl/etc/graphql/di.xml new file mode 100644 index 000000000000..444bb9b817d4 --- /dev/null +++ b/app/code/Magento/ThemeGraphQl/etc/graphql/di.xml @@ -0,0 +1,36 @@ + + + + + + + + design/head/head_shortcut_icon + design/head/default_title + design/head/title_prefix + design/head/title_suffix + design/head/default_description + design/head/default_keywords + design/head/includes + design/head/demonotice + + + design/header/header_logo_src + design/header/logo_width + design/header/logo_height + design/header/welcome + design/header/logo_alt + + + design/footer/copyright + design/footer/absolute_footer + + + + + diff --git a/app/code/Magento/ThemeGraphQl/etc/module.xml b/app/code/Magento/ThemeGraphQl/etc/module.xml new file mode 100644 index 000000000000..06ba03c14fe8 --- /dev/null +++ b/app/code/Magento/ThemeGraphQl/etc/module.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/app/code/Magento/ThemeGraphQl/etc/schema.graphqls b/app/code/Magento/ThemeGraphQl/etc/schema.graphqls new file mode 100644 index 000000000000..325fcc8bd983 --- /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 000000000000..e320fbc9868e --- /dev/null +++ b/app/code/Magento/ThemeGraphQl/registration.php @@ -0,0 +1,10 @@ +getTestAppConfig()->isSetFlag($path, $scopeType, $scopeCode); + } + + /** + * @inheritdoc + */ + public function getValue($path, $scopeType = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeCode = null) + { + return $this->getTestAppConfig()->getValue($path, $scopeType, $scopeCode); + } + + /** + * @inheritdoc + */ + public function setValue( + $path, + $value, + $scopeType = \Magento\Framework\App\Config\ScopeConfigInterface::SCOPE_TYPE_DEFAULT, + $scopeCode = null + ) { + $this->persistConfig($path, $value, $scopeType, $scopeCode); + return $this->getTestAppConfig()->setValue($path, $value, $scopeType, $scopeCode); + } + + /** + * Clean app config cache + * + * @param string|null $type + * @return void + */ + public function clean() + { + $this->getTestAppConfig()->clean(); + } + + /** + * Retrieve test app config instance + * + * @return \Magento\TestFramework\App\Config + */ + private function getTestAppConfig() + { + if (!$this->testAppConfig) { + $this->testAppConfig = ObjectManager::getInstance()->get(ScopeConfigInterface::class); + } + + return $this->testAppConfig; + } + + /** + * Persist config in database + * + * @param string $path + * @param string $value + * @param string $scopeType + * @param string|null $scopeCode + */ + private function persistConfig($path, $value, $scopeType, $scopeCode): void + { + $pathParts = explode('/', $path); + $store = ''; + if ($scopeType === \Magento\Store\Model\ScopeInterface::SCOPE_STORE) { + if ($scopeCode !== null) { + $store = ObjectManager::getInstance() + ->get(\Magento\Store\Api\StoreRepositoryInterface::class) + ->get($scopeCode) + ->getId(); + } else { + $store = ObjectManager::getInstance() + ->get(\Magento\Store\Model\StoreManagerInterface::class) + ->getStore() + ->getId(); + } + } + $configData = [ + 'section' => $pathParts[0], + 'website' => '', + 'store' => $store, + 'groups' => [ + $pathParts[1] => [ + 'fields' => [ + $pathParts[2] => [ + 'value' => $value + ] + ] + ] + ] + ]; + ObjectManager::getInstance() + ->get(\Magento\Config\Model\Config\Factory::class) + ->create(['data' => $configData]) + ->save(); + } +} diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/Bootstrap/WebapiDocBlock.php b/dev/tests/api-functional/framework/Magento/TestFramework/Bootstrap/WebapiDocBlock.php index 514d8e5344d5..336456de0ed2 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/Bootstrap/WebapiDocBlock.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/Bootstrap/WebapiDocBlock.php @@ -10,7 +10,8 @@ class WebapiDocBlock extends \Magento\TestFramework\Bootstrap\DocBlock { /** - * Get list of subscribers. In addition, register magentoApiDataFixture annotation processing. + * Get list of subscribers. In addition, register magentoApiDataFixture and magentoApiConfigFixture + * annotation processors * * @param \Magento\TestFramework\Application $application * @return array @@ -19,6 +20,7 @@ protected function _getSubscribers(\Magento\TestFramework\Application $applicati { $subscribers = parent::_getSubscribers($application); $subscribers[] = new \Magento\TestFramework\Annotation\ApiDataFixture($this->_fixturesBaseDir); + $subscribers[] = new \Magento\TestFramework\Annotation\ApiConfigFixture($this->_fixturesBaseDir); return $subscribers; } } 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 4e49bb63e49a..d45fa5d77d13 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogInventory/ProductOnlyXLeftInStockTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/CatalogInventory/ProductOnlyXLeftInStockTest.php @@ -39,11 +39,10 @@ public function testQueryProductOnlyXLeftInStockDisabled() /** * @magentoApiDataFixture Magento/Catalog/_files/product_simple_with_all_fields.php - * @magentoConfigFixture default_store cataloginventory/options/stock_threshold_qty 120 + * @magentoApiConfigFixture default_store cataloginventory/options/stock_threshold_qty 120 */ public function testQueryProductOnlyXLeftInStockEnabled() { - $this->markTestIncomplete('https://github.com/magento/graphql-ce/issues/167'); $productSku = 'simple'; $query = <<markTestIncomplete('https://github.com/magento/graphql-ce/issues/167'); $productSku = 'simple'; $query = << 'Default Store Front', + 'cms_home_page' => 'Default Store Homepage', + 'no_route' => 'default_store_no_route', + 'cms_no_route' => 'default_store_cms_no_route', + 'cms_no_cookies' => 'default_store_cms_no_cookies', + 'show_cms_breadcrumbs' => 0 + ]; + + $query + = <<graphQlQuery($query); + + $this->assertArrayHasKey('storeConfig', $response); + foreach ($expectedConfigs as $key => $expectedConfigValue) { + $this->assertEquals($expectedConfigValue, $response['storeConfig'][$key]); + } + } + + /** + * @magentoApiDataFixture Magento/Store/_files/second_store.php + * @magentoApiConfigFixture fixture_second_store_store web/default/front Test Store Front + * @magentoApiConfigFixture fixture_second_store_store web/default/cms_home_page Test Store Homepage + * @magentoApiConfigFixture fixture_second_store_store web/default/no_route test_store_no_route + * @magentoApiConfigFixture fixture_second_store_store web/default/cms_no_route test_store_cms_no_route + * @magentoApiConfigFixture fixture_second_store_store web/default/cms_no_cookies test_store_cms_no_cookies + * @magentoApiConfigFixture fixture_second_store_store web/default/show_cms_breadcrumbs 1 + */ + public function testGetNotDefaultStoreCmsConfig() + { + $expectedConfigs = [ + 'front' => 'Test Store Front', + 'cms_home_page' => 'Test Store Homepage', + 'no_route' => 'test_store_no_route', + 'cms_no_route' => 'test_store_cms_no_route', + 'cms_no_cookies' => 'test_store_cms_no_cookies', + 'show_cms_breadcrumbs' => 1 + ]; + + $query + = << $storeCodeFromFixture]; + $response = $this->graphQlQuery($query, [], '', $headerMap); + + $this->assertArrayHasKey('storeConfig', $response); + foreach ($expectedConfigs as $key => $expectedConfigValue) { + $this->assertEquals($expectedConfigValue, $response['storeConfig'][$key]); + } + } +} 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 4657a1e763ae..c7426caef711 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigResolverTest.php @@ -28,9 +28,6 @@ protected function setUp() $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); } - /** - * @magentoApiDataFixture Magento/Store/_files/store.php - */ public function testGetStoreConfig() { /** @var StoreConfigManagerInterface $storeConfigsManager */ diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Theme/ThemeConfigTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Theme/ThemeConfigTest.php new file mode 100644 index 000000000000..4232139da28b --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Theme/ThemeConfigTest.php @@ -0,0 +1,153 @@ + 'fixture_second_store_icon.ico', + 'default_title' => 'Default Store Title', + 'title_prefix' => 'Default Store Title Prefix', + 'title_suffix' => 'Default Store Suffix', + 'default_description' => 'Default Store Description', + 'default_keywords' => 'Default Store Keywords', + 'head_includes' => 'Default Store Scripts And Stylesheets', + 'demonotice' => 1, + 'header_logo_src' => 'fixture_second_store_logo.phg', + 'logo_width' => 200, + 'logo_height' => 300, + 'welcome' => 'Default Store Welcome', + 'logo_alt' => 'Default Store Alt', + 'copyright' => 'Default Store Copyright', + 'absolute_footer' => 'Default Store Footer' + ]; + + $query + = <<graphQlQuery($query); + + $this->assertArrayHasKey('storeConfig', $response); + foreach ($expectedConfigs as $key => $expectedConfigValue) { + $this->assertEquals($expectedConfigValue, $response['storeConfig'][$key]); + } + } + + /** + * @magentoApiDataFixture Magento/Store/_files/second_store.php + * @magentoApiConfigFixture fixture_second_store_store design/head/head_shortcut_icon test_store_icon.ico + * @magentoApiConfigFixture fixture_second_store_store design/head/default_title Test Store Title + * @magentoApiConfigFixture fixture_second_store_store design/head/title_prefix Test Store Title Prefix + * @magentoApiConfigFixture fixture_second_store_store design/head/title_suffix Test Store Suffix + * @magentoApiConfigFixture fixture_second_store_store design/head/default_description Test Store Description + * @magentoApiConfigFixture fixture_second_store_store design/head/default_keywords Test Store Keywords + * @magentoApiConfigFixture fixture_second_store_store design/head/includes Test Store Scripts And Stylesheets + * @magentoApiConfigFixture fixture_second_store_store design/head/demonotice 0 + * @magentoApiConfigFixture fixture_second_store_store design/header/header_logo_src test_store_logo.phg + * @magentoApiConfigFixture fixture_second_store_store design/header/logo_width 250 + * @magentoApiConfigFixture fixture_second_store_store design/header/logo_height 350 + * @magentoApiConfigFixture fixture_second_store_store design/header/welcome Test Store Welcome + * @magentoApiConfigFixture fixture_second_store_store design/header/logo_alt Test Store Alt + * @magentoApiConfigFixture fixture_second_store_store design/footer/copyright Test Store Copyright + * @magentoApiConfigFixture fixture_second_store_store design/footer/absolute_footer Test Store Footer + */ + public function testGetNotDefaultStoreThemeConfig() + { + $expectedConfigs = [ + 'head_shortcut_icon' => 'test_store_icon.ico', + 'default_title' => 'Test Store Title', + 'title_prefix' => 'Test Store Title Prefix', + 'title_suffix' => 'Test Store Suffix', + 'default_description' => 'Test Store Description', + 'default_keywords' => 'Test Store Keywords', + 'head_includes' => 'Test Store Scripts And Stylesheets', + 'demonotice' => 0, + 'header_logo_src' => 'test_store_logo.phg', + 'logo_width' => 250, + 'logo_height' => 350, + 'welcome' => 'Test Store Welcome', + 'logo_alt' => 'Test Store Alt', + 'copyright' => 'Test Store Copyright', + 'absolute_footer' => 'Test Store Footer' + ]; + + $query + = << $storeCodeFromFixture]; + $response = $this->graphQlQuery($query, [], '', $headerMap); + + $this->assertArrayHasKey('storeConfig', $response); + foreach ($expectedConfigs as $key => $expectedConfigValue) { + $this->assertEquals($expectedConfigValue, $response['storeConfig'][$key]); + } + } +} diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/ConfigFixture.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/ConfigFixture.php index dcac794703a5..1ec0b3eac6fc 100644 --- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/ConfigFixture.php +++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/ConfigFixture.php @@ -39,6 +39,11 @@ class ConfigFixture */ private $_storeConfigValues = []; + /** + * @var string + */ + protected $annotation = 'magentoConfigFixture'; + /** * Retrieve configuration node value * @@ -104,10 +109,10 @@ protected function _setConfigValue($configPath, $value, $storeCode = false) protected function _assignConfigData(\PHPUnit\Framework\TestCase $test) { $annotations = $test->getAnnotations(); - if (!isset($annotations['method']['magentoConfigFixture'])) { + if (!isset($annotations['method'][$this->annotation])) { return; } - foreach ($annotations['method']['magentoConfigFixture'] as $configPathAndValue) { + foreach ($annotations['method'][$this->annotation] as $configPathAndValue) { if (preg_match('/^.+?(?=_store\s)/', $configPathAndValue, $matches)) { /* Store-scoped config value */ $storeCode = $matches[0] != 'current' ? $matches[0] : null;