cozy/cozy-mobile

View on GitHub
src/app/lib/first_replication.coffee

Summary

Maintainability
Test Coverage
async = require 'async'
ChangeDispatcher = require '../replicator/change/change_dispatcher'
RemoteRequest = require './remote_request'
log = require('./persistent_log')
    prefix: "FirstReplication"
    date: true
instance = null


module.exports = class FirstReplication


    constructor: ->
        return instance if instance
        instance = @
        _.extend @, Backbone.Events

        @replicator = app.init.replicator
        @requestCozy = app.init.requestCozy
        @changeDispatcher = new ChangeDispatcher()
        @config = app.init.config
        @filterManager = app.init.filterManager
        @replicateDb = app.init.database.replicateDb
        @remoteRequest = new RemoteRequest()

        @queue = async.queue (task, callback) =>
            @trigger "change:queue", @, task
            @['_' + task] (err) =>
                if err
                    log.warn err
                    return @queue.unshift task, callback
                callback()

        @queue.drain = =>
            @trigger "change:queue", @, false


    isRunning: ->
        @queue.running() > 0


    getTaskName: ->
        if @isRunning()
            @queue.workersList()[0].data
        else
            ''


    addTask: (task, callback, retry = 3) ->
        @queue.push task, (err) =>
            if err
                status = 'error'
                log.error err
            else
                status = 'success'
                if task is 'files'
                    @config.set 'firstSyncFiles', true
                else if task is 'contacts'
                    @config.set 'firstSyncContacts', true
                else if task is 'calendars'
                    @config.set 'firstSyncCalendars', true

            log.info "task #{task} is finished with #{status}."

            retry--
            if err and retry > 0
                @addTask task, callback, retry
            else
                callback err


    addProgressionView: (@updateView) ->


    getRemoteCheckpoint: (callback) ->
        options =
            retry: 3
            method: 'get'
            type: 'replication'
            path: '/_changes?descending=true&limit=1'
        @requestCozy.request options, (err, res, body) ->
            return callback err if err
            callback null, body.last_seq


    getLocalCheckpoint: (callback) ->
        options =
            descending: true
            limit: 1
        @replicateDb.changes options, (err, changes) ->
            return callback err if err
            callback null, changes.last_seq


    _files: (callback) ->
        @getRemoteCheckpoint (err, remoteCheckpoint) =>
            return callback err if err

            @_copyView docType: 'file', (err) =>
                return callback err if err

                @_copyView docType: 'folder', (err) =>
                    return callback err if err

                    @_postCopyViewSync remoteCheckpoint, callback


    _contacts: (callback) ->
        @getRemoteCheckpoint (err, remoteCheckpoint) =>
            return callback err if err

            options =
                docType: 'contact'
                attachments: true
            @_copyView options, (err, contacts) =>
                return callback err if err

                async.eachSeries contacts, (contact, cb) =>
                    @_updateProgression()
                    # 2. dispatch inserted contacts to android
                    @changeDispatcher.dispatch contact, cb
                , (err) =>
                    return callback err if err

                    @_postCopyViewSync remoteCheckpoint, callback


    _calendars: (callback) ->
        @getRemoteCheckpoint (err, remoteCheckpoint) =>
            return callback err if err

            options =
                docType: 'event'
                attachments: true
            @_copyView options, (err, events) =>
                return callback err if err

                async.eachSeries events, (event, cb) =>
                    @_updateProgression()
                    # 2. dispatch inserted contacts to android
                    @changeDispatcher.dispatch event, cb
                , (err) =>
                    return callback err if err

                    @_postCopyViewSync remoteCheckpoint, callback


    _postCopyViewSync: (remoteCheckpoint, callback) ->
        @filterManager.setFilter (err) =>
            return callback err if err

            @getLocalCheckpoint (err, localCheckpoint) =>
                return callback err if err

                options =
                    remoteCheckpoint: remoteCheckpoint
                    localCheckpoint: localCheckpoint
                @replicator.sync options, (err) =>
                    return callback err if err

                    @_updateProgression()
                    @_updateProgression()
                    callback()


    # 1. Fetch all documents of specified docType
    # 2. Put in PouchDB
    # 2.1 : optionnaly, fetch attachments before putting in pouchDB
    # Return the list of added doc to PouchDB.
    _copyView: (options, callback) ->
        log.info "enter copyView for #{options.docType}."

        # Fetch all documents, with a previously put couchdb view.


        # Last step
        putInPouch = (doc, cb) =>
            @replicateDb.put doc, 'new_edits': false, (err) ->
                cb err, doc

        # 1. Fetch all documents
        retryOptions =
            times: options.retry or 1
            interval: 20 * 1000

        async.retry retryOptions, ((cb) => @remoteRequest.fetchAll options, cb)
        , (err, rows) =>
            return callback err if err
            return callback null, [] unless rows?.length isnt 0

            @trigger "change:total", @, rows.length + 1
            @progression = 0

            # 2. Put in PouchDB
            async.mapSeries rows, (row, cb) =>
                @_updateProgression()
                doc = row.doc

                # 2.1 Fetch attachment if needed (typically contact docType)
                if options.attachments is true and doc._attachments?
                    # TODO? needed : .picture?
                    requestOptions =
                        method: 'get'
                        type: 'replication'
                        path: "/#{doc._id}?attachments=true"
                        retry: 3
                    @requestCozy.request requestOptions, (err, res, body) ->
                        # Continue on error (we just miss the avatar in case
                        # of contacts)
                        unless err
                            doc = body

                        putInPouch doc, cb

                else # No attachments
                    putInPouch doc, cb

            , callback


    _updateProgression: ->
        @progression++
        @trigger "change:progress", @, @progression