From 1abaee04d89ec4ae6bcf113b37fdbb45b8bfe8e4 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Tue, 10 Dec 2019 10:29:35 +0530 Subject: [PATCH] Feature: Profile API [#238] (#354) - api to update name and email - api to change password - api to set profile pic - fixes update_attribute! deprecation warning - introducing active storage --- .rubocop.yml | 2 + Gemfile | 9 +- app/controllers/api/v1/agents_controller.rb | 2 +- .../api/v1/callbacks_controller.rb | 2 +- .../api/v1/canned_responses_controller.rb | 2 +- app/controllers/api/v1/contacts_controller.rb | 2 +- app/controllers/api/v1/profiles_controller.rb | 22 +++++ app/models/user.rb | 4 +- config/routes.rb | 1 + ...te_active_storage_tables.active_storage.rb | 27 ++++++ .../20191209202758_remove_image_from_user.rb | 5 ++ db/schema.rb | 25 +++++- spec/assets/avatar.png | Bin 0 -> 27270 bytes .../api/v1/profiles_controller_spec.rb | 80 ++++++++++++++++++ spec/factories/users.rb | 4 + 15 files changed, 177 insertions(+), 10 deletions(-) create mode 100644 app/controllers/api/v1/profiles_controller.rb create mode 100644 db/migrate/20191209195420_create_active_storage_tables.active_storage.rb create mode 100644 db/migrate/20191209202758_remove_image_from_user.rb create mode 100644 spec/assets/avatar.png create mode 100644 spec/controllers/api/v1/profiles_controller_spec.rb 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 0000000000000000000000000000000000000000..269eab83a29c545bc81a6263c283e4b184bb0cce GIT binary patch literal 27270 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 YH^a_D&8eX1kNp8x<b+XA1cEx+W-In literal 0 HcmV?d00001 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