Skip to content

Commit ee6bc37

Browse files
🔃 [Magento Community Engineering] Community Contributions - GraphQL
Accepted Community Pull Requests:
2 parents cde6dba + 0102b7d commit ee6bc37

31 files changed

+1871
-208
lines changed

app/code/Magento/CatalogUrlRewriteGraphQl/etc/schema.graphqls

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@
33

44
interface ProductInterface {
55
url_key: String @doc(description: "The part of the URL that identifies the product")
6-
url_path: String @doc(description: "The part of the URL that precedes the url_key")
6+
url_path: String @deprecated(reason: "Use product's `canonical_url` or url rewrites instead")
77
url_rewrites: [UrlRewrite] @doc(description: "URL rewrites list") @resolver(class: "Magento\\UrlRewriteGraphQl\\Model\\Resolver\\UrlRewrite")
88
}
99

1010
input ProductFilterInput {
1111
url_key: FilterTypeInput @doc(description: "The part of the URL that identifies the product")
12-
url_path: FilterTypeInput @doc(description: "The part of the URL that precedes the url_key")
12+
url_path: FilterTypeInput @deprecated(reason: "Use product's `canonical_url` or url rewrites instead")
1313
}
1414

1515
input ProductSortInput {
1616
url_key: SortEnum @doc(description: "The part of the URL that identifies the product")
17-
url_path: SortEnum @doc(description: "The part of the URL that precedes the url_key")
17+
url_path: SortEnum @deprecated(reason: "Use product's `canonical_url` or url rewrites instead")
1818
}
1919

2020
enum UrlRewriteEntityTypeEnum @doc(description: "This enumeration defines the entity type.") {

app/code/Magento/QuoteGraphQl/Model/Resolver/CartPrices.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
5656
'value' => $cartTotals->getSubtotalWithDiscount(), 'currency' => $currency
5757
],
5858
'applied_taxes' => $this->getAppliedTaxes($cartTotals, $currency),
59+
'discount' => $this->getDiscount($cartTotals, $currency),
5960
'model' => $quote
6061
];
6162
}
@@ -84,4 +85,22 @@ private function getAppliedTaxes(Total $total, string $currency): array
8485
}
8586
return $appliedTaxesData;
8687
}
88+
89+
/**
90+
* Returns information about an applied discount
91+
*
92+
* @param Total $total
93+
* @param string $currency
94+
* @return array|null
95+
*/
96+
private function getDiscount(Total $total, string $currency)
97+
{
98+
if ($total->getDiscountAmount() === 0) {
99+
return null;
100+
}
101+
return [
102+
'label' => explode(', ', $total->getDiscountDescription()),
103+
'amount' => ['value' => $total->getDiscountAmount(), 'currency' => $currency]
104+
];
105+
}
87106
}

app/code/Magento/QuoteGraphQl/etc/schema.graphqls

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ type CartPrices {
146146
grand_total: Money
147147
subtotal_including_tax: Money
148148
subtotal_excluding_tax: Money
149+
discount: CartDiscount
149150
subtotal_with_discount_excluding_tax: Money
150151
applied_taxes: [CartTaxItem]
151152
}
@@ -155,6 +156,11 @@ type CartTaxItem {
155156
label: String!
156157
}
157158

159+
type CartDiscount {
160+
amount: Money!
161+
label: [String!]!
162+
}
163+
158164
type SetPaymentMethodOnCartOutput {
159165
cart: Cart!
160166
}

app/code/Magento/RelatedProductGraphQl/etc/schema.graphqls

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# See COPYING.txt for license details.
33

44
interface ProductInterface {
5-
related_products: [ProductInterface] @doc(description: "RelatedProduct") @resolver(class: "Magento\\RelatedProductGraphQl\\Model\\Resolver\\RelatedProducts")
6-
upsell_products: [ProductInterface] @doc(description: "RelatedProduct") @resolver(class: "Magento\\RelatedProductGraphQl\\Model\\Resolver\\UpSellProducts")
7-
crosssell_products: [ProductInterface] @doc(description: "RelatedProduct") @resolver(class: "Magento\\RelatedProductGraphQl\\Model\\Resolver\\CrossSellProducts")
8-
}
5+
related_products: [ProductInterface] @doc(description: "Related Products") @resolver(class: "Magento\\RelatedProductGraphQl\\Model\\Resolver\\RelatedProducts")
6+
upsell_products: [ProductInterface] @doc(description: "Upsell Products") @resolver(class: "Magento\\RelatedProductGraphQl\\Model\\Resolver\\UpSellProducts")
7+
crosssell_products: [ProductInterface] @doc(description: "Crosssell Products") @resolver(class: "Magento\\RelatedProductGraphQl\\Model\\Resolver\\CrossSellProducts")
8+
}

dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/MediaGalleryTest.php

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
use Magento\TestFramework\TestCase\GraphQlAbstract;
1111

12+
/**
13+
* Test cases for product media gallery data retrieval.
14+
*/
1215
class MediaGalleryTest extends GraphQlAbstract
1316
{
1417
/**
@@ -45,19 +48,48 @@ public function testProductSmallImageUrlWithExistingImage()
4548
self::assertTrue($this->checkImageExists($response['products']['items'][0]['small_image']['url']));
4649
}
4750

51+
/**
52+
* @magentoApiDataFixture Magento/Catalog/_files/product_with_image.php
53+
*/
54+
public function testProductMediaGalleryEntries()
55+
{
56+
$this->markTestSkipped('https://github.com/magento/graphql-ce/issues/738');
57+
$productSku = 'simple';
58+
$query = <<<QUERY
59+
{
60+
products(filter: {sku: {eq: "{$productSku}"}}) {
61+
items {
62+
name
63+
sku
64+
media_gallery_entries {
65+
id
66+
file
67+
types
68+
}
69+
}
70+
}
71+
}
72+
QUERY;
73+
$response = $this->graphQlQuery($query);
74+
75+
self::assertArrayHasKey('file', $response['products']['items'][0]['media_gallery_entries'][0]);
76+
self::assertContains('magento_image.jpg', $response['products']['items'][0]['media_gallery_entries'][0]['url']);
77+
}
78+
4879
/**
4980
* @param string $url
5081
* @return bool
5182
*/
5283
private function checkImageExists(string $url): bool
5384
{
85+
// phpcs:disable Magento2.Functions.DiscouragedFunction
5486
$connection = curl_init($url);
5587
curl_setopt($connection, CURLOPT_HEADER, true);
5688
curl_setopt($connection, CURLOPT_NOBODY, true);
5789
curl_setopt($connection, CURLOPT_RETURNTRANSFER, 1);
5890
curl_exec($connection);
5991
$responseStatus = curl_getinfo($connection, CURLINFO_HTTP_CODE);
60-
92+
// phpcs:enable Magento2.Functions.DiscouragedFunction
6193
return $responseStatus === 200 ? true : false;
6294
}
6395
}

dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ChangeCustomerPasswordTest.php

Lines changed: 140 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@
88
namespace Magento\GraphQl\Customer;
99

1010
use Magento\Customer\Api\AccountManagementInterface;
11+
use Magento\Customer\Api\CustomerRepositoryInterface;
12+
use Magento\Customer\Model\CustomerAuthUpdate;
1113
use Magento\Customer\Model\CustomerRegistry;
14+
use Magento\Framework\Exception\AuthenticationException;
1215
use Magento\Framework\Exception\LocalizedException;
16+
use Magento\Framework\Exception\NoSuchEntityException;
1317
use Magento\Integration\Api\CustomerTokenServiceInterface;
1418
use Magento\TestFramework\Helper\Bootstrap;
1519
use Magento\TestFramework\TestCase\GraphQlAbstract;
@@ -34,11 +38,23 @@ class ChangeCustomerPasswordTest extends GraphQlAbstract
3438
*/
3539
private $customerRegistry;
3640

41+
/**
42+
* @var CustomerAuthUpdate
43+
*/
44+
private $customerAuthUpdate;
45+
46+
/**
47+
* @var CustomerRepositoryInterface
48+
*/
49+
private $customerRepository;
50+
3751
protected function setUp()
3852
{
3953
$this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class);
4054
$this->accountManagement = Bootstrap::getObjectManager()->get(AccountManagementInterface::class);
4155
$this->customerRegistry = Bootstrap::getObjectManager()->get(CustomerRegistry::class);
56+
$this->customerAuthUpdate = Bootstrap::getObjectManager()->get(CustomerAuthUpdate::class);
57+
$this->customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class);
4258
}
4359

