All files / lib jsonApi.js

39.13% Statements 27/69
0% Branches 0/28
0% Functions 0/10
41.54% Lines 27/65
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  1x 1x 1x 1x   1x       1x 1x 1x 1x 1x 1x 1x 1x 1x   1x 1x 1x 1x   1x               1x   1x             1x                         1x                                                                                                       1x       1x   1x                 1x                
'use strict'
const jsonApi = module.exports = { }
jsonApi._version = require(require('path').join(__dirname, '../package.json')).version
jsonApi._resources = { }
jsonApi._apiConfig = { }
 
const _ = {
  assign: require('lodash.assign'),
  pick: require('lodash.pick')
}
const ourJoi = require('./ourJoi.js')
const router = require('./router.js')
const responseHelper = require('./responseHelper.js')
const handlerEnforcer = require('./handlerEnforcer.js')
const pagination = require('./pagination.js')
const routes = require('./routes')
const url = require('url')
const metrics = require('./metrics.js')
const schemaValidator = require('./schemaValidator.js')
 
jsonApi.Joi = ourJoi.Joi
jsonApi.metrics = metrics.emitter
jsonApi.MemoryHandler = require('./MemoryHandler')
jsonApi.ChainHandler = require('./ChainHandler')
 
jsonApi.setConfig = apiConfig => {
  jsonApi._apiConfig = apiConfig
  jsonApi._apiConfig.base = jsonApi._cleanBaseUrl(jsonApi._apiConfig.base)
  jsonApi._apiConfig.pathPrefix = apiConfig.urlPrefixAlias || jsonApi._concatenateUrlPrefix(jsonApi._apiConfig)
  responseHelper.setBaseUrl(jsonApi._apiConfig.pathPrefix)
  responseHelper.setMetadata(jsonApi._apiConfig.meta)
}
 
jsonApi.authenticate = router.authenticateWith
 
jsonApi._concatenateUrlPrefix = config => url.format({
  protocol: config.protocol,
  hostname: config.hostname,
  port: config.port,
  pathname: config.base
})
 
jsonApi._cleanBaseUrl = base => {
  if (!base) {
    base = ''
  }
  if (base[0] !== '/') {
    base = `/${base}`
  }
  if (base[base.length - 1] !== '/') {
    base += '/'
  }
  return base
}
 
jsonApi.define = resourceConfig => {
  if (!resourceConfig.resource.match(/^[A-Za-z0-9_]*$/)) {
    throw new Error(`Resource '${resourceConfig.resource}' contains illegal characters!`)
  }
  resourceConfig.namespace = resourceConfig.namespace || 'default'
  resourceConfig.searchParams = resourceConfig.searchParams || { }
  jsonApi._resources[resourceConfig.resource] = resourceConfig
 
  handlerEnforcer.wrap(resourceConfig.handlers)
 
  resourceConfig.handlers.initialise = resourceConfig.handlers.initialise || resourceConfig.handlers.initialize
  if (resourceConfig.handlers.initialise) {
    resourceConfig.handlers.initialise(resourceConfig)
  }
 
  Object.keys(resourceConfig.attributes).forEach(attribute => {
    if (!attribute.match(/^[A-Za-z0-9_]*$/)) {
      throw new Error(`Attribute '${attribute}' on ${resourceConfig.resource} contains illegal characters!`)
    }
  })
 
  resourceConfig.searchParams = _.assign({
    type: ourJoi.Joi.any().required().valid(resourceConfig.resource)
      .description(`Always "${resourceConfig.resource}"`)
      .example(resourceConfig.resource),
    sort: ourJoi.Joi.any()
      .description('An attribute to sort by')
      .example('title'),
    filter: ourJoi.Joi.any()
      .description('An attribute+value to filter by')
      .example('title'),
    fields: ourJoi.Joi.any()
      .description('An attribute+value to filter by')
      .example('title'),
    include: ourJoi.Joi.any()
      .description('An attribute to include')
      .example('title')
  }, resourceConfig.searchParams, pagination.joiPageDefinition)
 
  resourceConfig.attributes = _.assign({
    id: ourJoi.Joi.string().required()
      .description('Unique resource identifier')
      .example('1234'),
    type: ourJoi.Joi.string().required().valid(resourceConfig.resource)
      .description(`Always "${resourceConfig.resource}"`)
      .example(resourceConfig.resource),
    meta: ourJoi.Joi.object().optional()
  }, resourceConfig.attributes)
 
  resourceConfig.onCreate = _.pick.apply(_, [].concat(resourceConfig.attributes, Object.keys(resourceConfig.attributes).filter(i => (resourceConfig.attributes[i]._meta.indexOf('readonly') === -1) && (!(resourceConfig.attributes[i]._settings || { }).__as))))
}
 
jsonApi.onUncaughtException = errHandler => {
  jsonApi._errHandler = errHandler
}
 
jsonApi.getExpressServer = () => router.getExpressServer()
 
jsonApi.start = () => {
  schemaValidator.validate(jsonApi._resources)
  router.applyMiddleware()
  routes.register()
  if (!jsonApi._apiConfig.router) {
    router.listen(jsonApi._apiConfig.port)
  }
}
 
jsonApi.close = () => {
  router.close()
  metrics.emitter.removeAllListeners('data')
  for (const i in jsonApi._resources) {
    const resourceConfig = jsonApi._resources[i]
    if (resourceConfig.handlers.close) resourceConfig.handlers.close()
  }
}