Skip to content

Node Examination - Wade Wu #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"presets": ["@babel/preset-env"],
"plugins": [["@babel/plugin-transform-runtime"]]
}
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,6 @@ bin/Release
.idea

# Mac files
.DS_Store
.DS_Store
dist
config.js
23 changes: 19 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,31 @@
"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": "NODE_ENV=development nodemon --exec babel-node src/server.js",
"build": "babel src --out-dir dist",
"clean": "rimraf dist",
"serve": "NODE_ENV=production node dist/server.js",
"test": "NODE_ENV=test mocha --require @babel/register --exit"
},
"dependencies": {
"@babel/runtime": "^7.7.7",
"body-parser": "^1.19.0",
"express": "^4.16.4",
"express-validator": "^6.3.0",
"mongoose": "^5.4.8"
},
"devDependencies": {
"chai": "^4.2.0"
"@babel/cli": "^7.7.7",
"@babel/core": "^7.7.7",
"@babel/node": "^7.7.7",
"@babel/plugin-transform-runtime": "^7.7.6",
"@babel/preset-env": "^7.7.7",
"@babel/register": "^7.7.7",
"mocha": "^6.2.2",
"mongodb-memory-server": "^6.2.0",
"nodemon": "^2.0.2",
"rimraf": "^3.0.0",
"should": "^13.2.3"
},
"engines": {
"node": ">=10.15.0"
Expand Down
11 changes: 0 additions & 11 deletions server.js

This file was deleted.

10 changes: 10 additions & 0 deletions src/controllers/error_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { validationResult } from "express-validator";

export const playerValidationErrors = (err, req, res, next) => {
const errors = validationResult(req).formatWith(({ param, msg }) => ({
[param]: msg
}));
if (!errors.isEmpty())
res.status(405).json(errors.array({ onlyFirstError: true }));
else res.status(400).json({ errors: "something went wrong" });
};
50 changes: 50 additions & 0 deletions src/controllers/player_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {
findAllPlayers,
findPlayerById,
createPlayer,
updatePlayer,
deletePlayer
} from "../services/player_service";

export default {
getAll: async (req, res, next) => {
const players = await findAllPlayers();
res.json(players);
},
getById: async (req, res, next) => {
try {
const { playerId } = req.params;
const player = await findPlayerById(playerId);
res.json(player);
} catch (e) {
next(e);
}
},
create: async (req, res, next) => {
try {
const data = req.body;
const result = await createPlayer(data);
res.json(result);
} catch (e) {
next(e);
}
},
update: async (req, res, next) => {
try {
const data = req.body;
const result = await updatePlayer(data);
res.json(result);
} catch (e) {
next(e);
}
},
delete: async (req, res, next) => {
try {
const { playerId } = req.params;
const result = await deletePlayer(playerId);
res.json(result);
} catch (e) {
next(e);
}
}
};
5 changes: 5 additions & 0 deletions src/dao/database/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const mongodbUri = {
test: "",
development: "mongodb://127.0.0.1:27017/054cf1d4-0eac-407d-ab22-d33197b1d306?",
production: "mongodb://127.0.0.1:27017/9f0710f2-be19-4045-954b-a2e3fcdcc639?"
};
15 changes: 15 additions & 0 deletions src/dao/database/mongodb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import mongoose from "mongoose";
import { mongodbUri } from "./config";

export const connectDatabase = () => {
const uri = mongodbUri[process.env.NODE_ENV];

if (!uri || uri.length === 0) throw new Error("Undefined node environment");

mongoose.connect(uri, { useNewUrlParser: true, useUnifiedTopology: true });
mongoose.connection.on("error", e =>
console.error("MongoDB connection error.", e.toString())
);
};

export default mongoose;
22 changes: 22 additions & 0 deletions src/dao/player.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import mongoose from "./database/mongodb";

const schema = mongoose.Schema(
{
id: {
type: Number
},
name: {
type: String,
required: [true, "name is required"]
},
position: {
type: String,
enum: ["C", "PF", "SF", "PG", "SG"]
}
},
{
timestamps: true
}
);

export default mongoose.model("Player", schema);
20 changes: 20 additions & 0 deletions src/routers/player_router.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import express from "express";
import player from "../controllers/player_controller";
import { playerIdSchema, playerSchema } from "../utils/validation_schema";
import { playerValidationErrors } from "../controllers/error_controller";

const router = express.Router();

router.get("/", player.getAll);

router.get("/:playerId", playerIdSchema, player.getById);

router.post("/", playerSchema, player.create);

router.put("/", playerSchema, player.update);

router.delete("/:playerId", playerIdSchema, player.delete);

router.use(playerValidationErrors);

export default router;
24 changes: 24 additions & 0 deletions src/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import express from "express";
import bodyParser from "body-parser";
import playerRouter from "./routers/player_router";
import { connectDatabase } from "./dao/database/mongodb";

const app = express();
const port = process.env.NODE_ENV === "production" ? 80 : 3000;

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use("/player", playerRouter);

app.get("/", (req, res) => {
res.json({
message:
"Building a RESTful CRUD API with Node.js, Express/Koa and MongoDB."
});
});

app.listen(port, () => {
console.log(`Server is listening on port ${port}`);
});

connectDatabase();
26 changes: 26 additions & 0 deletions src/services/player_service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Player from "../dao/player";

export const findAllPlayers = async () => {
const players = await Player.find();
return players;
};

export const findPlayerById = async id => {
const player = await Player.findOne({ id });
return player;
};

export const createPlayer = async doc => {
const result = await Player.create(doc);
return result;
};

export const updatePlayer = async doc => {
const result = await Player.updateOne({ id: doc.id }, doc);
return result;
};

export const deletePlayer = async id => {
const result = await Player.deleteOne({ id });
return result;
};
34 changes: 34 additions & 0 deletions src/utils/validation_schema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { checkSchema } from "express-validator";

export const playerIdSchema = checkSchema({
playerId: {
in: "params",
exists: true,
errorMessage: "must provide a player id"
}
});

export const playerSchema = checkSchema({
id: {
in: "body",
optional: true,
isInt: {
errorMessage: "id must be an integer"
}
},
name: {
in: "body",
exists: true,
isString: true,
notEmpty: true,
errorMessage: "name is required"
},
position: {
in: "body",
optional: true,
matches: {
options: [/\b(?:C|PF|SF|PG|SG)\b/],
errorMessage: "position must be one of C, PF, SF, PG, SG"
}
}
});
55 changes: 55 additions & 0 deletions test/player_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import should from "should";
import { MongoMemoryServer } from "mongodb-memory-server";
import { mongodbUri } from "../src/dao/database/config";
import { connectDatabase } from "../src/dao/database/mongodb";
import {
createPlayer,
findPlayerById,
updatePlayer,
deletePlayer
} from "../src/services/player_service";

describe("#NBA player RESTful API", async () => {
before(async () => {
const mongod = new MongoMemoryServer();
const uri = await mongod.getConnectionString();
mongodbUri.test = uri;
connectDatabase();
});

it("should return a object when the player has been created", async () => {
const doc = {
id: 123,
name: "Wade",
position: "PG"
};
const player = await createPlayer(doc);
should(player).have.property("id", 123);
should(player).have.property("name", "Wade");
should(player).have.property("position", "PG");
});

it("should return a object of the player 123", async () => {
const player = await findPlayerById(123);
should(player).have.property("id", 123);
should(player).have.property("name", "Wade");
should(player).have.property("position", "PG");
});

it("should return a object when the player has been updated", async () => {
const doc = {
id: 123,
name: "LeBron",
position: "PF"
};
const result = await updatePlayer(doc);
should(result).have.property("n", 1);
should(result).have.property("nModified", 1);
});

it("should return a object when the player has been deleted", async () => {
const result = await deletePlayer(123);
should(result).have.property("n", 1);
should(result).have.property("deletedCount", 1);
});
});