From e95d66ec398883965cfd704b3078739d567595d5 Mon Sep 17 00:00:00 2001 From: Jessica Stone Date: Mon, 22 Jun 2020 12:16:28 -0700 Subject: [PATCH 01/14] add comments --- app/controllers/movies_controller.rb | 4 +++- app/controllers/rentals_controller.rb | 3 ++- app/models/customer.rb | 1 + app/models/rental.rb | 1 + config/routes.rb | 2 +- lib/movie_wrapper.rb | 6 ++++-- 6 files changed, 12 insertions(+), 5 deletions(-) diff --git a/app/controllers/movies_controller.rb b/app/controllers/movies_controller.rb index 362e2791..e914b008 100644 --- a/app/controllers/movies_controller.rb +++ b/app/controllers/movies_controller.rb @@ -2,7 +2,7 @@ class MoviesController < ApplicationController before_action :require_movie, only: [:show] def index - if params[:query] + if params[:query] # maybe the user search input? data = MovieWrapper.search(params[:query]) else data = Movie.all @@ -11,7 +11,9 @@ def index render status: :ok, json: data end + # runs require_movie before this action def show + # if movie exists, status: ok and return movie info render( status: :ok, json: @movie.as_json( diff --git a/app/controllers/rentals_controller.rb b/app/controllers/rentals_controller.rb index 67e77073..aa919e35 100644 --- a/app/controllers/rentals_controller.rb +++ b/app/controllers/rentals_controller.rb @@ -14,7 +14,7 @@ def check_out end def check_in - rental = Rental.first_outstanding(@movie, @customer) + rental = Rental.first_outstanding(@movie, @customer) # grabs first rental that matches movie & customer and is not returned unless rental return render status: :not_found, json: { errors: { @@ -31,6 +31,7 @@ def check_in end def overdue + # return all rentals where returned: false, and due date is before today rentals = Rental.overdue.map do |rental| { title: rental.movie.title, diff --git a/app/models/customer.rb b/app/models/customer.rb index 6fc89447..47061da9 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -2,6 +2,7 @@ class Customer < ApplicationRecord has_many :rentals has_many :movies, through: :rentals + # returns number of movies checked out def movies_checked_out_count self.rentals.where(returned: false).length end diff --git a/app/models/rental.rb b/app/models/rental.rb index 18654f04..905a3c9b 100644 --- a/app/models/rental.rb +++ b/app/models/rental.rb @@ -19,6 +19,7 @@ def self.overdue end private + # due_date must be after today's date def due_date_in_future return unless self.due_date unless due_date > Date.today diff --git a/config/routes.rb b/config/routes.rb index f4c99688..9ba23b20 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,7 +3,7 @@ resources :customers, only: [:index] - resources :movies, only: [:index, :show], param: :title + resources :movies, only: [:index, :show], param: :title # show uses title instead of ID post "/rentals/:title/check-out", to: "rentals#check_out", as: "check_out" post "/rentals/:title/return", to: "rentals#check_in", as: "check_in" diff --git a/lib/movie_wrapper.rb b/lib/movie_wrapper.rb index 1c808e8d..151bfe3f 100644 --- a/lib/movie_wrapper.rb +++ b/lib/movie_wrapper.rb @@ -11,11 +11,13 @@ def self.search(query, retries_left=3) url = BASE_URL + "search/movie?api_key=" + KEY + "&query=" + query - response = HTTParty.get(url) + response = HTTParty.get(url) # MovieDB API call if response.success? + # if no results, return empty array if response["total_results"] == 0 return [] + # if results, create new Movie for each result else movies = response["results"].map do |result| self.construct_movie(result) @@ -25,7 +27,7 @@ def self.search(query, retries_left=3) elsif retries_left > 0 sleep(1.0 / (2 ** retries_left)) - return self.search(query, retries_left - 1) + return self.search(query, retries_left - 1) # recursively call search while there are more than 0 retries_left else raise "Request failed: #{url}" end From 99c732c6cea2dbe5b0eeab3acef76b8ff1928d45 Mon Sep 17 00:00:00 2001 From: Hannah Date: Tue, 23 Jun 2020 14:25:57 -0700 Subject: [PATCH 02/14] Add 'create' action in movies_controller.rb --- app/controllers/movies_controller.rb | 16 ++++++++++++++++ app/controllers/rentals_controller.rb | 1 - config/routes.rb | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/app/controllers/movies_controller.rb b/app/controllers/movies_controller.rb index e914b008..1a51afb0 100644 --- a/app/controllers/movies_controller.rb +++ b/app/controllers/movies_controller.rb @@ -23,6 +23,22 @@ def show ) end + + def create + movie = Movie.new(movie_params) + + if movie.save + render json: movie.as_json(only: [:id, :title, :overview, :release_date, :image_url, :external_id]), status: :created + return + + else + render json: { + errors: movie.errors.messages + }, status: :bad_request + + end + end + private def require_movie diff --git a/app/controllers/rentals_controller.rb b/app/controllers/rentals_controller.rb index aa919e35..00f40968 100644 --- a/app/controllers/rentals_controller.rb +++ b/app/controllers/rentals_controller.rb @@ -2,7 +2,6 @@ class RentalsController < ApplicationController before_action :require_movie, only: [:check_out, :check_in] before_action :require_customer, only: [:check_out, :check_in] - # TODO: make sure that wave 2 works all the way def check_out rental = Rental.new(movie: @movie, customer: @customer, due_date: params[:due_date]) diff --git a/config/routes.rb b/config/routes.rb index 9ba23b20..f582dd2a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,7 +3,7 @@ resources :customers, only: [:index] - resources :movies, only: [:index, :show], param: :title # show uses title instead of ID + resources :movies, only: [:index, :show, :create], param: :title # show uses title instead of ID post "/rentals/:title/check-out", to: "rentals#check_out", as: "check_out" post "/rentals/:title/return", to: "rentals#check_in", as: "check_in" From 851f5af6f96013db80534d16e4ee155688c5bc28 Mon Sep 17 00:00:00 2001 From: Hannah Date: Tue, 23 Jun 2020 15:16:25 -0700 Subject: [PATCH 03/14] Modify movies_controller.rb --- app/controllers/movies_controller.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/controllers/movies_controller.rb b/app/controllers/movies_controller.rb index 1a51afb0..cef4c83a 100644 --- a/app/controllers/movies_controller.rb +++ b/app/controllers/movies_controller.rb @@ -28,7 +28,9 @@ def create movie = Movie.new(movie_params) if movie.save - render json: movie.as_json(only: [:id, :title, :overview, :release_date, :image_url, :external_id]), status: :created + # render json: movie.as_json(only: [:id, :title, :overview, :release_date, :image_url, :external_id]), status: :created + # return + render json: movie.as_json(only: [:id]), status: :created return else @@ -47,4 +49,8 @@ def require_movie render status: :not_found, json: { errors: { title: ["No movie with title #{params["title"]}"] } } end end + + def movie_params + return params.permit(:title, :overview, :release_date, :image_url, :external_id) + end end From 39d0e61d4a0d2c653d0690c98d8daff3c13a6a07 Mon Sep 17 00:00:00 2001 From: Hannah Date: Tue, 23 Jun 2020 15:27:28 -0700 Subject: [PATCH 04/14] Add inventory to movies_controller.rb --- app/controllers/movies_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/movies_controller.rb b/app/controllers/movies_controller.rb index cef4c83a..d6c4d8d5 100644 --- a/app/controllers/movies_controller.rb +++ b/app/controllers/movies_controller.rb @@ -51,6 +51,6 @@ def require_movie end def movie_params - return params.permit(:title, :overview, :release_date, :image_url, :external_id) + return params.permit(:title, :overview, :release_date, :image_url, :external_id, :inventory) end end From fd3258b5e58ecf5bfa5fe95de58387a1ddfc1f63 Mon Sep 17 00:00:00 2001 From: Hannah Date: Tue, 23 Jun 2020 21:06:55 -0700 Subject: [PATCH 05/14] Change random image --- lib/movie_wrapper.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/movie_wrapper.rb b/lib/movie_wrapper.rb index 151bfe3f..186d9231 100644 --- a/lib/movie_wrapper.rb +++ b/lib/movie_wrapper.rb @@ -4,7 +4,9 @@ class MovieWrapper BASE_IMG_URL = "https://image.tmdb.org/t/p/" DEFAULT_IMG_SIZE = "w185" - DEFAULT_IMG_URL = "http://lorempixel.com/185/278/" + # DEFAULT_IMG_URL = "http://lorempixel.com/185/278/" + DEFAULT_IMG_URL = "https://i.imgur.com/Uw59wWE.png?1" + def self.search(query, retries_left=3) raise ArgumentError.new("Can't search without a MOVIEDB_KEY. Please check your .env file!") unless KEY From b1414e5033f181fbeb38150a6e28458a79746527 Mon Sep 17 00:00:00 2001 From: Hannah Date: Wed, 24 Jun 2020 01:24:11 -0700 Subject: [PATCH 06/14] Add 'show' action to customers_controller.rb --- app/controllers/customers_controller.rb | 27 ++++++++++++++++++++++++- config/routes.rb | 2 +- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/app/controllers/customers_controller.rb b/app/controllers/customers_controller.rb index be25f1be..f228899e 100644 --- a/app/controllers/customers_controller.rb +++ b/app/controllers/customers_controller.rb @@ -18,7 +18,32 @@ def index ) end -private + def show + @customer = Customer.find_by(id: params[:id]) + + unless @customer + render json: { + errors: ['Customer Not Found'], + }, status: :bad_request + + return + end + + rentals = @customer.rentals.map do |rental| + { + title: rental.movie.title, + checkout_date: rental.checkout_date, + due_date: rental.due_date, + returned: rental.returned + } + end + + render json: rentals.as_json(), status: :ok + return + end + + private + def parse_query_args errors = {} @sort = params[:sort] diff --git a/config/routes.rb b/config/routes.rb index f582dd2a..a183c63c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,7 @@ Rails.application.routes.draw do # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html - resources :customers, only: [:index] + resources :customers, only: [:index, :show] resources :movies, only: [:index, :show, :create], param: :title # show uses title instead of ID From 38f99600ec27c9b82d716012e3aeb62484090435 Mon Sep 17 00:00:00 2001 From: Hannah Date: Wed, 24 Jun 2020 09:33:02 -0700 Subject: [PATCH 07/14] Add a name to the show action (customers_controller.rb) --- app/controllers/customers_controller.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/customers_controller.rb b/app/controllers/customers_controller.rb index f228899e..cca32a4f 100644 --- a/app/controllers/customers_controller.rb +++ b/app/controllers/customers_controller.rb @@ -31,6 +31,7 @@ def show rentals = @customer.rentals.map do |rental| { + name: @customer.name, title: rental.movie.title, checkout_date: rental.checkout_date, due_date: rental.due_date, From 79c13cc28e9c1954e8dead9cbf4997df9fbccbf1 Mon Sep 17 00:00:00 2001 From: Hannah Date: Wed, 24 Jun 2020 11:24:22 -0700 Subject: [PATCH 08/14] Add rental id to the show action --- app/controllers/customers_controller.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/customers_controller.rb b/app/controllers/customers_controller.rb index cca32a4f..5958ead3 100644 --- a/app/controllers/customers_controller.rb +++ b/app/controllers/customers_controller.rb @@ -31,6 +31,7 @@ def show rentals = @customer.rentals.map do |rental| { + id: rental.id, name: @customer.name, title: rental.movie.title, checkout_date: rental.checkout_date, From 1411b7e85700d05e41d7e4563be32ebb3719160c Mon Sep 17 00:00:00 2001 From: Hannah Date: Wed, 24 Jun 2020 15:59:28 -0700 Subject: [PATCH 09/14] Add customer_id and movie_id --- app/controllers/customers_controller.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/customers_controller.rb b/app/controllers/customers_controller.rb index 5958ead3..ddeaf12a 100644 --- a/app/controllers/customers_controller.rb +++ b/app/controllers/customers_controller.rb @@ -32,6 +32,8 @@ def show rentals = @customer.rentals.map do |rental| { id: rental.id, + customer_id: rental.customer_id, + movie_id: rental.movie_id, name: @customer.name, title: rental.movie.title, checkout_date: rental.checkout_date, From 6bbdb6c22f531f7f43b2da7d5ec3a575cc3fd085 Mon Sep 17 00:00:00 2001 From: Jessica Stone Date: Thu, 25 Jun 2020 14:02:35 -0700 Subject: [PATCH 10/14] add conditional for if movie is already in db --- app/controllers/movies_controller.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/controllers/movies_controller.rb b/app/controllers/movies_controller.rb index d6c4d8d5..f64961ed 100644 --- a/app/controllers/movies_controller.rb +++ b/app/controllers/movies_controller.rb @@ -27,6 +27,14 @@ def show def create movie = Movie.new(movie_params) + # if movie is already in db, don't add + if Movie.find_by(external_id: movie.external_id) + render json: { + errors: "This movie is already in the database" + }, status: :forbidden + return + end + if movie.save # render json: movie.as_json(only: [:id, :title, :overview, :release_date, :image_url, :external_id]), status: :created # return From 900ada46840e8d3fa73cfdb43f505117534ae298 Mon Sep 17 00:00:00 2001 From: Hannah Date: Thu, 25 Jun 2020 15:16:12 -0700 Subject: [PATCH 11/14] Update movies_controller_test.rb --- app/models/movie.rb | 3 ++ db/schema.rb | 63 +++++++++++----------- test/controllers/movies_controller_test.rb | 58 ++++++++++++++++++++ 3 files changed, 94 insertions(+), 30 deletions(-) diff --git a/app/models/movie.rb b/app/models/movie.rb index fda94941..3dd0856b 100644 --- a/app/models/movie.rb +++ b/app/models/movie.rb @@ -2,6 +2,9 @@ class Movie < ApplicationRecord has_many :rentals has_many :customers, through: :rentals + validates :title, uniqueness: true, presence: true + validates :inventory, presence: true + def available_inventory self.inventory - Rental.where(movie: self, returned: false).length end diff --git a/db/schema.rb b/db/schema.rb index ffb28f7e..8c8b42d4 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -2,48 +2,51 @@ # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. # -# Note that this schema.rb definition is the authoritative source for your -# database schema. If you need to create the application database on another -# system, you should be using db:schema:load, not running all the migrations -# from scratch. The latter is a flawed and unsustainable approach (the more migrations -# you'll amass, the slower it'll run and the greater likelihood for issues). +# This file is the source Rails uses to define your schema when running `rails +# db:schema:load`. When creating a new database, `rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180618042754) do +ActiveRecord::Schema.define(version: 2018_06_18_042754) do + + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" create_table "customers", force: :cascade do |t| - t.string "name" + t.string "name" t.datetime "registered_at" - t.string "address" - t.string "city" - t.string "state" - t.string "postal_code" - t.string "phone" - t.float "account_credit" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.string "address" + t.string "city" + t.string "state" + t.string "postal_code" + t.string "phone" + t.float "account_credit" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end create_table "movies", force: :cascade do |t| - t.string "title" - t.text "overview" - t.date "release_date" - t.integer "inventory" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.string "image_url" - t.integer "external_id" + t.string "title" + t.text "overview" + t.date "release_date" + t.integer "inventory" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "image_url" + t.integer "external_id" end create_table "rentals", force: :cascade do |t| - t.integer "customer_id" - t.integer "movie_id" - t.date "checkout_date" - t.date "due_date" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.boolean "returned" + t.integer "customer_id" + t.integer "movie_id" + t.date "checkout_date" + t.date "due_date" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.boolean "returned" t.index ["customer_id"], name: "index_rentals_on_customer_id" t.index ["movie_id"], name: "index_rentals_on_movie_id" end diff --git a/test/controllers/movies_controller_test.rb b/test/controllers/movies_controller_test.rb index 9172cf6e..1e8f7bb5 100644 --- a/test/controllers/movies_controller_test.rb +++ b/test/controllers/movies_controller_test.rb @@ -75,4 +75,62 @@ class MoviesControllerTest < ActionDispatch::IntegrationTest end end + + describe "create" do + let(:movie_params) { + { + title: "Parasite", + overview: "test!", + inventory: 10, + image_url: "image test", + external_id: 123976 + } + } + + it "creates a movie with valid data" do + count = Movie.count + + expect { + post movies_path, params: movie_params + }.must_differ "Movie.count", 1 + + expect(Movie.count).must_equal count + 1 + + must_respond_with :created + end + + it "will respond with bad_request for invalid data" do + movie_params[:title] = nil + movie_params[:inventory] = nil + + expect { + post movies_path, params: movie_params + }.wont_change "Movie.count" + + must_respond_with :bad_request + + expect(response.header['Content-Type']).must_include 'json' + body = JSON.parse(response.body) + + expect(body["errors"].keys).must_include "title" + expect(body["errors"].keys).must_include "inventory" + end + + + it "cannot add the same movie twice" do + count = Movie.count + + post movies_path, params: movie_params + + expect { + post movies_path, params: movie_params + }.wont_differ "Movie.count" + + expect(Movie.count).must_equal count + 1 + + must_respond_with :bad_request + expect(body).must_include "title" + expect(body).must_include "has already been taken" + end + end end From a2b6b197c801dec6fc665ced3adb11782ad97d5c Mon Sep 17 00:00:00 2001 From: Hannah Date: Thu, 25 Jun 2020 15:18:48 -0700 Subject: [PATCH 12/14] Fix a bug --- test/controllers/movies_controller_test.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/controllers/movies_controller_test.rb b/test/controllers/movies_controller_test.rb index 1e8f7bb5..04d63e19 100644 --- a/test/controllers/movies_controller_test.rb +++ b/test/controllers/movies_controller_test.rb @@ -128,9 +128,8 @@ class MoviesControllerTest < ActionDispatch::IntegrationTest expect(Movie.count).must_equal count + 1 - must_respond_with :bad_request - expect(body).must_include "title" - expect(body).must_include "has already been taken" + must_respond_with :forbidden + expect(body).must_include "This movie is already in the database" end end end From 5983f7d4e16fa0955e194ee595f383ada09bc396 Mon Sep 17 00:00:00 2001 From: Jessica Stone Date: Fri, 26 Jun 2020 10:39:38 -0700 Subject: [PATCH 13/14] remove inventory validation --- app/models/movie.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/movie.rb b/app/models/movie.rb index 3dd0856b..0239ff8b 100644 --- a/app/models/movie.rb +++ b/app/models/movie.rb @@ -3,7 +3,7 @@ class Movie < ApplicationRecord has_many :customers, through: :rentals validates :title, uniqueness: true, presence: true - validates :inventory, presence: true + # validates :inventory, presence: true def available_inventory self.inventory - Rental.where(movie: self, returned: false).length From 0f73d3e440c3341ed21e1a01af0a922cbc9d530e Mon Sep 17 00:00:00 2001 From: Hannah Date: Fri, 26 Jun 2020 10:43:48 -0700 Subject: [PATCH 14/14] Fix a bug --- test/controllers/movies_controller_test.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/controllers/movies_controller_test.rb b/test/controllers/movies_controller_test.rb index 04d63e19..d555d002 100644 --- a/test/controllers/movies_controller_test.rb +++ b/test/controllers/movies_controller_test.rb @@ -101,7 +101,6 @@ class MoviesControllerTest < ActionDispatch::IntegrationTest it "will respond with bad_request for invalid data" do movie_params[:title] = nil - movie_params[:inventory] = nil expect { post movies_path, params: movie_params @@ -113,7 +112,6 @@ class MoviesControllerTest < ActionDispatch::IntegrationTest body = JSON.parse(response.body) expect(body["errors"].keys).must_include "title" - expect(body["errors"].keys).must_include "inventory" end