Skip to content

Commit 47da0c5

Browse files
authored
LYNX-950: [AC-2.4.9] Align NegotiableQuoteBillingAddressInput with BillingAddressInput (#374)
1 parent 6cda09a commit 47da0c5

17 files changed

+1423
-128
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CustomerGraphQl\Model\Customer\Address;
9+
10+
use Magento\Customer\Api\AddressRepositoryInterface;
11+
use Magento\Customer\Api\Data\AddressInterface;
12+
use Magento\Framework\Exception\LocalizedException;
13+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
14+
15+
class DeleteCustomerAddressV2
16+
{
17+
/**
18+
* DeleteCustomerAddressV2 Constructor
19+
*
20+
* @param AddressRepositoryInterface $addressRepository
21+
*/
22+
public function __construct(
23+
private readonly AddressRepositoryInterface $addressRepository
24+
) {
25+
}
26+
27+
/**
28+
* Delete customer address
29+
*
30+
* @param AddressInterface $address
31+
* @return void
32+
* @throws GraphQlInputException
33+
*/
34+
public function execute(AddressInterface $address): void
35+
{
36+
if ($address->isDefaultBilling()) {
37+
throw new GraphQlInputException(
38+
__('Customer Address with the specified ID is set as default billing address and can not be deleted')
39+
);
40+
}
41+
42+
if ($address->isDefaultShipping()) {
43+
throw new GraphQlInputException(
44+
__('Customer Address with the specified ID is set as default shipping address and can not be deleted')
45+
);
46+
}
47+
48+
try {
49+
$this->addressRepository->delete($address);
50+
} catch (LocalizedException $e) {
51+
throw new GraphQlInputException(__($e->getMessage()), $e);
52+
}
53+
}
54+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CustomerGraphQl\Model\Customer\Address;
9+
10+
use Magento\Customer\Api\AddressRepositoryInterface;
11+
use Magento\Customer\Api\Data\AddressInterface;
12+
use Magento\Framework\Exception\LocalizedException;
13+
use Magento\Framework\Exception\NoSuchEntityException;
14+
use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException;
15+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
16+
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
17+
18+
class GetCustomerAddressV2
19+
{
20+
/**
21+
* GetCustomerAddressV2 Constructor
22+
*
23+
* @param AddressRepositoryInterface $addressRepository
24+
*/
25+
public function __construct(
26+
private readonly AddressRepositoryInterface $addressRepository
27+
) {
28+
}
29+
30+
/**
31+
* Get customer address
32+
*
33+
* @param int $addressId
34+
* @param int $customerId
35+
* @return AddressInterface
36+
* @throws GraphQlInputException
37+
* @throws GraphQlNoSuchEntityException
38+
* @throws GraphQlAuthorizationException
39+
*/
40+
public function execute(int $addressId, int $customerId): AddressInterface
41+
{
42+
try {
43+
$customerAddress = $this->addressRepository->getById($addressId);
44+
} catch (NoSuchEntityException $e) {
45+
throw new GraphQlNoSuchEntityException(
46+
__('Could not find an address with the specified ID')
47+
);
48+
} catch (LocalizedException $e) {
49+
throw new GraphQlInputException(__($e->getMessage()), $e);
50+
}
51+
52+
if ((int)$customerAddress->getCustomerId() !== $customerId) {
53+
throw new GraphQlAuthorizationException(
54+
__('Current customer does not have permission to get address with the specified ID')
55+
);
56+
}
57+
58+
return $customerAddress;
59+
}
60+
}

app/code/Magento/CustomerGraphQl/Model/Resolver/CustomerAddressUid.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,7 @@ public function resolve(
3434
ResolveInfo $info,
3535
?array $value = null,
3636
?array $args = null
37-
): string {
38-
if (!isset($value['id'])) {
39-
throw new LocalizedException(__('Missing required address ID.'));
40-
}
41-
42-
return $this->idEncoder->encode((string) $value['id']);
37+
): ?string {
38+
return isset($value['id']) ? $this->idEncoder->encode((string) $value['id']): null;
4339
}
4440
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CustomerGraphQl\Model\Resolver;
9+
10+
use Magento\CustomerGraphQl\Model\Customer\Address\DeleteCustomerAddressV2 as DeleteAddress;
11+
use Magento\CustomerGraphQl\Model\Customer\Address\GetCustomerAddressV2;
12+
use Magento\Framework\GraphQl\Config\Element\Field;
13+
use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException;
14+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
15+
use Magento\Framework\GraphQl\Query\ResolverInterface;
16+
use Magento\Framework\GraphQl\Query\Uid;
17+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
18+
use Magento\GraphQl\Model\Query\ContextInterface;
19+
20+
class DeleteCustomerAddressV2 implements ResolverInterface
21+
{
22+
/**
23+
* DeleteCustomerAddressV2 Constructor
24+
*
25+
* @param GetCustomerAddressV2 $getCustomerAddress
26+
* @param DeleteAddress $deleteCustomerAddress
27+
* @param Uid $uidEncoder
28+
*/
29+
public function __construct(
30+
private readonly GetCustomerAddressV2 $getCustomerAddress,
31+
private readonly DeleteAddress $deleteCustomerAddress,
32+
private readonly Uid $uidEncoder
33+
) {
34+
}
35+
36+
/**
37+
* @inheritdoc
38+
*/
39+
public function resolve(
40+
Field $field,
41+
$context,
42+
ResolveInfo $info,
43+
?array $value = null,
44+
?array $args = null
45+
): bool {
46+
/** @var ContextInterface $context */
47+
if (false === $context->getExtensionAttributes()->getIsCustomer()) {
48+
throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.'));
49+
}
50+
51+
if (empty($args['uid'])) {
52+
throw new GraphQlInputException(__('Address "uid" value must be specified'));
53+
}
54+
55+
$address = $this->getCustomerAddress->execute(
56+
(int) $this->uidEncoder->decode((string) $args['uid']),
57+
$context->getUserId()
58+
);
59+
60+
$this->deleteCustomerAddress->execute($address);
61+
62+
return true;
63+
}
64+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CustomerGraphQl\Model\Resolver;
9+
10+
use Magento\CustomerGraphQl\Model\Customer\Address\ExtractCustomerAddressData;
11+
use Magento\CustomerGraphQl\Model\Customer\Address\UpdateCustomerAddress;
12+
use Magento\CustomerGraphQl\Model\Customer\Address\GetCustomerAddressV2;
13+
use Magento\Framework\GraphQl\Config\Element\Field;
14+
use Magento\Framework\GraphQl\Exception\GraphQlAuthorizationException;
15+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
16+
use Magento\Framework\GraphQl\Query\ResolverInterface;
17+
use Magento\Framework\GraphQl\Query\Uid;
18+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
19+
use Magento\GraphQl\Model\Query\ContextInterface;
20+
21+
class UpdateCustomerAddressV2 implements ResolverInterface
22+
{
23+
/**
24+
* UpdateCustomerAddressV2 Constructor
25+
*
26+
* @param GetCustomerAddressV2 $getCustomerAddress
27+
* @param UpdateCustomerAddress $updateCustomerAddress
28+
* @param ExtractCustomerAddressData $extractCustomerAddressData
29+
* @param Uid $uidEncoder
30+
*/
31+
public function __construct(
32+
private readonly GetCustomerAddressV2 $getCustomerAddress,
33+
private readonly UpdateCustomerAddress $updateCustomerAddress,
34+
private readonly ExtractCustomerAddressData $extractCustomerAddressData,
35+
private readonly Uid $uidEncoder
36+
) {
37+
}
38+
39+
/**
40+
* @inheritdoc
41+
*/
42+
public function resolve(
43+
Field $field,
44+
$context,
45+
ResolveInfo $info,
46+
?array $value = null,
47+
?array $args = null
48+
): array {
49+
/** @var ContextInterface $context */
50+
if (false === $context->getExtensionAttributes()->getIsCustomer()) {
51+
throw new GraphQlAuthorizationException(__('The current customer isn\'t authorized.'));
52+
}
53+
54+
if (empty($args['uid'])) {
55+
throw new GraphQlInputException(__('Address "uid" value must be specified'));
56+
}
57+
58+
if (empty($args['input']) || !is_array($args['input'])) {
59+
throw new GraphQlInputException(__('"input" value must be specified'));
60+
}
61+
62+
$address = $this->getCustomerAddress->execute(
63+
(int) $this->uidEncoder->decode((string) $args['uid']),
64+
$context->getUserId()
65+
);
66+
67+
$this->updateCustomerAddress->execute($address, $args['input']);
68+
69+
return $this->extractCustomerAddressData->execute($address);
70+
}
71+
}

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,15 @@ type Mutation {
2727
deleteCustomer: Boolean @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\DeleteCustomer") @doc(description:"Delete customer account")
2828
revokeCustomerToken: RevokeCustomerTokenOutput @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\RevokeCustomerToken") @doc(description:"Revoke the customer token.")
2929
createCustomerAddress(input: CustomerAddressInput!): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\CreateCustomerAddress") @doc(description: "Create a billing or shipping address for a customer or guest.")
30-
updateCustomerAddress(id: Int! @doc(description: "The ID assigned to the customer address."), input: CustomerAddressInput @doc(description: "An input object that contains changes to the customer address.")): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\UpdateCustomerAddress") @doc(description: "Update the billing or shipping address of a customer or guest.")
31-
deleteCustomerAddress(id: Int! @doc(description: "The ID of the customer address to be deleted.")): Boolean @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\DeleteCustomerAddress") @doc(description: "Delete the billing or shipping address of a customer.")
30+
updateCustomerAddress(id: Int! @doc(description: "The ID assigned to the customer address."), input: CustomerAddressInput @doc(description: "An input object that contains changes to the customer address.")): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\UpdateCustomerAddress") @doc(description: "Update the billing or shipping address of a customer or guest.") @deprecated(reason:"Use `updateCustomerAddressV2` instead.")
31+
deleteCustomerAddress(id: Int! @doc(description: "The ID of the customer address to be deleted.")): Boolean @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\DeleteCustomerAddress") @doc(description: "Delete the billing or shipping address of a customer.") @deprecated(reason:"Use `deleteCustomerAddressV2` instead.")
3232
requestPasswordResetEmail(email: String! @doc(description: "The customer's email address.")): Boolean @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\RequestPasswordResetEmail") @doc(description: "Request an email with a reset password token for the registered customer identified by the specified email.")
3333
resetPassword(email: String! @doc(description: "The customer's email address."), resetPasswordToken: String! @doc(description: "A runtime token generated by the `requestPasswordResetEmail` mutation."), newPassword: String! @doc(description: "The customer's new password.")): Boolean @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\ResetPassword") @doc(description: "Reset a customer's password using the reset password token that the customer received in an email after requesting it using `requestPasswordResetEmail`.")
3434
updateCustomerEmail(email: String! @doc(description: "The customer's email address."), password: String! @doc(description: "The customer's password.")): CustomerOutput @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\UpdateCustomerEmail") @doc(description: "Change the email address for the logged-in customer.")
3535
confirmEmail(input: ConfirmEmailInput! @doc(description: "An input object to identify the customer to confirm the email.")): CustomerOutput @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\ConfirmEmail") @doc(description: "Confirms the email address for a customer.")
3636
resendConfirmationEmail(email: String! @doc(description: "The email address to send the confirmation email to.")): Boolean @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\ResendConfirmationEmail") @doc(description: "Resends the confirmation email to a customer.")
37+
updateCustomerAddressV2(uid: ID! @doc(description: "The unique ID of the customer address.") input: CustomerAddressInput @doc(description: "An input object that contains changes to the customer address.")): CustomerAddress @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\UpdateCustomerAddressV2") @doc(description: "Update the billing or shipping address of a customer or guest.")
38+
deleteCustomerAddressV2(uid: ID! @doc(description: "The unique ID of the customer address to be deleted.")): Boolean @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\DeleteCustomerAddressV2") @doc(description: "Delete the billing or shipping address of a customer.")
3739
}
3840

3941
input ConfirmEmailInput @doc(description: "Contains details about a customer email address to confirm.") {
@@ -165,8 +167,8 @@ type CustomerAddresses {
165167
}
166168

167169
type CustomerAddress @doc(description: "Contains detailed information about a customer's billing or shipping address."){
168-
id: Int @doc(description: "The ID of a `CustomerAddress` object.")
169-
uid: ID! @doc(description: "The unique ID for a `CustomerAddress` object.") @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\CustomerAddressUid")
170+
id: Int @doc(description: "The ID of a `CustomerAddress` object.") @deprecated(reason: "Use `uid` instead.")
171+
uid: ID @doc(description: "The unique ID for a `CustomerAddress` object.") @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\CustomerAddressUid")
170172
customer_id: Int @doc(description: "The customer ID") @deprecated(reason: "`customer_id` is not needed as part of `CustomerAddress`. The `id` is a unique identifier for the addresses.")
171173
region: CustomerAddressRegion @doc(description: "An object containing the region name, region code, and region ID.")
172174
region_id: Int @doc(description: "The unique ID for a pre-defined region.")

app/code/Magento/QuoteGraphQl/Model/Cart/ExtractQuoteAddressData.php

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,42 +16,22 @@
1616
use Magento\Quote\Model\Quote\Item;
1717

1818
/**
19-
* Extract address fields from an Quote Address model
19+
* Extract address fields from Quote Address model
2020
*/
2121
class ExtractQuoteAddressData
2222
{
2323
/**
24-
* @var ExtensibleDataObjectConverter
25-
*/
26-
private $dataObjectConverter;
27-
28-
/**
29-
* @param ExtensibleDataObjectConverter $dataObjectConverter
30-
*/
31-
32-
/**
33-
* @var Uid
34-
*/
35-
private Uid $uidEncoder;
36-
37-
/**
38-
* @var GetAttributeValueInterface
39-
*/
40-
private GetAttributeValueInterface $getAttributeValue;
41-
42-
/**
24+
* ExtractQuoteAddressData Constructor
25+
*
4326
* @param ExtensibleDataObjectConverter $dataObjectConverter
4427
* @param Uid $uidEncoder
4528
* @param GetAttributeValueInterface $getAttributeValue
4629
*/
4730
public function __construct(
48-
ExtensibleDataObjectConverter $dataObjectConverter,
49-
Uid $uidEncoder,
50-
GetAttributeValueInterface $getAttributeValue
31+
private readonly ExtensibleDataObjectConverter $dataObjectConverter,
32+
private readonly Uid $uidEncoder,
33+
private readonly GetAttributeValueInterface $getAttributeValue
5134
) {
52-
$this->dataObjectConverter = $dataObjectConverter;
53-
$this->uidEncoder = $uidEncoder;
54-
$this->getAttributeValue = $getAttributeValue;
5535
}
5636

5737
/**
@@ -64,6 +44,9 @@ public function execute(QuoteAddress $address): array
6444
{
6545
$addressData = $this->dataObjectConverter->toFlatArray($address, [], AddressInterface::class);
6646
$addressData['model'] = $address;
47+
$customerAddressId = $address->getCustomerAddressId() ?? null;
48+
$customerAddressUID = $customerAddressId ?
49+
$this->uidEncoder->encode((string)$address->getCustomerAddressId()) : null;
6750

6851
$addressData = array_merge(
6952
$addressData,
@@ -78,7 +61,8 @@ public function execute(QuoteAddress $address): array
7861
'region_id'=> $address->getRegionId()
7962
],
8063
'uid' => $this->uidEncoder->encode((string)$address->getAddressId()) ,
81-
'id' => $address->getCustomerAddressId(),
64+
'id' => $customerAddressId,
65+
'customer_address_uid' => $customerAddressUID,
8266
'street' => $address->getStreet(),
8367
'items_weight' => $address->getWeight(),
8468
'customer_notes' => $address->getCustomerNotes(),

0 commit comments

Comments
 (0)