diff --git a/.rspec b/.rspec deleted file mode 100644 index 660778bd..00000000 --- a/.rspec +++ /dev/null @@ -1 +0,0 @@ ---colour --format documentation diff --git a/Gemfile b/Gemfile index 20e422c9..adbf9fd0 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,10 @@ ruby '2.0.0' gem 'rspec', '~> 2.14.1' gem 'sinatra', '~> 1.4.5' gem 'sinatra-contrib', '~> 1.4.2' - +gem 'rest-client' +gem 'netrc' # Testing gem 'pry-byebug' +gem 'pg' +gem 'json' +gem 'bcrypt' diff --git a/Gemfile.lock b/Gemfile.lock index 92b9a02a..dfd55b74 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,6 +2,7 @@ GEM remote: https://rubygems.org/ specs: backports (3.6.3) + bcrypt (3.1.9) byebug (3.5.1) columnize (~> 0.8) debugger-linecache (~> 1.2) @@ -10,8 +11,12 @@ GEM columnize (0.8.9) debugger-linecache (1.2.0) diff-lcs (1.2.5) + json (1.8.1) method_source (0.8.2) + mime-types (2.4.3) multi_json (1.10.1) + netrc (0.10.0) + pg (0.17.1) pry (0.10.1) coderay (~> 1.1.0) method_source (~> 0.8.1) @@ -24,6 +29,9 @@ GEM rack rack-test (0.6.2) rack (>= 1.0) + rest-client (1.7.2) + mime-types (>= 1.16, < 3.0) + netrc (~> 0.7) rspec (2.14.1) rspec-core (~> 2.14.0) rspec-expectations (~> 2.14.0) @@ -50,7 +58,12 @@ PLATFORMS ruby DEPENDENCIES + bcrypt + json + netrc + pg pry-byebug + rest-client rspec (~> 2.14.1) - sinatra - sinatra-contrib + sinatra (~> 1.4.5) + sinatra-contrib (~> 1.4.2) diff --git a/lib/cats_repo/cats_repo.rb b/lib/cats_repo/cats_repo.rb new file mode 100644 index 00000000..0f2680bb --- /dev/null +++ b/lib/cats_repo/cats_repo.rb @@ -0,0 +1,8 @@ +module PetShop + class CatsRepo + def self.all_from_shop(db, id) + db.exec(%q[SELECT id, shopid, name, imageurl AS "imageUrl", adopted FROM cats WHERE shopid = $1],[id]).to_a + + end + end +end \ No newline at end of file diff --git a/lib/dogs_repo/dogs_repo.rb b/lib/dogs_repo/dogs_repo.rb new file mode 100644 index 00000000..029a9f19 --- /dev/null +++ b/lib/dogs_repo/dogs_repo.rb @@ -0,0 +1,9 @@ +module PetShop + class DogsRepo + + def self.all_from_shop(db, id) + sql = %q[SELECT id, shopid, name, imageurl AS "imageUrl", adopted, happiness FROM dogs WHERE shopid = $1] + db.exec(sql, [id]).to_a + end + end +end \ No newline at end of file diff --git a/lib/pet-shop.rb b/lib/pet-shop.rb new file mode 100644 index 00000000..1a249b1e --- /dev/null +++ b/lib/pet-shop.rb @@ -0,0 +1,86 @@ +require 'pg' +require 'bcrypt' + +require_relative 'petshops_repo/petshops_repo.rb' +require_relative 'cats_repo/cats_repo.rb' +require_relative 'dogs_repo/dogs_repo.rb' +require_relative 'users_repo/users_repo.rb' + +module PetShop + def self.create_db_connection() + PG.connect(host: 'localhost', dbname: "petshop_db", user: "ruby", + password: "rubyRailsJS") + end + + def self.create_tables(db) + db.exec <<-SQL + CREATE TABLE IF NOT EXISTS petshops( + id SERIAL PRIMARY KEY, + name VARCHAR + ); + CREATE TABLE IF NOT EXISTS cats( + id SERIAL PRIMARY KEY, + shopid INTEGER references petshops(id), + imageUrl VARCHAR, + name VARCHAR, + adopted VARCHAR + ); + CREATE TABLE IF NOT EXISTS dogs( + id SERIAL PRIMARY KEY, + shopid INTEGER references petshops(id), + imageUrl VARCHAR, + name VARCHAR, + happiness VARCHAR, + adopted VARCHAR + ); + CREATE TABLE IF NOT EXISTS users( + id SERIAL PRIMARY KEY, + username VARCHAR, + password VARCHAR + ); + CREATE TABLE IF NOT EXISTS pet_adoptions( + id SERIAL PRIMARY KEY, + type VARCHAR, + user_id INTEGER references users(id), + pet_id INTEGER + ); + SQL + end + + def self.drop_tables(db) + db.exec <<-SQL + DROP TABLE petshops CASCADE; + DROP TABLE cats CASCADE; + DROP TABLE dogs CASCADE; + DROP TABLE users CASCADE; + DROP TABLE pet_adoptions; + SQL + end + + def self.seed_db(db) + r = db.exec <<-SQL + INSERT INTO petshops (name) values ('My Pet Shop') RETURNING id + SQL + + dogurl = "http://www.oldyelladogranch.com/puppies.jpg" + caturl = "http://nextranks.com/data_images/main/cats/cats-04.jpg" + shopid = r[0]['id'] + q = "INSERT INTO cats (shopid, imageUrl, name) values ($1, $2, 'cat1')" + db.exec(q,[shopid, caturl]) + q = "INSERT INTO dogs (shopid, imageUrl, name, happiness) values ($1, $2, 'dog1', '5')" + db.exec(q,[shopid, dogurl]) + password_hash = BCrypt::Password.create('ilovepets') + q = "INSERT INTO users (username, password) values ('ilovepets', '#{password_hash}')" + db.exec(q) + password_hash = BCrypt::Password.create('someuser') + q = "INSERT INTO users (username, password) values ('someuser', '#{password_hash}')" + db.exec(q) + password_hash = BCrypt::Password.create('someotherperson') + q = "INSERT INTO users (username, password) values ('someotherperson', '#{password_hash}')" + db.exec(q) + + end + + + +end \ No newline at end of file diff --git a/lib/petshops_repo/petshops_repo.rb b/lib/petshops_repo/petshops_repo.rb new file mode 100644 index 00000000..869380bf --- /dev/null +++ b/lib/petshops_repo/petshops_repo.rb @@ -0,0 +1,8 @@ +module PetShop + class Shops + def self.get_all_shops(db) + shops = db.exec('SELECT * FROM petshops').to_a + end + + end +end diff --git a/lib/users_repo/users_repo.rb b/lib/users_repo/users_repo.rb new file mode 100644 index 00000000..111a3cd8 --- /dev/null +++ b/lib/users_repo/users_repo.rb @@ -0,0 +1,80 @@ +module PetShop + class UsersRepo + # find a user by user ID. Intended to be used when + # someone is already authenticated. We keep their + # user id in the cookie. + def self.find db, user_id + sql = %q[SELECT * FROM users WHERE id = $1] + result = db.exec(sql, [user_id]).to_a + result.first + end + + # find user by username. Intended to be used when + # someone tries to sign in. + def self.find_by_name db, username + sql = %q[SELECT * FROM users WHERE username = $1] + result = db.exec(sql, [username]).to_a + result.first + end + + # when someone signs up use this method to save their + # information in the db. we're not encrypting passwords. + def self.save db, user_data + sql = %q[INSERT INTO users (username, password) VALUES ($1, $2) RETURNING *] + result = db.exec(sql, [user_data[:username], user_data[:password]]) + result.first + end + + def self.adopt db, user_data + sql = %q[INSERT INTO pet_adoptions (type, user_id, pet_id) VALUES ($1, $2, $3) RETURNING *] + result = db.exec(sql, [user_data[:type], user_data[:user_id], user_data[:pet_id]]) + + if(user_data[:type] == "cat") + sql = %q[UPDATE cats SET adopted = 'true' WHERE id = $1] + result = db.exec(sql, [user_data[:pet_id]]) + end + + if(user_data[:type] == "dog") + sql = %q[UPDATE dogs SET adopted = 'true' WHERE id = $1] + result = db.exec(sql, [user_data[:pet_id]]) + end + return "Adopted: true" + end + + def self.unadopt db, user_data + sql = %q[DELETE FROM pet_adoptions WHERE type = $1 and user_id = $2 and pet_id = $3] + result = db.exec(sql, [user_data[:type], user_data[:user_id], user_data[:pet_id]]) + + if(user_data[:type] == "cat") + sql = %q[UPDATE cats SET adopted = NULL WHERE id = $1] + result = db.exec(sql, [user_data[:pet_id]]) + end + + if(user_data[:type] == "dog") + sql = %q[UPDATE dogs SET adopted = NULL WHERE id = $1] + result = db.exec(sql, [user_data[:pet_id]]) + end + return "something to prevent server err." + end + + def self.get_dog_by_id db, user_data + sql = %q[SELECT * FROM dogs WHERE id = $1] + db.exec(sql, [user_data]).to_a.first + end + + def self.get_cat_by_id db, user_data + sql = %q[SELECT * FROM cats WHERE id = $1] + db.exec(sql, [user_data]).to_a.first + end + + + def self.get_adoptions db, user_data + user_id = user_data['id'] + sql = %q[SELECT d.id, d.name, d.imageurl AS "imageUrl" FROM pet_adoptions a JOIN dogs d ON d.id = a.pet_id WHERE user_id = $1 and type = 'dog'] + user_data["dogs"] = db.exec(sql, [user_id]).to_a + sql = %q[SELECT c.id, c.name, c.imageurl AS "imageUrl" FROM pet_adoptions a JOIN cats c ON c.id = a.pet_id WHERE a.user_id = $1 and type = 'cat'] + user_data["cats"] = db.exec(sql, [user_id]).to_a + return user_data + end + end +end diff --git a/public/js/pet-shop.js b/public/js/pet-shop.js index 8aa6893b..5adb6540 100644 --- a/public/js/pet-shop.js +++ b/public/js/pet-shop.js @@ -16,6 +16,7 @@ var pets = shop[type] var adoptUrl = "/shops/" + shop.id + "/" + type + "/" + petId + "/adopt" + m.request({ method: 'put', url: adoptUrl }).then(function() { var pet = pets().find(function(p){ return p.id == petId }) pet.adopted = true @@ -46,6 +47,10 @@ onclick: ctrl.adopt.coldCurry(type, pet.id), href: '#' }, 'Adopt this pet') + var unAdoptLink = m('a', { + onclick: ctrl.adopt.coldCurry(type, pet.id), + href: '#' + }, 'Ditch this loser pet.') return m('.pet', [ m('.photo', @@ -56,7 +61,7 @@ m('b', "Adopted: "), m('span', pet.adopted ? "Yes!" : "No..."), m('br'), - pet.adopted ? null : adoptLink + pet.adopted ? unAdoptLink : adoptLink ]) ]) }) diff --git a/server.rb b/server.rb index 9ae90b34..eaa9d30d 100644 --- a/server.rb +++ b/server.rb @@ -1,14 +1,23 @@ require 'sinatra' -require 'sinatra/reloader' +# require 'sinatra/reloader' require 'rest-client' +require 'json' +require_relative 'lib/pet-shop.rb' +set :bind, '0.0.0.0' +configure do + enable :sessions + end # # # This is our only html view... # get '/' do - if session[:user_id] + if session['user_id'] # TODO: Grab user from database - @current_user = $sample_user + db = PetShop.create_db_connection() + @user = PetShop::UsersRepo.find(db, session[:user_id]) + @current_user = @user['name'] + end erb :index end @@ -18,26 +27,42 @@ # get '/shops' do headers['Content-Type'] = 'application/json' - RestClient.get("http://pet-shop.api.mks.io/shops") + db = PetShop.create_db_connection() + JSON.generate(PetShop::Shops.get_all_shops(db)) end +get '/signup' do + + erb :"signup" +end +post '/signup' do + db = PetShop.create_db_connection() + password_hash = BCrypt::Password.create(params['password']) + PetShop::UsersRepo.save db, {username: params['username'], password: password_hash} + erb :"signup" +end post '/signin' do params = JSON.parse request.body.read username = params['username'] password = params['password'] - + db = PetShop.create_db_connection() + user = PetShop::UsersRepo.find_by_name(db, username) # TODO: Grab user by username from database and check password - user = { 'username' => 'alice', 'password' => '123' } + pass_from_db = BCrypt::Password.new(user['password']) - if password == user['password'] + if pass_from_db == password headers['Content-Type'] = 'application/json' + session['user_id'] = user['id'] # TODO: Return all pets adopted by this user # TODO: Set session[:user_id] so the server will remember this user has logged in - $sample_user.to_json + user = PetShop::UsersRepo.get_adoptions(db, user) + user.delete('password') + JSON.generate(user) else status 401 end + end # # # # @@ -46,17 +71,30 @@ get '/shops/:id/cats' do headers['Content-Type'] = 'application/json' id = params[:id] - # TODO: Grab from database instead - RestClient.get("http://pet-shop.api.mks.io/shops/#{id}/cats") + # TODO: Grab from database instead + db = PetShop.create_db_connection() + cats = PetShop::CatsRepo.all_from_shop(db, id) + JSON.generate(cats) end put '/shops/:shop_id/cats/:id/adopt' do headers['Content-Type'] = 'application/json' shop_id = params[:shop_id] id = params[:id] + adopt_data = { + type: 'cat', + user_id: session['user_id'], + pet_id: id + } # TODO: Grab from database instead - RestClient.put("http://pet-shop.api.mks.io/shops/#{shop_id}/cats/#{id}", - { adopted: true }, :content_type => 'application/json') + db = PetShop.create_db_connection() + cat = PetShop::UsersRepo.get_cat_by_id(db, id) + if(cat['adopted'].class.name == "string") + PetShop::UsersRepo.adopt(db, adopt_data) + else + PetShop::UsersRepo.unadopt(db, adopt_data) + end + PetShop::UsersRepo.adopt(db, adopt_data) # TODO (after you create users table): Attach new cat to logged in user end @@ -66,19 +104,38 @@ # # # # get '/shops/:id/dogs' do headers['Content-Type'] = 'application/json' + db = PetShop.create_db_connection() id = params[:id] + + dogs = PetShop::DogsRepo.all_from_shop(db, id) + JSON.generate(dogs) # TODO: Update database instead - RestClient.get("http://pet-shop.api.mks.io/shops/#{id}/dogs") + # RestClient.get("http://pet-shop.api.mks.io/shops/#{id}/dogs") end put '/shops/:shop_id/dogs/:id/adopt' do headers['Content-Type'] = 'application/json' shop_id = params[:shop_id] id = params[:id] + db = PetShop.create_db_connection() + @user = PetShop::UsersRepo.find(db, session["user_id"]) + dog = PetShop::UsersRepo.get_dog_by_id(db, id) + # TODO: Update database instead - RestClient.put("http://pet-shop.api.mks.io/shops/#{shop_id}/dogs/#{id}", - { adopted: true }, :content_type => 'application/json') + adopt_data = { + type: 'dog', + user_id: session[:user_id].to_i, + pet_id: id + } + if(dog['adopted'].class.name == "string") + PetShop::UsersRepo.adopt(db, adopt_data) + else + PetShop::UsersRepo.unadopt(db, adopt_data) + end + # RestClient.put("http://pet-shop.api.mks.io/shops/#{shop_id}/dogs/#{id}", + # { adopted: true }, :content_type => 'application/json') # TODO (after you create users table): Attach new dog to logged in user + JSON.generate(adopt_data) end diff --git a/spec/.gitkeep b/spec/.gitkeep deleted file mode 100644 index e69de29b..00000000