The error persists in 2.2.3.
We use Python to interface with Magento.
Our update call is:
requests.put('{}/rest/V1/customers/{}'.format(
api.endpoint, customer_id), headers=api.headers,
json=customer)
The customer-value passed onto the json argument is:
{'customer': {'store_id': 1, 'website_id': 1, 'group_id': 1, 'firstname': 'Anonymous', 'lastname': 'Anonymous', 'email': '[email protected]', 'taxvat': None, 'addresses': [{'firstname': 'Anonymous', 'lastname': 'Anonymous', 'telephone': '0123456789', 'company': 'Anonymous Inc.', 'street': ['Where the streets have no name 1'], 'postcode': '1000AA', 'city': 'Amsterdam', 'country_id': 'NL', 'default_billing': False, 'default_shipping': False}, {'firstname': 'Anonymous', 'lastname': 'Anonymous', 'telephone': '0123456789', 'company': 'Anonymous BV', 'street': ['Where the streets have no name 1'], 'postcode': '1000AA', 'city': 'Amsterdam', 'country_id': 'NL', 'default_billing': False, 'default_shipping': False}], 'custom_attributes': [{'attribute_code': 'external_reference', 'value': 10011}]}}
I have made some adjustments to the above to filter out sensitive data. Hopefully I didn't mess up the json data itself but our import worked on Magento 2.2.1.
JSON including the updated customer.
{'message': 'A customer with the same email already exists in an associated website.', 'trace': etc.
@ju5t, thank you for your report.
We were not able to reproduce this issue by following the steps you provided. Please provide more detailed steps to reproduce or try to reproduce this issue on a clean installation or latest release.
@magento-engcom-team this was tested thoroughly on clean installations. I've tested both the specified upgrade path from 2.2.1 to 2.2.2 as well as clean installations for 2.2.1, 2.2.2 and 2.2.3. A clean installation in our case comes down to composer.json having a minimum of:
"require": {
"magento/product-community-edition": "2.2.2",
"composer/composer": "@alpha"
},
No further plugins were installed.
I rewrote our API-call into a one-file example and created a gist of it that can be found here: https://gist.github.com/ju5t/b2822b0a6ce132adb4b5e0f896d46e6b. We use Python3.
Output on 2.2.1 (and expected output on other versions):
{'id': 1, 'group_id': 1, 'created_at': '2018-03-12 13:39:23', 'updated_at': '2018-03-12 13:39:23', 'created_in': 'Default Store View', 'email': '[email protected]', 'firstname': 'Anonymous', 'lastname': 'Anonymous', 'store_id': 1, 'website_id': 1, 'addresses': [{'id': 1, 'customer_id': 1, 'region': {'region_code': None, 'region': None, 'region_id': 0}, 'country_id': 'NL', 'street': ['Where the streets have no name 1'], 'company': 'Anonymous Inc.', 'telephone': '0123456789', 'postcode': '1000AA', 'city': 'Amsterdam', 'firstname': 'Anonymous', 'lastname': 'Anonymous'}, {'id': 2, 'customer_id': 1, 'region': {'region_code': None, 'region': None, 'region_id': 0}, 'country_id': 'NL', 'street': ['Where the streets have no name 1'], 'company': 'Anonymous BV', 'telephone': '0123456789', 'postcode': '1000AA', 'city': 'Amsterdam', 'firstname': 'Anonymous', 'lastname': 'Anonymous'}], 'disable_auto_group_change': 0}
{'id': 1, 'group_id': 1, 'created_at': '2018-03-12 13:39:23', 'updated_at': '2018-03-12 13:39:27', 'created_in': 'Default Store View', 'email': '[email protected]', 'firstname': 'Anonymous', 'lastname': 'Anonymous', 'store_id': 1, 'website_id': 1, 'addresses': [{'id': 3, 'customer_id': 1, 'region': {'region_code': None, 'region': None, 'region_id': 0}, 'country_id': 'NL', 'street': ['Where the streets have no name 1'], 'company': 'Anonymous Inc.', 'telephone': '0123456789', 'postcode': '1000AA', 'city': 'Amsterdam', 'firstname': 'Anonymous', 'lastname': 'Anonymous'}, {'id': 4, 'customer_id': 1, 'region': {'region_code': None, 'region': None, 'region_id': 0}, 'country_id': 'NL', 'street': ['Where the streets have no name 1'], 'company': 'Anonymous BV', 'telephone': '0123456789', 'postcode': '1000AA', 'city': 'Amsterdam', 'firstname': 'Anonymous', 'lastname': 'Anonymous'}], 'disable_auto_group_change': 0}
Output on 2.2.2 and 2.2.3:
{'id': 1, 'group_id': 1, 'created_at': '2018-03-12 13:51:38', 'updated_at': '2018-03-12 13:51:38', 'created_in': 'Default Store View', 'email': '[email protected]', 'firstname': 'Anonymous', 'lastname': 'Anonymous', 'store_id': 1, 'website_id': 1, 'addresses': [{'id': 1, 'customer_id': 1, 'region': {'region_code': None, 'region': None, 'region_id': 0}, 'country_id': 'NL', 'street': ['Where the streets have no name 1'], 'company': 'Anonymous Inc.', 'telephone': '0123456789', 'postcode': '1000AA', 'city': 'Amsterdam', 'firstname': 'Anonymous', 'lastname': 'Anonymous'}, {'id': 2, 'customer_id': 1, 'region': {'region_code': None, 'region': None, 'region_id': 0}, 'country_id': 'NL', 'street': ['Where the streets have no name 1'], 'company': 'Anonymous BV', 'telephone': '0123456789', 'postcode': '1000AA', 'city': 'Amsterdam', 'firstname': 'Anonymous', 'lastname': 'Anonymous'}], 'disable_auto_group_change': 0}
{'message': 'A customer with the same email already exists in an associated website.', 'trace': "#0 /var/www/html/vendor/magento/module-eav/Model/Entity/VersionControl/AbstractEntity.php(90): Magento\\Customer\\Model\\ResourceModel\\Customer->_beforeSave(Object(Magento\\Customer\\Model\\Customer\\Interceptor))\n#1 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(58): Magento\\Eav\\Model\\Entity\\VersionControl\\AbstractEntity->save(Object(Magento\\Customer\\Model\\Customer\\Interceptor))\n#2 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(138): Magento\\Customer\\Model\\ResourceModel\\Customer\\Interceptor->___callParent('save', Array)\n#3 /var/www/html/vendor/magento/framework/App/Cache/FlushCacheByTags.php(68): Magento\\Customer\\Model\\ResourceModel\\Customer\\Interceptor->Magento\\Framework\\Interception\\{closure}(Object(Magento\\Customer\\Model\\Customer\\Interceptor))\n#4 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(135): Magento\\Framework\\App\\Cache\\FlushCacheByTags->aroundSave(Object(Magento\\Customer\\Model\\ResourceModel\\Customer\\Interceptor), Object(Closure), Object(Magento\\Customer\\Model\\Customer\\Interceptor))\n#5 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(153): Magento\\Customer\\Model\\ResourceModel\\Customer\\Interceptor->Magento\\Framework\\Interception\\{closure}(Object(Magento\\Customer\\Model\\Customer\\Interceptor))\n#6 /var/www/html/generated/code/Magento/Customer/Model/ResourceModel/Customer/Interceptor.php(117): Magento\\Customer\\Model\\ResourceModel\\Customer\\Interceptor->___callPlugins('save', Array, NULL)\n#7 /var/www/html/vendor/magento/framework/Model/AbstractModel.php(647): Magento\\Customer\\Model\\ResourceModel\\Customer\\Interceptor->save(Object(Magento\\Customer\\Model\\Customer\\Interceptor))\n#8 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(58): Magento\\Framework\\Model\\AbstractModel->save()\n#9 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(138): Magento\\Customer\\Model\\Customer\\Interceptor->___callParent('save', Array)\n#10 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(153): Magento\\Customer\\Model\\Customer\\Interceptor->Magento\\Framework\\Interception\\{closure}()\n#11 /var/www/html/generated/code/Magento/Customer/Model/Customer/Interceptor.php(1118): Magento\\Customer\\Model\\Customer\\Interceptor->___callPlugins('save', Array, Array)\n#12 /var/www/html/vendor/magento/module-customer/Model/ResourceModel/CustomerRepository.php(215): Magento\\Customer\\Model\\Customer\\Interceptor->save()\n#13 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(58): Magento\\Customer\\Model\\ResourceModel\\CustomerRepository->save(Object(Magento\\Customer\\Model\\Data\\Customer), NULL)\n#14 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(138): Magento\\Customer\\Model\\ResourceModel\\CustomerRepository\\Interceptor->___callParent('save', Array)\n#15 /var/www/html/vendor/magento/module-customer/Model/Plugin/CustomerRepository/TransactionWrapper.php(44): Magento\\Customer\\Model\\ResourceModel\\CustomerRepository\\Interceptor->Magento\\Framework\\Interception\\{closure}(Object(Magento\\Customer\\Model\\Data\\Customer), NULL)\n#16 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(135): Magento\\Customer\\Model\\Plugin\\CustomerRepository\\TransactionWrapper->aroundSave(Object(Magento\\Customer\\Model\\ResourceModel\\CustomerRepository\\Interceptor), Object(Closure), Object(Magento\\Customer\\Model\\Data\\Customer), NULL)\n#17 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(153): Magento\\Customer\\Model\\ResourceModel\\CustomerRepository\\Interceptor->Magento\\Framework\\Interception\\{closure}(Object(Magento\\Customer\\Model\\Data\\Customer), NULL)\n#18 /var/www/html/generated/code/Magento/Customer/Model/ResourceModel/CustomerRepository/Interceptor.php(26): Magento\\Customer\\Model\\ResourceModel\\CustomerRepository\\Interceptor->___callPlugins('save', Array, Array)\n#19 [internal function]: Magento\\Customer\\Model\\ResourceModel\\CustomerRepository\\Interceptor->save(Object(Magento\\Customer\\Model\\Data\\Customer), NULL)\n#20 /var/www/html/vendor/magento/module-webapi/Controller/Rest.php(330): call_user_func_array(Array, Array)\n#21 /var/www/html/vendor/magento/module-webapi/Controller/Rest.php(239): Magento\\Webapi\\Controller\\Rest->processApiRequest()\n#22 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(58): Magento\\Webapi\\Controller\\Rest->dispatch(Object(Magento\\Framework\\App\\Request\\Http))\n#23 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(138): Magento\\Webapi\\Controller\\Rest\\Interceptor->___callParent('dispatch', Array)\n#24 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(153): Magento\\Webapi\\Controller\\Rest\\Interceptor->Magento\\Framework\\Interception\\{closure}(Object(Magento\\Framework\\App\\Request\\Http))\n#25 /var/www/html/generated/code/Magento/Webapi/Controller/Rest/Interceptor.php(39): Magento\\Webapi\\Controller\\Rest\\Interceptor->___callPlugins('dispatch', Array, Array)\n#26 /var/www/html/vendor/magento/framework/App/Http.php(135): Magento\\Webapi\\Controller\\Rest\\Interceptor->dispatch(Object(Magento\\Framework\\App\\Request\\Http))\n#27 /var/www/html/generated/code/Magento/Framework/App/Http/Interceptor.php(24): Magento\\Framework\\App\\Http->launch()\n#28 /var/www/html/vendor/magento/framework/App/Bootstrap.php(256): Magento\\Framework\\App\\Http\\Interceptor->launch()\n#29 /var/www/html/index.php(39): Magento\\Framework\\App\\Bootstrap->run(Object(Magento\\Framework\\App\\Http\\Interceptor))\n#30 {main}"}
One thing I did forgot to mention is that we're running PHP 7.0.23 in case it matters.
@ju5t , Unfortunately, I could not reproduce the issue as you described it. I tested in 2.1.x and 2.2.x latest release. Could you please confirm if I correctly doing:
{
"customer": {
"email": "[email protected]",
"firstname": "Test",
"lastname": "Customer",
"websiteId": 1
},
"password": "Abc123456"
}
.{
"customer": {
"id": {customer_id},
"email": "[email protected]",
"firstname": "Updatetest",
"lastname": "Testing",
"websiteId": 1
},
"password": "Abc123456"
}
Actual result: Customer successful updated.
I found the issue.
You're specifying id: {customer_id}
in your request body. This seems redundant (and was not required before 2.2.2) as the request uri already contains the customer id. I didn't find a reference to this in the documentation. The Swagger-documentation in fact doesn't mention this endpoint at all.
@ju5t, thank you for your report.
This seems to be correct Magento behavior. Please refer to the Community Forums or the Magento Stack Exchange site for advice or general discussion about this.
Otherwise you may submit Pull Request with the suggested changes.
@magento-engcom-team I don't think it's correct Magento behaviour. This was changed between versions and there doesn't seem to be any relevant documentation. At least not that I could find.
@magento-engcom-team it would be nice to find out why customer id became mandatory in the first place (is there some JSON schema maybe / why Swagger is not relevant?) and change documentation if it's really needed or change code behavior back if such change is not really necessary.
Hello @ju5t, thank you for your report.
We've acknowledged the issue and added to our backlog.
@orlangur, this bug introduced in https://github.com/magento/magento2/commit/0379ec65e08e2fb6e02739bc6149ecc4e6ab970b , by changing param name ":id" to ":customerId".
https://github.com/magento/magento2/blob/7621060f9c750de44f2229fcad837f57d62d780a/app/code/Magento/Customer/etc/webapi.xml#L131-L136
but, in CustomerInterface field entity id is "id", not "customerId". Therefore Webapi/Controller/Rest/ParamsOverrider cannot convert customerId param into body field id, so customer object has no "id" in CutomerRepositoy::save() and CustomerRepository tries to create new customer instead of update existing one.
Using the swagger interface we get the same issue. We cannot update a single attribute for a customer using the customer/customerId endpoint.
We must pass in the customer ID as well as email, name and first name.
This defeats the purpose of passing in the customer ID to the endpoint.
I asked this on SO no one has answered this yet...
https://magento.stackexchange.com/questions/258640/update-customer-data-with-rest-api
Good work mage2 team, you closed a PR (which had passed all tests) that fixed this issue because in the intervening year+ that you ignored this issue and fix more testing was added around this wrong API implementation and you can't be bothered to fix it 馃憥
@magento-engcom-team please explain why you have choose to ignore and close a PR?
Everybody is spending a lot of time on this (debugging, raising an issue, providing a solution) and you just drop the ball.
We have dropped Magento from our stack. I am still happy that we did. Changes like this to an API without documentation are unacceptable.
Hi @ju5t @engcom-Alfa ,
Seems like this issue was already fixed in https://github.com/magento/magento2/pull/28332. Could you confirm that?
@ihor-sviziev, sorry, I can't. As said we do not use Magento anymore.
Hi @engcom-Alfa could you confirm that issue isn鈥檛 reproducing anymore and we can close this PR?
Hi @ju5t .
Unfortunately, we are not able to reproduce this issue on fresh 2.4-develop.
Seems like this issue was already fixed in #28332.
Manual testing scenario:
{
"customer": {
"group_id": 1,
"email": "[email protected]",
"firstname": "test",
"lastname": "user",
"store_id": 1,
"website_id": 1
},
"password": "123123qQ"
}
{
"customer": {
"group_id": 1,
"email": "[email protected]",
"firstname": "test2",
"lastname": "user2",
"store_id": 1,
"website_id": 1
}
}
Actual Result: :heavy_check_mark: The customer was successfully updated
So, we have to close it.
Please feel free to comment, reopen or create new ticket according to the Issue reporting guidelines
if you are still facing this issue on the latest 2.4-develop
branch. Thank you for collaboration.
Finally.
Most helpful comment
Hello @ju5t, thank you for your report.
We've acknowledged the issue and added to our backlog.
@orlangur, this bug introduced in https://github.com/magento/magento2/commit/0379ec65e08e2fb6e02739bc6149ecc4e6ab970b , by changing param name ":id" to ":customerId".
https://github.com/magento/magento2/blob/7621060f9c750de44f2229fcad837f57d62d780a/app/code/Magento/Customer/etc/webapi.xml#L131-L136
https://github.com/magento/magento2/blob/50f974b0c8f3ada8d7a2c9b0f9924976dacd2088/app/code/Magento/Customer/etc/webapi.xml#L131-L136
but, in CustomerInterface field entity id is "id", not "customerId". Therefore Webapi/Controller/Rest/ParamsOverrider cannot convert customerId param into body field id, so customer object has no "id" in CutomerRepositoy::save() and CustomerRepository tries to create new customer instead of update existing one.