diff --git a/.rubocop.yml b/.rubocop.yml index 4246c9f3e..b2fa0de3a 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -6,6 +6,8 @@ inherit_from: .rubocop_todo.yml Metrics/LineLength: Max: 150 +RSpec/ExampleLength: + Max: 10 Documentation: Enabled: false Style/FrozenStringLiteralComment: diff --git a/Gemfile b/Gemfile index 16683f6d3..db36b8bda 100644 --- a/Gemfile +++ b/Gemfile @@ -19,6 +19,11 @@ gem 'responders' gem 'time_diff' gem 'tzinfo-data' gem 'valid_email2' +# compress javascript config.assets.js_compressor +gem 'uglifier' + +##-- for active storage --## +gem 'mini_magick' ##-- gems for database --# gem 'pg' @@ -40,7 +45,7 @@ gem 'jwt' gem 'pundit' ##--- gems for pubsub service ---## -# TODO investigate and remove this gem +# https://karolgalanciak.com/blog/2019/11/30/from-activerecord-callbacks-to-publish-slash-subscribe-pattern-and-event-driven-design/ gem 'wisper', '2.0.0' ##--- gems for reporting ---## @@ -66,9 +71,7 @@ gem 'sentry-raven' ##-- TODO: move these gems to appropriate groups --## # remove this gem in favor of active storage - github #158 gem 'carrierwave-aws' -gem 'mini_magick' gem 'sidekiq' -gem 'uglifier' group :development do gem 'annotate' diff --git a/app/controllers/api/v1/agents_controller.rb b/app/controllers/api/v1/agents_controller.rb index d0c777c67..681422515 100644 --- a/app/controllers/api/v1/agents_controller.rb +++ b/app/controllers/api/v1/agents_controller.rb @@ -13,7 +13,7 @@ class Api::V1::AgentsController < Api::BaseController end def update - @agent.update_attributes!(agent_params) + @agent.update!(agent_params) render json: @agent end diff --git a/app/controllers/api/v1/callbacks_controller.rb b/app/controllers/api/v1/callbacks_controller.rb index bdf764528..b084bc1cd 100644 --- a/app/controllers/api/v1/callbacks_controller.rb +++ b/app/controllers/api/v1/callbacks_controller.rb @@ -32,7 +32,7 @@ class Api::V1::CallbacksController < ApplicationController fb_page = current_account.facebook_pages.find_by(page_id: fb_page_id) if fb_page - fb_page.update_attributes!( + fb_page.update!( user_access_token: @user_access_token, page_access_token: page_detail['access_token'] ) diff --git a/app/controllers/api/v1/canned_responses_controller.rb b/app/controllers/api/v1/canned_responses_controller.rb index 03b5c3e63..aa82ea3c4 100644 --- a/app/controllers/api/v1/canned_responses_controller.rb +++ b/app/controllers/api/v1/canned_responses_controller.rb @@ -12,7 +12,7 @@ class Api::V1::CannedResponsesController < Api::BaseController end def update - @canned_response.update_attributes!(canned_response_params) + @canned_response.update!(canned_response_params) render json: @canned_response end diff --git a/app/controllers/api/v1/contacts_controller.rb b/app/controllers/api/v1/contacts_controller.rb index 080b5f4f9..b5885d708 100644 --- a/app/controllers/api/v1/contacts_controller.rb +++ b/app/controllers/api/v1/contacts_controller.rb @@ -22,7 +22,7 @@ class Api::V1::ContactsController < Api::BaseController end def update - @contact.update_attributes!(contact_params) + @contact.update!(contact_params) end private diff --git a/app/controllers/api/v1/profiles_controller.rb b/app/controllers/api/v1/profiles_controller.rb new file mode 100644 index 000000000..72e432f74 --- /dev/null +++ b/app/controllers/api/v1/profiles_controller.rb @@ -0,0 +1,22 @@ +class Api::V1::ProfilesController < Api::BaseController + before_action :fetch_user + + def show + render json: @user + end + + def update + @user.update!(profile_params) + render json: @user + end + + private + + def fetch_user + @user = current_user + end + + def profile_params + params.require(:profile).permit(:email, :name, :password, :password_confirmation, :avatar) + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 9c96f0c8b..376be9567 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -10,7 +10,6 @@ # current_sign_in_ip :string # email :string # encrypted_password :string default(""), not null -# image :string # last_sign_in_at :datetime # last_sign_in_ip :string # name :string not null @@ -60,6 +59,9 @@ class User < ApplicationRecord # Used by the actionCable/PubSub Service we use for real time communications has_secure_token :pubsub_token + # Uses active storage for the avatar + has_one_attached :avatar + # The validation below has been commented out as it does not # work because :validatable in devise overrides this. # validates_uniqueness_of :email, scope: :account_id diff --git a/config/routes.rb b/config/routes.rb index ce9fc5128..8a55f2ac1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -29,6 +29,7 @@ Rails.application.routes.draw do resources :inboxes, only: [:create] end + resource :profile, only: [:show, :update] resources :accounts, only: [:create] resources :inboxes, only: [:index, :destroy] resources :agents, except: [:show, :edit, :new] diff --git a/db/migrate/20191209195420_create_active_storage_tables.active_storage.rb b/db/migrate/20191209195420_create_active_storage_tables.active_storage.rb new file mode 100644 index 000000000..0b2ce257c --- /dev/null +++ b/db/migrate/20191209195420_create_active_storage_tables.active_storage.rb @@ -0,0 +1,27 @@ +# This migration comes from active_storage (originally 20170806125915) +class CreateActiveStorageTables < ActiveRecord::Migration[5.2] + def change + create_table :active_storage_blobs do |t| + t.string :key, null: false + t.string :filename, null: false + t.string :content_type + t.text :metadata + t.bigint :byte_size, null: false + t.string :checksum, null: false + t.datetime :created_at, null: false + + t.index [ :key ], unique: true + end + + create_table :active_storage_attachments do |t| + t.string :name, null: false + t.references :record, null: false, polymorphic: true, index: false + t.references :blob, null: false + + t.datetime :created_at, null: false + + t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true + t.foreign_key :active_storage_blobs, column: :blob_id + end + end +end diff --git a/db/migrate/20191209202758_remove_image_from_user.rb b/db/migrate/20191209202758_remove_image_from_user.rb new file mode 100644 index 000000000..1ece3d317 --- /dev/null +++ b/db/migrate/20191209202758_remove_image_from_user.rb @@ -0,0 +1,5 @@ +class RemoveImageFromUser < ActiveRecord::Migration[6.0] + def change + remove_column :users, :image, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 6a08c27f5..a479e0de2 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_12_04_192301) do +ActiveRecord::Schema.define(version: 2019_12_09_202758) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -21,6 +21,27 @@ ActiveRecord::Schema.define(version: 2019_12_04_192301) do t.datetime "updated_at", null: false end + create_table "active_storage_attachments", force: :cascade do |t| + t.string "name", null: false + t.string "record_type", null: false + t.bigint "record_id", null: false + t.bigint "blob_id", null: false + t.datetime "created_at", null: false + t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id" + t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true + end + + create_table "active_storage_blobs", force: :cascade do |t| + t.string "key", null: false + t.string "filename", null: false + t.string "content_type" + t.text "metadata" + t.bigint "byte_size", null: false + t.string "checksum", null: false + t.datetime "created_at", null: false + t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true + end + create_table "attachments", id: :serial, force: :cascade do |t| t.string "file" t.integer "file_type", default: 0 @@ -207,7 +228,6 @@ ActiveRecord::Schema.define(version: 2019_12_04_192301) do t.string "unconfirmed_email" t.string "name", null: false t.string "nickname" - t.string "image" t.string "email" t.json "tokens" t.integer "account_id", null: false @@ -223,6 +243,7 @@ ActiveRecord::Schema.define(version: 2019_12_04_192301) do t.index ["uid", "provider"], name: "index_users_on_uid_and_provider", unique: true end + add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "contact_inboxes", "contacts" add_foreign_key "contact_inboxes", "inboxes" add_foreign_key "users", "users", column: "inviter_id", on_delete: :nullify diff --git a/spec/assets/avatar.png b/spec/assets/avatar.png new file mode 100644 index 000000000..269eab83a Binary files /dev/null and b/spec/assets/avatar.png differ diff --git a/spec/controllers/api/v1/profiles_controller_spec.rb b/spec/controllers/api/v1/profiles_controller_spec.rb new file mode 100644 index 000000000..23ac669b1 --- /dev/null +++ b/spec/controllers/api/v1/profiles_controller_spec.rb @@ -0,0 +1,80 @@ +require 'rails_helper' + +RSpec.describe 'Profile API', type: :request do + let(:account) { create(:account) } + + describe 'GET /api/v1/profile' do + context 'when unauthenticated user' do + it 'returns unauthorized' do + get '/api/v1/profile' + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it authenticated user' do + let(:agent) { create(:user, account: account, role: :agent) } + + it 'returns current user information' do + get '/api/v1/profile', + headers: agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + json_response = JSON.parse(response.body) + expect(json_response['id']).to eq(agent.id) + expect(json_response['email']).to eq(agent.email) + end + end + end + + describe 'PUT /api/v1/profile' do + context 'when unauthenticated user' do + it 'returns unauthorized' do + put '/api/v1/profile' + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it authenticated user' do + let(:agent) { create(:user, account: account, role: :agent) } + + it 'updates the name & email' do + put '/api/v1/profile', + params: { profile: { name: 'test', 'email': 'test@test.com' } }, + headers: agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + json_response = JSON.parse(response.body) + agent.reload + expect(json_response['id']).to eq(agent.id) + expect(json_response['email']).to eq(agent.email) + expect(agent.email).to eq('test@test.com') + end + + it 'updates the password' do + put '/api/v1/profile', + params: { profile: { password: 'test123', password_confirmation: 'test123' } }, + headers: agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + end + + it 'updates avatar' do + # no avatar before upload + expect(agent.avatar.attached?).to eq(false) + file = fixture_file_upload(Rails.root.join('spec/assets/avatar.png'), 'image/png') + put '/api/v1/profile', + params: { profile: { name: 'test', 'email': 'test@test.com', avatar: file } }, + headers: agent.create_new_auth_token + + expect(response).to have_http_status(:success) + agent.reload + expect(agent.avatar.attached?).to eq(true) + end + end + end +end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 8c63779ed..aa9b718d3 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -18,5 +18,9 @@ FactoryBot.define do after(:build) do |user, evaluator| user.skip_confirmation! if evaluator.skip_confirmation end + + trait :with_avatar do + avatar { Rack::Test::UploadedFile.new('spec/assets/avatar.png', 'image/png') } + end end end