3
votes

My Code

thats is a short version of my current code.:

['tableA', 'tableB', 'tableC'].forEach(name => {
    let local = new PouchDB(name, { auto_compaction: true })
    let server = new PouchDB(serverUrl + name)

    var filtro = {
      include_docs: true,
      filter: 'replication/by_dispositivo',
      query_params: { 'dispositivo_id': obj.deviceId }
    }
    local.replicate.from(server, filtro).on('complete', report => {
      var sync = local.sync(server, {
        live: true,
        retry: true,
        ...filtro
      })
    })
})

I'm trying to do a live replication, but for some reason, that doesn't replicate the local data to the server, strangely, PouchDb didn't throw any exception.

Inspecting the Network tab on Dev Tools, I can see te follow request.:

URL: ${serverUrl}/{name}/_revs_diff
Response: {
    "4b0ea507-cd88-4998-baf0-01629b50516b": {
        "missing": [
            "2-2133d30de8d44ebd958cee2b68726ffb"
        ],
        "possible_ancestors": [
            "1-39904a7e55b1cb266c840a2acf34fdc2"
        ]
    }
}

Ok, PouchDb detected that something is missing in the server and must be replicated.

Auditing the Sync

Searching for a hint about what is happening, I modified my code to log the complete and error events.:

['tableA', 'tableB', 'tableC'].forEach(name => {
    let local = new PouchDB(name, { auto_compaction: true })
    let server = new PouchDB(serverUrl + name)

    let filtro = {
      include_docs: true,
      filter: 'replication/by_dispositivo',
      query_params: { 'dispositivo_id': obj.deviceId }
    }
    local.replicate.from(server, filtro).on('complete', report => {
      let sync = local.sync(server, {
        live: true,
        retry: true,
        ...filtro
      })
      sync.on('error', (error) => {
        console.error(error)
        console.error(JSON.stringify(error, null, 2))
      }).on('complete', (result) => {
        console.log(result)
        console.log(JSON.stringify(result, null, 2))
      })
      window.setTimeout(function (evt) {
        state.syncProcess[database].cancel()
      }, 15000)
    })
})

I didn't catch anything in the error event, and the complete event didn't show any errors as you can see bellow.

{
  "push": {
    "ok": true,
    "start_time": "2018-04-06T15:00:42.266Z",
    "docs_read": 0,
    "docs_written": 0,
    "doc_write_failures": 0,
    "errors": [],
    "status": "cancelled",
    "end_time": "2018-04-06T15:00:42.266Z",
    "last_seq": 0
  },
  "pull": {
    "ok": true,
    "start_time": "2018-04-06T15:00:26.422Z",
    "docs_read": 0,
    "docs_written": 0,
    "doc_write_failures": 0,
    "errors": [],
    "last_seq": "17-g1AAAAJDeJyd0EsOgjAQBuAqJj52nkCPILGldCU3UaYzBA3CQl3rTfQmehO9CRZKAiaGiJtpMs18mX8SxtgodpBNdXbSMUKQZDpM4uxwTMxXP2Qwy_N8Fzsh25vGMILIA62QjU8pUrRNCVvGYW4qrCphUgoCfMVd_W2mTQoKaV1JjpWWIUcuu0qbQjp_pBKeUESLH1OlA1PZxTwGudb7EC1dQt5xH6vdrHYvtF6pSZK-4Oov7WG1Z53QUy56UnRK-LJK406-TxIAm8ruDdzts44",
    "status": "cancelled",
    "end_time": "2018-04-06T15:00:41.427Z"
  }
}

Calling one time local to server replication manually

that is my second attempt to catch something usefull. I trying to audit the local.replicate.to method

['tableA', 'tableB', 'tableC'].forEach(name => {
    let local = new PouchDB(name, { auto_compaction: true })
    let server = new PouchDB(serverUrl + name)

    let filtro = {
      include_docs: true,
      filter: 'replication/by_dispositivo',
      query_params: { 'dispositivo_id': obj.deviceId }
    }
    local.replicate.from(server, filtro).on('complete', report => {
      local.replicate.to(server, filtro).on('complete', report => {
        console.log(report)
        console.log(JSON.stringify(report, null, 2))
        let sync = local.sync(server, {
          live: true,
          retry: true,
          ...filtro
        })
      }).on('error', (error) => {
        console.error(error)
        console.error(JSON.stringify(error, null, 2))
      })
    })
})

thats time complete event isn't fired and I catch a error, but that is too generic amd don't give any clues regarding whats is happening.

{
  "result": {
    "ok": false,
    "start_time": "2018-04-06T15:07:19.105Z",
    "docs_read": 1,
    "docs_written": 0,
    "doc_write_failures": 0,
    "errors": [],
    "status": "aborting",
    "end_time": "2018-04-06T15:07:19.768Z",
    "last_seq": 3
  }
}

Putting local data to the server

thats is my last attempt.:

  • I'll query local and remote databases (in that particular case, I have only one document)
  • Copy fields from local doc to remote doc.
  • Dispatch the updated remote doc to the remote database

