瀏覽代碼

work in progress.

Tomislav Cvetic 8 年之前
父節點
當前提交
175324915f
共有 7 個文件被更改,包括 421 次插入62 次删除
  1. 146 0
      core/RESTController.js
  2. 82 0
      core/RESTController.test.js
  3. 5 52
      index.js
  4. 3 1
      package.json
  5. 111 3
      routeGen.js
  6. 20 6
      routeGenModel.js
  7. 54 0
      server.js

+ 146 - 0
core/RESTController.js

@@ -0,0 +1,146 @@
+/**
+ * @module RESTController
+ *
+ * RESTController for MongoDB.
+ */
+import * as debugStuff from 'debug'
+const debug = debugStuff.debug('dbApiRESTCtrl')
+import mongoose from 'mongoose'
+mongoose.Promise = Promise
+import { Router } from 'express'
+
+const MAX_RESULTS = 100
+
+/**
+ * REST controller for MongoDB backend.
+ */
+class RESTController {
+  /**
+   * Generates a controller for a model type.
+   *
+   * @param  {mongoose.Model} mongoose model to use
+   * @param  {key} Key to use as index. Default is '_id'
+   * @return {null}
+   */
+  constructor (model, key = '_id') {
+    this.model = model
+    this.modelName = model.modelName.toLowerCase()
+    this.key = key
+  }
+
+  /**
+   * Creates a DB item.
+   *
+   * @param  {object} data object
+   * @return {null}
+   */
+  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 => {
+        console.log(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
+      })
+  }
+
+  delete (id) {
+    const filter = {}
+    filter[this.key] = id
+
+    return this.model
+      .remove(filter)
+      .then(() => {
+        return {}
+      })
+  }
+
+  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
+  }
+}
+
+debug('Defined RESTController')
+export default RESTController

+ 82 - 0
core/RESTController.test.js

@@ -0,0 +1,82 @@
+import mongoose from 'mongoose'
+import RESTController from './RESTController'
+import { Model } from '../routeGenModel'
+
+const spyFind = jest.spyOn(Model, 'find')
+const spyFindOne = jest.spyOn(Model, 'findOne')
+const spyCreate = jest.spyOn(Model, 'create')
+const spyRemove = jest.spyOn(Model, 'remove')
+
+describe('REST Controller', () => {
+  mongoose.connect('mongodb://localhost/rest-controller')
+  const ModelController = new RESTController(Model)
+  const createdModel = {
+    name: 'Gisele'
+  }
+  const updatedModel = {
+    name: 'Adriana'
+  }
+  let createdId = null
+
+  it('creates an element', () => {
+    return ModelController.create(createdModel)
+      .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]
+        })
+        createdId = response.model._id
+        expect(copy).toMatchObject({__v: 0, ...createdModel})
+        expect(spyCreate).toHaveBeenCalledWith(createdModel)
+        expect(spyCreate).toHaveBeenCalledTimes(1)
+        spyCreate.mockReset()
+      })
+  })
+
+  it('reads an element', () => {
+    return ModelController.read(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(copy).toMatchObject({__v: 0, ...createdModel})
+        expect(spyFindOne).toHaveBeenCalledWith({ _id: createdId })
+        expect(spyFindOne).toHaveBeenCalledTimes(1)
+        spyFindOne.mockReset()
+      })
+  })
+
+  it('lists elements', () => {
+    return ModelController.list()
+      .then(response => {
+        // Check the copy due to a bug with jest.
+        const copy = {}
+        Object.keys(Model.schema.paths).forEach(key => {
+          copy[key] = response.model[0][key]
+        })
+        expect(copy).toMatchObject({__v: 0, ...createdModel})
+        expect(response.model).toHaveLength(2)
+        expect(spyFind).toHaveBeenCalledWith({})
+        expect(spyFind).toHaveBeenCalledTimes(1)
+        spyFind.mockReset()
+      })
+  })
+
+  it('update elements', () => {
+    return ModelController.update(createdId, { name: 'Adriana' })
+      .then(response => {
+        // Check the copy due to a bug with jest.
+        const copy = {}
+        Object.keys(Model.schema.paths).forEach(key => {
+          copy[key] = response.model[0][key]
+        })
+        expect(copy).toMatchObject({__v: 0, ...updatedModel})
+        expect(spyFind).toHaveBeenCalledWith({})
+        expect(spyFind).toHaveBeenCalledTimes(1)
+        spyFind.mockReset()
+      })
+  })
+})

+ 5 - 52
index.js

