senecajs/seneca-entity

View on GitHub
src/lib/store.ts

Summary

Maintainability
A
0 mins
Test Coverage
/* Copyright (c) 2012-2020 Richard Rodger and other contributors, MIT License */

const allcmds = ['save', 'load', 'list', 'remove', 'close', 'native']

function Store(plugin_opts: any) {
  const tag_count_map: any = {}

  function make_tag(store_name: any) {
    tag_count_map[store_name] = (tag_count_map[store_name] || 0) + 1
    return tag_count_map[store_name]
  }

  // TODO: expose via seneca.export, and deprecate seneca.store
  const store = {
    cmds: allcmds.slice(0),

    // opts.map = { canon: [cmds] }
    // canon is in string format zone/base/name, with empty or - indicating undefined
    init: function (instance: any, store_opts: any, store: any, cb: any) {
      const entspecs = []
      const hasCanonMapping =
        store_opts.map && 0 < Object.keys(store_opts.map).length

      if (hasCanonMapping) {
        for (const canon in store_opts.map) {
          let cmds = store_opts.map[canon]
          if (cmds === '*') {
            cmds = allcmds
          }
          entspecs.push({ canon: canon, cmds: cmds })
        }
      } else {
        entspecs.push({ canon: '-/-/-', cmds: allcmds })
      }

      // TODO: messy!
      const plugin_tag =
        instance.fixedargs &&
        instance.fixedargs.plugin$ &&
        instance.fixedargs.plugin$.tag
      // plugin_tag cannot be a strict null equal
      const tag =
        plugin_tag == null || plugin_tag === '-'
          ? make_tag(store.name)
          : plugin_tag

      const storedesc = [store.name, tag]

      for (let esI = 0; esI < entspecs.length; esI++) {
        const entspec = entspecs[esI]

        storedesc.push(entspec.canon)
        let zone: string | undefined
        let base: string | undefined
        let name: string | undefined

        // FIX: should use parsecanon

        let m = /^(\w*|-)\/(\w*|-)\/(\w*|-)$/.exec(entspec.canon)
        if (m) {
          zone = m[1]
          base = m[2]
          name = m[3]
        } else if ((m = /^(\w*|-)\/(\w*|-)$/.exec(entspec.canon))) {
          base = m[1]
          name = m[2]
        } else if ((m = /^(\w*|-)$/.exec(entspec.canon))) {
          name = m[1]
        }

        zone = zone === '-' ? void 0 : zone
        base = base === '-' ? void 0 : base
        name = name === '-' ? void 0 : name

        const entargs: any = {}
        if (void 0 !== name) entargs.name = name
        if (void 0 !== base) entargs.base = base
        if (void 0 !== zone) entargs.zone = zone

        entspec.cmds.forEach(function (cmd: string) {
          const args = { ...entargs, cmd: cmd, ...plugin_opts.pattern_fix }
          const orig_cmdfunc = store[cmd]
          let cmdfunc = orig_cmdfunc

          if (null == cmdfunc) {
            return instance.die('store_cmd_missing', {
              cmd: cmd,
              store: storedesc,
            })
          }

          cmdfunc = Intern.reify_entity_wrap(cmdfunc, cmd, zone, base, name)

          // if (Intern.cmd_wrap[cmd]) {
          //   cmdfunc = Intern.cmd_wrap[cmd](cmdfunc)
          // }

          for (const p in orig_cmdfunc) {
            cmdfunc[p] = orig_cmdfunc[p]
          }

          if (cmd !== 'close') {
            instance.add(args, cmdfunc)
          } else if (cmd === 'close') {
            instance.add(
              'sys:seneca,cmd:close',
              function (this: any, close_args: any, done: any) {
                const closer = this

                if (!store.closed$) {
                  cmdfunc.call(closer, close_args, function (err: any) {
                    if (err) closer.log.error('close-error', close_args, err)

                    store.closed$ = true
                    closer.prior(close_args, done)
                  })
                } else {
                  return closer.prior(close_args, done)
                }
              },
            )
          }
        })
      }

      // legacy
      if (cb) {
        cb.call(instance, null, tag, storedesc.join('~'))
      } else {
        return {
          tag: tag,
          desc: storedesc.join('~'),
        }
      }
    },
  }

  return store
}

const Intern: any = {
  // Ensure entity objects are instantiated
  reify_entity_wrap: function (
    cmdfunc: any,
    cmd: string,
    zone?: string,
    base?: string,
    name?: string,
  ) {
    const outfunc = function (this: any, msg: any, reply: any, meta: any) {
      if ('save' !== msg.cmd) {
        if (null == msg.q) {
          msg.q = {}

          if (null != msg.id) {
            msg.q.id = msg.id
            delete msg.id
          }
        }

        if (null == msg.qent) {
          msg.qent = this.make$({
            entity$: {
              name: msg.name,
              base: msg.base,
              zone: msg.zone,
            },
          })
        }
      }

      if (null != msg.ent && 'function' != typeof msg.ent.canon$) {
        msg.ent = this.make$({
          entity$: {
            name: msg.name,
            base: msg.base,
            zone: msg.zone,
          },
        }).data$(msg.ent)
      }

      return cmdfunc.call(this, msg, reply, meta)
    }

    Object.defineProperty(outfunc, 'name', {
      value:
        'entity_' +
        cmd +
        (null == zone ? '' : zone + '_') +
        (null == base ? '' : base + '_') +
        (null == name ? '' : name),
    })

    return outfunc
  },
}

export { Intern, Store }