cozy-labs/cozy-desktop

View on GitHub
core/local/channel_watcher/filter_ignored.js

Summary

Maintainability
A
1 hr
Test Coverage
/** This step removes events about files and directories that are ignored.
 *
 * It's better to put this step as soon as possible in the chain to avoid
 * doing useless computing for ignored files/directories (like adding inotify
 * watchers), but it needs to be put after the AddInfos step as the docType is
 * required to know if the event can be ignored.
 *
 * @module core/local/channel_watcher/filter_ignored
 * @flow
 */

const _ = require('lodash')

const logger = require('../../utils/logger')

/*::
import type Channel from './channel'
import type { ChannelEvent, EventKind } from './event'
import type { Ignore } from '../../ignore'
*/

const STEP_NAME = 'filterIgnored'

const log = logger({
  component: `ChannelWatcher/${STEP_NAME}`
})

module.exports = {
  STEP_NAME,
  loop
}

function loop(
  channel /*: Channel */,
  opts /*: { ignore: Ignore, fatal: Error => any } */
) /*: Channel */ {
  const isIgnored = (path /*: string */, kind /*: EventKind */) =>
    opts.ignore.isIgnored({
      relativePath: path,
      isFolder: kind === 'directory'
    })

  return channel.map(events => {
    const batch = []

    for (const event of events) {
      const eventToKeep = notIgnoredEvent(event, isIgnored)

      if (eventToKeep) {
        batch.push(eventToKeep)
      } else {
        log.debug({ event }, 'Ignored via .cozyignore')
      }
    }

    return batch
  }, opts.fatal)
}

function notIgnoredEvent(
  event /*: ChannelEvent */,
  isIgnored /*: (string, EventKind) => boolean */
) /*: ?ChannelEvent */ {
  if (event.noIgnore) {
    return event
  }

  const isPathIgnored = isIgnored(event.path, event.kind)

  if (event.action === 'renamed' && event.oldPath != null) {
    const isOldPathIgnored = isIgnored(event.oldPath, event.kind)

    if (!isOldPathIgnored && isPathIgnored) {
      return movedToIgnoredPath(event)
    } else if (isOldPathIgnored && !isPathIgnored) {
      return movedFromIgnoredPath(event)
    } else if (!isOldPathIgnored && !isPathIgnored) {
      return event
    }
  } else if (!isPathIgnored) {
    return event
  }

  return null
}

function movedFromIgnoredPath(event /*: ChannelEvent */) /*: ChannelEvent */ {
  const createdEvent = {
    ...event,
    action: 'created'
  }
  _.set(createdEvent, [STEP_NAME, 'movedFromIgnoredPath'], createdEvent.oldPath)
  delete createdEvent.oldPath

  return createdEvent
}

function movedToIgnoredPath(event /*: ChannelEvent */) /*: ChannelEvent */ {
  const deletedEvent = {
    ...event,
    action: 'deleted'
  }
  _.set(deletedEvent, [STEP_NAME, 'movedToIgnoredPath'], deletedEvent.path)
  deletedEvent.path = deletedEvent.oldPath
  delete deletedEvent.oldPath

  return deletedEvent
}