Skip to content

Commit

Permalink
Merge branch '2.4-develop' of https://github.com/mage-os/mirror-magento2
Browse files Browse the repository at this point in the history
 into 2.4-develop
  • Loading branch information
mage-os-ci committed Apr 30, 2024
2 parents 9e3553d + 995a05c commit ad8ae83
Show file tree
Hide file tree
Showing 29 changed files with 711 additions and 153 deletions.
3 changes: 2 additions & 1 deletion app/code/Magento/Catalog/Test/Fixture/Product.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ class Product implements RevertibleDataFixtureInterface
'visibility' => Visibility::VISIBILITY_BOTH,
'status' => Status::STATUS_ENABLED,
'custom_attributes' => [
'tax_class_id' => '2'
'tax_class_id' => '2',
'special_price' => null,
],
'extension_attributes' => [
'website_ids' => [1],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
namespace Magento\CatalogInventoryGraphQl\Model\Resolver;

use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\CatalogInventory\Api\StockRegistryInterface;
use Magento\CatalogInventory\Model\Configuration;
use Magento\Framework\App\Config\ScopeConfigInterface;
Expand All @@ -23,25 +24,20 @@
class OnlyXLeftInStockResolver implements ResolverInterface
{
/**
* @var ScopeConfigInterface
* Configurable product type code
*/
private $scopeConfig;

/**
* @var StockRegistryInterface
*/
private $stockRegistry;
private const PRODUCT_TYPE_CONFIGURABLE = "configurable";

/**
* @param ScopeConfigInterface $scopeConfig
* @param StockRegistryInterface $stockRegistry
* @param ProductRepositoryInterface $productRepositoryInterface
*/
public function __construct(
ScopeConfigInterface $scopeConfig,
StockRegistryInterface $stockRegistry
private readonly ScopeConfigInterface $scopeConfig,
private readonly StockRegistryInterface $stockRegistry,
private readonly ProductRepositoryInterface $productRepositoryInterface
) {
$this->scopeConfig = $scopeConfig;
$this->stockRegistry = $stockRegistry;
}

/**
Expand All @@ -53,11 +49,12 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value
throw new LocalizedException(__('"model" value should be specified'));
}

/* @var $product ProductInterface */
$product = $value['model'];
$onlyXLeftQty = $this->getOnlyXLeftQty($product);

return $onlyXLeftQty;
if ($product->getTypeId() === self::PRODUCT_TYPE_CONFIGURABLE) {
$variant = $this->productRepositoryInterface->get($product->getSku());
return $this->getOnlyXLeftQty($variant);
}
return $this->getOnlyXLeftQty($product);
}

/**
Expand All @@ -73,7 +70,7 @@ private function getOnlyXLeftQty(ProductInterface $product): ?float
Configuration::XML_PATH_STOCK_THRESHOLD_QTY,
ScopeInterface::SCOPE_STORE
);
if ($thresholdQty === 0) {
if ($thresholdQty === 0.0) {
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,11 @@ protected function setUp(): void
$this->stockStatusMock = $this->getMockBuilder(StockStatusInterface::class)->getMock();
$this->productModelMock->expects($this->any())->method('getId')
->willReturn(1);
$this->productModelMock->expects($this->once())->method('getStore')
$this->productModelMock->expects($this->atMost(1))->method('getStore')
->willReturn($this->storeMock);
$this->stockRegistryMock->expects($this->once())->method('getStockStatus')
$this->stockRegistryMock->expects($this->atMost(1))->method('getStockStatus')
->willReturn($this->stockStatusMock);
$this->storeMock->expects($this->once())->method('getWebsiteId')->willReturn(1);
$this->storeMock->expects($this->atMost(1))->method('getWebsiteId')->willReturn(1);

$this->resolver = $this->objectManager->getObject(
OnlyXLeftInStockResolver::class,
Expand Down Expand Up @@ -181,15 +181,10 @@ public function testResolveOutStock()

public function testResolveNoThresholdQty()
{
$stockCurrentQty = 3;
$minQty = 2;
$thresholdQty = null;
$this->stockItemMock->expects($this->once())->method('getMinQty')
->willReturn($minQty);
$this->stockStatusMock->expects($this->once())->method('getQty')
->willReturn($stockCurrentQty);
$this->stockRegistryMock->expects($this->once())->method('getStockItem')
->willReturn($this->stockItemMock);
$this->stockItemMock->expects($this->never())->method('getMinQty');
$this->stockStatusMock->expects($this->never())->method('getQty');
$this->stockRegistryMock->expects($this->never())->method('getStockItem');
$this->scopeConfigMock->method('getValue')->willReturn($thresholdQty);

$this->assertEquals(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# See COPYING.txt for license details.

interface ProductInterface {
only_x_left_in_stock: Float @doc(description: "The value assigned to the Only X Left Threshold option in the Admin.") @resolver(class: "Magento\\CatalogInventoryGraphQl\\Model\\Resolver\\OnlyXLeftInStockResolver")
only_x_left_in_stock: Float @doc(description: "Remaining stock if it is below the value assigned to the Only X Left Threshold option in the Admin.") @resolver(class: "Magento\\CatalogInventoryGraphQl\\Model\\Resolver\\OnlyXLeftInStockResolver")
stock_status: ProductStockStatus @doc(description: "The stock status of the product.") @resolver(class: "Magento\\CatalogInventoryGraphQl\\Model\\Resolver\\StockStatusProvider")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,18 @@ public function execute(string $email, string $password): CustomerInterface
if ($this->authentication->isLocked($customerId)) {
throw new UserLockedException(__('The account is locked.'));
}
try {
$this->authentication->authenticate($customerId, $password);
} catch (InvalidEmailOrPasswordException $exception) {
throw new InvalidEmailOrPasswordException(__('Invalid login or password.'));
}

if ($customer->getConfirmation()
&& ($this->isConfirmationRequired($customer) || $this->isEmailChangedConfirmationRequired($customer))) {
throw new EmailNotConfirmedException(__('This account isn\'t confirmed. Verify and try again.'));
}

try {
$this->authentication->authenticate($customerId, $password);
} catch (InvalidEmailOrPasswordException $exception) {
throw new InvalidEmailOrPasswordException(__('Invalid login or password.'));
}

$customerModel = $this->customerFactory->create()->updateData($customer);
$this->eventManager->dispatch(
'customer_customer_authenticated',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<!--@subject {{trans "Please confirm your %store_name account" store_name=$store.frontend_name}} @-->
<!--@vars {
"var store.frontend_name":"Store Name",
"var this.getUrl($store,'customer/account/confirm/',[_query:[id:$customer.id,key:$customer.confirmation,back_url:$back_url],_nosid:1])":"Account Confirmation URL",
"var this.getUrl($store,'customer/account/confirm/',[_query:[id:$customer.id,key:$customer.confirmation,back_url:$back_url,email:$customer.email],_nosid:1])":"Account Confirmation URL",
"var this.getUrl($store, 'customer/account/')":"Customer Account URL",
"var customer.email":"Customer Email",
"var customer.name":"Customer Name"
Expand All @@ -24,7 +24,7 @@
<table class="inner-wrapper" border="0" cellspacing="0" cellpadding="0" align="center">
<tr>
<td align="center">
<a href="{{var this.getUrl($store,'customer/account/confirm/',[_query:[id:$customer.id,key:$customer.confirmation,back_url:$back_url],_nosid:1])}}" target="_blank">{{trans "Confirm Your Account"}}</a>
<a href="{{var this.getUrl($store,'customer/account/confirm/',[_query:[id:$customer.id,key:$customer.confirmation,back_url:$back_url,email:$customer.email],_nosid:1])}}" target="_blank">{{trans "Confirm Your Account"}}</a>
</td>
</tr>
</table>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<!--@vars {
"var store.frontend_name":"Store Name",
"var this.getUrl($store, 'customer/account/')":"Customer Account URL",
"var this.getUrl($store,'customer/account/createPassword',[_query:[id:$customer.id,token:$customer.rp_token],_nosid:1])":"Password Reset URL",
"var this.getUrl($store,'customer/account/createPassword',[_query:[id:$customer.id,token:$customer.rp_token,email:$customer.email],_nosid:1])":"Password Reset URL",
"var customer.name":"Customer Name"
} @-->
{{template config_path="design/email/header_template"}}
Expand All @@ -23,7 +23,7 @@
<table class="inner-wrapper" border="0" cellspacing="0" cellpadding="0" align="center">
<tr>
<td align="center">
<a href="{{var this.getUrl($store,'customer/account/createPassword',[_query:[id:$customer.id,token:$customer.rp_token],_nosid:1])}}" target="_blank">{{trans "Set a New Password"}}</a>
<a href="{{var this.getUrl($store,'customer/account/createPassword',[_query:[id:$customer.id,token:$customer.rp_token,email:$customer.email],_nosid:1])}}" target="_blank">{{trans "Set a New Password"}}</a>
</td>
</tr>
</table>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<!--@vars {
"var store.frontend_name":"Store Name",
"var customer.name":"Customer Name",
"var this.getUrl($store,'customer/account/createPassword/',[_query:[id:$customer.id,token:$customer.rp_token],_nosid:1])":"Reset Password URL"
"var this.getUrl($store,'customer/account/createPassword/',[_query:[id:$customer.id,token:$customer.rp_token,email:$customer.email],_nosid:1])":"Reset Password URL"
} @-->
{{template config_path="design/email/header_template"}}

Expand All @@ -22,7 +22,7 @@
<table class="inner-wrapper" border="0" cellspacing="0" cellpadding="0" align="center">
<tr>
<td align="center">
<a href="{{var this.getUrl($store,'customer/account/createPassword/',[_query:[id:$customer.id,token:$customer.rp_token],_nosid:1])}}" target="_blank">{{trans "Set a New Password"}}</a>
<a href="{{var this.getUrl($store,'customer/account/createPassword/',[_query:[id:$customer.id,token:$customer.rp_token,email:$customer.email],_nosid:1])}}" target="_blank">{{trans "Set a New Password"}}</a>
</td>
</tr>
</table>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
namespace Magento\CustomerGraphQl\Model\Resolver;

use Magento\Framework\Exception\AuthenticationException;
use Magento\Framework\Exception\EmailNotConfirmedException;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlAuthenticationException;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
Expand Down Expand Up @@ -55,8 +56,8 @@ public function resolve(
try {
$token = $this->customerTokenService->createCustomerAccessToken($args['email'], $args['password']);
return ['token' => $token];
} catch (AuthenticationException $e) {
throw new GraphQlAuthenticationException(__($e->getMessage()), $e);
} catch (EmailNotConfirmedException|AuthenticationException $e) {
throw new GraphQlAuthenticationException(__($e->getRawMessage()), $e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php
/**
* Copyright 2024 Adobe
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe and its suppliers, if any. The intellectual
* and technical concepts contained herein are proprietary to Adobe
* and its suppliers and are protected by all applicable intellectual
* property laws, including trade secret and copyright laws.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained from
* Adobe.
*/
declare(strict_types=1);

namespace Magento\CustomerGraphQl\Model\Resolver;

use Magento\Customer\Api\AccountManagementInterface;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Exception\State\InvalidTransitionException;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Query\Resolver\ContextInterface;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\Validator\EmailAddress as EmailValidator;

/**
* Customer resend confirmation email, used for GraphQL request processing
*/
class ResendConfirmationEmail implements ResolverInterface
{
/**
* @param AccountManagementInterface $accountManagement
* @param EmailValidator $emailValidator
*/
public function __construct(
private readonly AccountManagementInterface $accountManagement,
private readonly EmailValidator $emailValidator,
) {
}

/**
* Resend confirmation customer email mutation
*
* @param Field $field
* @param ContextInterface $context
* @param ResolveInfo $info
* @param array|null $value
* @param array|null $args
* @return bool
* @throws \Exception
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function resolve(
Field $field,
$context,
ResolveInfo $info,
array $value = null,
array $args = null
) {
if (!$this->emailValidator->isValid($args['email'])) {
throw new GraphQlInputException(__('Email address is not valid'));
}
try {
$this->accountManagement->resendConfirmation($args['email']);
} catch (InvalidTransitionException $e) {
throw new GraphQlInputException(__($e->getRawMessage()));
} catch (NoSuchEntityException) {
throw new GraphQlInputException(__('There is no user registered with that email address.'));
} catch (\Exception) {
throw new GraphQlInputException(__('There was an error when sending the confirmation email'));
}
return true;
}
}

0 comments on commit ad8ae83

Please sign in to comment.