Uploaded image for project: 'PUBLIC - Liferay Commerce'
  1. PUBLIC - Liferay Commerce
  2. COMMERCE-6502

Specifications on POST /v1.0/products are duplicated when executing same JSON request

    Details

      Description

      Problem
      The problem is that when you run the POST /v1.0/products method on a specific product with the exact same JSON request body containing specifications, we will see duplicate specifications being created, instead of the same ones being updated.

      This mirrors the problem in LPP-40453, but LPP-40453 deals with PATCH ​/v1.0​/products​/by-externalReferenceCode​/{externalReferenceCode}, while this specific ticket deals with POST /v1.0​/products​

      Unfortunately, it seems that LPP-40453 wasn't working properly in the latest hotfix, but also it was still behaving this way in Master (branch seems to be broken in its own way at the moment)

      Business Priority
      This has a high business priority along with LPP-40453 as this poses a blocker to a go-live by our GS team in the Australia/NZ region.

      Steps to Reproduce
      1. Start up Liferay DXP 7.3 + hotfix-1023
      2. Create a new site with the Minium template called "Sapphire"
      3. Go to Commerce > PRODUCT MANAGEMENT > Specifications
      4. Add two specifications the name and the key should match the following strings:

      • tester
      • barcode

      5. Open a new window and go to Commerce > PRODUCT MANAGEMENT > Product
      6. Search for "calipers" and open the product with the external reference code MIN93021, and keep it on this page as we will be refreshing it later.
      7. Go to Commerce > STORE MANAGEMENT > Catalogs, and click on the "Sapphire" catalog
      8. Obtain the catalog ID and keep this handy (e.g. 40075)
      9. Go to Headless Commerce Admin Catalog: http://localhost:8080/o/api?endpoint=http://localhost:8080/o/headless-commerce-admin-catalog/v1.0/openapi.json
      10. In Headless Commerce Admin Catalog, drop down Product > POST /v1.0/products
      11. In the Request Body, add the following taking care to change the "catalogId" property to what you have found in step 8:

      {
          "active":true,
          "catalogId":40075,
          "configuration":{
              "allowBackOrder":true,
              "multipleOrderQuantity":0
          },
          "externalReferenceCode":"MIN93021",
          "name":{
              "additionalProp1":"BINH TEST"
          },
          "neverExpire":true,
          "productType":"simple",
          "shortDescription":{
              "additionalProp1":"BINH TEST"
          },
          "productSpecifications":[
              {
                  "specificationKey":"Tester",
                  "value":{
                      "en_US":"N"
                  }
              },
              {
                  "specificationKey":"Barcode",
                  "value":{
                      "en_US":"123"
                  }
              }
          ]
      }

      12. Check the Product page for calipers that we kept open in Step 6, and refresh the page
      13. Observe that the name has been changed to BINH TEST, and the Tester and Barcode specifications are added - keep this page open on another tab
      14. Go back to the Headless Commerce Admin Catalog, drop down Product > POST ​/v1.0​/products
      15. Execute the exact same JSON request body specified in step 11.
      16. Go back to the product page for Calipers again, and refresh the page

      Actual Result
      Tester and Barcode are duplicated. You will see "Tester" and "Barcode" twice after running the POST method twice.

      Expected Result
      After running the POST method twice, Tester and Barcode should be updated on the second try instead of being duplicated.

      Reproduced in

      • Master Commit: 9f375528c91b2c745e13b92a25b67de3595523a3
      • 7.1 GA3 + hotfix-1023

      Broken in

      • 7.3.x-private
        • UNEXPECTED ERROR occurs, and the Product details for MIN93021 is not accessible.
        • 2021-04-20 09:05:22.421 ERROR [http-nio-8080-exec-8][FrontendClayApplication:116] com.liferay.commerce.product.exception.NoSuchCPSpecificationOptionException: No CPSpecificationOption exists with the primary key 0
          com.liferay.commerce.product.exception.NoSuchCPSpecificationOptionException: No CPSpecificationOption exists with the primary key 0
          	at com.liferay.commerce.product.service.persistence.impl.CPSpecificationOptionPersistenceImpl.findByPrimaryKey(CPSpecificationOptionPersistenceImpl.java:4498)
          	at com.liferay.commerce.product.service.persistence.impl.CPSpecificationOptionPersistenceImpl.findByPrimaryKey(CPSpecificationOptionPersistenceImpl.java:4516)
          	at com.liferay.commerce.product.service.base.CPSpecificationOptionLocalServiceBaseImpl.getCPSpecificationOption(CPSpecificationOptionLocalServiceBaseImpl.java:313)
          	at sun.reflect.GeneratedMethodAccessor680.invoke(Unknown Source)
          	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
          	at java.lang.reflect.Method.invoke(Method.java:498)
          	at com.liferay.portal.spring.aop.AopMethodInvocationImpl.proceed(AopMethodInvocationImpl.java:50)
          	at com.liferay.portal.spring.transaction.TransactionInterceptor.invoke(TransactionInterceptor.java:69)
          	at com.liferay.portal.spring.aop.AopMethodInvocationImpl.proceed(AopMethodInvocationImpl.java:57)
          	at com.liferay.change.tracking.internal.aop.CTTransactionAdvice.invoke(CTTransactionAdvice.java:80)
          	at com.liferay.portal.spring.aop.AopMethodInvocationImpl.proceed(AopMethodInvocationImpl.java:57)
          	at com.liferay.portal.spring.aop.AopInvocationHandler.invoke(AopInvocationHandler.java:49)
          	at com.sun.proxy.$Proxy1042.getCPSpecificationOption(Unknown Source)
          	at com.liferay.commerce.product.service.CPSpecificationOptionLocalServiceUtil.getCPSpecificationOption(CPSpecificationOptionLocalServiceUtil.java:279)
          	at com.liferay.commerce.product.model.impl.CPDefinitionSpecificationOptionValueImpl.getCPSpecificationOption(CPDefinitionSpecificationOptionValueImpl.java:59)
          	at com.liferay.commerce.product.definitions.web.internal.frontend.CommerceProductDefinitionSpecificationDataSetDataProvider.getItems(CommerceProductDefinitionSpecificationDataSetDataProvider.java:83)
          	at com.liferay.frontend.taglib.clay.internal.jaxrs.application.FrontendClayApplication.getClayDataSetData(FrontendClayApplication.java:104)
          	at sun.reflect.GeneratedMethodAccessor1195.invoke(Unknown Source)
          	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
          	at java.lang.reflect.Method.invoke(Method.java:498)
          	at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:179)
          	at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:96)
          	at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:201)
          	at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:104)
          	at org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:59)
          	at org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:96)
          	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
          	at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
          	at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:267)
          	at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:234)
          	at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:208)
          	at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:160)
          	at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:216)
          	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:301)
          	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doGet(AbstractHTTPServlet.java:225)
          	at javax.servlet.http.HttpServlet.service(HttpServlet.java:626)
          	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:276)
          	at org.eclipse.equinox.http.servlet.internal.registration.EndpointRegistration.service(EndpointRegistration.java:153)
          	at org.eclipse.equinox.http.servlet.internal.servlet.FilterChainImpl.doFilter(FilterChainImpl.java:50)
          	at com.liferay.portal.security.auth.verifier.internal.tracker.AuthVerifierFilterTracker$RemoteAccessFilter.doFilter(AuthVerifierFilterTracker.java:165)
          	at org.eclipse.equinox.http.servlet.internal.registration.FilterRegistration.doFilter(FilterRegistration.java:121)
          	at org.eclipse.equinox.http.servlet.internal.servlet.FilterChainImpl.doFilter(FilterChainImpl.java:45)
          	at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:147)
          	at com.liferay.portal.servlet.filters.authverifier.AuthVerifierFilter.processFilter(AuthVerifierFilter.java:196)
          	at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:49)
          	at org.eclipse.equinox.http.servlet.internal.registration.FilterRegistration.doFilter(FilterRegistration.java:121)
          	at org.eclipse.equinox.http.servlet.internal.servlet.FilterChainImpl.doFilter(FilterChainImpl.java:45)
          	at org.eclipse.equinox.http.servlet.internal.servlet.ResponseStateHandler.processRequest(ResponseStateHandler.java:70)
          	at org.eclipse.equinox.http.servlet.internal.context.DispatchTargets.doDispatch(DispatchTargets.java:120)
          	at org.eclipse.equinox.http.servlet.internal.HttpServiceRuntimeImpl.doDispatch(HttpServiceRuntimeImpl.java:372)
          	at org.eclipse.equinox.http.servlet.internal.servlet.ProxyServlet.service(ProxyServlet.java:70)
          	at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
          	at com.liferay.portal.module.framework.ModuleFrameworkServletAdapter.service(ModuleFrameworkServletAdapter.java:52)
          	at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
          	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
          	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
          	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
          	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
          	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:124)
          	at com.liferay.portal.servlet.filters.password.modified.PasswordModifiedFilter.processFilter(PasswordModifiedFilter.java:62)
          	at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:49)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDoFilter(InvokerFilterChain.java:215)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:116)
          	at com.liferay.portal.servlet.filters.lockout.LockoutFilter.processFilter(LockoutFilter.java:58)
          	at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:49)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDoFilter(InvokerFilterChain.java:215)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:116)
          	at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:147)
          	at com.liferay.portal.sharepoint.SharepointFilter.processFilter(SharepointFilter.java:88)
          	at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:49)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDoFilter(InvokerFilterChain.java:215)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:116)
          	at com.liferay.portal.kernel.servlet.BaseFilter.processFilter(BaseFilter.java:147)
          	at com.liferay.portal.servlet.filters.virtualhost.VirtualHostFilter.processFilter(VirtualHostFilter.java:248)
          	at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:49)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDoFilter(InvokerFilterChain.java:215)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:116)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDirectCallFilter(InvokerFilterChain.java:196)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:99)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDirectCallFilter(InvokerFilterChain.java:196)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:99)
          	at org.tuckey.web.filters.urlrewrite.RuleChain.handleRewrite(RuleChain.java:176)
          	at org.tuckey.web.filters.urlrewrite.RuleChain.doRules(RuleChain.java:145)
          	at org.tuckey.web.filters.urlrewrite.UrlRewriter.processRequest(UrlRewriter.java:92)
          	at org.tuckey.web.filters.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:389)
          	at com.liferay.portal.servlet.filters.urlrewrite.UrlRewriteFilter.processFilter(UrlRewriteFilter.java:65)
          	at com.liferay.portal.kernel.servlet.BaseFilter.doFilter(BaseFilter.java:49)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDoFilter(InvokerFilterChain.java:215)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:116)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDirectCallFilter(InvokerFilterChain.java:175)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:99)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDirectCallFilter(InvokerFilterChain.java:175)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:99)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.processDirectCallFilter(InvokerFilterChain.java:196)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterChain.doFilter(InvokerFilterChain.java:99)
          	at com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilter.doFilter(InvokerFilter.java:104)
          	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
          	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
          	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
          	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
          	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
          	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143)
          	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
          	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
          	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:346)
          	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374)
          	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
          	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:887)
          	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1684)
          	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
          	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
          	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
          	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
          	at java.lang.Thread.run(Thread.java:748)

        Attachments

        There are no Sub-Tasks for this issue.

          Activity

            People

            Assignee:
            pietro.bocale Pietro Bocale
            Reporter:
            piernicolo.pinna Pier Nicolò Pinna
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved:

                Packages

                Version Package
                7.4 CE GA2
                Master