Prechádzať zdrojové kódy

Added versioned REST controller.

Tomislav Cvetic 8 rokov pred
rodič
commit
fcc47ea699

+ 66 - 25
core/RESTController.js

@@ -26,55 +26,87 @@ class RESTController {
     this.model = model
     this.modelName = model.modelName.toLowerCase()
     this.key = key
+    this.findOptions = {}
+    this.findCriteria = {}
+    this.extendData = (data) => data
+    this.updateExtendedData = (data) => data
+    debug('created RESTController', this.modelName)
   }
 
   /**
    * Creates a DB item.
    *
    * @param  {object} data object
-   * @return {null}
+   * @return {Promise}
    */
   create (data) {
-    return this.model
-      .create(data)
+    debug('create data item', data)
+    const newData = this.extendData(data)
+    const p = this.model
+      .create(newData)
       .then(instance => {
-        const response = {}
-        response[this.modelName] = instance
-        return response
+        debug('created instance', instance)
+        return instance
       })
+    // debug('create return value', p)
+    return p
   }
 
+  /**
+   * Read a DB item.
+   *
+   * @param  {String} id
+   * @return {Promise}
+   */
   read (id) {
+    debug('reading data item', id)
     const filter = {}
     filter[this.key] = id
 
-    return this.model
+    const p = this.model
       .findOne(filter)
       .then(instance => {
-        const response = {}
-        response[this.modelName] = instance
-        return response
+        debug('read instance', instance)
+        return instance
       })
+    // debug('read return value', p)
+    return p
   }
 
+  /**
+   * Get a list of all DB items.
+   *
+   * @return {Promise}
+   */
   list () {
-    return this.model
+    debug('reading data list')
+    const p = this.model
       .find({})
       .limit(MAX_RESULTS)
       .then(instance => {
-        const response = {}
-        response[this.modelName] = instance
-        return response
+        debug('read list instance', instance)
+        return instance
       })
+    // debug('read return value', p)
+    return p
   }
 
