All files / src/hooks hooks.storage.js

5.41% Statements 4/74
0% Branches 0/36
0% Functions 0/8
5.63% Lines 4/71
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 1391x 1x 1x   1x                                                                                                                                                                                                                                                                            
import _ from 'lodash'
import { populateObject, unpopulateObject } from './hooks.query'
import makeDebug from 'debug'
 
const debug = makeDebug('kalisio:kCore:storage:hooks')
 
function isAttachmentEqual (file1, file2) {
  return file1._id === file2._id
}
 
export function populateAttachmentResource (hook) {
  if (hook.type !== 'before') {
    throw new Error(`The 'populateStorageResource' hook should only be used as a 'before' hook.`)
  }
 
  // Avoid populating any target resource when resource parameters are not present
  return populateObject({ serviceField: 'resourcesService', idField: 'resource', throwOnNotFound: false })(hook)
}
 
export function unpopulateAttachmentResource (hook) {
  if (hook.type !== 'after') {
    throw new Error(`The 'unpopulateAttachmentResource' hook should only be used as a 'after' hook.`)
  }
 
  return unpopulateObject({ serviceField: 'resourcesService', idField: 'resource' })(hook)
}
 
export async function attachToResource (hook) {
  if (hook.type !== 'after') {
    throw new Error(`The 'attachToResource' hook should only be used as a 'after' hook.`)
  }
  const data = hook.data
  const params = hook.params
  const query = params.query
  const file = hook.result
  const attachmentField = _.get(data, 'field') || _.get(query, 'field') || 'attachments'
  // By default attachments are stored in an array
  let isArray = _.get(data, 'isArray') || _.get(query, 'isArray') || true
  // Take care that because file uploads might be submitted by external multipart form data middlewares
  // all parameters types might be string
  if (typeof (isArray) !== 'boolean') {
    isArray = (isArray === 'true')
  }
  const context = hook.service.context
  const resourcesService = params.resourcesService
  let resource = params.resource
  let attachments = _.get(resource, attachmentField)
  let attachment = Object.assign({ _id: file._id }, _.omit(file, ['uri']))
  // Add context because attachments might come from different ones on the same target object
  if (context) {
    attachment.context = (typeof context === 'object' ? context._id : context)
  }
  if (isArray) {
    // Initialize on first attachment
    if (!attachments) attachments = []
    attachments.push(attachment)
  } else {
    attachments = attachment
  }
 
  await resourcesService.patch(resource._id.toString(), {
    [attachmentField]: attachments
  }, {
    user: params.user,
    // Forward query so that any update param could be processed as usual on resource
    // Delete own parameters from query otherwise it will be used to filter items
    query: _.omit(query, ['resource', 'resourcesService'])
  })
  debug('Attached file on resource ' + resource._id.toString(), attachment)
  return hook
}
 
export async function detachFromResource (hook) {
  if (hook.type !== 'after') {
    throw new Error(`The 'detachFromResource' hook should only be used as a 'after' hook.`)
  }
 
  const params = hook.params
  const query = params.query
  let file = hook.result
  const attachmentField = _.get(query, 'field') || 'attachments'
  const resourcesService = params.resourcesService
  let resource = params.resource
  let attachments = _.get(resource, attachmentField)
  let attachment
  // List of attachments
  if (Array.isArray(attachments)) {
    const attachmentIndex = _.findIndex(attachments, attachment => isAttachmentEqual(attachment, file))
    if (attachmentIndex >= 0) {
      // Keep track of it for logging
      attachment = attachments[attachmentIndex]
      _.pullAt(attachments, attachmentIndex)
    }
  } else {
    // Single attachment object
    attachment = attachments
    attachments = null
  }
 
  await resourcesService.patch(resource._id.toString(), {
    [attachmentField]: attachments
  }, {
    user: params.user,
    // Forward query so that any update param could be processed as usual on resource
    // Delete own parameters from query otherwise it will be used to filter items
    query: _.omit(query, ['resource', 'resourcesService'])
  })
  debug('Detached file on resource ' + resource._id.toString(), attachment)
  return hook
}
 
export function removeAttachments (attachmentField) {
  return async function (hook) {
    const context = hook.service.context
    let storageService = hook.app.getService('storage', context)
    if (!storageService) return Promise.reject(new Error('No valid context found to retrieve storage service for initiator service ' + hook.service.name))
    let resource = hook.result
    let attachments = _.get(resource, attachmentField)
    // Process with each attachment
    if (attachments) {
      debug('Removing attachments for resource ' + resource._id.toString(), attachments)
      if (Array.isArray(attachments)) {
        let removePromises = []
        attachments.forEach(attachment => {
          removePromises.push(storageService.remove(attachment._id))
          // Thumbnail as well
          removePromises.push(storageService.remove(attachment._id + '.thumbnail'))
        })
        await Promise.all(removePromises)
      } else {
        await storageService.remove(attachments._id)
        // Thumbnail as well
        await storageService.remove(attachments._id + '.thumbnail')
      }
    }
    return hook
  }
}