Skip to content
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
31 changes: 31 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
// Use IntelliSense to learn about possible Node.js debug attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [

{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${file}"
},
{
"name": "Jasmine-Node Debugging",
"cwd": "${workspaceRoot}",
"program": "${workspaceRoot}/node_modules/jasmine/bin/jasmine.js",
"request": "launch",
"type": "node",
"args": [
"${workspaceRoot}/spec/app_spec.js"
]
},
{
"request": "launch",
"type": "node",
"name": "Launch index.js",
"program": "${workspaceRoot}/index.js"
}
]
}
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Place your settings in this file to overwrite default and user settings.
{
"jshint.enable": false
}
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
# assignment_mad_lib_api
Serving up the madness with a Mad Lib API!
# Mad Lib API

## Introduction
This assignment involved implementing my first JSON-based API along with a test suite for its features.

## Technologies Used
Jasmine, Node, Express, MongoDB

## Getting Started
Simply clone the repository and install the dependencies. Create a user by running `npm run seed`. Run the test suite by running `npm run test`.
13 changes: 13 additions & 0 deletions config/mongo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"development": {
"database": "assignment_mad_lib_api_development",
"host": "localhost"
},
"test": {
"database": "assignment_mad_lib_api_test",
"host": "localhost"
},
"production": {
"use_env_variable": "MONGO_URL"
}
}
7 changes: 7 additions & 0 deletions helpers/debug.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const DebugHelper = {};

DebugHelper.debug = arg => {
return `<pre>${JSON.stringify(arg, null, 2)}</pre>`;
};

module.exports = DebugHelper;
13 changes: 13 additions & 0 deletions helpers/flash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const FlashHelper = {};

FlashHelper.bootstrapAlertClassFor = function(key) {
return (
{
error: "danger",
alert: "danger",
notice: "info"
}[key] || key
);
};

module.exports = FlashHelper;
41 changes: 41 additions & 0 deletions helpers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const fs = require("fs");
const path = require("path");
const express = require("express");
const basename = path.basename(__filename);

const Helpers = {};

// Object to hold registered helpers
Helpers.registered = {};

// Register a single helper or
// a module
Helpers.register = function(key, fn) {
if (typeof key === "object") {
// Iterate through keys
let helpers = key;
for (let key in helpers) {
// Register helper function
let fn = helpers[key];
this.registered[key] = fn;
}
} else {
// Register a single helper
// function
this.registered[key] = fn;
}
};

// Register all helper files
const files = fs.readdirSync(__dirname);
files.forEach(filename => {
// If the file is not this file
if (filename !== basename) {
// Require it and register its
// helpers
let helperModule = require(`./${filename}`);
Helpers.register(helperModule);
}
});

module.exports = Helpers;
6 changes: 6 additions & 0 deletions helpers/sessions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const SessionsHelper = {};

SessionsHelper.loginPath = () => "/login";
SessionsHelper.logoutPath = () => "/logout?_method=delete";

module.exports = SessionsHelper;
10 changes: 10 additions & 0 deletions helpers/users.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const UsersHelper = {};

UsersHelper.usersPath = () => `/users`;
UsersHelper.userPath = () => `/user`;
UsersHelper.newUserPath = () => `/user/new`;
UsersHelper.editUserPath = () => `/user/edit`;
UsersHelper.destroyUserPath = () => `/user?_method=delete`;
UsersHelper.updateUserPath = () => `/user?_method=put`;

module.exports = UsersHelper;
202 changes: 202 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
const express = require("express");
const app = express();

// ----------------------------------------
// Body Parser
// ----------------------------------------
const bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({ extended: true }));

// ----------------------------------------
// Flash Messages
// ----------------------------------------
const flash = require("express-flash-messages");
app.use(flash());

// ----------------------------------------
// Sessions/Cookies
// ----------------------------------------
const cookieSession = require("cookie-session");
const cookieParser = require("cookie-parser");