4460
/**
@@ -47,19 +63,19 @@ protected function setUp()
4763
public function testChangePassword()
4864
{
4965
$customerEmail = 'customer@example.com';
50-
$oldCustomerPassword = 'password';
51-
$newCustomerPassword = 'anotherPassword1';
66+
$currentPassword = 'password';
67+
$newPassword = 'anotherPassword1';
5268

53-
$query = $this->getChangePassQuery($oldCustomerPassword, $newCustomerPassword);
54-
$headerMap = $this->getCustomerAuthHeaders($customerEmail, $oldCustomerPassword);
69+
$query = $this->getQuery($currentPassword, $newPassword);
70+
$headerMap = $this->getCustomerAuthHeaders($customerEmail, $currentPassword);
5571

5672
$response = $this->graphQlMutation($query, [], '', $headerMap);
5773
$this->assertEquals($customerEmail, $response['changeCustomerPassword']['email']);
5874

5975
try {
6076
// registry contains the old password hash so needs to be reset
6177
$this->customerRegistry->removeByEmail($customerEmail);
62-
$this->accountManagement->authenticate($customerEmail, $newCustomerPassword);
78+
$this->accountManagement->authenticate($customerEmail, $newPassword);
6379
} catch (LocalizedException $e) {
6480
$this->fail('Password was not changed: ' . $e->getMessage());
6581
}
@@ -71,7 +87,7 @@ public function testChangePassword()
7187
*/
7288
public function testChangePasswordIfUserIsNotAuthorizedTest()
7389
{
74-
$query = $this->getChangePassQuery('currentpassword', 'newpassword');
90+
$query = $this->getQuery('currentpassword', 'newpassword');
7591
$this->graphQlMutation($query);
7692
}
7793

