diff --git a/app/config/config.js b/app/config/config.js new file mode 100644 index 0000000..dc9f2e4 --- /dev/null +++ b/app/config/config.js @@ -0,0 +1,34 @@ + +const RES = { + + INVALIDIDSUPPLIED:{ + code: 400, + description: "Invalid ID supplied" + }, + PLAYERNOTFOUND:{ + code: 404, + description: "Player not found" + }, + INVALIDINPUT:{ + code: 405, + description: "Invalid input" + }, + VALIDATIONEXCEPTION:{ + code: 405, + description: "Validation exception" + }, + SUCCESS:{ + code: 200, + description: "success" + }, + SUCCESSANDDATA:{ + code: 200, + description: "success", + data: null + } +} + +module.exports={ + RES + +} diff --git a/app/controller/player.js b/app/controller/player.js new file mode 100644 index 0000000..ebe30b6 --- /dev/null +++ b/app/controller/player.js @@ -0,0 +1,79 @@ +'use strict'; + +const db = require('../lib/mongodb.js'); +const RES = require('../config/config.js').RES; +const conllection = `NBA`; +const Enum = [ 'C', 'PF', 'SF', 'PG', 'SG' ]; + let addPlayer = async function addPlayer (req, res, next) { + let {id,name,position}=req.body; + if(!name||!position||!id || id*1 < 0){ + res.send(RES.INVALIDINPUT); + return; + } + if(Enum.indexOf(position) == -1){ + res.send(RES.VALIDATIONEXCEPTION); + return; + } + await db.insertOne(conllection,{id,name,position}); + res.send(RES.SUCCESS); +}; + + let deletePlayer = async function deletePlayer (req, res, next) { + let {playerid}=req.params; + if(playerid == '' || playerid == null){ + res.send(RES.INVALIDIDSUPPLIED); + return; + } + let getUserResult = await db.find(conllection,{id:playerid}); + if(getUserResult.length <= 0){ + res.send(RES.PLAYERNOTFOUND); + return; + } + await db.deleteOne(conllection,{id:playerid}); + res.send(RES.SUCCESS) +}; + + let getPlayerById = async function getPlayerById (req, res, next) { + let {playerid} = req.params; + let whereObject = ''; + if(playerid !='' && playerid != null && playerid*1 >0){ + whereObject = {id:playerid}; + } + let result = await db.find(conllection,whereObject); + if(result.length <= 0){ + res.send(RES.PLAYERNOTFOUND); + return; + } + let success = RES.SUCCESSANDDATA; + success.data = result; + res.send(success); + +}; + +let updatePlayer = async function updatePlayer (req, res, next) { + let {id,name,position}=req.body; + if(id == '' || id === null || id*1<0){ + res.send(RES.INVALIDIDSUPPLIED); + return; + } + let result = await db.find(conllection,{id:id}); + if(result.length<=0){ + res.send(RES.PLAYERNOTFOUND); + return; + } + console.log(position) + console.log(Enum.indexOf(position)) + if(Enum.indexOf(position) == -1){ + res.send(RES.VALIDATIONEXCEPTION); + return; + } + await db.update(conllection,{id:id},{$set:{name,position}}); + res.send(RES.SUCCESS); + +}; +module.exports ={ + addPlayer, + deletePlayer, + getPlayerById, + updatePlayer +} diff --git a/app/lib/mongodb.js b/app/lib/mongodb.js new file mode 100644 index 0000000..8fe2bec --- /dev/null +++ b/app/lib/mongodb.js @@ -0,0 +1,78 @@ +const MongoClient = require('mongodb').MongoClient; +const MONGOURL = 'mongodb://localhost:27017/";'; +const DBNAME = 'NBA'; + +let insertOne = async function(collection,data){ + return new Promise((resolve,reject)=>{ + MongoClient.connect(MONGOURL, function(err, db) { + if (err) throw err; + let dbo = db.db(DBNAME); + dbo.collection(collection).insertOne(data, function(err, res) { + if (err){ + throw err; + } else{ + db.close(); + resolve(res) + } + + }); + }); + }); +} + +let find = async function(collection,whereObject={}){ + return new Promise((resolve,reject)=>{ + MongoClient.connect(MONGOURL, function(err, db) { + if (err) throw err; + let dbo = db.db(DBNAME); + dbo.collection(collection).find(whereObject).toArray(function(err, res) { + if (err){ + throw err; + } else{ + db.close(); + resolve(res) + } + }); + }); + }); +} + +let update = async function(collection,whereObject={},updateObject){ + return new Promise((resolve,reject)=>{ + MongoClient.connect(MONGOURL, function(err, db) { + if (err) throw err; + let dbo = db.db(DBNAME); + dbo.collection(collection).update(whereObject, updateObject, function(err, res) { + if (err){ + throw err; + } else{ + db.close(); + resolve(res) + } + }); + }); + }); +} + +let deleteOne = async function(collection,whereObject={}){ + return new Promise((resolve,reject)=>{ + MongoClient.connect(MONGOURL, function(err, db) { + if (err) throw err; + let dbo = db.db(DBNAME); + dbo.collection(collection).deleteOne(whereObject, function(err, obj) { + if (err){ + throw err; + } else{ + db.close(); + resolve(obj) + } + }); + }); + }); +} +module.exports={ + insertOne, + find, + update, + deleteOne +} diff --git a/app/package.json b/app/package.json index 6b480b3..34369fc 100755 --- a/app/package.json +++ b/app/package.json @@ -1,20 +1,25 @@ { "private": true, "name": "node-examination", - "version": "0.0.1", + "version": "1.0.0", "description": "Building a RESTful CRUD API with Node.js, Express/Koa and MongoDB.", "main": "server.js", "scripts": { - "start": "NODE_ENV=development node server.js", - "start:prod": "NODE_ENV=production node server.js", - "test": "echo \"Error: no test specified\" && exit 1" + "start": "cross-env NODE_ENV=development node server.js", + "start:prod": "cross-env NODE_ENV=production node server.js", + "test": "mocha --no-timeouts --recursive ./test/test.js" }, "dependencies": { + "cross-env": "^7.0.3", "express": "^4.17.1", - "mongoose": "^5.9.2" + "mocha-junit-reporter": "^2.0.2", + "mongoose": "^5.9.2", + "request": "^2.88.2", + "supertest": "^6.2.2" }, "devDependencies": { - "chai": "^4.2.0" + "chai": "^4.2.0", + "mocha": "3.0.2" }, "engines": { "node": ">=10.15.0" diff --git a/app/router/v1/playerRouter.js b/app/router/v1/playerRouter.js new file mode 100644 index 0000000..a8f42d4 --- /dev/null +++ b/app/router/v1/playerRouter.js @@ -0,0 +1,21 @@ +const express=require('express'); +const playerController=require('../../controller/player.js'); +const router=express.Router(); +router.post('/',(req,res)=>{ + playerController.addPlayer(req,res) +}) +router.delete('/:playerid',(req,res)=>{ + playerController.deletePlayer(req,res) +}) +router.get('/:playerid',(req,res)=>{ + playerController.getPlayerById(req,res) +}) +router.get('/',(req,res)=>{ + playerController.getPlayerById(req,res) +}) +router.put('/',(req,res)=>{ + playerController.updatePlayer(req,res) +}) + + +module.exports=router; diff --git a/app/server.js b/app/server.js index 72e5b39..df78ce3 100755 --- a/app/server.js +++ b/app/server.js @@ -1,11 +1,35 @@ const express = require('express'); const app = express(); +const bodyParser = require('body-parser'); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); + +const playerRouter=require('./router/v1/playerRouter.js') +app.use('/api/v1/player',playerRouter) app.get('/', (req, res) => { res.json({"message": "Building a RESTful CRUD API with Node.js, Express/Koa and MongoDB."}); }); app.listen(3000, () => { console.log("Server is listening on port 3000"); -}); \ No newline at end of file +}); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + next(createError(404)); +}); + +// error handler +app.use(function(err, req, res, next) { + // set locals, only providing error in development + res.locals.message = err.message; + res.locals.error = req.app.get('env') === 'development' ? err : {}; + + // render the error page + res.status(err.status || 500); + res.render('error'); +}); + + diff --git a/app/test/test.js b/app/test/test.js new file mode 100644 index 0000000..692de53 --- /dev/null +++ b/app/test/test.js @@ -0,0 +1,232 @@ +let request = require('request'); +let expect = require('chai').expect; +const httpHost = 'http://localhost:3000/api/v1'; +const RES = require('../config/config.js').RES; + +// 添加球员 +describe('1.添加球员 POST: /player', function () { + it('测试描述: 参数正确', function (done) { + request({ + url: httpHost + '/player', + method: 'POST', + json: true, + headers: { + 'content-type': 'application/json', + }, + body: { + id: 100, + name: 'qiaodan', + position: 'C' + } + }, function (error, response, body) { + expect(!error && response.statusCode === 200); + expect(body.code === RES.SUCCESS.code); + done(); + }); + }); + it('测试描述: 参数错误', function (done) { + request({ + url: httpHost + '/player', + method: 'POST', + json: true, + headers: { + 'content-type': 'application/json', + }, + body: { + id: '101', + name: 'cilo', + position: 'SFF' + } + }, function (error, response, body) { + expect(!error && response.statusCode === 200); + expect(body.code === RES.INVALIDINPUT.code); + done(); + }); + }); +}); + +// 修改球员 +describe('2.修改球员 PUT /player', function () { + it('测试描述: 参数正确', function (done) { + request({ + url: httpHost + '/player', + method: 'PUT', + json: true, + headers: { + 'content-type': 'application/json', + }, + body: { + id: 100, + name: 'qiaodan', + position: 'C' + } + }, function (error, response, body) { + expect(!error && response.statusCode === 200); + expect(body.code === RES.SUCCESS.code); + done(); + }); + }); + it('测试描述: id错误', function (done) { + request({ + url: httpHost + '/player', + method: 'PUT', + json: true, + headers: { + 'content-type': 'application/json', + }, + body: { + id: -1, + name: 'qiaodan', + position: 'C' + } + }, function (error, response, body) { + expect(!error && response.statusCode === 200); + expect(body.code === RES.INVALIDIDSUPPLIED.code); + done(); + }); + }); + it('测试描述: 球员不存在', function (done) { + request({ + url: httpHost + '/player', + method: 'PUT', + json: true, + headers: { + 'content-type': 'application/json', + }, + body: { + id: 101, + name: 'qiaodan', + position: 'C' + } + }, function (error, response, body) { + expect(!error && response.statusCode === 200); + expect(body.code === RES.PLAYERNOTFOUND.code); + done(); + }); + }); + it('测试描述: 参数错误', function (done) { + request({ + url: httpHost + '/player', + method: 'PUT', + json: true, + headers: { + 'content-type': 'application/json', + }, + body: { + id: 100, + name: 'make', + position: 'SFF' + } + }, function (error, response, body) { + expect(!error && response.statusCode === 200); + expect(body.code === RES.VALIDATIONEXCEPTION.code); + done(); + }); + }); +}); + +// 获取球员 +describe('3.获取球员 GET /player/:playId or /player', function () { + it('测试描述: 参数正确', function (done) { + request({ + url: httpHost + '/player/100', + method: 'GET', + json: true, + headers: { + 'content-type': 'application/json', + } + }, function (error, response, body) { + expect(!error && response.statusCode === 200); + expect(body.code === RES.SUCCESS.code); + done(); + }); + }); + it('测试描述: 无参数', function (done) { + request({ + url: httpHost + '/player', + method: 'GET', + json: true, + headers: { + 'content-type': 'application/json', + } + }, function (error, response, body) { + expect(!error && response.statusCode === 200); + expect(body.code === RES.SUCCESS.code); + done(); + }); + }); + it('测试描述: id错误', function (done) { + request({ + url: httpHost + '/player/-1', + method: 'GET', + json: true, + headers: { + 'content-type': 'application/json', + } + }, function (error, response, body) { + expect(!error && response.statusCode === 200); + expect(body.code === RES.INVALIDIDSUPPLIED.code); + done(); + }); + }); + it('测试描述: 球员不存在', function (done) { + request({ + url: httpHost + '/player/999', + method: 'GET', + json: true, + headers: { + 'content-type': 'application/json', + } + }, function (error, response, body) { + expect(!error && response.statusCode === 200); + expect(body.code === RES.PLAYERNOTFOUND.code); + done(); + }); + }); +}); + +// 删除球员 +describe('4.删除球员 DELETE /player/:playId', function () { + it('测试描述: 参数正确', function (done) { + request({ + url: httpHost + '/player/100', + method: 'DELETE', + json: true, + headers: { + 'content-type': 'application/json', + } + }, function (error, response, body) { + expect(!error && response.statusCode === 200); + expect(body.code === RES.SUCCESS.code); + done(); + }); + it('测试描述: id错误', function (done) { + request({ + url: httpHost + '/player/-1', + method: 'DELETE', + json: true, + headers: { + 'content-type': 'application/json', + } + }, function (error, response, body) { + expect(!error && response.statusCode === 200); + expect(body.code === RES.INVALIDIDSUPPLIED.code); + done(); + }); + }); + it('测试描述: 球员不存在', function (done) { + request({ + url: httpHost + '/player/999', + method: 'DELETE', + json: true, + headers: { + 'content-type': 'application/json', + } + }, function (error, response, body) { + expect(!error && response.statusCode === 200); + expect(body.code === RES.PLAYERNOTFOUND.code); + done(); + }); + }); + }); +});