Documentationcurrent version
Help us improve the docs by fixing typos and proposing enhancements.

Nikita

Action "file.cache"

Download a file and place it on a local or remote folder for later usage.

Output

  • $status
    Value is "true" if cache file was created or modified.

HTTP example

Cache can be used from the file.download action:

const {$status} = await nikita.file.download({
  source: 'https://github.com/wdavidw/node-nikita/tarball/v0.0.1',
  cache_dir: '/var/tmp'
})
console.info(`File downloaded: ${$status}`)

Schema definitions

definitions =
  config:
    type: 'object'
    properties:
      'cache_dir':
        type: 'string'
        description: '''
        Path of the cache directory.
        '''
      'cache_file':
        type:[ 'boolean', 'string']
        description: '''
        Alias for 'target'.
        '''
      'cache_local':
        type: 'boolean'
        description: '''
        Apply to SSH mode, treat the cache file and directories as local from
        where the command is used instead of over SSH.
        '''
      'cookies':
        type: 'array'
        items: type: 'string'
        default: []
        description: '''
        Extra cookies  to include in the request when sending HTTP to a
        server.
        '''
      'fail':
        type: 'boolean'
        description: '''
        Send an error if the HTTP response code is invalid. Similar to the
        curl option of the same name.
        '''
      'force':
        type: 'boolean'
        description: '''
        Overwrite the target file if it exists, bypass md5 verification.
        '''
      'http_headers':
        type: 'array'
        items: type: 'string'
        default: []
        description: '''
        Extra header to include in the request when sending HTTP to a server.
        '''
      'location':
        type: 'boolean'
        description: '''
        If the server reports that the requested page has moved to a different
        location (indicated with a Location: header and a 3XX response code),
        this option will make curl redo the request on the new place.
        '''
      'md5':
        type: ['boolean', 'string']
        default: false
        description: '''
        Validate file with md5 checksum (only for binary upload for now),
        may be the string checksum or will be deduced from source if "true".
        '''
      'proxy':
        type: 'string'
        description: '''
        Use the specified HTTP proxy. If the port number is not specified, it
        is assumed at port 1080. See curl(1) man page.
        '''
      'sha1':
        default: false
        type: ['boolean', 'string']
        description: '''
        Validate file with sha1 checksum (only for binary upload for now),
        may be the string checksum or will be deduced from source if "true".
        '''
      'sha256':
        default: false
        type: ['boolean', 'string']
        description: '''
        Validate file with sha256 checksum (only for binary upload for now),
        may be the string checksum or will be deduced from source if "true".
        '''
      'source':
        type: 'string'
        description: '''
        File, HTTP URL, FTP, GIT repository. File is the default protocol if
        source is provided without any.
        '''
      'target':
        type: ['boolean', 'string']
        description: '''
        Cache the file on the executing machine, equivalent to cache unless an
        ssh connection is provided. If a string is provided, it will be the
        cache path. Default to the basename of source.
        '''
    required: ['source']
    anyOf: [
      required: ['target']
    ,
      required: ['cache_file']
    ,
      required: ['cache_dir']
    ]

Handler

handler = ({config, tools: {log}}) ->
  config.target ?= config.cache_file
  config.target ?= path.basename config.source
  config.target = path.resolve config.cache_dir, config.target
  config.source = config.source.substr 7 if /^file:\/\//.test config.source
  # todo, also support config.algo and config.hash
  # replace alog and _hash with
  # config.algo = null
  # config.hash = false
  if config.md5?
    algo = 'md5'
    _hash = config.md5
  else if config.sha1?
    algo = 'sha1'
    _hash = config.sha1
  else if config.sha256?
    algo = 'sha256'
    _hash = config.sha256
  else
    algo = 'md5'
    _hash = false
  u = url.parse config.source
  if u.protocol isnt null
    log message: "Bypass source hash computation for non-file protocols", level: 'WARN'
  else
    if _hash is true
      _hash = await @fs.hash config.source
      _hash = if _hash?.hash then _hash.hash else false
      log message: "Computed hash value is '#{_hash}'", level: 'INFO'
  # Download the file if
  # - file doesnt exist
  # - option force is provided
  # - hash isnt true and doesnt match
  {$status} = await @call ->
    log message: "Check if target (#{config.target}) exists", level: 'DEBUG'
    {exists} = await @fs.base.exists target: config.target
    if exists
      log message: "Target file exists", level: 'INFO'
      # If no checksum , we ignore MD5 check
      if config.force
        log message: "Force mode, cache will be overwritten", level: 'DEBUG'
        return true
      else if _hash and typeof _hash is 'string'
        # then we compute the checksum of the file
        log message: "Comparing #{algo} hash", level: 'DEBUG'
        {hash} = await @fs.hash config.target
        # And compare with the checksum provided by the user
        if _hash is hash
          log message: "Hashes match, skipping", level: 'DEBUG'
          return false
        log message: "Hashes don't match, delete then re-download", level: 'WARN'
        await @fs.base.unlink target: config.target
        true
      else
        log message: "Target file exists, check disabled, skipping", level: 'DEBUG'
        false
    else
      log message: "Target file does not exists", level: 'INFO'
      true
  return $status unless $status
  # Place into cache
  if u.protocol in protocols_http
    await @fs.mkdir
      $ssh: false if config.cache_local
      target: path.dirname config.target
    await @execute
      $ssh: false if config.cache_local
      $unless_exists: config.target
      command: [
        'curl'
        '--fail' if config.fail
        '--insecure' if u.protocol is 'https:'
        '--location' if config.location
        ...("--header '#{header.replace '\'', '\\\''}'" for header in config.http_headers)
        ...("--cookie '#{cookie.replace '\'', '\\\''}'" for cookie in config.cookies)
        "-s #{config.source}"
        "-o #{config.target}"
        "-x #{config.proxy}" if config.proxy
      ].join ' '
  else
    await @fs.mkdir # todo: copy shall handle this
      target: "#{path.dirname config.target}"
    await @fs.copy
      source: "#{config.source}"
      target: "#{config.target}"
  # Validate the cache
  {hash} = await @fs.hash
    $if: _hash
    target: config.target
  hash ?= false
  throw errors.NIKITA_FILE_INVALID_TARGET_HASH config: config, hash: hash, _hash: _hash unless _hash is hash
  {}

Exports

module.exports =
  handler: handler
  metadata:
    argument_to_config: 'source'
    definitions: definitions
module.exports.protocols_http = protocols_http = ['http:', 'https:']
module.exports.protocols_ftp = protocols_ftp = ['ftp:', 'ftps:']

Errors

errors =
  NIKITA_FILE_INVALID_TARGET_HASH: ({config, hash, _hash}) ->
    utils.error 'NIKITA_FILE_INVALID_TARGET_HASH', [
      "target #{JSON.stringify config.target} got #{hash} instead of #{_hash}"
    ]

Dependencies

path = require 'path'
url = require 'url'
utils = require './utils'
Edit on GitHub
Navigate
About

Nikita is an open source project hosted on GitHub and developed by Adaltas.