diff --git a/.gitignore b/.gitignore index e43b0f9..fe51cc4 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ .DS_Store +node_modules/ +db/development.db +db/test.db diff --git a/README.md b/README.md index 2137413..05737f4 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,15 @@ Kari & Amira # Project: VideoStoreAPI +##Testing +To run our test base, you will need to setup your local test.db file with: +$ DB=test npm run reset + +Then you can run our tests with: +$ npm test + +##Description + The overall goal of this project is to create a system that a video store (remember those?) could use to track their inventory of rental videos and their collection of customers. We will use [NodeJS](https://nodejs.org/en/) to construct a RESTful API. The goal of this API is to quickly serve information about the store's video collection, customer information, and to update rental status. This repository provides two JSON datafiles to serve as the initial seeds for this system. diff --git a/app.js b/app.js new file mode 100644 index 0000000..b5163a8 --- /dev/null +++ b/app.js @@ -0,0 +1,66 @@ +var express = require('express'); +var path = require('path'); +var favicon = require('serve-favicon'); +var logger = require('morgan'); +var cookieParser = require('cookie-parser'); +var bodyParser = require('body-parser'); + +var app = express(); + +// view engine setup +app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'jade'); + +// uncomment after placing your favicon in /public +//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); +app.use(logger('dev')); +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'); +app.use('/', routes); + +var customers = require('./routes/customers'); +app.use('/customers', customers); + +var movies = require('./routes/movies'); +app.use('/movies', movies); + +var rentals = require('./routes/rentals'); +app.use('/rentals', rentals); + +// catch 404 and forward to error handler +app.use(function(req, res, next) { + var err = new Error('Not Found'); + err.status = 404; + next(err); +}); + +// error handlers + +// development error handler +// will print stacktrace +if (app.get('env') === 'development') { + app.use(function(err, req, res, next) { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: err + }); + }); +} + +// production error handler +// no stacktraces leaked to user +app.use(function(err, req, res, next) { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: {} + }); +}); + + +module.exports = app; diff --git a/bin/www b/bin/www new file mode 100755 index 0000000..cc43085 --- /dev/null +++ b/bin/www @@ -0,0 +1,90 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var app = require('../app'); +var debug = require('debug')('C3Projects--VideoStoreAPI:server'); +var http = require('http'); + +/** + * Get port from environment and store in Express. + */ + +var port = normalizePort(process.env.PORT || '3000'); +app.set('port', port); + +/** + * Create HTTP server. + */ + +var server = http.createServer(app); + +/** + * Listen on provided port, on all network interfaces. + */ + +server.listen(port); +server.on('error', onError); +server.on('listening', onListening); + +/** + * Normalize a port into a number, string, or false. + */ + +function normalizePort(val) { + var port = parseInt(val, 10); + + if (isNaN(port)) { + // named pipe + return val; + } + + if (port >= 0) { + // port number + return port; + } + + return false; +} + +/** + * Event listener for HTTP server "error" event. + */ + +function onError(error) { + if (error.syscall !== 'listen') { + throw error; + } + + var bind = typeof port === 'string' + ? 'Pipe ' + port + : 'Port ' + port; + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + console.error(bind + ' requires elevated privileges'); + process.exit(1); + break; + case 'EADDRINUSE': + console.error(bind + ' is already in use'); + process.exit(1); + break; + default: + throw error; + } +} + +/** + * Event listener for HTTP server "listening" event. + */ + +function onListening() { + var addr = server.address(); + var bind = typeof addr === 'string' + ? 'pipe ' + addr + : 'port ' + addr.port; + debug('Listening on ' + bind); +} diff --git a/controllers/customers.js b/controllers/customers.js new file mode 100644 index 0000000..d0c1d3b --- /dev/null +++ b/controllers/customers.js @@ -0,0 +1,84 @@ +"use strict"; + +var Database = require('../database'); // Node module; Pulling in db object we made in a dif file + +var customersController = { + // ALL CUSTOMERS SEARCH... "/customers?order_by=name" + // PAGINATION... "/customers?number=25&page=2" => customers 26-50 + all_customers: function(req, callback) { + var column = req.query.order_by ? req.query.order_by : "id"; + + if (req.query.number && req.query.page) { + var limit = req.query.number; + var offset = req.query.page * limit - limit; + var statement = "SELECT * FROM customers ORDER BY " + column + " ASC LIMIT " + limit + " OFFSET " + offset + ";"; + } else { + var statement = "SELECT * FROM customers ORDER BY " + column + " ASC;"; + } + + var db_env = process.env.DB || 'development', + db = new Database('db/' + db_env + '.db'); + + db.query(statement, function(err, result) { + + var json_results = { + customers: result + }; + + callback(err, json_results); + }); + + }, + + customer: function(req, callback) { + var statement = "SELECT * FROM customers, rentals WHERE customers.id = " + req.params.id + " AND rentals.customer_id = " + req.params.id + " ORDER BY rentals.checkout_date ASC;"; + + var db_env = process.env.DB || 'development', + db = new Database('db/' + db_env + '.db'); + + db.query(statement, function(err, result) { + + var customer_info = { + id: result[0].customer_id, + name: result[0].name, + registered_at: result[0].registered_at, + address: result[0].address, + city: result[0].city, + state: result[0].state, + postal_code: result[0].postal_code, + phone: result[0].phone, + account_credit: result[0].account_credit + }; + + var renting_movies = []; + var rented_movies = []; + + for(var i = 0; i < result.length; i++) { + var movie = { + id: result[i].movie_id, + title: result[i].title, + checkout_date: result[i].checkout_date, + due_date: result[i].due_date, + return_date: result[i].return_date + }; + + if(movie.return_date == null) { + renting_movies.push(movie); + } else { + rented_movies.push(movie); + } + } + + var json_results = { + account: customer_info, + renting: renting_movies, + rented: rented_movies + }; + + callback(err, json_results); + }); + } + +}; + +module.exports = customersController; diff --git a/controllers/movies.js b/controllers/movies.js new file mode 100644 index 0000000..26595f6 --- /dev/null +++ b/controllers/movies.js @@ -0,0 +1,49 @@ +"use strict"; + +var Database = require('../database'); // Node module; Pulling in db object we made in a dif file + +var moviesController = { + // maybe move the var db = ... out here? + all_movies: function(req, callback) { + var column = req.query.order_by ? req.query.order_by : "id"; + + if (req.query.number && req.query.page) { + var limit = req.query.number; + var offset = req.query.page * limit - limit; + var statement = "SELECT * FROM movies ORDER BY " + column + " ASC LIMIT " + limit + " OFFSET " + offset + ";"; + } else { + var statement = "SELECT * FROM movies ORDER BY " + column + " ASC;"; + } + + var db_env = process.env.DB || 'development', + db = new Database('db/' + db_env + '.db'); + + db.query(statement, function(err, result) { + var json_result = { + movies: result + }; + + callback(err, json_result); + }); + }, + + movie: function(req, callback) { + var column = req.query.order_by ? req.query.order_by : "title"; + var title = req.params.title + var statement = "SELECT * FROM movies WHERE title LIKE '%" + title + "%' ORDER BY " + column + " ASC;"; + + var db_env = process.env.DB || 'development', + db = new Database('db/' + db_env + '.db'); + + db.query(statement, function(err, result) { + + var json_result = { + movie: result + }; + + callback(err, json_result); + }); + }, +}; + +module.exports = moviesController; diff --git a/controllers/rentals.js b/controllers/rentals.js new file mode 100644 index 0000000..b52ddb7 --- /dev/null +++ b/controllers/rentals.js @@ -0,0 +1,148 @@ +"use strict"; + +var Database = require('../database'); // Node module; Pulling in db object we made in a dif file + +var rentalsController = { + + rentals: function(req, callback) { + var column = req.query.order_by ? req.query.order_by : "customer_id"; + var title = req.params.title + var statement = + "SELECT \ + rentals.id, rentals.movie_id, rentals.title, rentals.customer_id, rentals.name, \ + rentals.checkout_date, rentals.due_date, rentals.return_date \ + FROM movies, rentals \ + WHERE rentals.movie_id = movies.id AND movies.title LIKE '%" + title + "%' \ + ORDER BY " + column + " ASC;"; + + var db_env = process.env.DB || 'development', + db = new Database('db/' + db_env + '.db'); + + db.query(statement, function(err, result) { + + var current_rentals_array = []; + var previous_rentals_array = []; + + for(var i = 0; i < result.length; i++) { + + var rental = { + rental_id: result[i].id, + movie_id: result[i].movie_id, + movie_title: result[i].title, + customer_id: result[i].customer_id, + customer_name: result[i].name, + checkout_date: result[i].checkout_date, + due_date: result[i].due_date, + return_date: result[i].return_date + }; + + if(rental.return_date == null) { + current_rentals_array.push(rental); + } else { + previous_rentals_array.push(rental); + }; + }; + + var json_result = { + current_rentals: current_rentals_array, + previous_rentals: previous_rentals_array + }; + + callback(err, json_result); + }); + }, + + rentals_overdue: function(req, callback) { + var statement = + "SELECT \ + rentals.id, rentals.title, rentals.customer_id, rentals.name, \ + rentals.checkout_date, rentals.due_date, rentals.return_date \ + FROM movies, rentals \ + WHERE rentals.movie_id = movies.id \ + AND rentals.due_date < date('now') \ + AND rentals.return_date IS NULL \ + ORDER BY rentals.due_date ASC;"; + + var db_env = process.env.DB || 'development', + db = new Database('db/' + db_env + '.db'); + + db.query(statement, function(err, result) { + + var json_result = { + overdue_movies: result + }; + + callback(err, json_result); + }); + }, + + checkout_movie: function(req, callback) { + var customer_id = parseInt(req.params.customer_id); + var movie_id = parseInt(req.params.movie_id); + + var select_name_and_title = "SELECT name, title FROM customers, movies WHERE customers.id = " + customer_id + " AND movies.id = " + movie_id + ";"; + + var select_last_rental_statement = + "SELECT rentals.id, rentals.title, rentals.customer_id, rentals.name, rentals.checkout_date, rentals.due_date, rentals.return_date \ + FROM movies, rentals \ + ORDER BY rentals.id DESC \ + LIMIT 1"; + + var db_env = process.env.DB || 'development', + db = new Database('db/' + db_env + '.db'); + + db.query(select_name_and_title, function(err, result) { + var customer_name = result[0].name; + var movie_title = result[0].title; + + var post_rental_statement = + "INSERT INTO rentals (customer_id, name, movie_id, title, checkout_date, due_date, return_date) \ + VALUES (" + customer_id + ", '" + customer_name + "', " + movie_id + ", '" + movie_title + "', date('now'), date('now', '+7 days'), NULL); \ + UPDATE movies SET inventory_avail = inventory_avail - 1 WHERE ID = " + movie_id + "; \ + UPDATE customers SET account_credit = account_credit - 3 WHERE ID = " + customer_id + ";"; + + db.multi_query(post_rental_statement, function(err) { // no result, bc it's calling .exec (db.js file) + + db.query(select_last_rental_statement, function(err, result) { + + var json_result = { + rental: result + }; + + callback(err, json_result); + }) + }); + }) + }, // end of checkout_movie + + return_movie: function(req, callback) { + var customer_id = parseInt(req.params.customer_id); + var movie_id = parseInt(req.params.movie_id); + + var return_movie_statement = + "UPDATE rentals SET return_date = date('now') WHERE movie_id = " + movie_id + " AND return_date IS NULL AND customer_id = " + customer_id + "; \ + UPDATE movies SET inventory_avail = inventory_avail + 1 WHERE id = " + movie_id + ";"; + + var select_last_rental_statement = + "SELECT rentals.id, rentals.movie_id, rentals.title, rentals.customer_id, rentals.name, rentals.checkout_date, rentals.due_date, rentals.return_date \ + FROM rentals \ + WHERE movie_id = " + movie_id + " AND customer_id = " + customer_id + " \ + ORDER BY return_date DESC;"; + + var db_env = process.env.DB || 'development', + db = new Database('db/' + db_env + '.db'); + + db.multi_query(return_movie_statement, function(err) { + db.query(select_last_rental_statement, function(err, result) { + + var json_result = { + rental_history: result + }; + + callback(err, json_result); + }) + }) + } // end of return_movie +}; + +module.exports = rentalsController; diff --git a/database.js b/database.js new file mode 100644 index 0000000..247ce1c --- /dev/null +++ b/database.js @@ -0,0 +1,41 @@ +"use strict"; + +var sqlite3 = require('sqlite3').verbose(); + +function StoreDatabase(path) { + this.path = path; +} + +StoreDatabase.prototype = { + query: function(statement, callback) { + var db = new sqlite3.Database(this.path); // creates a connection to the db + + db.serialize(function() { + // statement == some SQL string + db.all(statement, function(err, result) { + // we only get a callback if it's successful + if (callback) { callback(err, result); } + + db.close(); + }); + + }); + }, + + multi_query: function(statement, callback) { + var db = new sqlite3.Database(this.path); // creates a connection to the db + + db.serialize(function() { + // statement == some SQL string + db.exec(statement, function(err, result) { + // '.exec' does not give a result, so we don't need to pass it back into the callback() + if (callback) { callback(err); } + + db.close(); + }); + + }); + } +}; + +module.exports = StoreDatabase; diff --git a/endpoints.md b/endpoints.md new file mode 100644 index 0000000..cabe8d4 --- /dev/null +++ b/endpoints.md @@ -0,0 +1,320 @@ +# Video Store Project Routes +The following are all the endpoints available from our Video Store API. + +## Customer + +__Endpoint__ +`GET ./customers` + +Returns all the information on record of our customers. Can be sorted by `name`, `registered_at`, or `postal_code`. Also can be paginated with `number` and `page` (e.g. display the 2nd page of 20 customer records: `./customers?number=20&page=2`). + + +__Request Parameters__ + +| Query Parameter | Value | +| :-------------- | :------------- | +| `order_by ` | _Optional._ Provide this parameter if you would like the customers sorted. Options include: `name`, `registered_at`, or `postal_code`. When using `name`, customers are sorted by their first name. | +| `number` | _Optional._ An integer that represents the number of customer records you'd like returned. | +| `page` | _Optional, but requires the `number` query._ The page of customer records you'd like returned. | + +__Response Format__ + + { + "customers" : [ + { + "id": 1, + "name": "Shelley Rocha", + "registered_at": "Wed, 29 Apr 2015 07:54:14 -0700", + "address": "Ap #292-5216 Ipsum Rd.", + "city": "Hillsboro", + "state": "OR", + "postal_code": "24309", + "phone": "(322) 510-8695", + "account_credit": 13.15 + }, + { + "id": "2", + "name": "Curran Stout", + "registered_at": "Wed, 16 Apr 2014 21:40:20 -0700", + "address": "Ap #658-1540 Erat Rd.", + "city": "San Francisco", + "state": "California", + "postal_code": "94267", + "phone": "(908) 949-6758", + "account_credit": 35.66 + } + … + ] + } + +*** + +__Endpoint__ +`GET /customers/{:id}` + +Returns all the information on record of the selected customer, including all the movies they have currently and previously checked out. Rental records are ordered by checkout_date. + +__Request Parameter__ + +| Path Parameter | Value | +| :---------------| :----------------- | +| `id ` | The customer's ID. | + +__Response__ + + { + "account": { + "id": 2, + "name": "Curran Stout", + "registered_at": "Wed, 16 Apr 2014 21:40: -0770" + "address": "123 Street Road", + "city": "San Francisco", + "state": "CA", + "postal_code": "12345", + "phone": "(123) 123-1234", + "account_credit": "12.45" + }, + "renting": [ + { + "id": 1 + "title": "Psycho", + "checkout_date": "2014-09-19", + "due_date": "2014-09-26" , + "return_date": null + }, + { + "id": 2 + "title": "Jaws", + "checkout_date": "2015-09-19", + "due_date": "2015-09-26", + "return_date": null + } + ], + "rented" : [ + { + "id": 8 + "title": "The French Connection", + "checkout_date": "2015-01-03", + "due_date": "2015-01-10", + "return_date": "2015-01-09" + } + ] + } + + +## Movies + +__Endpoint__ +`GET ./movies` + +Returns all of the movies in our database with their complete information. Can be ordered by `title` or `release_date`. Also can be paginated with number and page (e.g. display the 2nd page of 20 customer records: `./movies?number=20&page=2`). + +__Request Parameters__ + +| Query Parameter | Value | +| :-------------------| :------------- | +| `order_by` | _Optional._ Provide this parameter if you would like the customers sorted. Options include: `title` or `release_date`. | +| `number` | _Optional._ An integer that represents the number of customer records you'd like returned. | +| `page` | _Optional, but requires the `number` query._ The page of customer records you'd like returned. | + +__Response__ + + { + "movies": [ + { + "id": 1, + "title": "Psycho", + "overview": "When larcenous real estate clerk Marion Crane goes on the lam with a wad of cash and hopes of starting a new life, she ends up at the notorious Bates Motel, where manager Norman Bates cares for his housebound mother. The place seems quirky, but fine… until Marion decides to take a shower.", + "release_date": "1960-06-16", + "inventory_total": 8, + "inventory_avail": 8 + }, + { + "id": 2, + "title": "Jaws", + "overview": "An insatiable great white shark terrorizes the townspeople of Amity Island, The police chief, an oceanographer and a grizzled shark hunter seek to destroy the bloodthirsty beast.", + "release_date": "1975-06-19", + "inventory_total": 6, + "inventory_avail": 6 + }, + … + ] + } + +*** + +__Endpoint__ +`GET ./movies/{:title}` + +Returns movies that fuzzy match the title query. The results are automatically ordered by `movie_id` but can also be ordered by `title` or `release_date`. + +__Request Parameters__ + +| Path Parameter | Value | +| :------------------| :------------------------| +| `title` | The title of the movie. | +| `order_by` | _Optional._ Provide this parameter if you would like the customers sorted. Options include: `title` or `release_date`. | + +__Response__ + + { + "movie" : [ + { + "id": 23, + "title": "The Night of the Hunter", + "overview": "Harry Powell marries and murders widows for their money, believing he is helping God do away with women who arouse men's carnal instincts. Arrested for auto theft, he shares a cell with condemned killer Ben Harper and tries to get him to reveal the whereabouts of the $10,000 he stole. Only Ben's nine-year-old son, John, and four-year-old daughter, Pearl, know the money is in Pearl's doll; and they have sworn to their father to keep this secret. After Ben is executed, Preacher goes to Cresap's Landing to court Ben's widow, Willa. When he overwhelms her with his Scripture quoting, sermons, and hymns, she agrees to marry him. On their wedding night, he tells her they will never have sex because it is sinful.", + "release_date": "1955-07-26", + "inventory_total": 9, + "inventory_avail": 7 + }, + ... + ] + } + +*** + +## Rentals + +__Endpoint__ +`GET /rentals/overdue` + +Returns an array of all rentals that are currently overdue ordered ASC by `due_date`. Includes information about the customer associated with each rental. + +__Response__ + + { + "overdue_movies": [ + { + "id": 3, + "title": "Frankenstein", + "customer_id": 1, + "name": "Shelley Rocha", + "checkout_date": "2014-09-19", + "due_date": "2014-09-26", + "return_date": null + }, + { + "id": 6, + "title": "The Night of the Hunter", + "customer_id": 2, + "name": "Curran Stout", + "checkout_date": "2014-09-19", + "due_date": "2014-09-26", + "return_date": null + }, + … + ] + } + +*** + +__Endpoint__ +`GET /rentals/title/{:title}` + +Returns an array of the given movie's rental history, grouping them in two sub arrays of `current_rentals` and `previous_rentals`. + +If there are multiple matched movies for the `{:title}` search, it will pull in all matching results. + +The results are automatically ordered by `customer_id` but can also be ordered by `name` (customer's name), `checkout_date`, `due_date`, `return_date`. + +__Request Parameters__ + +| Path Parameter | Value | +| :------------------| :------------------------| +| `title` | The title of the movie. | +| `order_by` | _Optional._ Provide this parameter if you would like the rentals sorted. Options include: `name` (for customer name), `checkout_date`, `due_date`, or `return_date`. | + +__Response__ + + { + "current_rentals": [ + { + "rental_id": 6, + "movie_title": "The Night of the Hunter", + "customer_id": 2, + "customer_name": "Curran Stout", + "checkout_date": "2014-09-19", + "due_date": "2014-09-26", + "return_date": null + }, + { + "rental_id": 51, + "movie_title": "The Night of the Hunter", + "customer_id": 17, + "customer_name": "Ginger Heath", + "checkout_date": "2014-09-19", + "due_date": "2014-09-26", + "return_date": null + } + ], + "previous_rentals": [ + { + "rental_id": 17, + "movie_title": "The Night of the Hunter", + "customer_id": 6, + "customer_name": "Phyllis Russell", + "checkout_date": "2014-09-09", + "due_date": "2014-09-16", + "return_date": "2014-09-10" + }, + ... + ] + } + +*** + +__Endpoint__ +`POST ./rentals/{:customer_id}/checkout/{:movie_id}` + +Checks out the given movie for the given customer, automatically setting the rental's return date to 7 days after checkout. It reduces that movie's inventory_avail by 1 and deducts a $3 rental fee from the customer's account_credit. + +__Request Parameters__ + +| Path Parameter | Value | +| :---------------| :------------------------| +| `customer_id` | The ID of the movie. | +| `movie_id` | The title of the movie. | + +__Response__ + + { + "rental": { + "id": 2, + "customer_id": "5", + "name": "Aquila Riddle", + "movie_id": "", + "title": "Close Encounters of the Third Kind", + "checkout_date": "2015-09-13", + "due_date": "2015-09-20", + "return_date": null + } + } + +*** + +__Endpoint__ +`PUT ./rentals/{:customer_id}/return/{:movie_id}` + +Returns the updated rental information for that checked in movie and that customer's previous rental history for that movie in descending order of `return_date`. + +__Request Parameters__ + +| Path Parameter | Value | +| :---------------| :------------------------| +| `customer_id` | The ID of the movie. | +| `movie_id` | The title of the movie. | + +__Response__ + + { + "rental_history": { + "customer_id": "5", + "name": "Aquila Riddle", + "movie_id": "", + "title": "Close Encounters of the Third Kind", + "checkout_date": "2015-09-13", + "due_date": "2015-09-20", + "return_date": "2015-09-19" + } + } + diff --git a/package.json b/package.json new file mode 100644 index 0000000..09835a2 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "C3Projects--VideoStoreAPI", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "node ./bin/www", + "test": "clear; DB=test mocha --recursive", + "reset": "node ./utils/schema.js", + "seed": "node ./utils/seeds.js" + }, + "dependencies": { + "body-parser": "~1.13.2", + "cookie-parser": "~1.3.5", + "debug": "~2.2.0", + "express": "~4.13.1", + "jade": "~1.11.0", + "mocha": "^2.3.2", + "morgan": "~1.6.1", + "serve-favicon": "~2.3.0", + "sqlite3": "^3.1.0", + "supertest": "^1.1.0" + } +} diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css new file mode 100644 index 0000000..9453385 --- /dev/null +++ b/public/stylesheets/style.css @@ -0,0 +1,8 @@ +body { + padding: 50px; + font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; +} + +a { + color: #00B7FF; +} diff --git a/routes/customers.js b/routes/customers.js new file mode 100644 index 0000000..340cd14 --- /dev/null +++ b/routes/customers.js @@ -0,0 +1,37 @@ +var express = require('express'); +var router = express.Router(); +var customersController = require('../controllers/customers'); + +/* GET customers listing. */ + +// "/customers " +router.get('/', function(req, res, next) { + // res = express' response object (how it responds to the GET request) + + customersController.all_customers(req, function(err, result) { + + if (err) { + res.status(500).json(err); + } else { + res.status(200).json(result); + }; + + }); + +}); + +// "/customers/{:id}" +router.get('/:id', function(req, res, next) { + customersController.customer(req, function(err, result) { + + if (err) { + res.status(500).json(err); + } else { + res.status(200).json(result); + }; + + }) + +}); + +module.exports = router; diff --git a/routes/index.js b/routes/index.js new file mode 100644 index 0000000..169a3bd --- /dev/null +++ b/routes/index.js @@ -0,0 +1,9 @@ +var express = require('express'); +var router = express.Router(); + +// Project Baseline Requirement +router.get('/zomg', function(req, res, next) { + res.json('it works!'); +}); + +module.exports = router; diff --git a/routes/movies.js b/routes/movies.js new file mode 100644 index 0000000..5138245 --- /dev/null +++ b/routes/movies.js @@ -0,0 +1,27 @@ +var express = require('express'); +var router = express.Router(); +var moviesController = require('../controllers/movies'); + +// "GET ./movies/" +router.get("/", function(req, res, next) { + moviesController.all_movies(req, function(err, result) { + if (err) { + res.status(500).json(err); + } else { + res.status(200).json(result); + } + }) +}) + +// "GET ./movies/{:title}" +router.get("/:title", function(req, res, next) { + moviesController.movie(req, function(err, result) { + if (err) { + res.status(500).json(err); + } else { + res.status(200).json(result); + } + }) +}) + +module.exports = router; diff --git a/routes/rentals.js b/routes/rentals.js new file mode 100644 index 0000000..0530815 --- /dev/null +++ b/routes/rentals.js @@ -0,0 +1,49 @@ +var express = require('express'); +var router = express.Router(); +var rentalsController = require('../controllers/rentals'); + +// "GET ./rentals/title/{:title}" +router.get("/title/:title", function(req, res, next) { + rentalsController.rentals(req, function(err, result) { + if (err) { + res.status(500).json(err); + } else { + res.status(200).json(result); + } + }) +}) + +// "GET ./rentals/overdue" +router.get("/overdue", function(req, res, next) { + rentalsController.rentals_overdue(req, function(err, result) { + if (err) { + res.status(500).json(err); + } else { + res.status(200).json(result); + } + }) +}) + +// "POST ./rentals/{:customer_id}/checkout/{:movie_id}" +router.post("/:customer_id/checkout/:movie_id", function(req, res, next) { + rentalsController.checkout_movie(req, function(err, result) { + if (err) { + res.status(500).json(err); + } else { + res.status(200).json(result); + } + }) +}) + +// "PUT ./rentals/{:customer_id}/return/{:movie_id}" +router.put("/:customer_id/return/:movie_id", function(req, res, next) { + rentalsController.return_movie(req, function(err, result) { + if (err) { + res.status(500).json(err); + } else { + res.status(200).json(result); + } + }) +}) + +module.exports = router; \ No newline at end of file diff --git a/test/customer_controller_tests.js b/test/customer_controller_tests.js new file mode 100644 index 0000000..bcfbeb1 --- /dev/null +++ b/test/customer_controller_tests.js @@ -0,0 +1,104 @@ +var request = require('supertest'), + assert = require('assert'), + app = require('../app'), + sqlite3 = require('sqlite3').verbose(), + agent = request.agent(app); + customersController = require("../controllers/customers"); + +describe("customersController", function() { + var test_db; + + beforeEach(function(done) { + test_db = new sqlite3.Database('db/test.db'); + + test_db.serialize(function() { + test_db.exec( + "BEGIN; \ + DELETE FROM customers; \ + DELETE FROM rentals; \ + DELETE FROM movies; \ + INSERT INTO customers(name, registered_at, address, city, state, postal_code, phone, account_credit) \ + VALUES('Sarah', 'Wed', 'Ipsum Rd', 'Seattle', 'WA', '12345', '(123) 123-4567', 25.15), \ + ('Jane', 'Thurs', '123 St', 'San Jose', 'CA', '56789', '(345) 124-2984', 12.00); \ + INSERT INTO rentals(customer_id, name, movie_id, title, checkout_date, due_date, return_date) \ + VALUES(1, 'Sarah', 1, 'Movie 1', '2015-07-14', '2015-07-21', '2015-07-21'), \ + (1, 'Sarah', 2, 'Movie 2', '2015-07-16', '2015-07-23', '2015-07-19'), \ + (1, 'Sarah', 3, 'Movie 3', '2015-07-01', '2015-07-08', '2015-07-09'), \ + (2, 'Jane', 4, 'Movie 4', '2015-07-14', '2015-07-21', '2015-07-15'), \ + (2, 'Jane', 5, 'Movie 5', '2015-07-16', '2015-07-23', '2015-07-17'), \ + (2, 'Jane', 6, 'Movie 6', '2015-07-01', '2015-07-08', '2015-07-02'); \ + COMMIT;" + , function(err) { + test_db.close(); + done(); + } + ); + }); + }); + + it("has an 'all_customers' property that is a function", function() { + assert.equal(typeof customersController.all_customers, "function"); + }); + + describe('GET /', function() { + var customer_request; + + beforeEach(function(done) { + customer_request = agent.get('/customers').set('Accept', 'application/json'); + done(); + }) + + it('responds with json', function(done) { + customer_request + .expect('Content-type', /application\/json/) + .expect(200, done); + }) + + it('returns an array of customer objects', function(done) { + customer_request.expect(200, function(error, result) { + assert.equal(result.body.customers.length, 2); + + var keys = ['id', 'name', 'registered_at', 'address', 'city', 'state', 'postal_code', 'phone', 'account_credit']; + assert.deepEqual(Object.keys(result.body.customers[0]), keys); + + done(); + }) + }) + }) + + it("has a 'customer' property that is a function", function() { + assert.equal(typeof customersController.customer, "function"); + }); + + describe('GET /:id', function() { + var customer_request; + + beforeEach(function(done) { + customer_request = agent.get('/customers/2').set('Accept', 'application/json'); + done(); + }) + + it('responds with json', function(done) { + customer_request + .expect('Content-type', /application\/json/) + .expect(200, done); + }) + + it('finds the right customer record', function(done) { + customer_request.expect(200, function(error, result) { + assert.deepEqual(result.body.account.name, 'Jane'); + done(); + }) + }) + + it("includes the customer's rental history", function(done) { + customer_request.expect(200, function(error, result) { + assert.equal(result.body.rented.length, 3); + assert.deepEqual(result.body.rented[0].title, "Movie 6"); + done(); + }) + }) + + }) + +}) diff --git a/test/database_tests.js b/test/database_tests.js new file mode 100644 index 0000000..6054eff --- /dev/null +++ b/test/database_tests.js @@ -0,0 +1,41 @@ +var assert = require("assert"); +var Database = require("../database"); // our database.js file + +describe("Database", function() { + var db; + var database_path = "db/test.db"; + + beforeEach(function() { + db = new Database(database_path); + }); + + it("can be instantiated", function() { + assert.equal(db instanceof Database, true); + }); + + it("has a 'query' property that is a function", function() { + assert.equal(typeof db.query, "function"); + }); + + it("holds onto the 'path' to the database", function() { + assert.equal(db.path, database_path); + }); + + // describe("'query' function", function() { + // before(function() { + // // create a customers table + // db.query("CREATE TABLE IF NOT EXISTS customers (name TEXT);"); + + // // insert some customers + // }); + + // it("has a customers table", function(done) { + // var table_exists = "SELECT count(*) AS table_count FROM sqlite_master WHERE type='table' AND name='customers'; "; + + // db.query(table_exists, function(result) { + // assert.equal(result[0].table_count, 1); + // done(); + // }); + // }); + // }); +}); \ No newline at end of file diff --git a/test/movies_controller_tests.js b/test/movies_controller_tests.js new file mode 100644 index 0000000..6cf1c29 --- /dev/null +++ b/test/movies_controller_tests.js @@ -0,0 +1,133 @@ +var request = require('supertest'), + assert = require('assert'), + app = require('../app'), + sqlite3 = require('sqlite3').verbose(), + agent = request.agent(app); + +describe("/movies endpoints", function() { + var test_db; + + beforeEach(function(done) { + test_db = new sqlite3.Database('db/test.db'); + + test_db.serialize(function() { + test_db.exec( + "BEGIN; \ + DELETE FROM movies; \ + DELETE FROM rentals; \ + INSERT INTO movies(title, overview, release_date, inventory_total, inventory_avail) \ + VALUES('Bring It On', 'Cheerleaders duel it out.', '2000-08-22', 10, 10), \ + ('Maws', 'Worm!', '1998-02-11', 11, 11), \ + ('Bring It On 2', 'Moar cheerleaders', '1990-04-12', 12, 12); \ + INSERT INTO rentals(customer_id, name, movie_id, title, checkout_date, due_date, return_date) \ + VALUES(1, 'Sarah', 1, 'Bring It On', '2015-07-14', '2015-07-21', '2015-07-21'), \ + (1, 'Sarah', 2, 'Maws', '2015-07-16', '2015-07-23', '2015-07-19'), \ + (1, 'Sarah', 3, 'Bring It On 2', '2015-07-01', '2015-07-08', '2015-07-09'), \ + (2, 'Jane', 1, 'Bring It On', '2015-07-14', '2015-07-21', '2015-07-15'), \ + (2, 'Jane', 2, 'Maws', '2015-07-16', '2015-07-23', '2015-07-17'), \ + (2, 'Jane', 3, 'Bring It On 2', '2015-07-01', '2015-07-08', '2015-07-02'); \ + COMMIT;" + , function(err) { + test_db.close(); + done(); + } + ); + }); + }) + + describe("GET /", function() { + var movie_request, movie_request_title, movie_request_date; + + it("responds with json", function(done) { + movie_request = agent.get('/movies').set('Accept', 'application/json'); + + movie_request + .expect('Content-Type', /application\/json/) + .expect(200, done); + }) + + it("returns an array of movie objects", function(done) { + movie_request = agent.get('/movies').set('Accept', 'application/json'); + + movie_request.expect(200, function(error, result) { + assert.equal(result.body.movies.length, 3); //the tesb_db inserted 2 records + + var keys = ['id', 'title', 'overview', 'release_date', 'inventory_total', 'inventory_avail']; + assert.deepEqual(Object.keys(result.body.movies[0]), keys); + done(); + }) + }) + + it("can be ordered by title", function(done) { + movie_request_title = agent.get('/movies?order_by=title').set('Accept', 'application/json'); + + movie_request_title.expect(200, function(error, result) { + assert.equal(result.body.movies[0].title, "Bring It On"); + assert.equal(result.body.movies[1].title, "Bring It On 2"); + assert.equal(result.body.movies[2].title, "Maws"); + done(); + }) + }) + + it("can be ordered by release_date", function(done) { + movie_request_date = agent.get('/movies?order_by=release_date').set('Accept', 'application/json'); + + movie_request_date.expect(200, function(error, result) { + assert.equal(result.body.movies[0].release_date, "1990-04-12"); + assert.equal(result.body.movies[1].release_date, "1998-02-11"); + assert.equal(result.body.movies[2].release_date, "2000-08-22"); + done(); + }) + }) + + it("can determine the number of returned records per page and which page is returned", function(done) { + movie_request = agent.get('/movies?number=1&page=2').set('Accept', 'application/json'); + + movie_request.expect(200, function(error, result) { + assert.equal(result.body.movies.length, 1); + assert.equal(result.body.movies[0].title, "Maws"); + done(); + }) + }) + }) + + describe('GET /:title', function() { + var movie_request; + + it("responds with json", function(done) { + movie_request = agent.get('/movies/maws').set('Accept', 'application/json'); + + movie_request + .expect('Content-Type', /application\/json/) + .expect(200, done); + }) + + it('can find a specific movie', function(done) { + movie_request = agent.get('/movies/maws').set('Accept', 'application/json'); + + movie_request.expect(200, function(error, result) { + assert.equal(result.body.movie[0].title, 'Maws'); + done(); + }) + }) + + it('returns all partial matches when searching by title', function(done) { + movie_request = agent.get('/movies/bring').set('Accept', 'application/json'); + + movie_request.expect(200, function(error, result) { + assert.equal(result.body.movie.length, 2); + done(); + }) + }) + + it('can order by other columns if there are multiple results', function(done) { + movie_request = agent.get('/movies/bring?order_by=release_date').set('Accept', 'application/json'); + + movie_request.expect(200, function(error, result) { + assert.equal(result.body.movie[0].title, "Bring It On 2"); + done(); + }) + }) + }) + +}) diff --git a/test/rentals_controllers_tests.js b/test/rentals_controllers_tests.js new file mode 100644 index 0000000..fd1b58b --- /dev/null +++ b/test/rentals_controllers_tests.js @@ -0,0 +1,218 @@ +var request = require('supertest'), + assert = require('assert'), + app = require('../app'), + sqlite3 = require('sqlite3').verbose(), + agent = request.agent(app); + +describe("/rentals endpoints", function() { + var test_db; + + beforeEach(function(done) { + test_db = new sqlite3.Database('db/test.db'); + + test_db.serialize(function() { + test_db.exec( + "BEGIN; \ + DELETE FROM movies; \ + DELETE FROM rentals; \ + INSERT INTO movies(title, overview, release_date, inventory_total, inventory_avail) \ + VALUES('Bring It On', 'Cheerleaders duel it out.', '2000-08-22', 10, 10), \ + ('Maws', 'Worm!', '1998-02-11', 11, 11), \ + ('Bring It On 2', 'Moar cheerleaders', '1990-04-12', 12, 12); \ + INSERT INTO rentals(customer_id, name, movie_id, title, checkout_date, due_date, return_date) \ + VALUES(1, 'Sarah', 1, 'Bring It On', '2015-07-14', '2015-07-21', '2015-07-21'), \ + (1, 'Sarah', 2, 'Maws', '2015-07-16', '2015-07-23', '2015-07-19'), \ + (1, 'Sarah', 3, 'Bring It On 2', '2015-07-01', '2015-07-08', '2015-07-09'), \ + (2, 'Jane', 1, 'Bring It On', '2015-07-14', '2015-07-21', '2015-07-15'), \ + (2, 'Jane', 2, 'Maws', '2015-07-16', '2015-07-23', '2015-07-17'), \ + (2, 'Jane', 3, 'Bring It On 2', '2015-07-01', '2015-07-08', NULL), \ + (2, 'Jane', 2, 'Maws', '2015-07-01', '2015-10-08', NULL), \ + (1, 'Sarah', 2, 'Maws', '2015-07-01', '2015-07-08', '2015-07-10'); \ + COMMIT;" + , function(err) { + test_db.close(); + done(); + } + ); + }); + }) + + describe("GET /rentals/title/{:title}", function() { + var rental_request; + + it("responds with json", function(done) { + rental_request = agent.get('/rentals/title/maws').set('Accept', 'application/json'); + + rental_request + .expect('Content-Type', /application\/json/) + .expect(200, done); + }) + + it("returns an array of rental objects", function(done) { + rental_request = agent.get('/rentals/title/bring').set('Accept', 'application/json'); + + rental_request.expect(200, function(error, result) { + + var keys = ['rental_id', 'movie_id', 'movie_title', 'customer_id', 'customer_name', 'checkout_date', 'due_date', 'return_date']; + assert.deepEqual(Object.keys(result.body.current_rentals[0]), keys); + done(); + }) + }) + + it('separates rental history into current and previous rentals', function(done) { + rental_request = agent.get('/rentals/title/bring').set('Accept', 'application/json'); + + rental_request.expect(200, function(error, result) { + assert.equal(result.body.current_rentals.length, 1); + assert.equal(result.body.previous_rentals.length, 3); + done(); + }) + }) + + it("can be ordered by name (customer's name)", function(done) { + rental_request = agent.get('/rentals/title/bring?order_by=name').set('Accept', 'application/json'); + + rental_request.expect(200, function(error, result) { + assert.equal(result.body.previous_rentals[0].customer_name, "Jane"); + assert.equal(result.body.previous_rentals[1].customer_name, "Sarah"); + assert.equal(result.body.previous_rentals[2].customer_name, "Sarah"); + done(); + }) + }) + + it("can be ordered by return_date", function(done) { + rental_request = agent.get('/rentals/title/bring?order_by=return_date').set('Accept', 'application/json'); + + rental_request.expect(200, function(error, result) { + assert.equal(result.body.previous_rentals[0].return_date, "2015-07-09"); + assert.equal(result.body.previous_rentals[1].return_date, "2015-07-15"); + assert.equal(result.body.previous_rentals[2].return_date, "2015-07-21"); + done(); + }) + }) + }) + + describe('GET /rentals/overdue', function() { + var rental_request; + + it("responds with json", function(done) { + rental_request = agent.get('/rentals/overdue').set('Accept', 'application/json'); + + rental_request + .expect('Content-Type', /application\/json/) + .expect(200, done); + }) + + it('returns an array of overdue rental records', function(done) { + rental_request = agent.get('/rentals/overdue').set('Accept', 'application/json'); + + rental_request.expect(200, function(error, result) { + assert.equal(result.body.overdue_movies.length, 1); // only 1 seed record has no return_date and has a past due_date + assert.equal(result.body.overdue_movies[0].title, "Bring It On 2"); + done(); + }) + }) + + it('does not consider movies that have been returned late as still overdue', function(done) { + rental_request = agent.get('/rentals/overdue').set('Accept', 'application/json'); + + rental_request.expect(200, function(error, result) { + assert.notEqual(result.body.overdue_movies[0].name, "Sarah"); // Sarah returned a movie late + done(); + }) + }) + }) + + describe('POST ./rentals/{:customer_id}/checkout/{:movie_id}', function() { + + it("responds with json", function(done) { + rental_request = agent.post('/rentals/2/checkout/1').set('Accept', 'application/json'); + + rental_request + .expect('Content-Type', /application\/json/) + .expect(200, done); + }) + + it('creates a new rental record and returns that rental record', function(done) { + rental_request = agent.post('/rentals/2/checkout/1').set('Accept', 'application/json'); + + rental_request.expect(200, function(error, result) { + + assert.equal(result.body.rental.length, 1); + assert.equal(result.body.rental[0].customer_id, 2); + done(); + }) + }) + + it("sets the checkout_date to be day('now')", function(done) { + rental_request = agent.post('/rentals/2/checkout/1').set('Accept', 'application/json'); + + rental_request.expect(200, function(error, result) { + var today = new Date; + var day = today.getDate(); + var month = today.getMonth() + 1; + var year = today.getFullYear(); + + if(day < 10) { day = '0' + day } + if(month < 10) { month = '0' + month } + + var now = year + "-" + month + "-" + day; + + assert.equal(result.body.rental[0].checkout_date, now); + done(); + }) + }) + + it("sets a due_date greater than today", function(done) { + rental_request = agent.post('/rentals/2/checkout/1').set('Accept', 'application/json'); + + rental_request.expect(200, function(error, result) { + var today = new Date; + var day = today.getDate(); + var month = today.getMonth() + 1; + var year = today.getFullYear(); + + if(day < 10) { day = '0' + day } + if(month < 10) { month = '0' + month } + + var now = year + "-" + month + "-" + day; + + assert(result.body.rental[0].due_date > now); + done(); + }) + }) + }); + + describe('PUT ./rentals/{:customer_id}/return/{:movie_id}', function() { + + it("responds with json", function(done) { + rental_request = agent.put('/rentals/2/return/2').set('Accept', 'application/json'); + + rental_request + .expect('Content-Type', /application\/json/) + .expect(200, done); + }) + + it("updates return_date to date('now') and returns the customer's rental history for that movie", function(done) { + rental_request = agent.put('/rentals/2/return/2').set('Accept', 'application/json'); + + rental_request.expect(200, function(error, result) { + var today = new Date; + var day = today.getDate(); + var month = today.getMonth() + 1; + var year = today.getFullYear(); + + if(day < 10) { day = '0' + day } + if(month < 10) { month = '0' + month } + + var now = year + "-" + month + "-" + day; + + assert.equal(result.body.rental_history.length, 2); + assert.equal(result.body.rental_history[0].customer_id, 2); + assert.equal(result.body.rental_history[0].return_date, now); + done(); + }) + }) + }) + +}) diff --git a/customers.json b/utils/customers.json similarity index 100% rename from customers.json rename to utils/customers.json diff --git a/movies.json b/utils/movies.json similarity index 100% rename from movies.json rename to utils/movies.json diff --git a/utils/rentals.js b/utils/rentals.js new file mode 100644 index 0000000..4834674 --- /dev/null +++ b/utils/rentals.js @@ -0,0 +1,56 @@ +"use strict"; + +var customers = require('./customers'); +var movies = require('./movies'); +var rentals = []; + +for(var i = 0; i < customers.length; i++) { + var customer = customers[i]; + // assuming our db creates these in over + // looking for the ID (which doesn't include 0) + var movie1_id = Math.floor(Math.random() * movies.length) + 1; + var movie2_id = Math.floor(Math.random() * movies.length) + 1; + var movie3_id = Math.floor(Math.random() * movies.length) + 1; + // using it for the INDEX (which starts at 0) + var movie1 = movies[movie1_id - 1]; + var movie2 = movies[movie2_id - 1]; + var movie3 = movies[movie3_id - 1]; + + var current_rental = { + "customer_id": customer.id, + "name": customer.name, + "movie_id": movie1_id, + "title": movie1.title, + "checkout_date": "2015-09-19", + "due_date": "3016-09-19", + "return_date": null + } + + rentals.push(current_rental); + + var past_rental = { + "customer_id": customer.id, + "name": customer.name, + "movie_id": movie2_id, + "title": movie2.title, + "checkout_date": "2014-09-09", + "due_date": "2014-09-16", + "return_date": "2014-09-10" + } + + rentals.push(past_rental); + + var overdue_rental = { + "customer_id": customer.id, + "name": customer.name, + "movie_id": movie3_id, + "title": movie3.title, + "checkout_date": "2014-09-19", + "due_date": "2014-09-26", + "return_date": null + } + + rentals.push(overdue_rental); +} + +module.exports = rentals; diff --git a/utils/schema.js b/utils/schema.js new file mode 100644 index 0000000..67334eb --- /dev/null +++ b/utils/schema.js @@ -0,0 +1,74 @@ +"use strict"; + +var sqlite3 = require('sqlite3').verbose(), + db_env = process.env.DB || 'development', + db = new sqlite3.Database('db/' + db_env + '.db'); + +var movie_fields = [ + ['title', 'text'], + ['overview', 'text'], + ['release_date', 'text'], + ['inventory_total', 'integer'], + ['inventory_avail', 'integer'] +]; + +var customer_fields = [ + ['name', 'text'], + ['registered_at', 'text'], + ['address', 'text'], + ['city', 'text'], + ['state', 'text'], + ['postal_code', 'text'], + ['phone', 'text'], + ['account_credit', 'text'] +]; + +var rental_fields = [ + ['customer_id', 'integer'], + ['name', 'text'], + ['movie_id', 'integer'], + ['title', 'text'], + ['checkout_date', 'text'], + ['due_date', 'text'], + ['return_date', 'text'] +]; + +db.serialize(function() { + // drop existing tables + db.run("DROP TABLE IF EXISTS movies;"); + db.run("DROP TABLE IF EXISTS customers;"); + db.run("DROP TABLE IF EXISTS rentals;"); + + // create fresh versions of tables + db.run("CREATE TABLE movies (id INTEGER PRIMARY KEY);"); + db.run("CREATE TABLE customers (id INTEGER PRIMARY KEY);"); + db.run("CREATE TABLE rentals (id INTEGER PRIMARY KEY);"); + + + // add columns to movies tables + for(var i = 0; i < movie_fields.length; i++) { + var name = movie_fields[i][0], + type = movie_fields[i][1]; + + db.run("ALTER TABLE movies ADD COLUMN " + name + " " + type + ";"); + } + + // add columns to customers table + for(var i = 0; i < customer_fields.length; i++) { + var name = customer_fields[i][0], + type = customer_fields[i][1]; + + db.run("ALTER TABLE customers ADD COLUMN " + name + " " + type + ";"); + } + + // add columns to rentals table + for(var i = 0; i < rental_fields.length; i++) { + var name = rental_fields[i][0], + type = rental_fields[i][1]; + + db.run("ALTER TABLE rentals ADD COLUMN " + name + " " + type + ";"); + } + +}); + +db.close(); diff --git a/utils/seeds.js b/utils/seeds.js new file mode 100644 index 0000000..5f532c9 --- /dev/null +++ b/utils/seeds.js @@ -0,0 +1,80 @@ +"use strict"; + +var sqlite3 = require('sqlite3').verbose(), + db_env = process.env.DB || 'development', + db = new sqlite3.Database('db/' + db_env + '.db'); + +var movies = require('./movies'); +var movie_statement = db.prepare( + "INSERT INTO movies(title, overview, release_date, inventory_total, inventory_avail) \ + VALUES (?, ?, ?, ?, ?);" +); + +var customers = require('./customers'); +var customer_statement = db.prepare( + "INSERT INTO customers(name, registered_at, address, city, state, postal_code, phone, account_credit) \ + VALUES (?, ?, ?, ?, ?, ?, ?, ?);" +); + +var rentals = require('./rentals'); +var rental_statement = db.prepare( + "INSERT INTO rentals(customer_id, name, movie_id, title, checkout_date, due_date, return_date) \ + VALUES (?, ?, ?, ?, ?, ?, ?);" +); + +db.serialize(function() { + // loop through movies + for(var i = 0; i < movies.length; i++) { + var movie = movies[i]; + + // insert each into the db + movie_statement.run( + movie.title, + movie.overview, + movie.release_date, + movie.inventory, + movie.inventory + ); + } + + movie_statement.finalize(); + + // loop through customers + for(var i = 0; i < customers.length; i++) { + var customer = customers[i]; + + // insert each into the db + customer_statement.run( + customer.name, + customer.registered_at, + customer.address, + customer.city, + customer.state, + customer.postal_code, + customer.phone, + customer.account_credit + ); + } + + customer_statement.finalize(); + + // loop through rentals + for(var i = 0; i < rentals.length; i++) { + var rental = rentals[i]; + + // insert each into the db + rental_statement.run( + rental.customer_id, + rental.name, + rental.movie_id, + rental.title, + rental.checkout_date, + rental.due_date, + rental.return_date + ); + } + + rental_statement.finalize(); +}); + +db.close(); diff --git a/views/error.jade b/views/error.jade new file mode 100644 index 0000000..51ec12c --- /dev/null +++ b/views/error.jade @@ -0,0 +1,6 @@ +extends layout + +block content + h1= message + h2= error.status + pre #{error.stack} diff --git a/views/index.jade b/views/index.jade new file mode 100644 index 0000000..3d63b9a --- /dev/null +++ b/views/index.jade @@ -0,0 +1,5 @@ +extends layout + +block content + h1= title + p Welcome to #{title} diff --git a/views/layout.jade b/views/layout.jade new file mode 100644 index 0000000..15af079 --- /dev/null +++ b/views/layout.jade @@ -0,0 +1,7 @@ +doctype html +html + head + title= title + link(rel='stylesheet', href='/stylesheets/style.css') + body + block content