diff --git a/.gitignore b/.gitignore index 646ac519e..e4259ac40 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .DS_Store node_modules/ +coverage diff --git a/README.md b/README.md index b38c92359..362b5c41d 100644 --- a/README.md +++ b/README.md @@ -1,100 +1,82 @@ -# Project: VideoStoreAPI -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. +- All endpoints return JSON data unless explicitly stated otherwise. If there is no data/record to return a message such as "This movie has not yet been checked out" or "This customer has not yet checked out a movie" will be returned. -We will use [NodeJS](https://nodejs.org/en/) and [Express](http://expressjs.com/) 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. - -## Project Baseline -- Read the API Requirements below and create a pseudo-code "routes" file that - - defines the _endpoints_ your API will need - - the _HTTP verbs_ each endpoint will use - - and any data that must be provided to the endpoint in order for it to do its work. -- Create a new Node/Express app to serve as the API. -- Create a route that responds to `/zomg` that serves a json-encoded "it works!" method. - -## Wave 1: Database Models, Tables, & Seeds -- Create an ERD for your database by reading through all of the requirements below. -- Be sure your ERD includes primary keys for all tables -- Leverage MassiveJS and create custom npm scripts to... - - create a development database - - drop a development database - - apply a schema to the database - - seed a database using the provided json data in `db/seeds` - -### Seed Data -`movies.json` contains information about the videos available to rent at the store. The data is presented as an array of objects, with each object having the following key-value pairs: - -- `title`: The title of the film -- `overview`: A short plot synopsis -- `release_date`: When the film was originally released -- `inventory`: How many copies of the film the video store owns - -`customers.json` contains information about the customers that have rented with the store in the past. The data is presented as, you guessed it, an array of objects, with each object have the following key-value pairs: - -- `name`: The customer's name -- `registered_at`: When the customer first visited the store -- The customer's physical address, composed of: - - `address` - - `city` - - `state` - - `postal_code` -- `phone`: Primary contact phone number -- `account_credit`: For reason we'd rather not get into, the store owes all of their customers a little bit of money; this amount is made available to customers as credit toward future rentals. - -## Wave 2: API Requirements -The API you build should have the following capabilities. The schema of your database and the structure of the endpoints are completely up to you. Make every effort to conform to RESTful routing patterns. Every endpoint (except for `/zomg` from the baseline) must serve `json` data. Use HTTP response codes to indicate the status of the request. - -#### Authentication -- There is not an authentication requirement for this project; assume all users interacting with the API are video store employees. - -#### Interface -- This part of the project is purely an API; all interactions should happen over HTTP requests. There is no front-end, user-facing interface. +- All endpoints are assumed to be GET unless explicitly stated otherwise #### Customers -- Retrive a list of all customers (`/customers`) -- Retrive a subset of customers (`/customers/sort/name?n=10&p=2`) +- Retrive a list of all customers: (`/customers`) +- Retrive a subset of customers (`/customers/sort/:sort_params`) - Given a sort column, return _n_ customer records, offset by _p_ records (this will be used to create "pages" of customers) - - Sort columns are - - `name` - - `registered_at` - - `postal_code` + - Sort params are + - `name` : (`/name?n=10&p=2`) + - `registered_at` example: (`/registered-at?n=10&p=2`) + - `postal_code` example: (`/postal-code?n=10&p=2`) - Given a customer's `id`... - - List the movies they _currently_ have checked out (`/customers/5/current`) - - List the movies a customer has checked out in the past (`/customers/5/history`) + - List the movies they _currently_ have checked out (`/customers/:customer_id/current`) + - List the movies a customer has checked out in the past (`/customers/:customer_id/history`) - ordered by check out date - includes return date #### Movies - Retrieve a list of all movies (`/movies`) -- Retrieve a subset of movies (`/movies/sort/release-date?n=5&p=1`) +- Retrieve a subset of movies (`/movies/sort/:sort_params`) - Given a sort column, return _n_ movie records, offset by _p_ records (this will be used to create "pages" of movies) - - Sort columns are - - `title` - - `release_date` -- Given a movie's `title`... - - Get a list of customers that have _currently_ checked out a copy of the film (`/movies/Jaws/current`) - - include each customer's name, phone number, and account credit - - Get a list of customers that have checked out a copy _in the past_ (`/movies/Jaws/history/sort/name`) - - include each customer's name, phone number, and account credit - - ordered by customer `name` __or__ - - ordered by check out date + - Sort params are + - `title` example: (`/title?n=5&p=1`) + - `release_date` example: (`/release-date?n=5&p=1`) +- Given a movie's `id`... + - Get a list of customers that have _currently_ checked out a copy of the film (`/movies/:movie_id/current`) + - includes each customer's name, phone number, and account credit + - Get a list of customers that have checked out a copy _in the past_ + - includes each customer's name, phone number, and account credit + - ordered by customer `name` (`/movies/:movie_id/history/sort/name`) + - ordered by `check-out` (`/movies/:movie_id/history/sort/date`) #### Rental -- Look a movie up by title to see (`/rentals/Jaws`) +- Look a movie up by id to see (`/movies/rentals/2`) - it's synopsis - release date - - available inventory (not currently checked-out to a customer) + - available inventory (not currently checked-out to a customer) - and inventory total -- See a list of customers that have _currently_ checked out any of the movie's inventory (`/rentals/Jaws/customers`) +- See a list of customers that have _currently_ checked out any of the movie's inventory (`/movies/rentals/:movie_id/customers`) - Given a customer's `id` and a movie's `title` ... - - "check out" one of the movie's inventory to the customer (`/rentals/Jaws/check-out`) + - "check out" one of the movie's inventory to the customer POST (`/movies/rentals/:movie_id/check-out/:customer_id`) - Establish a return date - Charge the customer's account (cost up to you) - - "check in" one of customer's rentals (`/rentals/Jaws/return`) + - "check in" one of customer's rentals POST (`/movies/rentals/:movie_id/return/:customer_id`) - return the movie to its inventory -- See a list of customers with overdue movies (`/rentals/overdue`) +- See a list of customers with overdue movies (`/movies/rentals/overdue`) - include customer name, movie title, check-out date, and return date -#### Testing -- All endpoints must be tested. -- We will use [Jasmine](https://github.com/mhevery/jasmine-node) for tests. -- There isn't a coverage requirement for this project, beyond demonstrating that every endpoint is covered by some manner of tests. + ================================= + +####Visual tree +All urls are from root (localhost:3000/) + +- (`/customers`) (All customers) + - (`/customers/sort/:sort_params`) (Some customers) + + `name` : (`/name?n=10&p=2`) (sorted by name) + + `registered_at` : (`/registered-at?n=10&p=2`) (sortedby join date) + + `postal_code` : (`/postal-code?n=10&p=2`) (sorted by location) + - (`/customers/:customer_id/current`) (Currently checked out movies by a customer) + - (`/customers/:customer_id/history`) (Previously checked out movies by a customer) + + +- (`/movies`) (All movies) + - (`/movies/sort/:sort_params`) (Some movies) + + `title` : (`/title?n=5&p=1`) (Sorted by title) + + `release_date` : (`/release-date?n=5&p=1`) (Sorted by release date) + - (`/movies/:movie_id/current`) (Current rentals per movie, ordered by due date) + - (`/movies/:movie_id/history/sort/:sort_params`) (Some customers who have previously checked out movies) + + (`/movies/:movie_id/history/sort/name`) (Sort by customer name) + + (`/movies/:movie_id/history/sort/date`) (Sort by check-out date) + +#####[Rentals, an extension of Movies] + - (`/movies/rentals/:movie_id`) (To see movie details, and number of available copies in addition to total stock) + - (`/movies/rentals/:movie_id/customers`) (Customers who currently have this movie checked out) + - POST (`/movies/rentals/:movie_id/check-out/:customer_id`) (Creates a "check-out" of this movie to this customer, assigning the record a due date and charge) + - POST (`/movies/rentals/:movie_id/return/:customer_id`) ("Returns" this movie checked out to this customer, changing checked-out status to false, and changing the available inventory accordingly) + - (`/movies/rentals/overdue`) + +##Informational endpoints +- (`/api/docs`) (Serves a *HTML view* of documentation) +- (`/api/docs.json`) (Serves JSON documentation) diff --git a/app.js b/app.js index f0579b1dc..5f4d24ed2 100644 --- a/app.js +++ b/app.js @@ -6,9 +6,18 @@ var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); var routes = require('./routes/index'); - var app = express(); +var massive = require("massive") +var connectionString = "postgres://localhost/video_store" + +// divide responsibilities. everything in "movies" will have /movies in its url. +var movies = require('./routes/movies'); +app.use('/movies', movies) + +var customers = require('./routes/customers'); +app.use('/customers', customers) + // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'jade'); diff --git a/controllers/customers.js b/controllers/customers.js new file mode 100644 index 000000000..f200d26bb --- /dev/null +++ b/controllers/customers.js @@ -0,0 +1,90 @@ +var Massive = require("massive"); +var db = Massive.connectSync({db : "video_store"}); + +var CustomerController = { + allCustomers: function (req, res, next) { + db.query("select * from customers", function(err, customerRecords){ + if(err) { + var err = new Error(err.message) + next(err) + } else { + res.json(customerRecords) + } + }); + }, + + sortName: function (req, res, next) { + var n = req.query.n + var p = req.query.p + if (n === undefined) { n = 10 } + if ( p === undefined) { p = 1 } + db.query("select * from customers order by name asc limit $1 offset $2", [n, p], function(err, customerRecords){ + if(err) { + var err = new Error(err.message) + next(err) + } else { + res.json(customerRecords) + } + }); + }, + + sortDate: function (req, res, next) { + var n = req.query.n + var p = req.query.p + if ( n === undefined) { n = 10 } + if ( p === undefined) { p = 1 } + db.query("select * from customers order by registered_at asc limit $1 offset $2", [n, p], function(err, customerRecords){ + if(err) { + var err = new Error(err.message) + next(err) + } else { + res.json(customerRecords) + } + }); + }, + + sortPostalCode: function (req, res, next) { + var n = req.query.n + var p = req.query.p + if ( n === undefined) { n = 10 } + if ( p === undefined) { p = 1 } + db.query("select * from customers order by postal_code asc limit $1 offset $2", [n, p], function(err, customerRecords){ + if(err) { + var err = new Error(err.message) + next(err) + } else { + res.json(customerRecords) + } + }); + }, + + current: function (req, res, next) { + var customerId = req.params.id + db.query("select * from rentals where checked_out = true and customer_id=$1 order by due_date asc", [customerId], function(err, movieRecords){ + if(err) { + var err = new Error(err.message) + next(err) + } else if (movieRecords.length < 1) { + res.json("This customer has not yet checked out any movies") + } else { + res.json(movieRecords) + } + }); + }, + + history: function (req, res, next) { + var customerId = req.params.id + db.query("select * from rentals where checked_out = false and customer_id=$1 order by due_date asc", [customerId], function(err, movieRecords){ + if(err) { + var err = new Error(err.mesage) + next(err) + } else if (movieRecords.length < 1) { + res.json("This customer has not yet checked out any movies") + } else { + res.json(movieRecords) + } + }); + } +} + +module.exports = CustomerController diff --git a/controllers/movies.js b/controllers/movies.js new file mode 100644 index 000000000..ff7c5fcf0 --- /dev/null +++ b/controllers/movies.js @@ -0,0 +1,231 @@ +var Massive = require("massive"); +var db = Massive.connectSync({db : "video_store"}); + +var MovieController = { + + allMovies: function (req, res, next) { + + db.query("select * from movies", function(err, movieRecords){ + if(err) { + var err = new Error(err.message) + next(err) + } else { + res.json(movieRecords) + } + }); + }, + + sortTitle: function (req, res, next) { + var n = req.query.n + var p = req.query.p + if ( n === undefined) { n = 10 } + if ( p === undefined) { p = 1 } + db.query("select * from movies order by title limit $1 offset $2", [n, p], function(err, movieRecords){ + if(err) { + var err = new Error(err.message) + next(err) + } else { + res.json(movieRecords) + } + }); + }, + + sortRelease: function (req, res, next) { + var n = req.query.n + var p = req.query.p + if ( n === undefined) { n = 10 } + if ( p === undefined) { p = 1 } + db.query("select * from movies order by release_date limit $1 offset $2", [n, p], function(err, movieRecords){ + if(err) { + var err = new Error(err.message) + next(err) + } else { + res.json(movieRecords) + } + }); + }, + + current: function (req, res, next) { + var movie_id = req.params.id + db.query("select * from rentals where checked_out = true and movie_id=$1 order by due_date asc", [movie_id], function(err, movieRecords){ + console.log(movieRecords) + if(err) { + var err = new Error(err.message) + next(err) + } else if (movieRecords.length < 1) { + res.json("This movie has not been checked out yet") + } + else { + var customers = [] + for (var movie of movieRecords) { + db.customer_select([movie.customer_id], function(err, customerRecords){ + if(err) { + var err = new Error(err.message) + next(err) + } else { + customers.push(customerRecords) + // console.log(customers) + } + res.json(customers) + + } + )} + } + } + )}, + + historyName: function (req, res, next) { + var movie_id = req.params.id + db.query("select * from rentals where checked_out = false and movie_id=$1 order by due_date asc", [movie_id], function(err, movieRecords){ + if(err) { + var err = new Error(err.message) + next(err) + } else if (movieRecords.length < 1) { + res.json("This movie has not been checked out yet") + } else { + var customers = [] + for (var movie of movieRecords) { + db.query("select * from customers where id=$1 order by name", [movie.customer_id], function(err, customerRecords){ + console.log("HERE") + if(err) { + var err = new Error(err.message) + next(err) + } else { + customers.push(customerRecords) + // console.log(customers) + } + res.json(customers) + + } + )} + } + }); + }, + + historyDate: function (req, res, next) { + var movie_id = req.params.id + db.query("select * from rentals where checked_out = false and movie_id=$1 order by checked_out asc", [movie_id], function(err, movieRecords){ + if(err) { + var err = new Error(err.message) + next(err) + } else if (movieRecords.length < 1) { + res.json("This movie has not been checked out yet") + } else { + var customers = [] + for (var movie of movieRecords) { + db.customer_select([movie.customer_id], function(err, customerRecords){ + if(err) { + var err = new Error(err.message) + next(err) + } else { + customers.push(customerRecords) + } + res.json(customers) + } + )} + } + }); + }, + + rentalsTitle: function (req, res, next) { + var movie_id = req.params.id + db.checked_out_movie_select([movie_id], function(err, rentalRecords) { + if(err) { + var err = new Error(err.message) + next(err) + } else if (rentalRecords.length < 1) { + res.json("This movie has not been checked out yet") + }else { + db.query("select * from movies where id=$1", [movie_id], function(err, movieRecords){ + if(err) { + var err = new Error(err.message) + next(err) + } else { + var available = movieRecords[0].inventory - rentalRecords.length + res.json({movieRecords: movieRecords, available: available}) + } + }); + } + }) +}, +// refactor with join if + + rentalsCustomers: function (req, res, next) { + var movie_id = req.params.id + db.checked_out_movie_select([movie_id], function(err, rentalRecords) { + if(err) { + var err = new Error(err.message) + next(err) + } else if (movieRecords.length < 1) { + res.json("This movie has not been checked out yet") + }else { + var customerNumbers = [] + for (var record of rentalRecords) { + customerNumbers.push(record.customer_id) + } + res.json({customerIdNumbers: customerNumbers}) + }} + ) +}, + + checkout: function (req, res, next) { + var movie_id = req.params.id + var customer_id = req.params.customer + + var current_date = new Date() + var current_day = current_date.getDate() + var current_month = current_date.getMonth() + 1 + var current_year = current_date.getFullYear() + var check_out_date = current_year + "-" + current_month + "-" + current_day + + var future_date = new Date(current_date.getTime()+(14*24*60*60*1000)); + var future_day = future_date.getDate() + var future_month = future_date.getMonth() + 1 + var future_year = future_date.getFullYear() + var due_date = future_year + "-" + future_month + "-" + future_day + + db.query("insert into rentals (customer_id,movie_id,check_out_date,checked_out,due_date) values ($1,$2,$3,$4,$5)", [customer_id,movie_id,check_out_date,true,due_date], function(err, createRental){ + if(err) { + var err = new Error(err.message) + next(err) + } else { + db.query("update customers set account_credit = account_credit - 1 where id = $1", [customer_id], function(err, customerRecord){ + if(err) { + var err = new Error(err.message) + next(err) + } else { + res.json(200) + } + }); + } + }); + + }, + + return: function (req, res, next) { + var movie_id = req.params.id + var customer_id = req.params.customer + + db.query("update rentals set checked_out = false where movie_id=$1 AND customer_id=$2", [movie_id,customer_id], function(err, updateRental){ + if(err) { + var err = new Error(err.message) + next(err) + } else { + res.json(200) + } + }); + }, + + overdue: function (req, res, next) { + db.query("SELECT customer_id, movie_id, check_out_date, due_date FROM rentals WHERE checked_out=true AND due_date < now()", function(err, movieRecords){ + if(err) { + var err = new Error(err.message) + next(err) + } else { + res.json(movieRecords) + } + }); + } + +} +module.exports = MovieController diff --git a/db/checked_out_movie_select.sql b/db/checked_out_movie_select.sql new file mode 100644 index 000000000..f07e0683e --- /dev/null +++ b/db/checked_out_movie_select.sql @@ -0,0 +1 @@ +select * from rentals where movie_id = $1 and checked_out = true; diff --git a/db/customer_select.sql b/db/customer_select.sql new file mode 100644 index 000000000..36f99e2d3 --- /dev/null +++ b/db/customer_select.sql @@ -0,0 +1 @@ +select * from customers where id=$1; diff --git a/db/seeds/rentals.json b/db/seeds/rentals.json new file mode 100644 index 000000000..2d5829448 --- /dev/null +++ b/db/seeds/rentals.json @@ -0,0 +1,23 @@ +[ +{ +"customer_id": 1, +"movie_id": 2, +"check_out_date": "2016-06-14", +"checked_out": true, +"due_date": "2016-06-24" +}, +{ +"customer_id": 2, +"movie_id": 4, +"check_out_date": "2015-02-14", +"checked_out": true, +"due_date": "2015-02-24" +}, +{ +"customer_id": 2, +"movie_id": 7, +"check_out_date": "2013-06-15", +"checked_out": false, +"due_date": "2013-06-30" +} +] diff --git a/db/setup/schema.sql b/db/setup/schema.sql new file mode 100644 index 000000000..34dbb39ab --- /dev/null +++ b/db/setup/schema.sql @@ -0,0 +1,41 @@ +DROP TABLE IF EXISTS customers; +CREATE TABLE customers( + id serial PRIMARY KEY, + name text, + address text, + city text, + state text, + postal_code text, + phone text, + account_credit float, + registered_at date +); + +CREATE INDEX customers_name ON customers (name); + -- FIX THIS, I HAVE NO IDEA WHAT INDEX + +DROP TABLE IF EXISTS movies; +CREATE TABLE movies( + id serial PRIMARY KEY, + title text, + overview text, + inventory integer, + available integer, + release_date date +); + +CREATE INDEX movies_title ON movies (title); +-- FIX THIS, I HAVE NO IDEA WHAT INDEX + +DROP TABLE IF EXISTS rentals; +CREATE TABLE rentals( + id serial PRIMARY KEY, + customer_id integer, + movie_id integer, + check_out_date date, + checked_out boolean, + due_date date +); + +CREATE INDEX rentals_movie_id ON rentals (movie_id); +-- FIX THIS, I HAVE NO IDEA WHAT INDEX diff --git a/package.json b/package.json index d39b26403..10cb8674f 100644 --- a/package.json +++ b/package.json @@ -4,14 +4,25 @@ "private": true, "scripts": { "start": "nodemon ./bin/www", - "test": "clear; jasmine-node --verbose spec/" + "start-test": "NODE_ENV=test ./node_modules/.bin/nodemon ./bin/www", + "test": "clear; ./node_modules/.bin/istanbul cover -x 'spec/**/*' -- ./node_modules/.bin/jasmine-node --verbose spec/", + "db:create": "createdb video_store", + "db:seed": "clear; node tasks/db_seed.js", + "db:drop": "dropdb video_store", + "db:reset": "npm run db:drop; npm run db:create; npm run db:schema; npm run db:seed", + "db:schema": "clear; node tasks/load_schema.js" }, "dependencies": { "body-parser": "~1.13.2", "cookie-parser": "~1.3.5", "debug": "~2.2.0", "express": "~4.13.1", + "himalaya": "^0.2.0", + "istanbul": "^0.4.4", "jade": "~1.11.0", + "jasmine-node": "^1.14.5", + "markdown": "^0.5.0", + "massive": "^2.3.0", "morgan": "~1.6.1", "sequelize": "^3.23.3", "serve-favicon": "~2.3.0" diff --git a/routes/customers.js b/routes/customers.js new file mode 100644 index 000000000..4be718d55 --- /dev/null +++ b/routes/customers.js @@ -0,0 +1,14 @@ +var express = require('express') +var router = express.Router() +var Controller = require('../controllers/customers') + +/* GET home page. */ +router.get('/', Controller.allCustomers) +router.get('/sort/name', Controller.sortName) +router.get('/sort/registered-at', Controller.sortDate) +router.get('/sort/postal-code', Controller.sortPostalCode) + +router.get('/:id/current', Controller.current) +router.get('/:id/history', Controller.history) + +module.exports = router diff --git a/routes/index.js b/routes/index.js index 06cfc1137..d7d9ce02b 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,9 +1,24 @@ var express = require('express'); var router = express.Router(); +var himalaya = require('himalaya'); +var html = require('fs').readFileSync('README.md'); +var json = himalaya.parse(html); //actually md. close enough. /* GET home page. */ router.get('/', function(req, res, next) { res.status(200).json({whatevs: 'whatevs!!!'}) }); +router.get('/zomg', function(req, res, next) { + res.status(200).json({whatevs: 'it works!!!'}) +}); + +router.get('/api/docs', function(req, res, next) { + res.render('../views/index') +}); + +router.get('/api/docs.json', function(req, res, next) { + res.json(json) +}); + module.exports = router; diff --git a/routes/movies.js b/routes/movies.js new file mode 100644 index 000000000..db230d2fd --- /dev/null +++ b/routes/movies.js @@ -0,0 +1,19 @@ +var express = require('express') +var router = express.Router() +var Controller = require('../controllers/movies') + +/* GET home page. */ +router.get('/', Controller.allMovies) +router.get('/sort/title', Controller.sortTitle) +router.get('/sort/release-date', Controller.sortRelease) + +router.get('/:id/current', Controller.current) +router.get('/:id/history/sort/date', Controller.historyDate) +router.get('/:id/history/sort/name', Controller.historyName) + +router.get('/rentals/overdue', Controller.overdue) +router.get('/rentals/:id', Controller.rentalsTitle) +router.get('/rentals/:id/customers', Controller.rentalsCustomers) +router.post('/rentals/:id/checkout/:customer', Controller.checkout) +router.post('/rentals/:id/return/:customer', Controller.return) +module.exports = router; diff --git a/spec/controllers/customers.spec.js b/spec/controllers/customers.spec.js new file mode 100644 index 000000000..9701a3370 --- /dev/null +++ b/spec/controllers/customers.spec.js @@ -0,0 +1,37 @@ +var request = require('request') +var base_url = "http://localhost:3000/customers" +var endpoints = ['/', '/customers', '/customers/sort/name', '/customers/sort/registered-at', '/customers/sort/postal-code', '/customers/2/current', '/customers/2/history'] + +var testing = function (endpoints) { + for (var endpoint of endpoints) { + describe("Endpoint at /customers", function () { + it('responds with a 200 status code', function (done) { + request.get(base_url, function(error, response, body) { + expect(response.statusCode).toEqual(200) + done() + }) + }) + + it("returns JSON", function(done) { + request.get(base_url, 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, 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', 'address', 'city', 'state', 'postal_code', 'phone', 'account_credit', 'registered_at']) + } + done() + }) + }) + + }) + } + } diff --git a/spec/controllers/movies.spec.js b/spec/controllers/movies.spec.js index ddcaf2f68..83ba4f38c 100644 --- a/spec/controllers/movies.spec.js +++ b/spec/controllers/movies.spec.js @@ -1,5 +1,94 @@ -var request = require('request'); +var request = require('request') +var base_url = "http://localhost:3000" -describe("Endpoints under /movies", function() { - -}) +////sample shizz +var endpoints = ['/movies', '/movies/sort/title', '/movies/sort/release-date', '/movies/1/current', '/movies/1/history/sort/name', '/movies/1/history/sort/date', '/movies/rentals/1', '/movies/rentals/1/customers', '/movies/rentals/overdue'] + +var testing = function(endpoints) { + for (var endpoint of endpoints) { + console.log(endpoint) + + describe("Endpoint at " + endpoint, function () { + it('responds with a 200 status code', function (done) { + request.get(base_url + endpoint, function(error, response, body) { + expect(response.statusCode).toEqual(200) + done() + }) + }) + + it("returns JSON", function(done) { + request.get(base_url + endpoint, 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 + endpoint, function(error, response, body) { + var data = JSON.parse(body) + expect(typeof data).toEqual('object') + + if (data.length === 6) { + for (var record of data) { + expect(Object.keys(record)).toEqual([ 'id', 'title', 'overview', 'inventory', 'available', 'release_date']) + } + done() + } else { + for (var record of data) { + expect(Object.keys(record)).toEqual([ 'customer_id', 'movie_id', 'check_out_date', 'due_date' ]) + } + done() + } + + }) + }) + + // it('has the right values', function(done) { + // request.get(base_url + endpoint, function(error, response, body) { + // var data = JSON.parse(body) + // expect(data['0'].title).toEqual('Psycho') + // done() + // }) + // }) + }) + }; //end of loop +} +testing(endpoints) + +/////ORIGINAL STUFF BELOW + +// describe("Endpoint at /movies", function () { +// it('responds with a 200 status code', function (done) { +// request.get(base_url, function(error, response, body) { +// expect(response.statusCode).toEqual(200) +// done() +// }) +// }) +// +// it("returns JSON", function(done) { +// request.get(base_url, 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, 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', 'title', 'overview', 'inventory', 'available', 'release_date']) +// } +// done() +// }) +// }) +// +// it('has the right values', function(done) { +// request.get(base_url, function(error, response, body) { +// var data = JSON.parse(body) +// expect(data['0'].title).toEqual('Psycho') +// done() +// }) +// }) +// }) diff --git a/tasks/db_seed.js b/tasks/db_seed.js new file mode 100644 index 000000000..41418cc57 --- /dev/null +++ b/tasks/db_seed.js @@ -0,0 +1,72 @@ +var massive = require('massive') +var connectionString = 'postgres://localhost/video_store' +var customerSeed = require('../db/seeds/customers') +var movieSeed = require('../db/seeds/movies') +var rentalSeed = require('../db/seeds/rentals') + +var db = massive.connectSync({connectionString: connectionString}) +var movieRecords = movieSeed.length +var customerRecords = movieSeed.length +var rentalRecords = rentalSeed.length + +for (var record of movieSeed) { + db.movies.save(record, function (err, res) { + if (err) { + throw (new Error(err.message)) + } + console.log('saved: ', JSON.stringify(res)) + db.movies.count(function (err, res) { + if (err) { + throw (new Error(err.message)) + } + console.log('records in db: ', res) + if (res >= movieRecords) { process.exit() } + }) + }) +} + +for (var record of customerSeed) { + db.customers.save(record, function (err, res) { + if (err) { + throw (new Error(err.message)) + } + console.log('saved: ', JSON.stringify(res)) + db.customers.count(function (err, res) { + if (err) { + throw (new Error(err.message)) + } + console.log('records in db: ', res) + if (res >= customerRecords) { process.exit() } + }) + }) +} + +for (var record of rentalSeed) { + db.rentals.save(record, function (err, res) { + if (err) { + throw (new Error(err.message)) + } + console.log('saved: ', JSON.stringify(res)) + db.rentals.count(function (err, res) { + if (err) { + throw (new Error(err.message)) + } + console.log('records in db: ', res) + if (res >= rentalRecords) { process.exit() } + }) + }) +} +// ========================================================= +// for (var record of movieSeed) { +// console.log(record.title, record.release_date) +// db.movies.saveSync(record) +// } +// +// process.exit() +// +// for (var record of customerSeed) { +// console.log(record.name, record.address) +// db.customers.saveSync(record) +// } +// +// process.exit() diff --git a/tasks/load_schema.js b/tasks/load_schema.js new file mode 100644 index 000000000..ca1c99ddf --- /dev/null +++ b/tasks/load_schema.js @@ -0,0 +1,21 @@ +var massive = require('massive') +var connectionString = 'postgres://localhost/video_store' + +var db = massive.connectSync({connectionString: connectionString}) + +db.setup.schema([], function (err, res) { + if (err) { + throw (new Error(err.message)) + } + + console.log('schema!') + process.exit() +}) + +// db.run("CREATE DATABASE massive;", function (err, res) { +// if(err) { +// throw (new Error(err.message)) +// } +// console.log(res) +// process.exit() +// }) diff --git a/views/index.jade b/views/index.jade index 3d63b9a04..1459f5280 100644 --- a/views/index.jade +++ b/views/index.jade @@ -1,5 +1 @@ -extends layout - -block content - h1= title - p Welcome to #{title} +include plain.html diff --git a/views/plain.html b/views/plain.html new file mode 100644 index 000000000..49fd8ea1b --- /dev/null +++ b/views/plain.html @@ -0,0 +1,154 @@ +
All endpoints return JSON data unless explicitly stated otherwise. If there is no data/record to return a message such as “This movie has not yet been checked out” or “This customer has not yet checked out a movie” will be returned. If there is an error, an error page will display.
+All endpoints are assumed to be GET unless explicitly stated otherwise
+/customers)/customers/sort/:sort_params)
+name : (/name?n=10&p=2)registered_at example: (/registered-at?n=10&p=2)postal_code example: (/postal-code?n=10&p=2)id…
+/customers/:customer_id/current)/customers/:customer_id/history)
+/movies)/movies/sort/:sort_params)
+title example: (/title?n=5&p=1)release_date example: (/release-date?n=5&p=1)id…
+/movies/:movie_id/current)
+name (/movies/:movie_id/history/sort/name)check-out (/movies/:movie_id/history/sort/date)Look a movie up by id to see (/movies/rentals/2)
See a list of customers that have currently checked out any of the movie’s inventory (/movies/rentals/:movie_id/customers)
Given a customer’s id and a movie’s title …
/movies/rentals/:movie_id/checkout/:customer_id)
+/movies/rentals/:movie_id/return/:customer_id)
+See a list of customers with overdue movies (/movies/rentals/overdue)
=================================
+/customers) (All customers)/customers/sort/:sort_params) (Some customers)
+name : (/name?n=10&p=2) (sorted by name)registered_at : (/registered-at?n=10&p=2) (sorted by join date)postal_code : (/postal-code?n=10&p=2) (sorted by location)/customers/:customer_id/current) (Currently checked out movies by a customer)/customers/:customer_id/history) (Previously checked out movies by a customer)/movies) (All movies)
+/movies/sort/:sort_params) (Some movies)
+title : (/title?n=5&p=1) (Sorted by title)release_date : (/release-date?n=5&p=1) (Sorted by release date)/movies/:movie_id/current) (Current rentals per movie, ordered by due date)/movies/:movie_id/history/sort/:sort_params) (Some customers who have previously checked out movies)
+/movies/:movie_id/history/sort/name) (Sort by customer name)/movies/:movie_id/history/sort/date) (Sort by check-out date)/movies/rentals/:movie_id) (To see movie details, and number of available copies in addition to total stock)/movies/rentals/:movie_id/customers) (Customers who currently have this movie checked out)/movies/rentals/:movie_id/checkout/:customer_id) (Creates a “check-out” of this movie to this customer, assigning the record a due date and charge)/movies/rentals/:movie_id/return/:customer_id) (“Returns” this movie checked out to this customer, changing checked-out status to false, and changing the available inventory accordingly)/movies/rentals/overdue)/api/docs) (Serves a HTML view of documentation)/api/docs.json) (Serves JSON documentation)