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..d4ba193637 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
@@ -33,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 82d0397817..5531c54034 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
@@ -74,11 +75,20 @@ 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)
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 +99,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 +127,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)
@@ -212,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)
@@ -259,11 +290,16 @@ PLATFORMS
DEPENDENCIES
bootsnap
+ brakeman
capybara
coffee-rails (~> 4.2)
+ factory_bot_rails
+ faker
+ figaro
jbuilder (~> 2.5)
launchy
listen (>= 3.0.5, < 3.2)
+ omniauth-google-oauth2
pg (>= 0.18, < 2.0)
pry
puma (~> 3.7)
@@ -271,6 +307,7 @@ DEPENDENCIES
rspec-rails
rubocop-rails
sass-rails (~> 5.0)
+ shoulda-matchers
simplecov
travis
tzinfo-data
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/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
new file mode 100644
index 0000000000..a40a3335bd
--- /dev/null
+++ b/db/schema.rb
@@ -0,0 +1,29 @@
+# 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: 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