From 74a95caaf47aeac23800ea46691cd3a38c53120c Mon Sep 17 00:00:00 2001 From: ian douglas Date: Wed, 19 Aug 2020 22:22:07 -0600 Subject: [PATCH 1/2] Travis CI setup, README notes to get started --- .generators | 8 ++++++++ .gitignore | 4 ++++ .rakeTasks | 7 +++++++ .travis.yml | 9 +++++++-- Gemfile | 4 +++- Gemfile.lock | 25 +++++++++++++++++++++++++ README.md | 40 ++++++++++++++++++++++++++++++++++------ db/schema.rb | 18 ++++++++++++++++++ 8 files changed, 106 insertions(+), 9 deletions(-) create mode 100644 .generators create mode 100644 .rakeTasks create mode 100644 db/schema.rb diff --git a/.generators b/.generators new file mode 100644 index 0000000000..1618976692 --- /dev/null +++ b/.generators @@ -0,0 +1,8 @@ + + diff --git a/.gitignore b/.gitignore index 655965c00c..95f72af3b2 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,7 @@ # Ignore test coverage /coverage/* + +# Ignore application configuration +/config/application.yml +.idea diff --git a/.rakeTasks b/.rakeTasks new file mode 100644 index 0000000000..c6865d9a1b --- /dev/null +++ b/.rakeTasks @@ -0,0 +1,7 @@ + + diff --git a/.travis.yml b/.travis.yml index 36c748f496..0bc05e1f3f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,10 +4,15 @@ rbenv: addons: postgresql: 9.6 script: -- bundle exec rails db:{create,migrate} RAILS_ENV=test +- bundle exec rails db:{create,migrate,seed} RAILS_ENV=test - bundle exec rspec deploy: provider: heroku api_key: - app: + secure: xFascAxQSlb0Ky8KQNB3En6+z4Q8SSPqtlDfZc0pu3g4dVvKLuJzdqu57LcPnJ7eENTAC4iqmz+o7vDcFn/lR/SKug1xGS9+165nK+nGVg6wohRq4DWpZHXH31mUk20ApkUztTZPLbFJj8uR8LpWiU5p0xDxhqt8K5Y9pAiuzG0bU2FDHKGSnQpW0gfnnQ73/mCORcXkvR3qY4q6vqNXs1Z59TdUo0wMMBHiW7VT1wV5mm27mTrNKR8+UDKVBJA71paL55F11C14hHpDWCeHS7D6KEPSj9oQLzLoEapwuEpoOxsG2L8VsSrsdOGdpSYhUOBCShYVVSGZocsEroqQ8IEXZzOuZqv83LBzrulyBnSbncp+J7cBUSY507dYlmVQGwT4OnTLBdqryjcoWiUM8HbEOfJUw3DLKPqwuoI6cG5YO1JS4j04iw5g/qocBGSTRaXbVaCHlqQetBLTSr8vdtOTlaBGKURQ3GsBdjHr+oEKSJjMZJnM3yOaX2msMg8X0qwf4XmP8zo+gGW2j79tSEDp44QSxsWmrjc/14Lc0c6sV4xL+f56evTSpC5ayw040eLnQXqvvtQ5gzq4O26wlFUSK5tgE4fedZ8IraqceWDQv3X9JUvPAtgl+tHf9QgHWJTYLixRZsRqj2q3R1NQqYPwB0dT4nTlfyfKjUwJBao= + app: movie-screening + on: + repo: backburnerstudios/viewing_party + branch: main + skip_cleanup: 'true' run: rails db:migrate diff --git a/Gemfile b/Gemfile index 4beccfcf94..5a21bbca65 100644 --- a/Gemfile +++ b/Gemfile @@ -15,17 +15,19 @@ gem 'uglifier', '>= 1.3.0' gem 'coffee-rails', '~> 4.2' gem 'bootsnap' gem 'jbuilder', '~> 2.5' +gem 'omniauth-google-oauth2' group :development, :test do gem 'pry' gem 'travis' + gem 'figaro' end group :development do gem 'web-console', '>= 3.3.0' gem 'listen', '>= 3.0.5', '< 3.2' gem 'rubocop-rails' - gem 'travis' + gem 'brakeman' end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index 82d0397817..ea1d5a06eb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -49,6 +49,7 @@ GEM bindex (0.8.1) bootsnap (1.4.7) msgpack (~> 1.0) + brakeman (4.9.0) builder (3.2.4) capybara (3.33.0) addressable @@ -79,6 +80,8 @@ GEM faraday_middleware (1.0.0) faraday (~> 1.0) ffi (1.13.1) + figaro (1.2.0) + thor (>= 0.14.0, < 2) gh (0.18.0) activesupport (~> 5.0) addressable (~> 2.4) @@ -89,12 +92,14 @@ GEM net-http-pipeline globalid (0.4.2) activesupport (>= 4.2.0) + hashie (4.1.0) highline (2.0.3) i18n (1.8.5) concurrent-ruby (~> 1.0) jbuilder (2.10.0) activesupport (>= 5.0.0) json (2.3.1) + jwt (2.2.1) launchy (2.4.3) addressable (~> 2.3) listen (3.1.5) @@ -115,12 +120,29 @@ GEM minitest (5.14.1) msgpack (1.3.3) multi_json (1.15.0) + multi_xml (0.6.0) multipart-post (2.1.1) net-http-persistent (2.9.4) net-http-pipeline (1.0.1) nio4r (2.5.2) nokogiri (1.10.10) mini_portile2 (~> 2.4.0) + oauth2 (1.4.4) + faraday (>= 0.8, < 2.0) + jwt (>= 1.0, < 3.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (>= 1.2, < 3) + omniauth (1.9.1) + hashie (>= 3.4.6) + rack (>= 1.6.2, < 3) + omniauth-google-oauth2 (0.8.0) + jwt (>= 2.0) + omniauth (>= 1.1.1) + omniauth-oauth2 (>= 1.6) + omniauth-oauth2 (1.6.0) + oauth2 (~> 1.1) + omniauth (~> 1.9) parallel (1.19.2) parser (2.7.1.4) ast (~> 2.4.1) @@ -259,11 +281,14 @@ PLATFORMS DEPENDENCIES bootsnap + brakeman capybara coffee-rails (~> 4.2) + figaro jbuilder (~> 2.5) launchy listen (>= 3.0.5, < 3.2) + omniauth-google-oauth2 pg (>= 0.18, < 2.0) pry puma (~> 3.7) diff --git a/README.md b/README.md index 60d7fd745c..0be1b492ed 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,49 @@ # Viewing Party -This is the base repo for the viewing party project used for Turing's Backend Module 3. +[![Build Status](https://travis-ci.org/backburnerstudios/viewing_party.svg?branch=main)](https://travis-ci.org/backburnerstudios/viewing_party) +This application is a means to explore movies and create viewing parties for you and friends. -### About this Project +The application utilizes [Google OAuth](https://developers.google.com/identity/protocols/oauth2) +for [Calendar API access](https://developers.google.com/calendar) to save these events. We also +use the [Movie DB API](https://developers.themoviedb.org/3/getting-started/introduction) for movie data. -Viewing party is an application in which users can explore movie options and create a viewing party event for the user and friend's. +Example wireframes to follow are found [here](https://backend.turing.io/module3/projects/viewing_party/wireframes) + +## Learning Goals of the Project + +- Consume JSON APIs that require authentication +- Build an application that authenticates using OAuth +- Implement a self-referential relationship in ActiveRecord +- Utilize Continuous Integration using Travis CI +- Organize and refactor code to be more maintainable +- Apply RuboCop’s style guide for code quality +- Deploy to Heroku + +#### Extension / Exploration Goals (1 extension is required) + +- Send email from a Rails application +- Use ActionCable for chat functionality +- Implement front-end JavaScript for more dynamic pages +- Extend movie exploration by consuming additional API endpoints +- Deploy with another hosting provider + +## Project Board and Hosting + +- Project Board: https://github.com/backburnerstudios/viewing_party/projects/1 +- Live site: https://movie-screening.herokuapp.com/ ## Local Setup 1. Fork and Clone the repo 2. Install gem packages: `bundle install` -3. Setup the database: `rails db:create` +3. Setup the database: `rails db:{dropcreate,migrate,seed}` +4. Run all tests: `rspec` -## Versions +## Tech Stack Versions - Ruby 2.5.3 - - Rails 5.2.4.3 + + diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000000..2611543b38 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,18 @@ +# This file is auto-generated from the current state of the database. Instead +# 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). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 0) do + + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + +end From 2464bf1e4f72cceaa6c56a3a7e8f1fccf0f2836b Mon Sep 17 00:00:00 2001 From: ian douglas Date: Thu, 20 Aug 2020 00:53:53 -0600 Subject: [PATCH 2/2] finished the welcome page, and testing for session controller --- Gemfile | 4 ++ Gemfile.lock | 12 ++++ app/controllers/application_controller.rb | 5 ++ app/controllers/sessions_controller.rb | 15 +++++ app/controllers/welcome_controller.rb | 5 ++ app/models/user.rb | 12 ++++ app/views/welcome/index.html.erb | 14 +++++ config/initializers/omniauth.rb | 3 + config/routes.rb | 4 ++ db/migrate/20200820043729_create_users.rb | 14 +++++ db/schema.rb | 13 ++++- spec/factories/user.rb | 10 ++++ spec/features/sessions_spec.rb | 14 +++++ spec/features/welcome_spec.rb | 22 +++++++ spec/models/user_spec.rb | 29 +++++++++ spec/rails_helper.rb | 71 +++++++++++++++++++++++ 16 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 app/controllers/sessions_controller.rb create mode 100644 app/controllers/welcome_controller.rb create mode 100644 app/models/user.rb create mode 100644 app/views/welcome/index.html.erb create mode 100644 config/initializers/omniauth.rb create mode 100644 db/migrate/20200820043729_create_users.rb create mode 100644 spec/factories/user.rb create mode 100644 spec/features/sessions_spec.rb create mode 100644 spec/features/welcome_spec.rb create mode 100644 spec/models/user_spec.rb diff --git a/Gemfile b/Gemfile index 5a21bbca65..d4ba193637 100644 --- a/Gemfile +++ b/Gemfile @@ -35,6 +35,10 @@ group :test do gem 'capybara' gem 'launchy' gem 'simplecov' + gem 'factory_bot_rails' + gem 'simplecov' + gem 'faker' + gem 'shoulda-matchers' end gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] diff --git a/Gemfile.lock b/Gemfile.lock index ea1d5a06eb..5531c54034 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -75,6 +75,13 @@ GEM ethon (0.12.0) ffi (>= 1.3.0) execjs (2.7.0) + factory_bot (6.1.0) + activesupport (>= 5.0.0) + factory_bot_rails (6.1.0) + factory_bot (~> 6.1.0) + railties (>= 5.0.0) + faker (2.13.0) + i18n (>= 1.6, < 2) faraday (1.0.1) multipart-post (>= 1.2, < 3) faraday_middleware (1.0.0) @@ -234,6 +241,8 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) + shoulda-matchers (4.3.0) + activesupport (>= 4.2.0) simplecov (0.18.5) docile (~> 1.1) simplecov-html (~> 0.11) @@ -284,6 +293,8 @@ DEPENDENCIES brakeman capybara coffee-rails (~> 4.2) + factory_bot_rails + faker figaro jbuilder (~> 2.5) launchy @@ -296,6 +307,7 @@ DEPENDENCIES rspec-rails rubocop-rails sass-rails (~> 5.0) + shoulda-matchers simplecov travis tzinfo-data diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1c07694e9d..543296feef 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,3 +1,8 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception + helper_method :current_user + + def current_user + @current_user ||= User.find(session[:user_id]) if session[:user_id] + end end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 0000000000..988a0934d8 --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,15 @@ +class SessionsController < ApplicationController + def create + access_data = request.env['omniauth.auth'] + user = User.parse_omniauth(access_data) + user.google_token = access_data.credentials.token + + refresh_token = access_data.credentials.refresh_token + user.google_refresh_token = refresh_token if refresh_token.present? + + user.save + + session[:user_id] = user.id + redirect_to root_path + end +end \ No newline at end of file diff --git a/app/controllers/welcome_controller.rb b/app/controllers/welcome_controller.rb new file mode 100644 index 0000000000..aa6421db97 --- /dev/null +++ b/app/controllers/welcome_controller.rb @@ -0,0 +1,5 @@ +class WelcomeController < ApplicationController + def index + # do something here? + end +end \ No newline at end of file diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000000..e4ed38b672 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,12 @@ +class User < ApplicationRecord + validates_presence_of :username + validates :uid, presence: true, uniqueness: true + validates :google_token, presence: true, uniqueness: true + + def self.parse_omniauth(access_data) + where(uid: access_data.info.uid).first_or_initialize do |user| + user.username = access_data.info.email + user.uid = access_data.uid + end + end +end diff --git a/app/views/welcome/index.html.erb b/app/views/welcome/index.html.erb new file mode 100644 index 0000000000..6cbb8c2623 --- /dev/null +++ b/app/views/welcome/index.html.erb @@ -0,0 +1,14 @@ +