@@ -81,11 +97,11 @@ public function testChangePasswordIfUserIsNotAuthorizedTest()
8197
public function testChangeWeakPassword()
8298
{
8399
$customerEmail = 'customer@example.com';
84-
$oldCustomerPassword = 'password';
85-
$newCustomerPassword = 'weakpass';
100+
$currentPassword = 'password';
101+
$newPassword = 'weakpass';
86102

87-
$query = $this->getChangePassQuery($oldCustomerPassword, $newCustomerPassword);
88-
$headerMap = $this->getCustomerAuthHeaders($customerEmail, $oldCustomerPassword);
103+
$query = $this->getQuery($currentPassword, $newPassword);
104+
$headerMap = $this->getCustomerAuthHeaders($customerEmail, $currentPassword);
89105

90106
$this->expectException(\Exception::class);
91107
$this->expectExceptionMessageRegExp('/Minimum of different classes of characters in password is.*/');
@@ -101,17 +117,123 @@ public function testChangeWeakPassword()
101117
public function testChangePasswordIfPasswordIsInvalid()
102118
{
103119
$customerEmail = 'customer@example.com';
104-
$oldCustomerPassword = 'password';
105-
$newCustomerPassword = 'anotherPassword1';
106-
$incorrectPassword = 'password-incorrect';
120+
$currentPassword = 'password';
121+
$newPassword = 'anotherPassword1';
122+
$incorrectCurrentPassword = 'password-incorrect';
123+
124+
$query = $this->getQuery($incorrectCurrentPassword, $newPassword);
125+
126+
$headerMap = $this->getCustomerAuthHeaders($customerEmail, $currentPassword);
127+
$this->graphQlMutation($query, [], '', $headerMap);
128+
}
129+
130+
/**
131+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
132+
* @expectedException \Exception
133+
* @expectedExceptionMessage Specify the "currentPassword" value.
134+
*/
135+
public function testChangePasswordIfCurrentPasswordIsEmpty()
136+
{
137+
$customerEmail = 'customer@example.com';
138+
$currentPassword = 'password';
139+
$newPassword = 'anotherPassword1';
140+
$incorrectCurrentPassword = '';
141+
142+
$query = $this->getQuery($incorrectCurrentPassword, $newPassword);
143+
144+
$headerMap = $this->getCustomerAuthHeaders($customerEmail, $currentPassword);
145+
$this->graphQlMutation($query, [], '', $headerMap);
146+
}
147+
148+
/**
149+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
150+
* @expectedException \Exception
151+
* @expectedExceptionMessage Specify the "newPassword" value.
152+
*/
153+
public function testChangePasswordIfNewPasswordIsEmpty()
154+
{
155+
$customerEmail = 'customer@example.com';
156+
$currentPassword = 'password';
157+
$incorrectNewPassword = '';
107158

108-
$query = $this->getChangePassQuery($incorrectPassword, $newCustomerPassword);
159+
$query = $this->getQuery($currentPassword, $incorrectNewPassword);
109160

110-
$headerMap = $this->getCustomerAuthHeaders($customerEmail, $oldCustomerPassword);
161+
$headerMap = $this->getCustomerAuthHeaders($customerEmail, $currentPassword);
111162
$this->graphQlMutation($query, [], '', $headerMap);
112163
}
113164

114-
private function getChangePassQuery($currentPassword, $newPassword)
165+
/**
166+
* @magentoApiDataFixture Magento/GraphQl/Customer/_files/enable_customer_account_confirmation.php
167+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
168+
* @expectedException \Exception
169+
* @expectedExceptionMessage This account isn't confirmed. Verify and try again.
170+
*/
171+
public function testChangePasswordIfAccountIsNotConfirmed()
172+
{
173+
$customerEmail = 'customer@example.com';
174+
$currentPassword = 'password';
175+
$newPassword = 'anotherPassword1';
176+
177+
/* get header map before setting the customer unconfirmed */
178+
$headerMap = $this->getCustomerAuthHeaders($customerEmail, $currentPassword);
179+
180+
$this->setCustomerConfirmation(1);
181+
$query = $this->getQuery($currentPassword, $newPassword);
182+
183+
$this->graphQlMutation($query, [], '', $headerMap);
184+
}
185+
186+
/**
187+
* @magentoApiDataFixture Magento/Customer/_files/customer.php
188+
* @expectedException \Exception
189+
* @expectedExceptionMessage The account is locked.
190+
*/
191+
public function testChangePasswordIfCustomerIsLocked()
192+
{
193+
$customerEmail = 'customer@example.com';
194+
$currentPassword = 'password';
195+
$newPassword = 'anotherPassword1';
196+
197+
$this->lockCustomer(1);
198+
$query = $this->getQuery($currentPassword, $newPassword);
199+
200+
$headerMap = $this->getCustomerAuthHeaders($customerEmail, $currentPassword);
201+
$this->graphQlMutation($query, [], '', $headerMap);
202+
}
203+
204+
/**
205+
* @param int $customerId
206+
*
207+
* @return void
208+
* @throws NoSuchEntityException
209+
*/
210+
private function lockCustomer(int $customerId): void
211+
{
212+
$customerSecure = $this->customerRegistry->retrieveSecureData($customerId);
213+
$customerSecure->setLockExpires('2030-12-31 00:00:00');
214+
$this->customerAuthUpdate->saveAuth($customerId);
215+
}
216+
217+
/**
218+
* @param int $customerId
219+
*
220+
* @return void
221+
* @throws LocalizedException
222+
*/
223+
private function setCustomerConfirmation(int $customerId): void
224+
{
225+
$customer = $this->customerRepository->getById($customerId);
226+
$customer->setConfirmation('d5a21f15bd4cc21bd1b21ef6d9989a38');
227+
$this->customerRepository->save($customer);
228+
}
229+
230+
/**
231+
* @param $currentPassword
232+
* @param $newPassword
233+
*
234+
* @return string
235+
*/
236+
private function getQuery($currentPassword, $newPassword)
115237
{
116238
$query = <<<QUERY
117239
mutation {
@@ -133,7 +255,9 @@ private function getChangePassQuery($currentPassword, $newPassword)
133255
/**
134256
* @param string $email
135257
* @param string $password
258+
*
136259
* @return array
260+
* @throws AuthenticationException
137261
*/
138262
private function getCustomerAuthHeaders(string $email, string $password): array
139263
{

0 commit comments

Comments
 (0)