Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
9af1f0d
Names
thebopshoobop Aug 18, 2017
3609d9a
init
EdTriplett Aug 18, 2017
f8503a0
boilerplate
thebopshoobop Aug 18, 2017
1a6c770
Merge branch 'master' of https://github.com/EdTriplett/assignment_mad…
thebopshoobop Aug 18, 2017
fbfa4d0
getting running
thebopshoobop Aug 18, 2017
8d3c920
passport-local working
EdTriplett Aug 18, 2017
a77b56f
token
EdTriplett Aug 18, 2017
ce3ef98
password required on register
thebopshoobop Aug 18, 2017
113a93e
testing boilerplate
thebopshoobop Aug 18, 2017
431fc67
promisetest
EdTriplett Aug 18, 2017
f4276d1
listing route tests
thebopshoobop Aug 18, 2017
b3f06fc
save specHelper
thebopshoobop Aug 18, 2017
9848167
passed first test
EdTriplett Aug 18, 2017
eab611a
check token middleware, 404 handler
thebopshoobop Aug 18, 2017
9490570
passing nouns
thebopshoobop Aug 18, 2017
051a814
passing verbs
thebopshoobop Aug 18, 2017
9f1c49b
passing adverbs
thebopshoobop Aug 18, 2017
24fca01
passing adjectives
thebopshoobop Aug 18, 2017
d58a027
to fancy by half refactor
thebopshoobop Aug 18, 2017
50a5e93
it returns a sentence
EdTriplett Aug 18, 2017
34872cd
sentences require valid inputs
thebopshoobop Aug 18, 2017
a257a9e
senteces must return a string
thebopshoobop Aug 18, 2017
c52d5ca
returns sentence
EdTriplett Aug 18, 2017
5a900a7
Simple client
thebopshoobop Aug 18, 2017
3d5209c
almost client
thebopshoobop Aug 18, 2017
5c47f06
fully clientified
thebopshoobop Aug 19, 2017
358be3b
update readme
EdTriplett Feb 12, 2018
f8d9c2d
update readme
EdTriplett Feb 12, 2018
cdbfe62
readme edit
EdTriplett Feb 14, 2018
04775d8
readme edits
EdTriplett Feb 14, 2018
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 .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NODE_ENV=development
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
# assignment_mad_lib_api
Serving up the madness with a Mad Lib API!
# Mad Lib API
A RESTful API that supplies different parts of speech for fun fill-in-the-blank word games.

![Alt text](/madLibReadme1.gif?raw=true "Demo")

This API enables users to register with name, unique e-mail address, and password to receive an API token that can be used to request lists of random nouns, verbs, adjectives and adverbs.

Download and install dependencies, then type 'npm start' to open the application. Type 'npm test' to run tests.

# Key Technologies
Promises, Sessions, Cookies, Logging, Authentication with Passport, Testing with Jasmine, MongoDB database, Mongoose ORM, Express server, Node.js
110 changes: 110 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
const express = require("express");
const app = express();
const h = require("./helpers");
const { User } = require("./models");

// .env
if (process.env.NODE_ENV !== "production") {
require("dotenv").config();
}

// Templates
const expressHandlebars = require("express-handlebars");
const hbs = expressHandlebars.create({
partialsDir: "views/",
defaultLayout: "application",
helpers: require("./helpers")
});
app.engine("handlebars", hbs.engine);
app.set("view engine", "handlebars");

// Method Overriding
const methodOverride = require("method-override");
const getPostSupport = require("express-method-override-get-post-support");
app.use(methodOverride(getPostSupport.callback, getPostSupport.options));

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

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

// Session
const expressSession = require("express-session");
app.use(
expressSession({
resave: false,
saveUninitialized: true,
secret: "asdf;werxcklj;jxcvui3qksf;"
})
);

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

// Log Request Info in Development
if (process.env.NODE_ENV === "development") {
const morgan = require("morgan");
const morganToolkit = require("morgan-toolkit")(morgan);
app.use(morganToolkit());
}

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

// Authentication Middleware
const passport = require("passport");

app.use(passport.initialize());
app.use(passport.session());

passport.serializeUser(function(user, done) {
done(null, user._id);
});

passport.deserializeUser(function(userId, done) {
User.findById(userId, (err, user) => done(err, user));
});

passport.use("local", require("./strategies/local"));

passport.use("bearer", require("./strategies/bearer"));

// Add populated user and referral URL to locals
app.use(async (req, res, next) => {
if (req.user) {
res.locals.user = req.user;
}
next();
});

// Routes
app.use("/", require("./routes")(passport));
app.use(
"/api/v1",
passport.authenticate("bearer", { session: false }),
require("./routes/apiV1")
);

// Set up port/host
const port = process.env.PORT || process.argv[2] || 3000;
const host = "localhost";
let args = process.env.NODE_ENV === "production" ? [port] : [port, host];

// helpful log when the server starts
args.push(() => {
console.log(`Listening: http://${host}:${port}`);
});

// Use apply to pass the args to listen if we're run directly
if (require.main === module) {
app.listen.apply(app, args);
}

