diff --git a/.gitignore b/.gitignore index 646ac519e..be2d1737f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .DS_Store +coverage/ node_modules/ diff --git a/app.js b/app.js index f0579b1dc..61f56ef59 100644 --- a/app.js +++ b/app.js @@ -4,24 +4,51 @@ var favicon = require('serve-favicon'); var logger = require('morgan'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); +var massive = require("massive"); +var app = module.exports = express(); -var routes = require('./routes/index'); +// view engine setup +// app.set('views', path.join(__dirname, 'views')); +// app.set('view engine', 'ejs'); + +// var connectionString = "postgres://localhost/video_store" +var connectionString = "postgres://localhost/video_store_" + app.get('env'); + +// connect to Massive and get the db instance. You can safely use the +// convenience sync method here because its on app load +// you can also use loadSync - it's an alias +var massiveInstance = massive.connectSync({connectionString : connectionString}) + +// Set a reference to the massive instance on Express' app: +app.set('db', massiveInstance); -var app = express(); // view engine setup app.set('views', path.join(__dirname, 'views')); -app.set('view engine', 'jade'); +app.set('view engine', 'ejs'); // uncomment after placing your favicon in /public //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); app.use(logger('dev')); +// this will terminate the request with an error 400 if the POST body +// doesn't contain valid json app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); +var routes = require('./routes/index'); +var customers = require('./routes/customers'); +var movies = require('./routes/movies'); +var rentals = require('./routes/rentals'); + + + app.use('/', routes); +app.use('/customers', customers); +app.use('/movies', movies); +app.use('/rentals', rentals); + // catch 404 and forward to error handler app.use(function(req, res, next) { diff --git a/controllers/customers.js b/controllers/customers.js new file mode 100644 index 000000000..cbab3dac4 --- /dev/null +++ b/controllers/customers.js @@ -0,0 +1,59 @@ +var Customer = require("../models/customers_model"); +var Rental = require("../models/rentals_model"); + + +var CustomersController = { + getCustomers: function(req, res) { + Customer.all(function(error, customers) { + if(error=="Could not retrieve customers") { + res.status(404).send(error) + } else if (error) { + var err = "Please try again" + res.status(500).send(err) + } else { + res.json(customers) + } + }) + }, + // customer id, sort column, offest ????? + subsetCustomers: function(req, res) { + Customer.sort(req.params.column, req.query.p, req.query.n, function(error, data) { + if(error=="Could not retrieve customers") { + res.status(404).send(error) + } else if (error) { + var err = "Please try again" + res.status(500).send(err) + } else { + res.json(data) + } + }) + }, + + // customer id, all rentals attached to customer id within data params + getCustomersCurrent: function(req, res) { + Rental.getCurrentRentals(req.params.customer_id, function(error, data) { + if(error=="No results from database") { + res.status(404).send(error) + } else if (error) { + var err = "Please try again" + res.status(500).send(err) + } else { + res.json(data) + } + }) + }, + + getCustomersHistory: function(req, res) { + Rental.getPastRentals(req.params.customer_id, function(error, data) { + if(error=="No past rentals") { + res.status(404).send(error) + } else if (error) { + var err = "Please try again" + res.status(500).send(err) + } else { + res.json(data) + } + }) + } +} +module.exports = CustomersController diff --git a/controllers/docs.js b/controllers/docs.js new file mode 100644 index 000000000..39eb02efe --- /dev/null +++ b/controllers/docs.js @@ -0,0 +1,16 @@ +var docs = require('../public/docs.json'); + +var DocsController = { + getDocs: function(req, res) { + res.render('docs', { + title: 'The Docs', + docs: docs + }); + }, + + getJsonDocs: function(req, res) { + res.json(docs); + } +} + +module.exports = DocsController diff --git a/controllers/movies.js b/controllers/movies.js new file mode 100644 index 000000000..c0e26c23a --- /dev/null +++ b/controllers/movies.js @@ -0,0 +1,50 @@ +var Movie = require("../models/movies_model"); +var Rental = require("../models/rentals_model"); + + +MoviesController = { + getMovies: function(req, res) { + Movie.all(function(error, movies) { + if(error=="Could not retrieve movies") { + res.status(404).send(error) + } else if (error) { + var err = "Please try again" + res.status(500).send(err) + } else { + res.json(movies) + } + }) + }, + + subsetMovies: function(req, res) { + Movie.sort(req.params.column, req.query.p, req.query.n, function(error, data) { + if(error=="Could not retrieve movies") { + res.status(404).send(error) + } else if (error) { + var err = "Please try again" + res.status(500).send(err) + } else { + res.json(data) + } + }) + }, + + getRentalsCustomers: function(req, res) { + Rental.getCustomers(req.params.title, function(error, checked_out) { + if(error=="Could not retrieve movies") { + res.status(404).send(error) + } else if (error) { + var err = "Please try again" + res.status(500).send(err) + } else { + res.json(checked_out) + } + }) + }, + + getMoviesHistory: function(req, res) { + + } +} + +module.exports = MoviesController diff --git a/controllers/rentals.js b/controllers/rentals.js new file mode 100644 index 000000000..f199008e5 --- /dev/null +++ b/controllers/rentals.js @@ -0,0 +1,72 @@ +var Rental = require("../models/rentals_model"); +var Movie = require("../models/movies_model"); + +RentalsController = { + getRentals: function(req, res) { + Movie.findMovie(req.params.title, function(error, movie) { + if(error) { + var err = new Error("No such movie"); + err.status = 404; + } else { + Rental.getCurrentlyCheckedOut(movie, function(error, checked_out) { + var return_data = { + title:movie.title, + overview:movie.overview, + release_date:movie.release_date, + total_inventory:movie.inventory, + available_copies:(parseInt(movie.inventory))-(parseInt(checked_out)) + } + res.json(return_data) + }) + } + + }) + }, + + getRentalsCustomers: function(req, res) { + Rental.getCustomers(req.params.title, function(error, checked_out) { + if(error) { + var err = new Error("No one has that movie checked out"); + err.status = 404; + } else { + res.json(checked_out) + } + }) + }, + + getRentalsCheckOut: function(req, res, next) { + Rental.getCheckout(req.params.title, req.params.customerid, function(error, checked_out) { + if(error) { + var err = new Error("No one has that movie checked out"); + err.status = 404; + } else { + res.json(checked_out) + } + }); + }, + + getRentalsReturn: function(req, res) { + Rental.getReturn(req.params.id, function(error, returned) { + if(error) { + var err = new Error("No one has that movie checked out"); + err.status = 404; + } else { + res.json(returned) + } + }); + + }, + + getRentalsOverdue: function(req, res) { + Rental.getOverdue(function(error, over_due) { + if(error) { + var err = new Error("Ooops"); + err.status = 404; + } else { + res.json(over_due) + } + }); + } +} + +module.exports = RentalsController diff --git a/db/seeds/rentals.json b/db/seeds/rentals.json new file mode 100644 index 000000000..14891f4d9 --- /dev/null +++ b/db/seeds/rentals.json @@ -0,0 +1,42 @@ +[ +{ +"movie_id": 2, +"customer_id": 5, +"created_date": "1980-06-16", +"due_date": "1980-06-18", +"returned": true, +"returned_date": "1980-06-18T20:24:01.218Z" +}, +{ +"movie_id": 2, +"customer_id": 5, +"created_date": "1960-06-16", +"due_date": "2016-06-18T20:24:01.218Z", +"returned": false +}, +{ +"movie_id": 2, +"customer_id": 5, +"created_date": "1960-06-16", +"due_date": "1960-06-18", +"returned": true, +"returned_date": "1960-06-18T20:24:01.218Z" +}, +{ +"movie_id": 2, +"customer_id": 5, +"created_date": "1970-06-16", +"due_date": "1970-06-18", +"returned": true, +"returned_date": "1967-06-18T20:24:01.218Z" +}, +{ +"movie_id": 10, +"customer_id": 4, +"created_date": "1960-06-16", +"due_date": "1960-06-17", +"returned": false +} + + +] diff --git a/db/setup/schema.sql b/db/setup/schema.sql new file mode 100644 index 000000000..e13cb391e --- /dev/null +++ b/db/setup/schema.sql @@ -0,0 +1,32 @@ +DROP TABLE IF EXISTS movies; +CREATE TABLE movies( + id serial PRIMARY KEY, + title text, + overview text, + release_date date, + inventory integer +); + +DROP TABLE IF EXISTS rentals; +CREATE TABLE rentals( + id serial PRIMARY KEY, + movie_id int, + customer_id int, + created_date date, + due_date date, + returned boolean, + returned_date date +); + +DROP TABLE IF EXISTS customers; +CREATE TABLE customers( + id serial PRIMARY KEY, + name text, + registered_at timestamp, + address text, + city text, + state text, + postal_code text, + phone text, + account_credit decimal +); \ No newline at end of file diff --git a/models/customers_model.js b/models/customers_model.js new file mode 100644 index 000000000..c8927e373 --- /dev/null +++ b/models/customers_model.js @@ -0,0 +1,37 @@ +var app = require("../app"); +var db = app.get("db"); + +var Customer = function(id) { + this.id = id; +} + +Customer.all = function(callback) { + db.customers.find(function(error, customers) { + if (error|| !customers) { + callback(error || new Error("Could not retrieve customers"), undefined); + } else { + callback(null, customers.map(function(customer) { + return new Customer(customer.id); + })) + }; + }) +}; + +Customer.sort = function(column, p, n, callback) { + // sort by column, for n number of records starting at p + db.customers.find({}, { + order: column, + limit: n, + offset: p + }, function(error, customers) { + if (error || !customers) { + callback(error || new Error("Could not retrieve customers"), undefined) + } else { + callback(null, customers.map (function (customer) { + return customer + })) + } + }) +} + +module.exports = Customer; diff --git a/models/movies_model.js b/models/movies_model.js new file mode 100644 index 000000000..a956a9562 --- /dev/null +++ b/models/movies_model.js @@ -0,0 +1,48 @@ +var app = require("../app"); +var db = app.get("db"); + +var Movie = function(title, inventory) { + this.title = title; + this.inventory = inventory; +} + +Movie.all = function(callback) { + db.movies.find(function(error, movies) { + if (error|| !movies) { + callback(error || new Error("Could not retrieve movies"), undefined); + } else { + callback(null, movies.map(function(movie) { + return new Movie(movie.title); + })) + }; + }) +}; + +Movie.findMovie = function(title, callback) { + db.movies.findOne({title: title}, function(error, movie) { + if (error || !movie) { + callback(error || new Error("Could not retrieve movies"), undefined); + } else { + callback(null, movie) + } + }) +}; + +Movie.sort = function(column, p, n, callback) { + // sort by column, for n number of records starting at p + db.movies.find({}, { + order: column, + limit: n, + offset: p + }, function(error, movies) { + if (error || !movies) { + callback(error || new Error("Could not retrieve movies"), undefined) + } else { + callback(null, movies.map (function (movie) { + return movie + })) + } + }) +} + +module.exports = Movie; diff --git a/models/rentals_model.js b/models/rentals_model.js new file mode 100644 index 000000000..99708bad0 --- /dev/null +++ b/models/rentals_model.js @@ -0,0 +1,130 @@ +var app = require("../app"); +var db = app.get("db"); + +var Rental = function(movie_id, customer_id, created_date, due_date, returned = false, returned_date = null) { + this.movie_id = movie_id; + this.customer_id = customer_id; + this.created_date = created_date; + this.due_date = due_date; + this.returned = returned; + this.returned_date = returned_date; + +}; + +Rental.getCheckedOut = function(movie_id, callback) { + db.rentals.where("movie_id=$1 AND returned=$2", [movie_id, false], function(error, checked_out) { + if(error|| !checked_out) { + return callback(error || new Error("No results from database"), undefined); + } else { + callback(null, new Rental(checked_out)); + } + }) +} + +Rental.getCurrentRentals = function(customer_id, callback) { + db.rentals.where("customer_id=$1 AND returned=$2", [customer_id, false], function(error, checked_out) { + if(error|| !checked_out) { + return callback(error || new Error("No results from database"), undefined); + } else { + callback(null, checked_out); + } + }) +} + +Rental.getCurrentlyCheckedOut = function(movie, callback) { + db.rentals.count({movie_id: movie.id, returned: false}, function (error, count) { + if(error|| !count) { + return callback(error || new Error("No results from database"), undefined); + } else { + callback(null, count); + } + }) +} + +Rental.getPastRentals = function(customer_id, callback) { + db.run("SELECT customer_id, created_date, movie_id, returned_date FROM rentals WHERE customer_id=$1 AND returned=$2 ORDER BY returned_date ASC", [customer_id, true], function(error, past_rentals) { + if(error|| !past_rentals) { + return callback(error || new Error("No past rentals"), undefined); + } else { + callback(null, past_rentals); + } + }) +} + +Rental.getCustomers = function(movie_title, callback) { + db.run("SELECT * FROM customers WHERE id=(SELECT customer_id FROM rentals WHERE movie_id=(SELECT id FROM movies WHERE title=$1) AND returned=false)", [movie_title], function(error, customers) { + if(error|| !customers) { + return callback(error || new Error("No results from database"), undefined); + } else { + callback(null, customers); + } + }) +} + +Rental.getOverdue = function(callback) { + var now = new Date() + db.run("SELECT customers.name, movies.title, rentals.created_date, rentals.due_date FROM rentals INNER JOIN movies ON movies.id=rentals.movie_id INNER JOIN customers ON customers.id=rentals.customer_id WHERE rentals.returned=false AND rentals.due_date<$1", [now], function (error, overdues) { + if (error|| !overdues) { + return callback(error || new Error("No results from database"), undefined); + } + callback(null, overdues); + }); +} + +Rental.getReturn = function(rental_id, callback) { + db.rentals.update({ + id: rental_id, + returned: true, + returned_date: new Date() + }, function(error, checked_out) { + if(error|| !checked_out) { + return callback(error || new Error("No results from database"), undefined); + } else { + callback(null, checked_out); + } + }) +} + +Rental.getCheckout = function(movie_title, id, callback) { + // get movie id from movie table by title + db.movies.findOne({ + title: movie_title + // call back with movie id + }, function (error, movie) { + if (error) { + return callback(error); + } else if (!movie) { + return callback(new Error("No results in database")); + } + + // remove money from account + db.run("UPDATE customers SET account_credit=account_credit-2.0 WHERE id=$1;", [id], function (error, movie_id = movie, account_balance_update) { + if (error) { + return callback(error); + } + + // set date vars + var now = new Date(); + var due = new Date(now); + due.setDate(due.getDate() + 7); + + // save new rental (nested inside find) + db.rentals.save({ + movie_id: movie_id.id, + customer_id: id, + created_date: now, + due_date: due, + returned: false + // callback with rental info + }, function (error, rental) { + if (error) { + return callback(error); + } + // pass rental back to controller + return callback(null, rental); + }); + }); + }); +} + +module.exports = Rental; diff --git a/package.json b/package.json index d39b26403..02f08f82c 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,18 @@ "private": true, "scripts": { "start": "nodemon ./bin/www", - "test": "clear; jasmine-node --verbose spec/" + "db:drop": "dropdb video_store_development", + "db:create": "createdb video_store_development", + "db:schema": "node tasks/load_schema.js video_store_development", + "db:seed": "node tasks/seed.js video_store_development", + "reset": "npm run db:drop; npm run db:create; npm run db:schema; npm run db:seed;", + "start-test": "NODE_ENV=test ./node_modules/.bin/nodemon ./bin/www", + "db:drop-test": "dropdb video_store_test", + "db:create-test": "createdb video_store_test", + "db:schema-test": "node tasks/load_schema.js video_store_test", + "db:seed-test": "node tasks/seed.js video_store_test", + "reset-test": "npm run db:drop-test; npm run db:create-test; npm run db:schema-test; npm run db:seed-test;", + "test": "clear; ./node_modules/.bin/istanbul cover -x 'spec/**/*' -- ./node_modules/.bin/jasmine-node --captureExceptions --verbose spec/" }, "dependencies": { "body-parser": "~1.13.2", @@ -12,12 +23,14 @@ "debug": "~2.2.0", "express": "~4.13.1", "jade": "~1.11.0", + "massive": "^2.3.0", "morgan": "~1.6.1", - "sequelize": "^3.23.3", "serve-favicon": "~2.3.0" }, "devDependencies": { + "istanbul": "^0.4.4", "jasmine-node": "^1.14.5", + "massive": "^2.3.0", "nodemon": "^1.9.2", "request": "^2.72.0" } diff --git a/public/docs.json b/public/docs.json new file mode 100644 index 000000000..2fa9ec2b7 --- /dev/null +++ b/public/docs.json @@ -0,0 +1,74 @@ +{ +"/customers": { + "verb": "get", + "optional parameters": "none", + "required parameters": "none", + "description of use": "returns list of all customers ids", + "if some data is found": "json", + "if no data is found": "404 & Could not retrieve customers error from model", + "if error occured": "Status 500 and Error retrieving data message from controller" + }, +"/customers/sort/:column": { + "verb": "get", + "optional parameters": "?n=10&p=2: return n customer records, offset by p records", + "required parameters": "column", + "description of use": "retrieve subset of customers", + "if some data is found": "json", + "if no data is found": "404 & Could not retrieve customers error from model", + "if error occured": "500 & Please try again" + }, +"/customers/:customer_id/current": { + "verb": "get", + "optional parameters": "none", + "required parameters": "customer id", + "description of use": "get list of current rentals for customer", + "if some data is found": "json data for rentals", + "if no data is found": "404 & No current rentals message from model", + "if error occured": "500 & Please try again" + }, +"/customers/:customer_id/history": { + "verb": "get", + "optional parameters": "none", + "required parameters": "customer id", + "description of use": "get list of past rentals for customer", + "if some data is found": "json data for rentals", + "if no data is found": "404 & No past rentals", + "if error occured": "500 & Please try again" + }, +"/movies": { + "verb": "get", + "optional parameters": "none", + "required parameters": "none", + "description of use": "returns a list of all movie titles", + "if some data is found": "json data for movies", + "if no data is found": "404 & Could not retrieve movies", + "if error occuered": "500 & Please try again" + }, +"/movies/sort/:column": { + "verb": "get", + "optional parameters": "?n=10&p=2: return n customer records, offset by p records", + "required parameters": "column", + "description of use": "retrieve a subset of movies", + "if some data is found": "json data", + "if no data is found": "404 & Could not retrieve movies", + "if error occuered": "500 & Please try again" + }, +"/movies/:title/current": { + "verb": "get", + "optional parameters": "none", + "required parameters": "title", + "description of use": "list of customers currently with a checked out copy of this movie", + "if some data is found": "json data", + "if no data is found": "404 & Could not retrieve movies", + "if error occuered": "500 & Please try again" + }, +"/movies/:title/history": { + "verb": "get", + "optional parameters": "none", + "required parameters": "title", + "description of use": "list of customers who have previously checked out this movie", + "if some data is found": "json data", + "if no data is found": "404 & Could not retrieve movies", + "if error occuered": "500 & Please try again" + } +} diff --git a/routes/customers.js b/routes/customers.js new file mode 100644 index 000000000..89d6a3852 --- /dev/null +++ b/routes/customers.js @@ -0,0 +1,12 @@ +var express = require('express'); +var router = express.Router(); +var Controller = require('../controllers/customers') + +// select all from db +router.get('/', Controller.getCustomers) +router.get('/sort/:column', Controller.subsetCustomers) +router.get('/:customer_id/current', Controller.getCustomersCurrent) +router.get('/:customer_id/history', Controller.getCustomersHistory) + + +module.exports = router; diff --git a/routes/index.js b/routes/index.js index 06cfc1137..c96859043 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,9 +1,21 @@ var express = require('express'); var router = express.Router(); +var Controller = require('../controllers/docs') + +/* GET docs json. */ +router.get('/api/docs.json', Controller.getJsonDocs) + +/* GET docs html page. */ +router.get('/api/docs', Controller.getDocs) + /* GET home page. */ router.get('/', function(req, res, next) { - res.status(200).json({whatevs: 'whatevs!!!'}) + res.status(200).json({index: 'index'}) }); +// router.get('/zomg', function(req, res, next) { +// res.status(200).json({whatevs: 'it works!!!!'}) +// }); + module.exports = router; diff --git a/routes/movies.js b/routes/movies.js new file mode 100644 index 000000000..558420d2e --- /dev/null +++ b/routes/movies.js @@ -0,0 +1,12 @@ +var express = require('express'); +var router = express.Router(); +var Controller = require('../controllers/movies') + + +router.get('/', Controller.getMovies) +router.get('/sort/:column', Controller.subsetMovies) +router.get('/:title/current', Controller.getRentalsCustomers) +router.get('/:title/history', Controller.getMoviesHistory) + + +module.exports = router; diff --git a/routes/rentals.js b/routes/rentals.js new file mode 100644 index 000000000..b961eb664 --- /dev/null +++ b/routes/rentals.js @@ -0,0 +1,17 @@ +var express = require('express'); +var router = express.Router(); +var Controller = require('../controllers/rentals') + +router.get('/overdue', Controller.getRentalsOverdue) +router.get('/:title', Controller.getRentals) +router.get('/:title/customers', Controller.getRentalsCustomers) +// can't post from browser URL +router.get('/:title/check-out/:customerid', Controller.getRentalsCheckOut) +// must pass rental id, sorry.... +// passing movie titles is UNRELIABLE! +router.get('/return/:id', Controller.getRentalsReturn) + + + + +module.exports = router; diff --git a/spec/controllers/customers.spec.js b/spec/controllers/customers.spec.js new file mode 100644 index 000000000..5ea962701 --- /dev/null +++ b/spec/controllers/customers.spec.js @@ -0,0 +1,115 @@ +var request = require('request'); +var base_url = "http://localhost:3000/" +var route = "customers/" + +describe("Endpoints under /customers", function() { + + it('responds with a 200 status code', function (done) { + request.get(base_url + route, function(error, response, body) { + expect(response.statusCode).toEqual(200) + done() + }) + }); + + it("should be json", function(done) { + request.get(base_url + route, function(error, response, body) { + expect(response.headers['content-type']).toContain('application/json') + done() + }) + }); + + it("should be an array of objects", function(done) { + request.get(base_url + route, function(error, response, body) { + var data = JSON.parse(body) + expect(typeof data).toEqual('object') + + expect(Object.keys(data[0])).toEqual([ 'id' ]) + + done() + }) + }); +}); + +describe("Endpoints under /customers/sort/name", function() { + + it('responds with a 200 status code', function (done) { + request.get(base_url + route + "/sort/name", function(error, response, body) { + expect(response.statusCode).toEqual(200) + done() + }) + }); + + it("should be json", function(done) { + request.get(base_url + route + "/sort/name", function(error, response, body) { + expect(response.headers['content-type']).toContain('application/json') + done() + }) + }); + + it("should be an array of objects", function(done) { + request.get(base_url + route + "/sort/name", function(error, response, body) { + var data = JSON.parse(body) + expect(typeof data).toEqual('object') + + expect(Object.keys(data[0])).toEqual([ 'id', 'name', 'registered_at', 'address', 'city', 'state', 'postal_code', 'phone', 'account_credit' ]) + + done() + }) + }); +}); + +describe("Endpoints under /customers/:id/current", function() { + + it('responds with a 200 status code', function (done) { + request.get(base_url + route + "/5/current", function(error, response, body) { + expect(response.statusCode).toEqual(200) + done() + }) + }); + + it("should be json", function(done) { + request.get(base_url + route + "/5/current", function(error, response, body) { + expect(response.headers['content-type']).toContain('application/json') + done() + }) + }); + + it("should be an array of objects", function(done) { + request.get(base_url + route + "/5/current", function(error, response, body) { + var data = JSON.parse(body) + expect(typeof data).toEqual('object') + + expect(Object.keys(data[0])).toEqual([ 'id', 'movie_id', 'customer_id', 'created_date', 'due_date', 'returned', 'returned_date' ]) + + done() + }) + }); +}); + +describe("Endpoints under /customers/:id/history", function() { + + it('responds with a 200 status code', function (done) { + request.get(base_url + route + "/5/history", function(error, response, body) { + expect(response.statusCode).toEqual(200) + done() + }) + }); + + it("should be json", function(done) { + request.get(base_url + route + "/5/history", function(error, response, body) { + expect(response.headers['content-type']).toContain('application/json') + done() + }) + }); + + it("should be an array of objects", function(done) { + request.get(base_url + route + "/5/history", function(error, response, body) { + var data = JSON.parse(body) + expect(typeof data).toEqual('object') + + expect(Object.keys(data[0])).toEqual([ 'customer_id', 'created_date', 'movie_id', 'returned_date' ]) + + done() + }) + }); +}) diff --git a/spec/controllers/index.spec.js b/spec/controllers/index.spec.js index e4151d267..6c989a917 100644 --- a/spec/controllers/index.spec.js +++ b/spec/controllers/index.spec.js @@ -9,21 +9,24 @@ describe("Endpoint at /", function () { }) }) - describe("the returned json data", function() { - it('has the right keys', function(done) { - request.get(base_url, function(error, response, body) { - var data = JSON.parse(body) - expect(Object.keys(data)).toEqual(['whatevs']) - done() - }) - }) + // it("should be json", function(done) { + // request.get(base_url, function(error, response, body) { + // expect(response.headers['content-type']).toBeNull + // done() + // }) + // }); + // + // it("should be an array of objects", function(done) { + // request.get(base_url, function(error, response, body) { + // var data = JSON.parse(body) + // expect(typeof data).toEqual('object') + // + // expect(Object.keys(data[0])).toBeNull + // + // done() + // }) + // }); + + - it('has the right values for the keys', function(done) { - request.get(base_url, function(error, response, body) { - var data = JSON.parse(body) - expect(data.whatevs).toEqual('whatevs!!!') - done() - }) - }) - }) }) diff --git a/spec/controllers/movies.spec.js b/spec/controllers/movies.spec.js index ddcaf2f68..b6bef7a10 100644 --- a/spec/controllers/movies.spec.js +++ b/spec/controllers/movies.spec.js @@ -1,5 +1,121 @@ var request = require('request'); +var base_url = "http://localhost:3000/" +var route = "movies/" + describe("Endpoints under /movies", function() { - -}) + + it('responds with a 200 status code', function (done) { + request.get(base_url + route, function(error, response, body) { + expect(response.statusCode).toEqual(200) + done() + }) + }); + + it("should be json", function(done) { + request.get(base_url + route, function(error, response, body) { + expect(response.headers['content-type']).toContain('application/json') + done() + }) + }); + + it("should be an array of objects", function(done) { + request.get(base_url + route, function(error, response, body) { + var data = JSON.parse(body) + expect(typeof data).toEqual('object') + + expect(Object.keys(data[0])).toEqual([ 'title' ]) + + done() + }) + }); +}); + + +describe("Endpoints under /movies/sort/:column", function() { + + it('responds with a 200 status code', function (done) { + request.get(base_url + route + '/sort/title', function(error, response, body) { + expect(response.statusCode).toEqual(200) + done() + }) + }); + + it("should be json", function(done) { + request.get(base_url + route + '/sort/title', function(error, response, body) { + expect(response.headers['content-type']).toContain('application/json') + done() + }) + }); + + it("should be an array of objects", function(done) { + request.get(base_url + route + '/sort/title', function(error, response, body) { + var data = JSON.parse(body) + expect(typeof data).toEqual('object') + + expect(Object.keys(data[0])).toEqual([ 'id', 'title', 'overview', 'release_date', 'inventory' ]) + + done() + }) + }); + +}); + +describe("Endpoints under /movies/:title/current", function() { + + it('responds with a 200 status code', function (done) { + request.get(base_url + route + "/Jaws/current", function(error, response, body) { + expect(response.statusCode).toEqual(200) + done() + }) + }); + + it("should be json", function(done) { + request.get(base_url + route + "/Jaws/current", function(error, response, body) { + expect(response.headers['content-type']).toContain('application/json') + done() + }) + }); + + it("should be an array of objects", function(done) { + request.get(base_url + route + "/Jaws/current", function(error, response, body) { + var data = JSON.parse(body) + expect(typeof data).toEqual('object') + + expect(Object.keys(data[0])).toEqual([ 'id', 'name', 'registered_at', 'address', 'city', 'state', 'postal_code', 'phone', 'account_credit' ]) + + done() + }) + }); +}); + +// ******** history path tests are causing server timeouts. wtf?? ******** +// describe("Endpoints under /movies/:id/history", function() { +// +// it('responds with a 200 status code', function (done) { +// request.get(base_url + route + "/5/history", function(error, response, body) { +// expect(response.statusCode).toEqual(200) +// done() +// }) +// }); +// +// it("should be json", function(done) { +// request.get(base_url + route + "/5/history", function(error, response, body) { +// expect(response.headers['content-type']).toContain('application/json') +// done() +// }) +// }); +// +// it("should be an array of objects", function(done) { +// request.get(base_url + route + "/5/history", function(error, response, body) { +// var data = JSON.parse(body) +// expect(typeof data).toEqual('object') +// +// for (var record of data) { +// expect(Object.keys(data[0])).toEqual(['id']) +// } +// +// done() +// }) +// }); +// }); diff --git a/spec/controllers/rentals.spec.js b/spec/controllers/rentals.spec.js new file mode 100644 index 000000000..8d8fe57c2 --- /dev/null +++ b/spec/controllers/rentals.spec.js @@ -0,0 +1,150 @@ +var request = require('request'); +var base_url = "http://localhost:3000/" +var route = "rentals/" + + +describe("Endpoints under /overdue", function() { + + it('responds with a 200 status code', function (done) { + request.get(base_url + route + '/overdue', function(error, response, body) { + expect(response.statusCode).toEqual(200) + done() + }) + }); + + it("should be json", function(done) { + request.get(base_url + route + '/overdue', function(error, response, body) { + expect(response.headers['content-type']).toContain('application/json') + done() + }) + }); + + it("should be an array of objects", function(done) { + request.get(base_url + route + '/overdue', function(error, response, body) { + var data = JSON.parse(body) + expect(typeof data).toEqual('object') + + for (var record of data) { + expect(Object.keys(record)).toEqual([ 'name', 'title', 'created_date', 'due_date' ]) + } + + done() + }) + }); +}); + + +describe("Endpoints under /:title", function() { + + it('responds with a 200 status code', function (done) { + request.get(base_url + route + '/Jaws', function(error, response, body) { + expect(response.statusCode).toEqual(200) + done() + }) + }); + + it("should be json", function(done) { + request.get(base_url + route + '/Jaws', function(error, response, body) { + expect(response.headers['content-type']).toContain('application/json') + done() + }) + }); + + it("should be an array of objects", function(done) { + request.get(base_url + route + '/Jaws', function(error, response, body) { + var data = JSON.parse(body) + expect(typeof data).toEqual('object') + + expect(Object.keys(data)).toEqual([ 'title', 'overview', 'release_date', 'total_inventory', 'available_copies' ]) + + done() + }) + }); +}); + + +describe("Endpoints under /rentals/:title/customers", function() { + + it('responds with a 200 status code', function (done) { + request.get(base_url + route + "/Jaws/customers", function(error, response, body) { + expect(response.statusCode).toEqual(200) + done() + }) + }); + + it("should be json", function(done) { + request.get(base_url + route + "/Jaws/customers", function(error, response, body) { + expect(response.headers['content-type']).toContain('application/json') + done() + }) + }); + + it("should be an array of objects", function(done) { + request.get(base_url + route + "/Jaws/customers", function(error, response, body) { + var data = JSON.parse(body) + expect(typeof data).toEqual('object') + + for (var record of data) { + expect(Object.keys(record)).toEqual([ 'id', 'name', 'registered_at', 'address', 'city', 'state', 'postal_code', 'phone', 'account_credit' ]) + } + + done() + }) + }); +}); + +describe("Endpoints under /rentals/:title/check-out/:customer_id", function() { + + it('responds with a 200 status code', function (done) { + request.get(base_url + route + "/Jaws/check-out/5", function(error, response, body) { + expect(response.statusCode).toEqual(200) + done() + }) + }); + + it("should be json", function(done) { + request.get(base_url + route + "/Jaws/check-out/5", function(error, response, body) { + expect(response.headers['content-type']).toContain('application/json') + done() + }) + }); + + it("should be an array of objects", function(done) { + request.get(base_url + route + "/Jaws/check-out/5", function(error, response, body) { + var data = JSON.parse(body) + expect(typeof data).toEqual('object') + + expect(Object.keys(data)).toEqual([ 'id', 'movie_id', 'customer_id', 'created_date', 'due_date', 'returned', 'returned_date' ]) + + done() + }) + }); +}); + +describe("Endpoints under /rentals/return/:id", function() { + + it('responds with a 200 status code', function (done) { + request.get(base_url + route + "/return/5", function(error, response, body) { + expect(response.statusCode).toEqual(200) + done() + }) + }); + + it("should be json", function(done) { + request.get(base_url + route + "/return/5", function(error, response, body) { + expect(response.headers['content-type']).toContain('application/json') + done() + }) + }); + + it("should be an array of objects", function(done) { + request.get(base_url + route + "/return/5", function(error, response, body) { + var data = JSON.parse(body) + expect(typeof data).toEqual('object') + + expect(Object.keys(data)).toEqual([ 'id', 'movie_id', 'customer_id', 'created_date', 'due_date', 'returned', 'returned_date' ]) + + done() + }) + }); +}); diff --git a/spec/models/customers_model.spec.js b/spec/models/customers_model.spec.js new file mode 100644 index 000000000..05360c51f --- /dev/null +++ b/spec/models/customers_model.spec.js @@ -0,0 +1,32 @@ +var app = require("../../app"); +var db = app.get("db"); +var Customer = require('../../models/customers_model') + + + +describe('#all', function () { + + it('should return an array of customers', function (done) { + Customer.all(function (error, data) { + expect(typeof data).toEqual('object') + done() + }) + }) +}) + +describe('#sort', function () { + + it('should return an array of customers', function (done) { + Customer.sort('name', 10, 2, function (error, data) { + expect(typeof data).toEqual('object') + done() + }) + }) + + it('should not sort by invalid column name', function (done) { + Customer.sort('nemo', 10, 2, function (error, data) { + expect(typeof data).toBeNull; + done() + }) + }) +}) diff --git a/spec/models/movies_model.spec.js b/spec/models/movies_model.spec.js new file mode 100644 index 000000000..f8d0dbd48 --- /dev/null +++ b/spec/models/movies_model.spec.js @@ -0,0 +1,65 @@ +var app = require("../../app"); +var db = app.get("db"); +var Movie = require('../../models/movies_model') + +describe('Movie', function () { + it('find valid movie', function (done) { + Movie.findMovie("Jaws", function(error, movie) { + expect(error).toBeNull; + expect(movie).toBeDefined; + done(); + }) + }) + + it('find invalid movie', function (done) { + Movie.findMovie("Jawz", function(error, movie) { + expect(error).toBeDefined; + expect(movie).toBeUndefined; + done(); + }) + }) + + it('should return an array', function (done) { + Movie.all(function (error, movies) { + expect(movies).toEqual(jasmine.any(Array)) + done() + }) + }) + +}) + + + +describe('return all movies', function () { + it('should not be null', function (done) { + Movie.all(function (error, movies) { + expect(movies).toNotBe(null) + done() + }) + }) +}) + +describe('return sorted movies', function () { + it('not be null', function (done) { + Movie.sort("title", 2, 10, function(error, movie) { + expect(error).toBeNull; + expect(movie).toBeDefined; + done(); + }) + }) + + it('should contain sorted movie objects', function (done) { + Movie.sort("title", 2, 10, function (error, movies) { + expect(movies[0].title).toEqual("A Clockwork Orange") + done() + }) + }) + + it('should contain all movies', function (done) { + Movie.sort("title", 2, 100, function (error, movies) { + expect(movies.length).toEqual(98) + done() + }) + }) + +}) diff --git a/spec/models/rentals_model.spec.js b/spec/models/rentals_model.spec.js new file mode 100644 index 000000000..21e42ceac --- /dev/null +++ b/spec/models/rentals_model.spec.js @@ -0,0 +1,182 @@ +var app = require("../../app"); +var db = app.get("db"); +var Rental = require('../../models/rentals_model') + + +describe('Rental getCheckedOut', function () { + it('not be null', function (done) { + Rental.getCheckedOut(5, function(error, rental) { + expect(error).toBeNull; + expect(rental).toBeDefined; + done(); + }) + }) + + it('will not find invalid rentals', function (done) { + Rental.getCheckedOut("Jawz", function(error, rental) { + expect(error).toBeDefined; + expect(rental).toBeUndefined; + done(); + }) + }) +}) + + + +describe('return current rentals`', function () { + it('should not be null', function (done) { + Rental.getCurrentRentals(5, function (error, rentals) { + expect(rentals).toNotBe(null) + done() + }) + }) + + it('will not find invalid customers', function (done) { + Rental.getCurrentRentals(333, function(error, rental) { + expect(error).toBeDefined; + expect(rental).toBeUndefined; + done(); + }) + }) +}) + +describe('return currently checked out movies', function () { + it('not be null', function (done) { + Rental.getCurrentlyCheckedOut("Jaws", function(error, movie) { + expect(error).toBeNull; + expect(movie).toBeDefined; + done(); + }) + }) + + it('will not find invalid movies`', function (done) { + Rental.getCurrentlyCheckedOut("aldkfaslkfj", function(error, rental) { + expect(error).toBeDefined; + expect(rental).toBeUndefined; + done(); + }) + }) +}) + +describe('return previously checked out movies', function () { + it('not be null', function (done) { + Rental.getPastRentals("Jaws", function(error, movie) { + expect(error).toBeNull; + expect(movie).toBeDefined; + done(); + }) + }) + + it('will not find invalid movies', function (done) { + Rental.getPastRentals("aldkfaslkfj", function(error, rental) { + expect(error).toBeDefined; + expect(rental).toBeUndefined; + done(); + }) + }) + + it('not be null', function (done) { + Rental.getPastRentals(5, function(error, movie) { + expect(error).toBeNull; + expect(movie).toBeDefined; + done(); + }) + }) +}) + +describe('return previously checked out movies', function () { + it('not be null', function (done) { + Rental.getCustomers("Jaws", function(error, movie) { + expect(error).toBeNull; + expect(movie).toBeDefined; + done(); + }) + }) + + it('will not find invalid movies', function (done) { + Rental.getCustomers("aldkfaslkfj", function(error, rental) { + expect(error).toBeDefined; + expect(rental).toBeUndefined; + done(); + }) + }) +}) + + +describe('return overdue rentals', function () { + it('not be null', function (done) { + Rental.getOverdue(function(error, movie) { + expect(error).toBeNull; + expect(movie).toBeDefined; + done(); + }) + }) + + it('will not find invalid movies', function (done) { + Rental.getOverdue(function(error, rental) { + expect(error).toBeDefined; + expect(rental).toBeUndefined; + done(); + }) + }) + + it('should return an array', function(done) { + Rental.getOverdue(function(error,rentals){ + expect(rentals).toEqual(jasmine.any(Array)) + done() + }) + }) +}) + +describe('return rentals', function () { + it('not be null', function (done) { + Rental.getReturn(3, function(error, movie) { + expect(error).toBeNull; + expect(movie).toBeDefined; + done(); + }) + }) + + it('will not return invalid rentals', function (done) { + Rental.getReturn(1255, function(error, rental) { + expect(error).toBeDefined; + expect(rental).toBeUndefined; + done(); + }) + }) +}) + + +describe('checkout rentals', function () { + it('not be null', function (done) { + Rental.getCheckout("Jaws", 2, function(error, rental) { + expect(error).toBeNull; + expect(rental).toBeDefined; + done(); + }) + }) + + it('will not checkout invalid movies', function (done) { + Rental.getCheckout("a;sldfjlaskdf", 5, function(error, rental) { + expect(error).toBeDefined; + expect(rental).toBeUndefined; + done(); + }) + }) + + it('should be a new instance of rental', function(done) { + Rental.getCheckout('Jaws', 4, function(error,rentals){ + expect(this.movie_id).toNotBe(null) + done() + }) + }) + + it('should not create an invalid instance of rental', function (done) { + Rental.getCheckout('nemo', 999, function (error, data) { + expect(typeof data).toBeNull; + done() + }) + }) + + +}) diff --git a/tasks/load_schema.js b/tasks/load_schema.js new file mode 100644 index 000000000..801e8b655 --- /dev/null +++ b/tasks/load_schema.js @@ -0,0 +1,14 @@ +var massive = require('massive') + +var databaseName = process.argv[2]; +var connectionString = "postgres://localhost/" + databaseName; +var db = massive.connectSync({connectionString : connectionString}); + +db.setup.schema([], function(err, res) { + if (err) { + throw(new Error(err.message)) + } + + console.log("yay schema!") + process.exit() +}) diff --git a/tasks/seed.js b/tasks/seed.js new file mode 100644 index 000000000..8513ee0a2 --- /dev/null +++ b/tasks/seed.js @@ -0,0 +1,66 @@ +var massive = require('massive') + +var databaseName = process.argv[2]; +var connectionString = "postgres://localhost/" + databaseName; +var db = massive.connectSync({connectionString : connectionString}); + +// produces array with each element is an object key:value pair +var moviesParsedJSON = require('../db/seeds/movies.json'); +// var movieLength = moviesParsedJSON.length +var customersParsedJSON = require('../db/seeds/customers.json'); +// var customerLength = customersParsedJSON.length +var rentalsParsedJSON = require('../db/seeds/rentals.json'); + + +// ***********synchronous method +for (var movie of moviesParsedJSON) { + console.log(movie) + // pass save + // db.video_store.saveSync({movie: movie.title.....}) + db.movies.saveSync(movie); +}; + + +for (var customer of customersParsedJSON) { + // console.log(customer.name, customer.registered_at, ) + // db.video_store.saveSync({name: customer.name, customer.registered_at, }) + db.customers.saveSync(customer); +}; + +for (var rental of rentalsParsedJSON) { + // console.log(customer.name, customer.registered_at, ) + // db.video_store.saveSync({name: customer.name, customer.registered_at, }) + db.rentals.saveSync(rental); +}; + +console.log("seeding done!") +process.exit() + + + + +// check length of db +// function checkFinish(records) { +// db.video_store.count(function(err, res) { +// console.log("words in db: ", res) +// // need to make sure both files are done +// if (res >= records) { process.exit() } +// }) +// } + +// // **********a-synchronous method +// for (var movie of moviesParsedJSON) { +// db.video_store.save(movie, function(err, res) { +// console.log('saved: ', JSON.stringify(res)) +// checkFinish(movieLength) +// }) +// } + +// for (var customer of customersParsedJSON) { +// db.video_store.save(customer, function(err, res) { +// console.log('saved: ', JSON.stringify(res)) +// checkFinish(customerLength) +// }) +// } + + diff --git a/views/docs.ejs b/views/docs.ejs new file mode 100644 index 000000000..c655f0a17 --- /dev/null +++ b/views/docs.ejs @@ -0,0 +1,24 @@ + + +
+Welcome to <%= title %>
+