Skip to content

Commit a771bd8

Browse files
authored
LYNX-120: Add custom_attributes to createCustomerV2 mutation (#99)
* LYNX-120: Initial commit * LYNX-120: Create custom attributes in createCustomerV2 mutation + test * LYNX-120: fix test * LYNX-120: fix test * LYNX-120: Final implementation + tests * LYNX-120: Refactoring * LYNX-120: Refactoring * LYNX-120: Refactoring * LYNX-120: Refactoring * LYNX-120: Refactoring; CR changes * LYNX-120: Refactoring; CR changes * LYNX-120: Refactoring; CR changes * LYNX-120: Refactoring; CR changes * LYNX-120: CR changes * LYNX-120: CR changes * LYNX-120: CR changes * LYNX-120: CR changes * LYNX-120: Fix broken tests
1 parent 0e8655d commit a771bd8

File tree

5 files changed

+231
-5
lines changed

5 files changed

+231
-5
lines changed

app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
* Copyright © Magento, Inc. All rights reserved.
44
* See COPYING.txt for license details.
55
*/
6-
76
namespace Magento\Customer\Model\ResourceModel;
87

98
use Magento\Customer\Api\CustomerMetadataInterface;
@@ -407,8 +406,8 @@ public function getById($customerId)
407406
* Retrieve customers which match a specified criteria.
408407
*
409408
* This call returns an array of objects, but detailed information about each object’s attributes might not be
410-
* included. See https://developer.adobe.com/commerce/webapi/rest/attributes#CustomerRepositoryInterface to determine
411-
* which call to use to get detailed information about all attributes for an object.
409+
* included. See https://developer.adobe.com/commerce/webapi/rest/attributes#CustomerRepositoryInterface
410+
* to determine which call to use to get detailed information about all attributes for an object.
412411
*
413412
* @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
414413
* @return \Magento\Customer\Api\Data\CustomerSearchResultsInterface
@@ -540,6 +539,14 @@ private function prepareCustomerData(array $customerData): array
540539
{
541540
if (isset($customerData[CustomerInterface::CUSTOM_ATTRIBUTES])) {
542541
foreach ($customerData[CustomerInterface::CUSTOM_ATTRIBUTES] as $attribute) {
542+
if (empty($attribute['value'])
543+
&& !empty($attribute['selected_options'])
544+
&& is_array($attribute['selected_options'])
545+
) {
546+
$attribute['value'] = implode(',', array_map(function ($option): string {
547+
return $option['value'] ?? '';
548+
}, $attribute['selected_options']));
549+
}
543550
$customerData[$attribute['attribute_code']] = $attribute['value'];
544551
}
545552
unset($customerData[CustomerInterface::CUSTOM_ATTRIBUTES]);

app/code/Magento/CustomerGraphQl/Model/Customer/CreateCustomerAccount.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ private function createAccount(array $data, StoreInterface $store): CustomerInte
122122
$customerDataObject,
123123
CustomerInterface::class
124124
);
125+
125126
$data = array_merge($requiredDataAttributes, $data);
126127
$this->validateCustomerData->execute($data);
127128
$this->dataObjectHelper->populateWithArray(

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ input CustomerAddressInput @doc(description: "Contains details about a billing o
4949
prefix: String @doc(description: "An honorific, such as Dr., Mr., or Mrs.")
5050
suffix: String @doc(description: "A value such as Sr., Jr., or III.")
5151
vat_id: String @doc(description: "The customer's Tax/VAT number (for corporate customers).")
52-
custom_attributes: [CustomerAddressAttributeInput] @doc(description: "Deprecated: Custom attributes should not be put into container.")
52+
custom_attributes: [AttributeValueInput] @doc(description: "Deprecated: Custom attributes should not be put into container.")
5353
}
5454

5555
input CustomerAddressRegionInput @doc(description: "Defines the customer's state or province.") {
@@ -95,6 +95,7 @@ input CustomerCreateInput @doc(description: "An input object for creating a cus
9595
gender: Int @doc(description: "The customer's gender (Male - 1, Female - 2).")
9696
password: String @doc(description: "The customer's password.")
9797
is_subscribed: Boolean @doc(description: "Indicates whether the customer is subscribed to the company's newsletter.")
98+
custom_attributes: [AttributeValueInput!] @doc(description: "The customer's custom attributes.")
9899
}
99100

100101
input CustomerUpdateInput @doc(description: "An input object for updating a customer.") {
@@ -136,7 +137,7 @@ type Customer @doc(description: "Defines the customer name, addresses, and other
136137
is_subscribed: Boolean @doc(description: "Indicates whether the customer is subscribed to the company's newsletter.") @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\IsSubscribed")
137138
addresses: [CustomerAddress] @doc(description: "An array containing the customer's shipping and billing addresses.") @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\CustomerAddresses")
138139
gender: Int @doc(description: "The customer's gender (Male - 1, Female - 2).")
139-
custom_attributes: [AttributeValueInterface!]! @doc(description: "Customer's custom attributes.")
140+
custom_attributes: [AttributeValueInterface] @doc(description: "Customer's custom attributes.")
140141
}
141142

142143
type CustomerAddress @doc(description: "Contains detailed information about a customer's billing or shipping address."){

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ type StorefrontProperties @doc(description: "Indicates where an attribute can be
3434
use_in_search_results_layered_navigation: Boolean @doc(description: "Indicates whether the attribute can be used in layered navigation on search results pages.")
3535
}
3636

37+
input AttributeValueInput @doc(description: "Specifies the value for attribute.") {
38+
attribute_code: String! @doc(description: "The code of the attribute.")
39+
value: String @doc(description: "The value which should be set for the attribute")
40+
selected_options: [AttributeInputSelectedOption!] @doc(description: "An array with selected option(s) for select or multiselect attribute")
41+
}
42+
43+
input AttributeInputSelectedOption @doc(description: "Specifies selected option for dropdown or multiselect attribute value .") {
44+
value: String! @doc(description: "The attribute option value.")
45+
}
46+
3747
enum UseInLayeredNavigationOptions @doc(description: "Defines whether the attribute is filterable in layered navigation.") {
3848
NO
3949
FILTERABLE_WITH_RESULTS
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\GraphQl\Customer;
9+
10+
use Magento\TestFramework\Helper\Bootstrap;
11+
use Magento\Customer\Api\CustomerMetadataInterface;
12+
use Magento\TestFramework\Fixture\DataFixture;
13+
use Magento\Customer\Test\Fixture\CustomerAttribute;
14+
use Magento\TestFramework\TestCase\GraphQlAbstract;
15+
use Magento\Customer\Api\CustomerRepositoryInterface;
16+
use Magento\Framework\Registry;
17+
18+
/**
19+
* Tests for create customer (V2)
20+
*/
21+
class CreateCustomerV2WithCustomAttributesTest extends GraphQlAbstract
22+
{
23+
/**
24+
* @var Registry
25+
*/
26+
private $registry;
27+
28+
/**
29+
* @var CustomerRepositoryInterface
30+
*/
31+
private $customerRepository;
32+
33+
protected function setUp(): void
34+
{
35+
parent::setUp();
36+
37+
$this->registry = Bootstrap::getObjectManager()->get(Registry::class);
38+
$this->customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class);
39+
}
40+
41+
/**
42+
* @throws \Exception
43+
*/
44+
#[
45+
DataFixture(
46+
CustomerAttribute::class,
47+
[
48+
'entity_type_id' => CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER,
49+
'sort_order' => 1,
50+
'attribute_code' => 'custom_attribute_one',
51+
'attribute_set_id' => CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER,
52+
'attribute_group_id' => 1
53+
],
54+
'attribute1'
55+
),
56+
DataFixture(
57+
CustomerAttribute::class,
58+
[
59+
'entity_type_id' => CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER,
60+
'sort_order' => 2,
61+
'attribute_code' => 'custom_attribute_two',
62+
'attribute_set_id' => CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER,
63+
'attribute_group_id' => 1
64+
],
65+
'attribute2'
66+
)
67+
]
68+
public function testCreateCustomerAccountWithCustomAttributes()
69+
{
70+
$query = <<<QUERY
71+
mutation {
72+
createCustomerV2(
73+
input: {
74+
firstname: "Adam"
75+
lastname: "Smith"
76+
email: "adam@smith.com"
77+
password: "test123#"
78+
custom_attributes: [
79+
{
80+
attribute_code: "custom_attribute_one",
81+
value: "value_one"
82+
},
83+
{
84+
attribute_code: "custom_attribute_two",
85+
value: "value_two"
86+
}
87+
]
88+
}
89+
) {
90+
customer {
91+
firstname
92+
lastname
93+
email
94+
custom_attributes {
95+
uid
96+
code
97+
... on AttributeValue {
98+
value
99+
}
100+
}
101+
}
102+
}
103+
}
104+
QUERY;
105+
$response = $this->graphQlMutation($query);
106+
$this->assertEquals(
107+
[
108+
'createCustomerV2' =>
109+
[
110+
'customer' =>
111+
[
112+
'firstname' => 'Adam',
113+
'lastname' => 'Smith',
114+
'email' => 'adam@smith.com',
115+
'custom_attributes' =>
116+
[
117+
0 =>
118+
[
119+
'uid' => 'Y3VzdG9tZXIvY3VzdG9tX2F0dHJpYnV0ZV9vbmU=',
120+
'code' => 'custom_attribute_one',
121+
'value' => 'value_one',
122+
],
123+
1 =>
124+
[
125+
'uid' => 'Y3VzdG9tZXIvY3VzdG9tX2F0dHJpYnV0ZV90d28=',
126+
'code' => 'custom_attribute_two',
127+
'value' => 'value_two',
128+
],
129+
],
130+
],
131+
],
132+
],
133+
$response
134+
);
135+
}
136+
137+
public function testCreateCustomerAccountWithNonExistingCustomAttribute()
138+
{
139+
$query = <<<QUERY
140+
mutation {
141+
createCustomerV2(
142+
input: {
143+
firstname: "John"
144+
lastname: "Doe"
145+
email: "john@doe.com"
146+
password: "test123#"
147+
custom_attributes: [
148+
{
149+
attribute_code: "non_existing_custom_attribute",
150+
value: "void"
151+
}
152+
]
153+
}
154+
) {
155+
customer {
156+
firstname
157+
lastname
158+
email
159+
custom_attributes {
160+
uid
161+
code
162+
... on AttributeValue {
163+
value
164+
}
165+
}
166+
}
167+
}
168+
}
169+
QUERY;
170+
$response = $this->graphQlMutation($query);
171+
$this->assertEquals(
172+
[
173+
'createCustomerV2' =>
174+
[
175+
'customer' =>
176+
[
177+
'firstname' => 'John',
178+
'lastname' => 'Doe',
179+
'email' => 'john@doe.com',
180+
'custom_attributes' => []
181+
],
182+
],
183+
],
184+
$response
185+
);
186+
}
187+
188+
protected function tearDown(): void
189+
{
190+
$email1 = 'adam@smith.com';
191+
$email2 = 'john@doe.com';
192+
try {
193+
$customer1 = $this->customerRepository->get($email1);
194+
$customer2 = $this->customerRepository->get($email2);
195+
} catch (\Exception $exception) {
196+
return;
197+
}
198+
199+
$this->registry->unregister('isSecureArea');
200+
$this->registry->register('isSecureArea', true);
201+
$this->customerRepository->delete($customer1);
202+
$this->customerRepository->delete($customer2);
203+
$this->registry->unregister('isSecureArea');
204+
$this->registry->register('isSecureArea', false);
205+
parent::tearDown();
206+
}
207+
}

0 commit comments

Comments
 (0)