Skip to content
Snippets Groups Projects
file.model.js 3.3 KiB
Newer Older
Yannis Barlas's avatar
Yannis Barlas committed
const { v4: uuid } = require('uuid')
const { logger } = require('@pubsweet/logger')
const find = require('lodash/find')
Yannis Barlas's avatar
Yannis Barlas committed

const BaseModel = require('../base.model')
const useTransaction = require('../useTransaction')

const {
  arrayOfStrings,
  stringNullable,
  stringNotEmpty,
  idNullable,
  integerPositive,
  id,
} = require('../_helpers/types')

// Type declaration
const mimetype = {
  type: 'string',
  pattern:
    '^(application|audio|font|image|model|multipart|text|video)/[a-z0-9]+([-+.][a-z0-9]+)*$',
  // if you want to know why this is default, look at
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types
  default: 'application/octet-stream',
}

const arrayOfStoredObjects = {
  type: 'array',
  items: {
    type: 'object',
    additionalProperties: false,
    required: ['type', 'key', 'mimetype', 'extension', 'size'],
    properties: {
      id,
      type: { type: 'string', enum: ['original', 'small', 'medium', 'full'] },
Yannis Barlas's avatar
Yannis Barlas committed
      key: stringNotEmpty,
      mimetype,
      extension: stringNotEmpty,
      imageMetadata: {
        type: ['object', 'null'],
        required: ['height', 'width'],
        additionalProperties: false,
        properties: {
          id,
          density: integerPositive,
          height: integerPositive,
          space: stringNotEmpty,
          width: integerPositive,
        },
      },
      size: integerPositive,
    },
  },
}

class File extends BaseModel {
  constructor(properties) {
    super(properties)
    this.type = 'file'
  }

  static get tableName() {
    return 'files'
  }

  static get schema() {
    return {
      type: 'object',
      required: ['name', 'storedObjects'],
      properties: {
        alt: stringNullable,
        caption: stringNullable,
        name: stringNotEmpty,
        objectId: idNullable,
        storedObjects: arrayOfStoredObjects,
        referenceId: idNullable,
        tags: arrayOfStrings,
        uploadStatus: stringNullable,
      },
    }
  }

  ensureIds() {
    if (this.storedObjects) {
      this.storedObjects.forEach((storedObject, index) => {
        if (!storedObject.id) {
          this.storedObjects[index].id = uuid()
        }

        if (storedObject.imageMetadata) {
          if (!storedObject.imageMetadata.id) {
            this.storedObjects[index].imageMetadata.id = uuid()
          }
        }
      })
    }
  }

  $beforeInsert(queryContext) {
    super.$beforeInsert(queryContext)
    this.ensureIds()
  }

  $beforeUpdate() {
    super.$beforeUpdate()
    this.ensureIds()
  }

  static async getEntityFiles(objectId, options = {}) {
    try {
      const { trx } = options
      return useTransaction(
        async tr => {
          const { result: files } = await File.find({ objectId }, options)
          return files
Yannis Barlas's avatar
Yannis Barlas committed
        },
        { trx, passedTrxOnly: true },
      )
    } catch (e) {
      logger.error('File model: getEntityFiles failed', e)
      throw new Error(
        `File model: Cannot get files for entity with id ${objectId}`,
      )
    }
  }

  getStoredObjectBasedOnType(type) {
    try {
      const found = find(this.storedObjects, { type })

      if (!found) {
        throw new Error('Unknown type of stored object provided')
      }

      return found
    } catch (e) {
      throw new Error(e.message)
    }
  }
Yannis Barlas's avatar
Yannis Barlas committed
}

module.exports = File