Skip to content

Commit 0ba9eef

Browse files
authored
Merge pull request magento#9311 from magento-lynx/2.4.8-graphql-api-enhancements
2 parents 651928c + 3b194c4 commit 0ba9eef

File tree

49 files changed

+3116
-607
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+3116
-607
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
<?php
2+
/**
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Customer\Test\Fixture;
9+
10+
use Magento\Customer\Api\AccountManagementInterface;
11+
use Magento\Customer\Api\CustomerRepositoryInterface;
12+
use Magento\Customer\Api\Data\AddressInterface;
13+
use Magento\Customer\Api\Data\CustomerInterface;
14+
use Magento\Customer\Model\CustomerRegistry;
15+
use Magento\Framework\DataObject;
16+
use Magento\Framework\Exception\NoSuchEntityException;
17+
use Magento\TestFramework\Fixture\Api\DataMerger;
18+
use Magento\TestFramework\Fixture\Api\ServiceFactory;
19+
use Magento\TestFramework\Fixture\Data\ProcessorInterface;
20+
use Magento\TestFramework\Fixture\RevertibleDataFixtureInterface;
21+
22+
/**
23+
* Data fixture for customer with multiple addresses
24+
*/
25+
class CustomerWithAddresses implements RevertibleDataFixtureInterface
26+
{
27+
private const DEFAULT_DATA_ADDRESS = [
28+
[
29+
AddressInterface::ID => null,
30+
AddressInterface::CUSTOMER_ID => null,
31+
AddressInterface::REGION => 'California',
32+
AddressInterface::REGION_ID => '12',
33+
AddressInterface::COUNTRY_ID => 'US',
34+
AddressInterface::STREET => ['%street_number% Test Street%uniqid%'],
35+
AddressInterface::COMPANY => null,
36+
AddressInterface::TELEPHONE => '1234567893',
37+
AddressInterface::FAX => null,
38+
AddressInterface::POSTCODE => '02108',
39+
AddressInterface::CITY => 'Boston',
40+
AddressInterface::FIRSTNAME => 'Firstname%uniqid%',
41+
AddressInterface::LASTNAME => 'Lastname%uniqid%',
42+
AddressInterface::MIDDLENAME => null,
43+
AddressInterface::PREFIX => null,
44+
AddressInterface::SUFFIX => null,
45+
AddressInterface::VAT_ID => null,
46+
AddressInterface::DEFAULT_BILLING => true,
47+
AddressInterface::DEFAULT_SHIPPING => true,
48+
AddressInterface::CUSTOM_ATTRIBUTES => [],
49+
AddressInterface::EXTENSION_ATTRIBUTES_KEY => [],
50+
],
51+
[
52+
AddressInterface::ID => null,
53+
AddressInterface::CUSTOMER_ID => null,
54+
AddressInterface::REGION => 'California',
55+
AddressInterface::REGION_ID => '12',
56+
AddressInterface::COUNTRY_ID => 'US',
57+
AddressInterface::STREET => ['%street_number% Sunset Boulevard%uniqid%'],
58+
AddressInterface::COMPANY => null,
59+
AddressInterface::TELEPHONE => '0987654321',
60+
AddressInterface::FAX => null,
61+
AddressInterface::POSTCODE => '90001',
62+
AddressInterface::CITY => 'Los Angeles',
63+
AddressInterface::FIRSTNAME => 'Firstname%uniqid%',
64+
AddressInterface::LASTNAME => 'Lastname%uniqid%',
65+
AddressInterface::MIDDLENAME => null,
66+
AddressInterface::PREFIX => null,
67+
AddressInterface::SUFFIX => null,
68+
AddressInterface::VAT_ID => null,
69+
AddressInterface::DEFAULT_BILLING => false,
70+
AddressInterface::DEFAULT_SHIPPING => false,
71+
AddressInterface::CUSTOM_ATTRIBUTES => [],
72+
AddressInterface::EXTENSION_ATTRIBUTES_KEY => [],
73+
],
74+
[
75+
AddressInterface::ID => null,
76+
AddressInterface::CUSTOMER_ID => null,
77+
AddressInterface::REGION => 'New York',
78+
AddressInterface::REGION_ID => '43',
79+
AddressInterface::COUNTRY_ID => 'US',
80+
AddressInterface::STREET => ['%street_number% 5th Avenue%uniqid%'],
81+
AddressInterface::COMPANY => null,
82+
AddressInterface::TELEPHONE => '1112223333',
83+
AddressInterface::FAX => null,
84+
AddressInterface::POSTCODE => '10001',
85+
AddressInterface::CITY => 'New York City',
86+
AddressInterface::FIRSTNAME => 'Firstname%uniqid%',
87+
AddressInterface::LASTNAME => 'Lastname%uniqid%',
88+
AddressInterface::MIDDLENAME => null,
89+
AddressInterface::PREFIX => null,
90+
AddressInterface::SUFFIX => null,
91+
AddressInterface::VAT_ID => null,
92+
AddressInterface::DEFAULT_BILLING => false,
93+
AddressInterface::DEFAULT_SHIPPING => false,
94+
AddressInterface::CUSTOM_ATTRIBUTES => [],
95+
AddressInterface::EXTENSION_ATTRIBUTES_KEY => [],
96+
]
97+
];
98+
99+
private const DEFAULT_DATA = [
100+
'password' => 'password',
101+
CustomerInterface::ID => null,
102+
CustomerInterface::CREATED_AT => null,
103+
CustomerInterface::CONFIRMATION => null,
104+
CustomerInterface::UPDATED_AT => null,
105+
CustomerInterface::DOB => null,
106+
CustomerInterface::CREATED_IN => null,
107+
CustomerInterface::EMAIL => 'customer%uniqid%@mail.com',
108+
CustomerInterface::FIRSTNAME => 'Firstname%uniqid%',
109+
CustomerInterface::GROUP_ID => null,
110+
CustomerInterface::GENDER => null,
111+
CustomerInterface::LASTNAME => 'Lastname%uniqid%',
112+
CustomerInterface::MIDDLENAME => null,
113+
CustomerInterface::PREFIX => null,
114+
CustomerInterface::SUFFIX => null,
115+
CustomerInterface::STORE_ID => null,
116+
CustomerInterface::TAXVAT => null,
117+
CustomerInterface::WEBSITE_ID => null,
118+
CustomerInterface::DEFAULT_SHIPPING => null,
119+
CustomerInterface::DEFAULT_BILLING => null,
120+
CustomerInterface::DISABLE_AUTO_GROUP_CHANGE => null,
121+
CustomerInterface::KEY_ADDRESSES => [],
122+
CustomerInterface::CUSTOM_ATTRIBUTES => [],
123+
CustomerInterface::EXTENSION_ATTRIBUTES_KEY => [],
124+
];
125+
126+
/**
127+
* CustomerWithAddresses Constructor
128+
*
129+
* @param ServiceFactory $serviceFactory
130+
* @param AccountManagementInterface $accountManagement
131+
* @param CustomerRegistry $customerRegistry
132+
* @param ProcessorInterface $dataProcessor
133+
* @param DataMerger $dataMerger
134+
*/
135+
public function __construct(
136+
private readonly ServiceFactory $serviceFactory,
137+
private readonly AccountManagementInterface $accountManagement,
138+
private readonly CustomerRegistry $customerRegistry,
139+
private readonly DataMerger $dataMerger,
140+
private readonly ProcessorInterface $dataProcessor
141+
) {
142+
}
143+
144+
/**
145+
* Apply the changes for the fixture
146+
*
147+
* @param array $data
148+
* @return DataObject|null
149+
* @throws NoSuchEntityException
150+
*/
151+
public function apply(array $data = []): ?DataObject
152+
{
153+
$customerSave = $this->serviceFactory->create(CustomerRepositoryInterface::class, 'save');
154+
$data = $this->prepareCustomerData($data);
155+
$passwordHash = $this->accountManagement->getPasswordHash($data['password']);
156+
unset($data['password']);
157+
158+
$customerSave->execute(
159+
[
160+
'customer' => $data,
161+
'passwordHash' => $passwordHash
162+
]
163+
);
164+
165+
return $this->customerRegistry->retrieveByEmail($data['email'], $data['website_id']);
166+
}
167+
168+
/**
169+
* Revert the test customer creation
170+
*
171+
* @param DataObject $data
172+
* @return void
173+
*/
174+
public function revert(DataObject $data): void
175+
{
176+
$data->setCustomerId($data->getId());
177+
$customerService = $this->serviceFactory->create(CustomerRepositoryInterface::class, 'deleteById');
178+
179+
$customerService->execute(
180+
[
181+
'customerId' => $data->getId()
182+
]
183+
);
184+
}
185+
186+
/**
187+
* Prepare customer's data
188+
*
189+
* @param array $data
190+
* @return array
191+
*/
192+
private function prepareCustomerData(array $data): array
193+
{
194+
$data = $this->dataMerger->merge(self::DEFAULT_DATA, $data);
195+
$data[CustomerInterface::KEY_ADDRESSES] = $this->prepareAddresses($data[CustomerInterface::KEY_ADDRESSES]);
196+
197+
return $this->dataProcessor->process($this, $data);
198+
}
199+
200+
/**
201+
* Prepare customer's addresses
202+
*
203+
* @param array $data
204+
* @return array
205+
*/
206+
private function prepareAddresses(array $data): array
207+
{
208+
$addressesData = [];
209+
$default = self::DEFAULT_DATA_ADDRESS;
210+
$streetNumber = 123;
211+
foreach ($default as $address) {
212+
if ($data) {
213+
$address = $this->dataMerger->merge($default, $address);
214+
}
215+
$placeholders = ['%street_number%' => $streetNumber++];
216+
$address[AddressInterface::STREET] = array_map(
217+
fn ($str) => strtr($str, $placeholders),
218+
$address[AddressInterface::STREET]
219+
);
220+
$addressesData[] = $address;
221+
}
222+
223+
return $addressesData;
224+
}
225+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
/**
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CustomerGraphQl\Model\Formatter;
9+
10+
use Magento\Customer\Api\Data\AddressSearchResultsInterface;
11+
use Magento\CustomerGraphQl\Model\Customer\Address\ExtractCustomerAddressData;
12+
13+
class CustomerAddresses
14+
{
15+
/**
16+
* CustomerAddresses Constructor
17+
*
18+
* @param ExtractCustomerAddressData $extractCustomerAddressData
19+
*/
20+
public function __construct(
21+
private readonly ExtractCustomerAddressData $extractCustomerAddressData,
22+
) {
23+
}
24+
25+
/**
26+
* Format customer addressesV2
27+
*
28+
* @param AddressSearchResultsInterface $searchResult
29+
* @return array
30+
*/
31+
public function format(AddressSearchResultsInterface $searchResult): array
32+
{
33+
$addressArray = [];
34+
foreach ($searchResult->getItems() as $address) {
35+
$addressArray[] = $this->extractCustomerAddressData->execute($address);
36+
}
37+
38+
return [
39+
'total_count' => $searchResult->getTotalCount(),
40+
'items' => $addressArray,
41+
'page_info' => [
42+
'page_size' => $searchResult->getSearchCriteria()->getPageSize(),
43+
'current_page' => $searchResult->getSearchCriteria()->getCurrentPage(),
44+
'total_pages' => (int)ceil($searchResult->getTotalCount()
45+
/ (int)$searchResult->getSearchCriteria()->getPageSize()),
46+
]
47+
];
48+
}
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
/**
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CustomerGraphQl\Model\Resolver;
9+
10+
use Magento\Customer\Api\AddressRepositoryInterface;
11+
use Magento\Customer\Model\Customer;
12+
use Magento\CustomerGraphQl\Model\ValidateAddressRequest;
13+
use Magento\Framework\Api\SearchCriteriaBuilder;
14+
use Magento\Framework\Exception\InputException;
15+
use Magento\Framework\GraphQl\Config\Element\Field;
16+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
17+
use Magento\Framework\GraphQl\Query\ResolverInterface;
18+
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
19+
use Magento\CustomerGraphQl\Model\Formatter\CustomerAddresses as AddressFormatter;
20+
21+
/**
22+
* Provides data for customer.addressesV2 with pagination
23+
*/
24+
class CustomerAddressesV2 implements ResolverInterface
25+
{
26+
/**
27+
* CustomerAddressesV2 Constructor
28+
*
29+
* @param AddressRepositoryInterface $addressRepository
30+
* @param SearchCriteriaBuilder $searchCriteriaBuilder
31+
* @param AddressFormatter $addressesFormatter
32+
* @param ValidateAddressRequest $validateAddressRequest
33+
*/
34+
public function __construct(
35+
private readonly AddressRepositoryInterface $addressRepository,
36+
private readonly SearchCriteriaBuilder $searchCriteriaBuilder,
37+
private readonly AddressFormatter $addressesFormatter,
38+
private readonly ValidateAddressRequest $validateAddressRequest
39+
) {
40+
}
41+
42+
/**
43+
* @inheritDoc
44+
*/
45+
public function resolve(
46+
Field $field,
47+
$context,
48+
ResolveInfo $info,
49+
array $value = null,
50+
array $args = null
51+
): array {
52+
$this->validateAddressRequest->execute($value, $args);
53+
54+
/** @var Customer $customer */
55+
$customer = $value['model'];
56+
57+
try {
58+
$this->searchCriteriaBuilder->addFilter('parent_id', (int)$customer->getId());
59+
$this->searchCriteriaBuilder->setCurrentPage($args['currentPage']);
60+
$this->searchCriteriaBuilder->setPageSize($args['pageSize']);
61+
$searchResult = $this->addressRepository->getList($this->searchCriteriaBuilder->create());
62+
} catch (InputException $e) {
63+
throw new GraphQlInputException(__($e->getMessage()));
64+
}
65+
66+
return $this->addressesFormatter->format($searchResult);
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
/**
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CustomerGraphQl\Model;
9+
10+
use Magento\Framework\Exception\LocalizedException;
11+
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
12+
13+
class ValidateAddressRequest
14+
{
15+
/**
16+
* Validate addresses Request
17+
*
18+
* @param array $value
19+
* @param array $args
20+
* @return void
21+
* @throws GraphQlInputException
22+
* @throws LocalizedException
23+
*/
24+
public function execute(array $value, array $args): void
25+
{
26+
if (!isset($value['model'])) {
27+
throw new LocalizedException(__('"model" value should be specified'));
28+
}
29+
30+
if ($args['pageSize'] < 1) {
31+
throw new GraphQlInputException(__('pageSize value must be greater than 0.'));
32+
}
33+
34+
if ($args['currentPage'] < 1) {
35+
throw new GraphQlInputException(__('currentPage value must be greater than 0.'));
36+
}
37+
}
38+
}

0 commit comments

Comments
 (0)