text: /*
   * input handler on DatabaseService level
   *
   * - remove virtual fields
   * - add managed, if not provided
   * - assert not nullable without default (for better error message)
   * - add default values, if not provided (mainly for rest response body, odata does read after write)
   * - add UUIDs
   * - propagate keys
   */
  
  const cds = require('../../cds')
  
  const getTemplate = require('../../common/utils/template')
  const templateProcessor = require('../../common/utils/templateProcessor')
  const { foreignKeyPropagations, propagateForeignKeys } = require('../../common/utils/foreignKeyPropagations')
  const enrichDataWithKeysFromWhere = require('../../common/utils/enrichWithKeysFromWhere')
  
  const { DRAFT_COLUMNS_MAP } = require('../../common/constants/draft')
  
  const _isManaged = (category, event) =>
    (category === '@cds.on.insert' && event === 'CREATE') || (category === '@cds.on.update' && event === 'UPDATE')
  
  const _setManaged = (row, key, arg, req) => {
    switch (arg) {
      case '$user':
        row[key] = '$user'
        break
      case '$now':
        row[key] = '$now'
        break
      default:
        row[key] = arg
    }
  }
  
  const _processCategory = (row, key, val, category, req) => {
    // use args only inside this if (sonar type error warning)
    if (typeof category === 'object') {
      const args = category.args
      category = category.category
  
      // propagate keys
      if (category === 'propagateForeignKeys') {
        propagateForeignKeys(key, row, args.foreignKeyPropagations)
        return
      }
      // managed
      if (val === undefined && _isManaged(category, req.event)) {
        _setManaged(row, key, args['='], req)
        return
      }
      // REVISIT: not null with default (for rest response body)
      if (category === 'default' && val === undefined && req.event === 'CREATE' && 'val' in args) {
        row[key] = args.val
        return
      }
  
      return
    }
  
    // virtual
    if (category === 'virtual') {
      delete row[key]
      return
    }
    // not null without default (for better error message)
    if (category === '!default' && val == null && req.event === 'CREATE') {
      req.error(400, 'ASSERT_NOT_NULL', key, [key])
      return
    }
    // generate UUIDs
    if (category === 'uuid' && !val && req.event === 'CREATE') {
      row[key] = cds.utils.uuid()
    }
  }
  
  const processorFn = req => (row, key, element, plain) => {
    const categories = plain.categories
    const val = row[key]
  
    for (const category of categories) {
      _processCategory(row, key, val, category, req)
    }
  }
  
  // params: element, target, parent, templateElements
  const _pick = element => {
    // collect actions to apply
    const categories = []
  
    if (element.virtual) {
      categories.push('virtual')
    }
    if (
      element.notNull &&
      element['@assert.notNull'] !== false &&
      !element.default &&
      element.type !== 'cds.Association'
    ) {
      categories.push('!default')
    }
    if (element.default && !DRAFT_COLUMNS_MAP[element.name]) {
      categories.push({ category: 'default', args: element.default })
    }
    if (element['@cds.on.insert']) {
      categories.push({ category: '@cds.on.insert', args: element['@cds.on.insert'] })
    }
    if (element['@cds.on.update']) {
      categories.push({ category: '@cds.on.update', args: element['@cds.on.update'] })
    }
    if (element.isAssociation) {
      const _foreignKeyPropagations = foreignKeyPropagations(element)
      if (_foreignKeyPropagations) {
        categories.push({ category: 'propagateForeignKeys', args: { foreignKeyPropagations: _foreignKeyPropagations } })
      }
    }
    // generate uuid
    if (element.key && !DRAFT_COLUMNS_MAP[element.name] && element.type === 'cds.UUID') {
      categories.push('uuid')
    }
  
    if (categories.length) return { categories }
  }
  
  function _handler(req) {
    if (!this.model || typeof req.query === 'string' || !req.target || req.target._unresolved) {
      return
    }
  
    // REVISIT: probably need to filter for .columns/.rows combination as well
    if (req.query.INSERT && !req.query.INSERT.entries) {
      return
    }
  
    const template = getTemplate('db-input', this, req.target, { pick: _pick })
  
    if (template.elements.size === 0) return
  
    enrichDataWithKeysFromWhere(req.data, req, this)
  
    const data = Array.isArray(req.data) ? req.data : [req.data]
    for (const row of data) {
      templateProcessor(processorFn(req), row, template)
    }
  }
  
  _handler._initial = true
  
  module.exports = _handler
  