module.exports = app;
66 changes: 66 additions & 0 deletions client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
const express = require("express");
const app = express();
const request = require("request");
const rp = require("request-promise");

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

// Handlebars
const expressHandlebars = require("express-handlebars");
const hbs = expressHandlebars.create({
partialsDir: "views/",
defaultLayout: "application"
});
app.engine("handlebars", hbs.engine);
app.set("view engine", "handlebars");

// Logging
const morgan = require("morgan");
const morganToolkit = require("morgan-toolkit")(morgan);
const Promise = require("bluebird");
app.use(morganToolkit());

const urlFor = (type, token) => {
const apiUrl = "http://localhost:3000/api/v1/";
return `${apiUrl}${type}?access_token=${token}`;
};

// Route Handlers
app.get("/", (req, res) => {
res.render("client/index");
});

app.post("/prepare", (req, res) => {
const token = req.body.token;
const promises = [
rp.get(urlFor("nouns", token)),
rp.get(urlFor("verbs", token)),
rp.get(urlFor("adverbs", token)),
rp.get(urlFor("adjectives", token))
];
Promise.all(promises)
.spread((nouns, verbs, adverbs, adjectives) => {
const words = [
{ name: "Nouns", words: JSON.parse(nouns) },
{ name: "Verbs", words: JSON.parse(verbs) },
{ name: "Adverbs", words: JSON.parse(adverbs) },
{ name: "Adjectives", words: JSON.parse(adjectives) }
];
res.render("client/build", { words, token });
})
.catch(e => res.end(e.stack));
});

app.post("/build", (req, res) => {
let { token, template, words } = req.body;
words = words.split(" ");
const formData = { form: { words, template } };
request.post(urlFor("sentences", token), formData, (err, r, body) => {
const sentence = JSON.parse(body).sentence;
res.render("client/display", { sentence });
});
});

app.listen(3030, () => console.log("Up and running!"));
6 changes: 6 additions & 0 deletions config/mongoUrl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const env = process.env.NODE_ENV || "development";
const config = require("./mongoose.json")[env];
module.exports =
process.env.NODE_ENV === "production"
? process.env[config.use_env_variable]
: `mongodb://${config.host}/${config.database}`;
13 changes: 13 additions & 0 deletions config/mongoose.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"development": {
"database": "mad_lib_development",
"host": "localhost"
},
"test": {
"database": "mad_lib_test",
"host": "localhost"
},
"production": {
"use_env_variable": "MONGO_URL"
}
}
7 changes: 7 additions & 0 deletions helpers/APIHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
APIPath: v => `/api/v${v}`,
nounsPath: v => `/api/v${v}/nouns`,
verbsPath: v => `/api/v${v}/verbs`,
adverbsPath: v => `/api/v${v}/adverbs`,
adjectivesPath: v => `/api/v${v}/adjectives`
};
5 changes: 5 additions & 0 deletions helpers/AuthHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
loginPath: () => "/login",
logoutPath: () => "/logout",
registerPath: () => "/register"
};
15 changes: 15 additions & 0 deletions helpers/FlashHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module.exports = {
bootstrapAlertClassFor: key => {
return (
{
error: "danger",
alert: "danger",
notice: "info"
}[key] || key
);
},
missingFlashRedirect: (req, res, path, item) => {
req.flash("alert", `Sorry, that ${item} does not exist`);
res.redirect(path);
}
};
5 changes: 5 additions & 0 deletions helpers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const LoadHelpers = require("load-helpers");
const helperLoader = new LoadHelpers();
const helpers = helperLoader.load("helpers/*Helper.js").cache;

module.exports = helpers;
Binary file added madLibReadme1.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions models/User.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const mongoose = require("mongoose");
const uniqueValidator = require("mongoose-unique-validator");
const bcrypt = require("bcrypt");
const md5 = require("md5");
const uuid = require("uuid");
const Schema = mongoose.Schema;

const UserSchema = new Schema(
{
fname: { type: String, required: true },
lname: { type: String, required: true },
email: { type: String, required: true, unique: true },
passwordHash: { type: String, required: true },
token: { type: String, unique: true }
},
{
timestamps: true
}
);

UserSchema.plugin(uniqueValidator);

// Instance Methods
UserSchema.methods.validPassword = function(password) {
return bcrypt.compareSync(password, this.passwordHash);
};

// Virtual Properties
UserSchema.virtual("password").set(function(value) {
this.passwordHash = bcrypt.hashSync(value, 8);
});


UserSchema.pre('save', function(next) {
this.token = md5(`${ this.email }${ uuid() }`);
next();
});


const User = mongoose.model("User", UserSchema);

module.exports = User;
5 changes: 5 additions & 0 deletions models/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const mongoose = require("mongoose");
mongoose.Promise = require("bluebird");
module.exports = {
User: require("./User")
};
8 changes: 8 additions & 0 deletions mongo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const mongoose = require("mongoose");
module.exports = () => {
mongoose.Promise = require("bluebird");
return mongoose.connect(require("./config/mongoUrl"), {
useMongoClient: true
});
};

Loading