My Junk Code

var deviceId = ''
var listLocal = []
var listServer = []

getDeviceId().then(response => {
    deviceId = response
    return local.find({ selector: { dispositivo_id: deviceId } })
}).then(response => {
    listLocal = response.docs
    return server.find({ selector: { dispositivo_id: deviceId } })
}).then(response => {
    listServer = response.docs

    var tlocal = listLocal[0]
    var tServer = listServer[0]
    Object.keys(tServer).forEach(key => {
      if (key.indexOf("_") !== 0) {
        tServer[key] = undefined
      }
    })
    Object.keys(tlocal).forEach(key => {
      if (key.indexOf("_") !== 0) {
        tServer[key] = tlocal[key]
      }
    })

    return server.put(tServer).then(result => {
      console.log(result)
      console.log(JSON.stringify(result, null, 2))
    }).catch(error => {
      console.error(error)
      console.error(JSON.stringify(error, null, 2))
    })
})

The junk code worked as expected, and i received that response.:

{
  "ok": true,
  "id": "4b0ea507-cd88-4998-baf0-01629b50516b",
  "rev": "2-d9363f28e53fdc145610f5ad3f75a043"
}

Additional Information

My design documents in the CouchDb

_design/replication

{
  "_id": "_design/replication",
  "_rev": "1-42df919aaee8ed3fb309bbda999ba03d",
  "language": "javascript",
  "filters": {
    "by_dispositivo": "function(doc, req) {\r\n  return doc._id === '_design/replication' || (doc.dispositivo_id === req.query.dispositivo_id && !doc._deleted)\r\n}",
    "by_situacao_remote": "function(doc, req) {\r\n  return [2, 3, 4, 5].indexOf(doc.situacao) !== -1 && !doc._deleted\r\n}"
  }
}

_design/authorization

{
  "_id": "_design/authorization",
  "_rev": "9-64c4a22645d783c9089c95d69e9424ad",
  "language": "javascript",
  "validate_doc_update": "..."
}

authorization/validate_doc_update

function(newDoc, oldDoc, userCtx) {
  var isAdmin = userCtx.roles.indexOf('_admin') !== -1 || userCtx.roles.indexOf('admin') !== -1;
  if (!isAdmin) {
    if (newDoc._deleted) {
      if (oldDoc.dispositivo_id !== userCtx.name) {
        throw({forbidden: "..." });
      }
    } 
    else {
      if (!newDoc.dispositivo_id || !newDoc.dispositivo_id.trim())
        throw({forbidden: "..." });
      if (newDoc.dispositivo_id !== userCtx.name) {
        throw({forbidden: "..." });
      }

      if (oldDoc && oldDoc.dispositivo_id !== userCtx.name) {
        throw({forbidden: "..." });
      }

      var isRequired = function (prop, msg) {
        var value = newDoc[prop];
        if (!value)
            throw({forbidden: '...' });
      }

      var isDate = function (prop, msg, allow_null) {
        if (!allow_null) 
            isRequired(prop, msg)
        var value = newDoc[prop];
        if (value) {
            var date = new Date(value);
            var isDate = date !== "Invalid Date" && !isNaN(date);
            if (!isDate) {
              throw({forbidden: msg });
            }
        }
      }

      var isFloat = function (prop, msg, allow_null) {
        if (!allow_null) 
            isRequired(prop, msg)
        var value = newDoc[prop];
        if (value) {
            var numero = new Number(value);
            if (!numero || isNaN(numero) || !isFinite(numero)) {
              throw({forbidden: msg });
            }
        }
      }

      var isInteger = function (prop, msg, allow_null) {
        isFloat(prop, msg, allow_null)
        var value = newDoc[prop];
        if (value) {
            var numero = new Number(value);
            var isInteger = Math.floor(numero) == numero;
            if (!isInteger) {
              throw({forbidden: msg });
            }
        }
      }

      isRequired("talao_id", "...");
      isRequired("equipe_id", "...");

      isInteger("situacao", '...');
      isDate("data_envio", "...");
      isDate("data_recebimento", "...", true);
      isDate("data_decisao", "...", true);

      isRequired("tipo_ocorrencia_codigo", "...");
      isRequired("tipo_ocorrencia_descricao", "...");
      isInteger("talao_codigo", "...");
      isRequired("talao_descricao", "...");
      isRequired("talao_solicitante", "...");
      isRequired("talao_endereco", "...");
    }
  }
  else if (!newDoc._deleted) {
    if (!newDoc.dispositivo_id || !newDoc.dispositivo_id.trim())
      throw({forbidden: "..." });
  }
}
1

1 Answers

1
votes

While analyzing the stack trace of the exception throw by local.replicate.to, I noticied that reason: promise.all is not a function.

So i googled for a while and found that topic Webpack: Promise is not a constructor. I just need to copy the workaround bellow to my webpack.config and everything worked like a charm.:

resolve: {
  alias: {
    'pouchdb-promise$': "pouchdb-promise/lib/index.js"
  }
}