+  /**
+   * Update a DB item.
+   *
+   * @param  {String} id
+   * @param  {String} data
+   * @return {Promise}
+   */
   update (id, data) {
+    debug('update data item', id, data)
     const filter = {}
     filter[this.key] = id
 
-    return this.model
+    const p = this.model
       .findOne(filter)
       .then(instance => {
+        debug('found data instance', instance)
         for (let attribute in data) {
           if (data.hasOwnProperty(attribute) && attribute !== this.key && attribute !== '_id') {
             instance[attribute] = data[attribute]
@@ -83,21 +115,31 @@ class RESTController {
         return instance.save()
       })
       .then(instance => {
-        const response = {}
-        response[this.modelName] = instance
-        return response
+        debug('updated data instance', instance)
+        return instance
       })
+    // debug('read return value', p)
+    return p
   }
 
+  /**
+   * Delete a DB item.
+   *
+   * @param  {String} id
+   * @return {Promise}
+   */
   delete (id) {
+    debug('delete data item')
     const filter = {}
     filter[this.key] = id
 
-    return this.model
+    const p = this.model
       .remove(filter)
       .then(() => {
         return {}
       })
+    // debug('read return value', p)
+    return p
   }
 
   route () {
@@ -107,40 +149,39 @@ class RESTController {
       this
         .list()
         .then(data => res.json(data))
-        .then(null, error => res.status(404).send(error))
+        .catch(error => res.status(404).send(error))
     })
 
     router.post('/', (req, res) => {
       this
         .create(req.body)
         .then(data => res.json(data))
-        .then(null, error => res.status(404).send(error))
+        .catch(error => res.status(404).send(error))
     })
 
     router.get('/:key', (req, res) => {
       this
         .read(req.params.key)
         .then(data => res.json(data))
-        .then(null, error => res.status(404).send(error))
+        .catch(error => res.status(404).send(error))
     })
 
     router.put('/:key', (req, res) => {
       this
         .update(req.params.key, req.body)
         .then(data => res.json(data))
-        .then(null, error => res.status(404).send(error))
+        .catch(error => res.status(404).send(error))
     })
 
     router.delete('/:key', (req, res) => {
       this
         .delete(req.params.key)
         .then(data => res.json(data))
-        .then(null, error => res.status(404).send(error))
+        .catch(error => res.status(404).send(error))
     })
 
     return router
   }
 }
 
-debug('Defined RESTController')
 export default RESTController

+ 8 - 11
core/RESTController.test.js

@@ -1,3 +1,5 @@
+import * as dotenv from 'dotenv'
+dotenv.config()
 import mongoose from 'mongoose'
 import RESTController from './RESTController'
 import { Model } from '../routeGenModel'
@@ -33,9 +35,9 @@ describe('REST Controller', () => {
         // Check the copy due to a bug with jest.
         const copy = {}
         Object.keys(Model.schema.paths).forEach(key => {
-          copy[key] = response.model[key]
+          copy[key] = response[key]
         })
-        createdId = response.model._id
+        createdId = response._id
         expect(copy).toMatchObject({__v: 0, ...createdModel})
         expect(spyCreate).toHaveBeenCalledWith(createdModel)
         expect(spyCreate).toHaveBeenCalledTimes(1)
@@ -49,7 +51,7 @@ describe('REST Controller', () => {
         // Check the copy due to a bug with jest.
         const copy = {}
         Object.keys(Model.schema.paths).forEach(key => {
-          copy[key] = response.model[key]
+          copy[key] = response[key]
         })
         expect(copy).toMatchObject({__v: 0, ...createdModel})
         expect(spyFindOne).toHaveBeenCalledWith({ _id: createdId })
@@ -64,10 +66,10 @@ describe('REST Controller', () => {
         // Check the copy due to a bug with jest.
         const copy = {}
         Object.keys(Model.schema.paths).forEach(key => {
-          copy[key] = response.model[0][key]
+          copy[key] = response[0][key]
         })
         expect(copy).toMatchObject({__v: 0, ...createdModel})
-        expect(response.model).toHaveLength(1)
+        expect(response).toHaveLength(1)
         expect(spyFind).toHaveBeenCalledWith({})
         expect(spyFind).toHaveBeenCalledTimes(1)
         spyFind.mockClear()
@@ -80,7 +82,7 @@ describe('REST Controller', () => {
         // Check the copy due to a bug with jest.
         const copy = {}
         Object.keys(Model.schema.paths).forEach(key => {
-          copy[key] = response.model[key]
+          copy[key] = response[key]
         })
         expect(copy).toMatchObject({ __v: 0, ...updatedModel })
         expect(spyFindOne).toHaveBeenCalledWith({ _id: createdId })
@@ -92,11 +94,6 @@ describe('REST Controller', () => {
   it('delete elements', () => {
     return ModelController.delete(createdId)
       .then(response => {
-        /* / Check the copy due to a bug with jest.
-        const copy = {}
-        Object.keys(Model.schema.paths).forEach(key => {
-          copy[key] = response.model[key]
-        }) */
         expect(response).toMatchObject({})
         expect(spyRemove).toHaveBeenCalledWith({ _id: createdId })
         expect(spyRemove).toHaveBeenCalledTimes(1)

+ 159 - 0
core/RestControllerVersioned.js

@@ -0,0 +1,159 @@
+/**
+ * @module RESTControllerVersionned
+ *
+ * RESTController with revision control for MongoDB.
+ */
+import * as debugStuff from 'debug'
+const debug = debugStuff.debug('dbApiRESTCtrlVer')
+import RESTController from './RESTController'
+
+const MAX_RESULTS = 100
+
+/**
+ * REST controller for MongoDB backend.
+ */
+class RESTControllerVersioned extends RESTController {
+  constructor (model, key) {
+    super(model, key)
+    this.filterOptions = { limit: MAX_RESULTS, skip: 0 }
+    this.filterCriteria = { '_history.head': true }
+    debug('created RESTControllerVersioned', this.modelName)
+  }
+
+  /**
+   * Override create function to support versioning.
+   *
+   * @param  {object} data object
+   * @return {Promise}
+   */
+  create (data, author, version = '', comment = '') {
+    debug('create data item', data)
+    const history = {
+      author,
+      created: new Date(),
+      version,
+      comment,
+      head: true,
+      reference: []
+    }
+    const newData = { ...data, _history: history }
+    const p = this.model
+      .create(newData)
+      .then(instance => {
+        debug('created instance', instance)
+        return instance
+      })
+    // debug('create return value', p)
+    return p
+  }
+
+  /**
+   * Override read to search by default for head version of given tag.
+   *
+   * @param  {[type]}
+   * @return {[type]}
+   */
+  read (tag) {
+    if (typeof tag === 'string') {
+      tag = { tag: tag }
+    }
+    debug('reading data item', tag)
+    const filter = { ...this.filterCriteria, ...tag }
+
+    const p = this.model
+      .findOne(filter)
+      .then(instance => {
+        debug('read instance', instance)
+        return instance
+      })
+    // debug('read return value', p)
+    return p
+  }
+
+  /**
+   * Get a list of all DB items.
+   *
+   * @return {Promise}
+   */
+  list (filter = this.filterCriteria) {
+    debug('reading data list')
+    const p = this.model
+      .find(filter)
+      .limit(MAX_RESULTS)
+      .then(instance => {
+        debug('read list instance', instance)
+        return instance
+      })
+    // debug('read return value', p)
+    return p
+  }
+
+  /**
+   * Override update to support versioning.
+   *
+   * @param  {String} id
+   * @param  {String} data
+   * @return {Promise}
+   */
+  update (tag, data, author, version = '', comment = '') {
+    if (typeof tag === 'string') {
+      tag = { tag: tag }
+    }
+    debug('update data item', tag, data)
+    const filter = { ...this.filterCriteria, ...tag }
+
+    const p = this.model
+      .findOne(filter)
+      .then(instance => {
+        debug('found data instance', instance)
+        instance._history.head = false
+        return instance.save()
+      })
+      .then(instance => {
+        const { _id, __v, _history, ...oldData } = instance._doc
+        debug('creating new data instance', oldData)
+        const newData = { ...oldData, ...data }
+        newData._history = {
+          author,
+          created: new Date(),
+          version,
+          comment,
+          head: true,
+          reference: [ _id, ..._history.reference ]
+        }
+        return this.model
+          .create(newData)
+          .then(instance => {
+            debug('created instance', instance)
+            return instance
+          })
+      })
+    // debug('read return value', p)
+    return p
+  }
+
+  /**
+   * Override delete. We don't delete. Just not our thing...
+   *
+   * @param  {String} id
+   * @return {Promise}
+   */
+  delete (tag) {
+    if (typeof tag === 'string') {
+      tag = { tag: tag }
+    }
+    debug('delete data item', tag)
+    const filter = { ...this.filterCriteria, ...tag }
+
+    const p = this.model
+      .findOne(filter)
+      .then(instance => {
+        instance._history.head = false
+        return instance.save()
+      })
+    // debug('read return value', p)
+    return p
+  }
+}
+
+export default RESTControllerVersioned

+ 131 - 0
core/RestControllerVersioned.test.js

@@ -0,0 +1,131 @@
+import * as dotenv from 'dotenv'
+dotenv.config()
+import mongoose from 'mongoose'
+import RESTControllerVersioned from './RESTControllerVersioned'
+import { Model } from '../routeGenModel'
+
+const spyFind = jest.spyOn(Model, 'find')
+const spyFindOne = jest.spyOn(Model, 'findOne')
+const spyCreate = jest.spyOn(Model, 'create')
+const spyUpdate = jest.spyOn(Model, 'update')
+const spyRemove = jest.spyOn(Model, 'remove')
+
+describe('REST Controller Versioned', () => {
+  const ModelController = new RESTControllerVersioned(Model)
+  const createdModel = {
+    name: 'Gisele',
+    tag: 'gisele'
+  }
+  const updatedModel = {
+    name: 'Adriana'
+  }
+  const createdHistory = {
+    author: 'Tomi',
+    version: '1st shot',
+    comment: 'Nothing to brag about',
+    head: true
+  }
+  const updatedHistory = {
+    author: 'Ben',
+    version: '2nd shot',
+    comment: 'I\'m a little narcissict',
+    head: true
+  }
+  let createdId = null
+
+  beforeAll(() => {
+    return mongoose.connect('mongodb://localhost/rest-controller')
+    Model.remove({}, err => {
+      console.log(err)
+    })
+  })
+  afterAll(() => {
+  })
+
+  it('creates an element', () => {
+    return ModelController.create(createdModel, createdHistory.author, createdHistory.version, createdHistory.comment)
+      .then(response => {
+        // Check the copy due to a bug with jest.
+        const copy = JSON.parse(JSON.stringify(response))
+        createdId = response._id
+        expect(copy).toMatchObject({__v: 0, _history: createdHistory, ...createdModel })
+        expect(spyCreate).toHaveBeenCalledTimes(1)
+        spyCreate.mockClear()
+      })
+  })
+
+  it('reads an element', () => {
+    return ModelController.read(createdModel.tag)
+      .then(response => {
+        // Check the copy due to a bug with jest.
+        const copy = JSON.parse(JSON.stringify(response))
+        expect(copy).toMatchObject({__v: 0, ...createdModel})
+        expect(spyFindOne).toHaveBeenCalledWith({ '_history.head': true, tag: createdModel.tag })
+        expect(spyFindOne).toHaveBeenCalledTimes(1)
+        spyFindOne.mockClear()
+      })
+  })
+
+  it('lists elements', () => {
+    return ModelController.list()
+      .then(response => {
+        // Check the copy due to a bug with jest.
+        const copy = JSON.parse(JSON.stringify(response))
+        expect(copy[0]).toMatchObject({__v: 0, ...createdModel})
+        expect(response).toHaveLength(1)
+        // expect(spyFind).toHaveBeenCalledWith({})
+        expect(spyFind).toHaveBeenCalledTimes(1)
+        spyFind.mockClear()
+      })
+  })
+
+  it('update elements', () => {
+    return ModelController.update('gisele', updatedModel, updatedHistory.author, updatedHistory.version, updatedHistory.comment)
+      .then(response => {
+        // Check the copy due to a bug with jest.
+        const copy = JSON.parse(JSON.stringify(response))
+        expect(copy).toMatchObject({ __v: 0, ...updatedModel })
+        expect(spyFindOne).toHaveBeenCalledWith({ '_history.head': true, tag: 'gisele' })
+        expect(spyFindOne).toHaveBeenCalledTimes(1)
+        spyFindOne.mockClear()
+      })
+  })
+
+  it('delete elements', () => {
+    return ModelController.delete('gisele')
+      .then(response => {
+        // Check the copy due to a bug with jest.
+        const copy = JSON.parse(JSON.stringify(response))
+        expect(copy).toMatchObject({ tag: 'gisele', _history: { head: false } })
+        expect(spyFindOne).toHaveBeenCalledWith({ '_history.head': true, tag: 'gisele' })
+        expect(spyFindOne).toHaveBeenCalledTimes(1)
+        spyFindOne.mockClear()
+      })
+  })
+
+  it('lists elements after all operations', () => {
+    return ModelController.list()
+      .then(response => {
+        // Check the copy due to a bug with jest.
+        const copy = JSON.parse(JSON.stringify(response))
+        expect(copy).toEqual([])
+        expect(spyFind).toHaveBeenCalledWith({'_history.head': true})
+        expect(spyFind).toHaveBeenCalledTimes(1)
+        spyFind.mockClear()
+      })
+  })
+
+  it('lists elements after all operations', () => {
+    return ModelController.list({})
+      .then(response => {
+        // Check the copy due to a bug with jest.
+        const copy = JSON.parse(JSON.stringify(response))
+        expect(copy[0]).toMatchObject({ ...createdModel, _history: { head: false } })
+        expect(copy[1]).toMatchObject({ ...updatedModel, _history: { head: false } })
+        expect(response).toHaveLength(2)
+        // expect(spyFind).toHaveBeenCalledWith({})
+        expect(spyFind).toHaveBeenCalledTimes(1)
+        spyFind.mockClear()
+      })
+  })
+})

+ 2 - 2
core/basicSchema.js

@@ -12,8 +12,8 @@ import mongoose from 'mongoose'
 export const historySchema = new mongoose.Schema({
   author: { type: String, required: true },
   created: Date,
-  tag: String,
-  reference: { type: String, required: true },
+  version: String,
+  reference: [mongoose.Schema.Types.ObjectId],
   head: { type: Boolean, required: true },
   comment: String
 })

+ 0 - 303
routeGen.js

@@ -1,303 +0,0 @@
-/**
- * @module mongoRouter
- *
- * Backend module to store React-Redux components in MongoDB.
- */
-import mongoose from 'mongoose'
-import { Router } from 'express'
-mongoose.Promise = Promise
-
-const MAX_RESULTS = 100
-
-class RESTController {
-  constructor (model, key) {
-    this.model = model
-    this.modelName = model.modelName.toLowerCase()
-    this.key = key
-  }
-
-  create (data) {
-    return this.model
-      .create(data)
-      .then(instance => {
-        const response = {}
-        response[this.modelName] = instance
-        return response
-      })
-  }
-
-  read (id) {
-    const filter = {}
-    filter[this.key] = id
-    return this.model
-      .findOne(filter)
-      .then(instance => {
-        const response = {}
-        response[this.modelName] = instance
-        return response
-      })
-  }
-
-  list () {
-    return this.model
-      .find({})
-      .limit(MAX_RESULTS)
-      .then(instance => {
-        const response = {}
-        response[this.modelName] = instance
-        return response
-      })
-  }
-
-  update (id, data) {
-    const filter = {}
-    filter[this.key] = id
-
-    return this.model
-      .findOne(filter)
-      .then(instance => {
-        for (let attribute in data) {
-          if (data.hasOwnProperty(attribute) && attribute !== this.key && attribute !== '_id') {
-            instance[attribute] = data[attribute]
-          }
-        }
-        return instance.save()
-      })
-      .then(instance => {
-        const response = {}
-        response[this.modelName] = instance
-        return response
-      })
-  }
-
-  route () {
-    const router = new Router()
-
-    router.get('/', (req, res) => {
-      this
-        .list()
-        .then(data => res.json(data))
-        .then(null, error => res.status(404).send(error))
-    })
-
-    router.post('/', (req, res) => {
-      this
-        .create(req.body)
-        .then(data => res.json(data))
-        .then(null, error => res.status(404).send(error))
-    })
-
-    router.get('/:key', (req, res) => {
-      this
-        .read(req.params.key)
-        .then(data => res.json(data))
-        .then(null, error => res.status(404).send(error))
-    })
-
-    router.put('/:key', (req, res) => {
-      this
-        .update(req.params.key, req.body)
-        .then(data => res.json(data))
-        .then(null, error => res.status(404).send(error))
-    })
-
-    router.delete('/:key', (req, res) => {
-      this
-        .delete(req.params.key)
-        .then(data => res.json(data))
-        .then(null, error => res.status(404).send(error))
-    })
-
-    return router
-  }
-}
-
-export default RESTController
-
-/**
- * Generates GET, POST, UPDATE and DELETE handlers which interact with
- * MongoDB.
- *
- * For versioned objects, allow querying for tags or for IDs. When querying
- * tags, return the head version, when querying for IDs, just return the
- * element.
- *
- * @param  {object} Model - mongoose model to use
- * @param  {boolean} versioned - use a revision control mechanism
- * @return {[type]}
- */
-function routeGen (Model, versioned = true) {
-  /** GET handler (request one item or a list of items) */
-  const get = (req, res) => {
-    let { id } = req.params
-
-    /** check whether an id was specified. */
-    if (typeof id === 'string' && id.length > 1) {
-      try {
-        // try to convert the id string to a valid ObjectId
-        const _id = mongoose.Types.ObjectId(id)
-        // if yes, return the one item
-        Model.findOne({ _id }, function (err, item) {
-          console.log(item)
-          if (err) {
-            res.status(404)
-            res.send(err)
-            return
-          }
-          res.json(item)
-        })
-      } catch (err) {
-        // If id couldn't be converted, assume it's a tag.
-        const tag = id
-        // if yes, return the one item
-        Model.findOne({ tag, 'history.head': true }, function (err, item) {
-          if (err) {
-            res.status(404)
-            res.send(err)
-            return
-          }
-          res.json(item)
-        })
-      }
-    } else {
-      // if not, return a list of items
-      // limit the number of returned items and use an offset
-      const limit = req.query.limit ? Math.min(Math.max(parseInt(req.query.limit), 1), 100) : 20
-      const offset = req.query.offset ? Math.max(parseInt(req.query.offset), 0) : 0
-      Model.find({'history.head': true}, {limit: limit, skip: offset}, function (err, items) {
-        console.log(err, items)
-        if (err) {
-          res.status(404)
-          res.send(err)
-          return
-        }
-        res.json(items)
-      })
-    }
-  }
-
-  /** POST handler (insert a new item into the database) */
-  const post = (req, res) => {
-    // create the new item
-    console.log(req.body)
-    const item = new Model(req.body)
-    console.log(item)
-
-    // Check, if the model supports revision control
-    if (typeof Model.schema.obj.__history !== 'undefined') {
-      console.log('creating new history')
-      item.__history = {
-        author: 'Tomi',
-        created: new Date(),
-        tag: 'myTag',
-        reference: 'reference',
-        head: true,
-        comment: 'My Comment'
-      }
-    }
-    console.log(item)
-
-    // save the item
-    console.log('before save')
-    const p = item.save()
-    console.log(p)
-    p.then(savedItem => {
-      console.log('in save', savedItem)
-      res.status(200)
-      res.send({ _id: savedItem._id })
-    }).catch(err => {
-      console.log('in save', err)
-      res.status(422)
-      res.send(err)
-    })
-    console.log('after save', p)
-  }
-
-  /** PUT handler (update existing project) */
-  const put = (req, res) => {
-    let id = req.params['0']
-    /** check whether an id was specified. */
-    if (typeof id === 'string' && id.length > 1) {
-      // remove leading slash
-      id = id.substring(1)
-      try {
-        // try to convert the id string to a valid ObjectId
-        id = mongoose.Types.ObjectId(req.params['1'].substring(1))
-      } catch (err) {
-        // If id couldn't be converted, return an error message.
-        res.status(422)
-        res.send({error: err.message})
-        return
-      }
-    }
-
-    // Find the object in the database
-    Model.findOne({ _id: id }, function (err, item) {
-      if (err) {
-        res.status(404)
-        res.send(err)
-      }
-
-      // Check, if the model supports revision control
-      if (typeof Model.schema.obj.__history !== 'undefined') {
-        // If yes, don't update the item, but create a new one based on the original
-        const newItem = Model(item)
-        // replace the requested elements
-        for (let prop in req.body) {
-          newItem[prop] = req.body[prop]
-        }
-        // give it a new history with updated reference array. Set the head-indicator to true
-        newItem.__history.create({
-          author: '',
-          created: Date(),
-          tag: '',
-          reference: [ ...item.__history.reference, item._id ],
-          head: true
-        })
-        // set the original item's head indicator to false
-        item.__history.head = false
-        // save the documents
-        newItem.save(function (err) {
-          if (err) {
-            res.status(422)
-            res.send(err)
-          }
-          res.json({ message: `${Model.modelName} updated.` })
-        })
-      } else {
-        for (let prop in req.body) {
-          item[prop] = req.body[prop]
-        }
-      }
-
-      item.save(function (err) {
-        if (err) {
-          res.status(422)
-          res.send(err)
-        }
-        res.json({ message: `${Model.modelName} updated.` })
-      })
-    })
-  }
-
-  /** DELETE handler (delete project) */
-  const del = (req, res) => {
-    const id = mongoose.Types.ObjectId(req.params['0'])
-
-    Model.remove({ _id: id }, function (err, project) {
-      if (err) {
-        res.send(err)
-      }
-      res.json({ message: 'Movie deleted.' })
-    })
-  }
-
-  return {
-    get,
-    post,
-    put,
-    del
-  }
-}
-
-export { routeGen }

+ 0 - 97
routeGen.test.js

@@ -1,97 +0,0 @@
-import mongoose from 'mongoose'
-const table = '__routeGenTest__'
-const db = `mongodb://localhost/${table}`
-mongoose.connect(db)
-
-import routeGen from './routeGen'
-import Model from './routeGenModel'
-import { model, agency, newModel } from './_samples'
-// jest.mock('./projects/model')
-
-// import Project from './projects/model'
-const spyFindOne = jest.spyOn(Model, 'findOne')
-const spyFind = jest.spyOn(Model, 'find')
-// const spySave = jest.spyOn(Model, 'save')
-const spyRemove = jest.spyOn(Model, 'remove')
-
-class Result {
-  constructor () {
-    this._body = null
-    this._status = null
-  }
-  status (value) {
-    this._status = value
-  }
-  send (data) {
-    console.log('SEND', data)
-    this._body = data
-  }
-  json (data) {
-    console.log('JSON', data)
-    this._status = 200
-    this._body = data
-  }
-}
-
-describe('routeGen for versioned models', () => {
-  const route = routeGen(Model)
-  const res = new Result()
-  it('generates the handlers', () => {
-    // Generate a version controlled model
-    expect(route.get).not.toBeUndefined()
-    expect(route.post).not.toBeUndefined()
-    expect(route.put).not.toBeUndefined()
-    expect(route.del).not.toBeUndefined()
-  })
-  describe('get handler', () => {
-    it('get without arguments returns a list of items', () => {
-      // call the GET handler
-      const reqList = { params: {}, query: { limit: 2.1, offset: 1.1 } }
-      route.get(reqList, res)
-      expect(res._body).toEqual(model.slice(1, 3))
-      expect(res._status).toBe(200)
-      expect(spyFind).toHaveBeenCalledWith({'history.head': true}, {limit: 2, skip: 1}, expect.anything())
-    })
-    it('return empty lists.', () => {
-      // call the GET handler
-      const reqList = { params: {}, query: { limit: 1, offset: 100 } }
-      route.get(reqList, res)
-      expect(res._body).toEqual([])
-      expect(res._status).toBe(200)
-    })
-    it('get with arguments returns queried item by id', () => {
-      const reqElem = { params: { id: '18a0d6883d34293254afae42' }, query: {} }
-      route.get(reqElem, res)
-      expect(res._body).toEqual(projects[0])
-      expect(res._status).toBe(200)
-    })
-    it('get with arguments return queried item by tag (only HEAD in history)', () => {
-      const reqElem = { params: { id: 'JU_CSD' }, query: {} }
-      route.get(reqElem, res)
-      expect(res._body).toEqual(projects[1])
-      expect(res._status).toBe(200)
-    })
-    it('get returns error, when item is not found', () => {
-      const reqElem = { params: { id: '99a0d6883d34293254afae42' }, query: {} }
-      route.get(reqElem, res)
-      expect(res._body).toEqual(Error('item not found'))
-      expect(res._status).toBe(404)
-      console.log(spyFind.mock, spyFindOne.mock)
-      expect(spyFindOne.mock).toBe(true)
-    })
-  })
-  describe('post handler', () => {
-    it('post creates a new history object', () => {
-      console.log(newModel)
-      const reqElem = { body: newModel }
-      route.post(reqElem, res)
-      console.log(res)
-      expect(res._body).toEqual({_id: expect.anything()})
-      expect(res._status).toBe(200)
-      console.log(res)
-      route.get({params: {id: res._body}, query: {}}, res)
-      // expect(res._body).toEqual(true)
-      console.log(res)
-    })
-  })
-})

+ 4 - 2
routeGenModel.js

@@ -4,7 +4,7 @@ import RESTController from './core/RESTController'
 
 const agencySchema = new mongoose.Schema({
   name: String,
-  __history: [historySchema]
+  _history: historySchema
 })
 
 const jobSchema = new mongoose.Schema({
@@ -14,7 +14,9 @@ const jobSchema = new mongoose.Schema({
 })
 
 const modelSchema = new mongoose.Schema({
-  name: String
+  name: String,
+  tag: String,
+  _history: historySchema
 })
 
 const Agency = mongoose.model('Agency', agencySchema)