8 Replies Latest reply on Jun 21, 2013 7:04 AM by Guillermo James

    Duplicated records

    Guillermo James

      Hi. Currently, my RhoConnect Server is creating the same record multiple times, generating duplicates records in our backend system.  The client application is running in a MC55 device, and it is sending the create command for a single record multiple times to Rhoconnect server. Apparently, the device does not know that Rhoconnect already created the record. How does the device knows that Rhoconnect already created the record to do not send the create command for the same record again?

       

      Thanks in advance.

        • Re: Duplicated records
          Kutir Mobility

          Hi Guillermo,

           

          This doesn't looks like RhoConnect issue but a  programming issue. RhoConnect won't check for duplicate records, it will simply create records when it is asked to do so.

          Can you check why MC55 device sending the create command for a single record multiple times to Rhoconnect server ?

          If you can share the code snippet for the call made from Rhodes to RhoConnect, then I can try to dig more on this.\

           

          Thanks

          Surendran S

          Kutir Mobility

            • Re: Duplicated records
              Guillermo James

              Hi Surendran thanks for your answer. Here is the code that make the sync process with our source adapters.

               

              def do_sync     

                  SyncEngine.dosync(false, ":sync_changes => true")              

              end

               

              def sync_notify

                          status = @params['status'] ? @params['status'] : ""

                           

                          if status == "in_progress"

                            # do nothing            

                          elsif status == "complete"            

                    #WebView.navigate Rho::RhoConfig.start_path if @params['sync_type'] != 'bulk'

                    WebView.navigate Rho::RhoConfig.start_path

                          elsif status == "error"

                    if @params['server_errors'] && @params['server_errors']['create-error']

                      SyncEngine.on_sync_create_error(

                        @params['source_name'], @params['server_errors']['create-error'].keys, :recreate )

                    end

               

                    if @params['server_errors'] && @params['server_errors']['update-error']

                      SyncEngine.on_sync_update_error(

                        @params['source_name'], @params['server_errors']['update-error'], :retry )

                    end

                   

                    err_code = @params['error_code'].to_i

                    rho_error = Rho::RhoError.new(err_code)

                   

                    @msg = @params['error_message'] if err_code == Rho::RhoError::ERR_CUSTOMSYNCSERVER

                    @msg = rho_error.message unless @msg && @msg.length > 0  

               

                    if rho_error.unknown_client?( @params['error_message'] )

                      Rhom::Rhom.database_client_reset

                      SyncEngine.dosync(false, ":sync_changes => true")

                    elsif err_code == Rho::RhoError::ERR_UNATHORIZED

                      WebView.navigate(url_for(:action => :login, :query => {:msg => "Server credentials are expired"}))               

                    elsif err_code != Rho::RhoError::ERR_CUSTOMSYNCSERVER

                      WebView.navigate Rho::RhoConfig.start_path

                    end   

                           end

              end

               

              Thanks in advance.

              Guillermo.

                • Re: Duplicated records
                  Mark Nongkhlaw

                  That doesn't seem to be source adapter code to me. Rather, it looks like its controller code under app-Settings of the Rho app

                    • Re: Duplicated records
                      Guillermo James

                      Hi Mark. This is the code of the source adapter.

                       

                        def query(params=nil)

                            client = Savon.client(wsdl: @ws_wsdl, open_timeout: 60, read_timeout: 60)                     

                            body_msg = {"Centro_Servicio" => current_user.login.to_s}   

                            response = client.call(:get_buffer_data, message: body_msg)

                       

                            # Obtiene el "Buffer de Datos" desde la respuesta SOAP.

                            if response.success?

                              @temp = response.to_hash

                              @temp = @temp[:get_buffer_data_response][:get_buffer_data_result]

                               

                              if @temp == nil          

                                @result = {}

                              else         

                                # Genera el Hash de Hashes. 

                                parsed = JSON.parse(@temp)

                                if parsed         

                                    @result = {}

                                    parsed.each do |item|

                                        @result[item["buffer"]["id"].to_s] = item["buffer"]

                                    end                    

                                else   

                                    raise SourceAdapterException.new("JSON Parsing Error.")           

                                end

                              end  

                            else

                                raise SourceAdapterException.new("WS READ Response Error.")     

                            end

                        end

                       

                        def create(create_hash)   

                            xml_hash = "<buffer>"

                            xml_hash = xml_hash + "<user>" + current_user.login.to_s + "</user>"

                            xml_hash = xml_hash + "<concesionario>" + create_hash["concesionario_id"].to_s + "</concesionario>"

                            xml_hash = xml_hash + "<centro_servicio>" + create_hash["centroservicio_id"].to_s + "</centro_servicio>"

                            xml_hash = xml_hash + "<fecha>" + create_hash["fecha"].to_s + "</fecha>"    

                            xml_hash = xml_hash + "<operador>" + create_hash["operador_id"].to_s + "</operador>"  

                            xml_hash = xml_hash + "<ot>" + create_hash["ot"].to_s + "</ot>"

                            xml_hash = xml_hash + "<ppu>" + create_hash["ppu"].to_s + "</ppu>"

                            xml_hash = xml_hash + "<marca>" + create_hash["marca"].to_s + "</marca>"       

                            xml_hash = xml_hash + "<modelo>" + create_hash["modelo"].to_s + "</modelo>"

                            xml_hash = xml_hash + "<year>" + create_hash["year"].to_s + "</year>"   

                            xml_hash = xml_hash + "<servicios>" + create_hash["servicios_id"].to_s + "</servicios>"    

                            xml_hash = xml_hash + "<checkpoint>" + create_hash["checkpoint"].to_s + "</checkpoint>"

                            xml_hash = xml_hash + "</buffer>" 

                           

                            client = Savon.client(wsdl: @ws_wsdl, open_timeout: 60, read_timeout: 60)

                            response = client.call(:create_buffer_data, message: {"xml_hash" => xml_hash})

                       

                            if response.success? 

                              @temp = response.to_hash

                              @temp = @temp[:create_buffer_data_response][:create_buffer_data_result]

                           

                              if @temp != nil 

                                 @temp.to_s

                              else

                                 @temp = ""     

                              end

                            else

                              raise SourceAdapterException.new("WS CREATE Response Error.")

                            end   

                        end

                       

                      Any comments will be appreciated.

                       

                      Regards.

                        • Re: Duplicated records
                          Mark Nongkhlaw

                          Hi Guillermo, I have no idea regarding Savon or wsdl, but I do have some code using JSON and the Products adapter. Maybe it will help :

                           

                          require 'json'

                          require 'rest_client'

                           

                          class Product < SourceAdapter

                            def initialize(source)

                              @base = 'http://rhostore.herokuapp.com/products'

                              super(source)

                            end

                           

                            def login

                              # TODO: Login to your data source here if necessary

                              # MyWebService.login(current_user.login)

                            end

                           

                           

                            def query(params=nil)

                              # TODO: Query your backend data source and assign the records

                              # to a nested hash structure called @result. For example:

                              # @result = {

                              #   "1"=>{"name"=>"Acme", "industry"=>"Electronics"},

                              #   "2"=>{"name"=>"Best", "industry"=>"Software"}

                              # }

                              # raise SourceAdapterException.new("Please provide some code to read records from the backend data source")

                              rest_result = RestClient.get("#{@base}.json").body

                             

                                  if rest_result.code != 200

                                    raise SourceAdapterException.new("Error connecting!")

                                  end

                                  parsed = JSON.parse(rest_result)

                             

                                  @result={}

                                  parsed.each do |item|

                                    @result[item["product"]["id"].to_s] = item["product"]

                                  end if parsed   

                            end

                           

                            def sync

                              # Manipulate @result before it is saved, or save it

                              # yourself using the Rhoconnect::Store interface.

                              # By default, super is called below which simply saves @result

                              super

                            end

                           

                            def search(params)

                              # Search your backend based on params and build a hash of hashes (optional).

                              # Similar to query, however the master document accumulates the data in @result instead of replacing when it runs

                              parsed = JSON.parse(RestClient.get("#{@base}.json").body)

                             

                                @result = {}

                                parsed.each do |item|

                                  if item["product"]["name"].downcase == params['name'].downcase

                                    @result[item["product"]["id"].to_s] = item["product"]

                                  end

                                end if parsed

                              end

                           

                          def create(create_hash,blob=nil)

                          result = RestClient.post(@base,:product => create_hash)

                          # after create we are redirected to the new record.

                          # The URL of the new record is given in the location header

                          location = "#{result.headers[:location]}.json"

                          # We need to get the id of that record and return it as part of create

                          # so rhosync can establish a link from its temporary object on the

                          # client to this newly created object on the server

                          new_record = RestClient.get(location).body

                          JSON.parse(new_record)["product"]["id"].to_s

                          end

                           

                           

                          def update(update_hash)

                          obj_id = update_hash['id']

                          update_hash.delete('id')

                          RestClient.put("#{@base}/#{obj_id}",:product => update_hash)

                          end

                           

                          def delete(object_id)

                          RestClient.delete("#{@base}/#{object_id['id']}")

                          end

                           

                            def logoff

                              # TODO: Logout from the data source if necessary

                              # MyWebService.logoff(current_user.login)

                            end

                          end

                           

                           

                          BTW, I'm not sure if you're using Rhoconnect in Windows, but if you are, then please be informed that I've observed inconsistent behaviour, because Redis doesn't work very well in Windows.

                          • Re: Duplicated records
                            Kutir Mobility

                            I would add some logging before these lines:

                             

                            if @temp != nil

                                       @temp.to_s

                                    else

                                       @temp = ""    

                                    end

                             

                            to be sure of what is being returned from the method. What will @temp contain?

                              • Re: Duplicated records
                                Guillermo James

                                HI Mark, thank you for your post!

                                 

                                Actually we realice that our customer's internet connection it is not working property, for a reason that we are still trying to figure it out, but this internet behavior is the reason for what we receive duplicated records in our database backend. Obviously there is some kind of RhoElement and RhoConnect behaviour that affect the correct sent of data.

                                 

                                We are using RhoConnect and all of the gems need it in a Linux box.

                                Best regards

                      • Re: Duplicated records
                        Kutir Mobility

                        The way RhoConnect knows not to duplicate records is that the "create" method in a source adapter must return the primary key of the new record. For your reference, there is an example RhoConnect implementation tutorial at https://developer.motorolasolutions.com/community/rhomobile-suite/docs/developer-reference/blog/2013/05/20/the-fast-way-to-multiplatform-data-aware-mobile-applications-part-2

                         

                        Thanks,

                        Javier

                        Kutir Mobility