app.use(cookieParser());

app.use(
cookieSession({
name: "session",
keys: [process.env.SESSION_SECRET || "asdf1234567890qwer"]
})
);

app.use((req, res, next) => {
res.locals.session = req.session;
next();
});

// ----------------------------------------
// Referrer
// ----------------------------------------
app.use((req, res, next) => {
req.session.backUrl = req.header("Referer") || "/";
next();
});

// ----------------------------------------
// Static Public Files
// ----------------------------------------
app.use(express.static(`${__dirname}/public`));

// ----------------------------------------
// Logging
// ----------------------------------------
const morgan = require("morgan");
const highlight = require("cli-highlight").highlight;

// Add :data format token
// to `tiny` format
let format = [
":separator",
":newline",
":method ",
":url ",
":status ",
":res[content-length] ",
"- :response-time ms",
":newline",
":newline",
":data",
":newline",
":separator",
":newline",
":newline"
].join("");

// Use morgan middleware with
// custom format
if (process.env.NODE_ENV !== "test") {
app.use(morgan(format));
}

// Helper tokens
morgan.token("separator", () => "****");
morgan.token("newline", () => "\n");

// Set data token to output
// req query params and body
morgan.token("data", (req, res, next) => {
if (/\.[\w]+$/.test(req.url)) {
return "";
}

let data = [];
["query", "params", "body", "session", "user"].forEach(key => {
if (req[key]) {
let capKey = key[0].toUpperCase() + key.substr(1);
let value = JSON.stringify(req[key], null, 2);
data.push(`${capKey}: ${value}`);
}
});
data = highlight(data.join("\n"), {
language: "json",
ignoreIllegals: true
});
return `${data}`;
});

// ----------------------------------------
// Mongoose
// ----------------------------------------
const mongoose = require("mongoose");

app.use((req, res, next) => {
if (mongoose.connection.readyState) {
next();
} else {
require("./mongo")().then(() => next());
}
});

// ----------------------------------------
// Template Engine
// ----------------------------------------
const expressHandlebars = require("express-handlebars");
const h = require("./helpers").registered;

const hbs = expressHandlebars.create({
helpers: h,
partialsDir: "views/",
defaultLayout: "application"
});

app.engine("handlebars", hbs.engine);
app.set("view engine", "handlebars");

// ----------------------------------------
// Services
// ----------------------------------------
const authService = require("./services/auth");
const User = require("./models").User;

app.use(
authService({
findUserByEmail: email => {
return User.findOne({ email: email });
},
findUserByToken: token => {
return User.findOne({ token: token });
},
validateUserPassword: (user, password) => {
return user.validatePassword(password);
}
})
);

// ----------------------------------------
// Routes
// ----------------------------------------
const users = require("./routes/users");
const madLibsApi = require("./routes/madlibs");
app.use("/", users);
app.use("/api/v1", madLibsApi);

// ----------------------------------------
// Server
// ----------------------------------------
const port = process.env.PORT || process.argv[2] || 4000;
const host = "localhost";

let args;
process.env.NODE_ENV === "production" ? (args = [port]) : (args = [port, host]);

args.push(() => {});

if (require.main === module) {
app.listen.apply(app, args);
}

// ----------------------------------------
// Error Handling
// ----------------------------------------
app.use("/api", (err, req, res, next) => {
if (res.headersSent) {
return next(err);
}

if (err.stack) {
err = err.stack;
}
res.status(500).json({ error: err });
});

app.use((err, req, res, next) => {
if (res.headersSent) {
return next(err);
}

if (err.stack) {
err = err.stack;
}
res.status(500).render("errors/500", { error: err });
});

module.exports = app;
10 changes: 10 additions & 0 deletions models/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const mongoose = require("mongoose");
const bluebird = require("bluebird");

mongoose.Promise = bluebird;

const models = {};

models.User = require("./user");

module.exports = models;
Loading