Welcome to the Viewing Party!

+ +<% if current_user %> +

Welcome back, <%= current_user.username %>!

+<% else %> + <%= link_to 'Log in with Google', '/auth/google_oauth2' %> +<% end %> + +
+ +

This application is a means to explore movies and create viewing parties for you and friends.

+ +Utilizing <%= link_to 'the Movie DB API', 'https://www.themoviedb.org/' %> and the power of +<%= link_to 'Google Calendar', 'https://calendar.google.com' %>, you can create a viewing party with your friends! diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb new file mode 100644 index 0000000000..a3063cc4c5 --- /dev/null +++ b/config/initializers/omniauth.rb @@ -0,0 +1,3 @@ +Rails.application.config.middleware.use OmniAuth::Builder do + provider :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'], {scope: "userinfo.email, calendar"} +end \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 787824f888..e82a3d55a1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,7 @@ Rails.application.routes.draw do # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html + root to: 'welcome#index' + + get '/auth/:provider/callback', to: 'sessions#create' + get '/auth/failure', to: redirect('/') end diff --git a/db/migrate/20200820043729_create_users.rb b/db/migrate/20200820043729_create_users.rb new file mode 100644 index 0000000000..6cefb8b135 --- /dev/null +++ b/db/migrate/20200820043729_create_users.rb @@ -0,0 +1,14 @@ +class CreateUsers < ActiveRecord::Migration[5.2] + def change + create_table :users do |t| + t.string :uid + t.string :username + t.string :google_token + t.string :google_refresh_token + + t.timestamps + end + add_index :users, :uid + add_index :users, :username + end +end diff --git a/db/schema.rb b/db/schema.rb index 2611543b38..a40a3335bd 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,9 +10,20 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 0) do +ActiveRecord::Schema.define(version: 2020_08_20_043729) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + create_table "users", force: :cascade do |t| + t.string "uid" + t.string "username" + t.string "google_token" + t.string "google_refresh_token" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["uid"], name: "index_users_on_uid" + t.index ["username"], name: "index_users_on_username" + end + end diff --git a/spec/factories/user.rb b/spec/factories/user.rb new file mode 100644 index 0000000000..c7027b592a --- /dev/null +++ b/spec/factories/user.rb @@ -0,0 +1,10 @@ +require 'faker' + +FactoryBot.define do + factory :user do + username { "#{Faker::Games::WorldOfWarcraft.hero} #{Faker::Number.number(digits: 3)}" } + uid { Faker::Number.within(range: 100000..999999) } + google_token { Faker::Number.within(range: 100000..999999) } + google_refresh_token { Faker::Number.within(range: 100000..999999) } + end +end \ No newline at end of file diff --git a/spec/features/sessions_spec.rb b/spec/features/sessions_spec.rb new file mode 100644 index 0000000000..97f9ec416f --- /dev/null +++ b/spec/features/sessions_spec.rb @@ -0,0 +1,14 @@ +require 'rails_helper' + +RSpec.describe 'Sessions spec', type: :feature do + it 'logs the user in via google mock' do + stub_omniauth + user = create(:user, username: 'john@example.com') + + visit root_path + + click_link 'Log in with Google' + + expect(page).to have_content("Welcome back, #{user.username}") + end +end \ No newline at end of file diff --git a/spec/features/welcome_spec.rb b/spec/features/welcome_spec.rb new file mode 100644 index 0000000000..29ed50e37c --- /dev/null +++ b/spec/features/welcome_spec.rb @@ -0,0 +1,22 @@ +require 'rails_helper' + +RSpec.describe 'Welcome page', type: :feature do + it 'shows a welcome page' do + visit root_path + + expect(page).to have_content('Welcome to the Viewing Party!') + end + it 'shows a login-with-google button for non-session users' do + visit root_path + + expect(page).to have_link('Log in with Google') + end + it 'shows a welcome back message to session users' do + user = create(:user) + allow_any_instance_of(ApplicationController).to receive(:current_user).and_return(user) + + visit root_path + + expect(page).to have_content("Welcome back, #{user.username}") + end +end \ No newline at end of file diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb new file mode 100644 index 0000000000..32b700dafa --- /dev/null +++ b/spec/models/user_spec.rb @@ -0,0 +1,29 @@ +require 'rails_helper' + +RSpec.describe User, type: :model do + describe 'validations' do + it { should validate_presence_of :username } + it { should validate_presence_of :uid } + it { should validate_uniqueness_of :uid } + it { should validate_uniqueness_of :google_token } + end + + describe 'class methods' do + describe 'User.parse_omniauth' do + it 'finds a user in the database' do + # binding.pry + # user = create(:user, uid: '100000000000000000000', username: 'john@example.com') + # + # result = User.parse_omniauth() + # expect(result.id).to eq(user.id) + end + it 'creates a new user in the database' do + # user = create(:user, uid: '12345', username: 'something-new@gmail.com') + # + # result = User.parse_omniauth(@access_data) + # expect(result.id).to eq(user.id) + # expect(result.username).to eq(@access_data[:info][:email]) + end + end + end +end \ No newline at end of file diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 00345af7c0..b6b5d208d5 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -61,4 +61,75 @@ config.filter_rails_from_backtrace! # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") + # + config.include FactoryBot::Syntax::Methods +end + +require 'simplecov' +SimpleCov.start 'rails' do + add_filter '/bin/' + add_filter '/app/channels/' + add_filter '/app/jobs/' + add_filter '/app/mailers/' + add_filter '/db/' + add_filter '/spec/' # for rspec end + + +def stub_omniauth + OmniAuth.config.test_mode = true + omniauth_google_hash = { + provider: 'google_oauth2', + uid: '100000000000000000000', + info: { + name: 'John Smith', + email: 'john@example.com', + first_name: 'John', + last_name: 'Smith', + image: 'https://lh4.googleusercontent.com/photo.jpg', + urls: { + google: 'https://plus.google.com/+JohnSmith' + } + }, + credentials: { + token: 'TOKEN', + refresh_token: 'REFRESH_TOKEN', + expires_at: 1496120719, + expires: true + }, + extra: { + id_token: 'ID_TOKEN', + id_info: { + azp: 'APP_ID', + aud: 'APP_ID', + sub: '100000000000000000000', + email: 'john@example.com', + email_verified: true, + at_hash: 'HK6E_P6Dh8Y93mRNtsDB1Q', + iss: 'accounts.google.com', + iat: 1496117119, + exp: 1496120719 + }, + raw_info: { + sub: '100000000000000000000', + name: 'John Smith', + given_name: 'John', + family_name: 'Smith', + profile: 'https://plus.google.com/+JohnSmith', + picture: 'https://lh4.googleusercontent.com/photo.jpg?sz=50', + email: 'john@example.com', + email_verified: 'true', + locale: 'en', + hd: 'company.com' + } + } + } + OmniAuth.config.mock_auth[:google_oauth2] = OmniAuth::AuthHash.new(omniauth_google_hash) +end + +Shoulda::Matchers.configure do |config| + config.integrate do |with| + with.test_framework :rspec + with.library :rails + end +end \ No newline at end of file