@@ -1,52 +1,5 @@
-const express = require('express')
-const bodyParser = require('body-parser')
-const http = require('http')
-// const oauthserver = require('oauth2-server')
-const mongoose = require('mongoose')
-
-/** Load the submodules */
-import projects from './projects/route'
-
-/** Create the express app */
-const app = express()
-
-/** MongoDB middleware */
-const dbName = 'AutoMateDB'
-const connectionString = `mongodb://localhost:27017/${dbName}`
-
-/** Bind the http server to express */
-const server = http.createServer(app)
-
-/** Connect the middleware to the server */
-mongoose.connect(connectionString)
-
-function welcomeRouter (req, res) {
-  res.status(200)
-  res.json({ message: 'Welcome to the AutoMate DB API!' })
-}
-function errorRouter (req, res) {
-  res.status(404)
-  res.send()
-}
-
-app.use(bodyParser.json())
-app.use(bodyParser.urlencoded({ extended: true }))
-
-/*
-app.oauth = oauthserver({
-  model: {},
-  grants: ['password'],
-  debug: true
-})
-app.all('/oauth/token', app.oauth.grant())
-app.get('/', app.oauth.authorise(), (req, res) => {
-  res.send('Secret area')
-})
-app.use(app.oauth.errorHandler())
-*/
-
-app.get('/', welcomeRouter)
-app.use('/projects', projects)
-app.use(/.*/, errorRouter)
-
-server.listen(process.env.PORT || 4100)
+'use strict'
+require('dotenv').config()
+require('babel-core/register')({})
+require('babel-polyfill')
+require('./server')

+ 3 - 1
package.json

@@ -4,7 +4,7 @@
   "description": "API Server for Mongo DB",
   "main": "index.js",
   "scripts": {
-    "start": "babel-node index.js",
+    "start": "nodemon index.js",
     "test": "jest --watch --env=jsdom .",
     "doc": "jsdoc -c jsdoc.json -d code_docu -r ."
   },
@@ -17,12 +17,14 @@
     "babel-plugin-transform-object-rest-spread": "^6.23.0",
     "babel-preset-latest": "^6.24.0",
     "body-parser": "^1.17.1",
+    "debug": "^2.6.3",
     "dotenv": "^4.0.0",
     "express": "^4.15.2",
     "jest-cli": "^19.0.2",
     "jsdoc": "3.4.3",
     "mongoose": "^4.9.1",
     "node-babel": "^0.1.2",
+    "nodemon": "^1.11.0",
     "oauth2-server": "^2.4.1",
     "sinon": "^2.1.0"
   }

+ 111 - 3
routeGen.js

@@ -4,7 +4,115 @@
  * Backend module to store React-Redux components in MongoDB.
  */
 import mongoose from 'mongoose'
-mongoose.Promise = global.Promise
+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
@@ -97,7 +205,7 @@ function routeGen (Model, versioned = true) {
       console.log('in save', savedItem)
       res.status(200)
       res.send({ _id: savedItem._id })
-    }, err => {
+    }).catch(err => {
       console.log('in save', err)
       res.status(422)
       res.send(err)
@@ -192,4 +300,4 @@ function routeGen (Model, versioned = true) {
   }
 }
 
-export default routeGen
+export { routeGen }

+ 20 - 6
routeGenModel.js

@@ -1,5 +1,6 @@
 import mongoose from 'mongoose'
 import { historySchema } from './core/basicSchema'
+import RESTController from './core/RESTController'
 
 const agencySchema = new mongoose.Schema({
   name: String,
@@ -12,11 +13,24 @@ const jobSchema = new mongoose.Schema({
   salery: Number
 })
 
-export const modelSchema = new mongoose.Schema({
-  name: String,
-  jobs: [jobSchema],
-  agencies: String,
-  __history: historySchema
+const modelSchema = new mongoose.Schema({
+  name: String
 })
 
-export default mongoose.model('Model', modelSchema)
+const Agency = mongoose.model('Agency', agencySchema)
+const Job = mongoose.model('Job', jobSchema)
+const Model = mongoose.model('Model', modelSchema)
+
+export { Agency, Job, Model }
+
+export class AgencyController extends RESTController {
+  constructor () {
+    super(Agency, '_id')
+  }
+}
+
+export class ModelController extends RESTController {
+  constructor () {
+    super(Model, 'name')
+  }
+}

+ 54 - 0
server.js

@@ -0,0 +1,54 @@
+import * as debugStuff from 'debug'
+const debug = debugStuff.debug('dbApiServer')
+debug('Starting the dbApiServer')
+const express = require('express')
+debug(' - Loaded express')
+const bodyParser = require('body-parser')
+debug(' - Loaded bodyParser')
+const http = require('http')
+debug(' - Loaded http')
+// const oauthserver = require('oauth2-server')
+const mongoose = require('mongoose')
+debug(' - Loaded mongoose')
+
+/** Create the express app */
+const app = express()
+debug('Created app')
+
+/** Bind the http server to express */
+const server = http.createServer(app)
+debug('Created server')
+
+/** MongoDB middleware */
+const dbName = 'AutoMateDB'
+const connectionString = `mongodb://localhost:27017/${dbName}`
+
+/** Connect the middleware to the server */
+mongoose.connect(connectionString)
+debug('Connected DB')
+
+function welcomeRouter (req, res) {
+  res.status(200)
+  res.json({ message: 'Welcome to the AutoMate DB API!' })
+}
+function errorRouter (req, res) {
+  res.status(404)
+  res.send()
+}
+debug('Defined default routers')
+
+app.use(bodyParser.json())
+app.use(bodyParser.urlencoded({ extended: true }))
+
+/** Load the submodules */
+// import projects from './projects/route'
+import routeGenModel from './routeGenModel'
+
+app.get('/', welcomeRouter)
+// app.use('/projects', projects)
+app.use(/.*/, errorRouter)
+debug('Configured app')
+
+const port = process.env.PORT || 4100
+server.listen(port)
+debug('Started server on port %s', port)